Merge branch 'bug-2366-ssh-debug-logging_master' into custom-sftp-private-key_patches

This commit is contained in:
Rick Brown
2023-07-19 17:12:53 -04:00
80 changed files with 6431 additions and 1508 deletions

53
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Bug Report
description: Report a bug.
title: "[BUG] "
labels: bug
body:
- type: markdown
attributes:
value: |
Please check out the [FAQ section](https://github.com/PhilippC/keepass2android/blob/master/docs/Documentation.md#faq) and [search for open issues](https://github.com/PhilippC/keepass2android/issues?q=is%3Aopen+is%3Aissue+label%3Abug) first.
- type: checkboxes
attributes:
label: Checks
options:
- label: I have read the FAQ section, searched the open issues, and still think this is a new bug.
required: true
- type: textarea
id: bug
attributes:
label: "Describe the bug you encountered:"
validations:
required: true
- type: textarea
id: expected
attributes:
label: "Describe what you expected to happen:"
- type: markdown
attributes:
value: |
Please follow these steps to find your app version:
1. Click the **⁝** icon in the top right corner
2. Select **Settings**
3. Click **About**
4. Find the "Version" information and provide it below
- type: input
id: version
attributes:
label: "What version of Keepass2Android are you using?"
validations:
required: true
- type: markdown
attributes:
value: |
Please follow these steps to find your Android version:
1. Open your device's **Settings** app
2. Scroll down and select **About phone** or **About tablet**
3. Find the **Android version** section and provide it below
- type: input
id: os
attributes:
label: "Which version of Android are you on?"
validations:
required: true

View File

@@ -0,0 +1,8 @@
---
name: Feature Request
about: Suggest an idea for this project.
title: '[FEAT] '
labels: enhancement
assignees: ''
---

16
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,16 @@
---
name: Question
about: Ask a question about 'Keepass2Android'.
title: '[QUESTION] '
labels: question
assignees: ''
---
**What version of Keepass2Android are you using?**
Please follow these steps to find your app version:
1. Click the **⁝** icon in the top right corner
2. Select **Settings**
3. Click **About**
4. Find the "Version" information and provide it here:

View File

@@ -3,109 +3,111 @@ name: Build keepass2android app
on: [push, pull_request]
jobs:
macos:
# macos:
# Disabled. Does not work, maybe due to nuget version, see https://github.com/PhilippC/keepass2android/actions/runs/4297640426/jobs/7490853348
# should work again when the Project solution is converted to sdk style .csproj files.
runs-on: macos-12
# runs-on: macos-12
steps:
- uses: actions/checkout@v3
# steps:
# - uses: actions/checkout@v3
- name: Fetch submodules
run: git submodule init && git submodule update
# - name: Fetch submodules
# run: git submodule init && git submodule update
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
# - name: Setup Gradle
# uses: gradle/gradle-build-action@v2
- name: Cache NuGet packages
uses: actions/cache@v3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('src/**/*.csproj', 'src/**/packages.config') }}
restore-keys: |
${{ runner.os }}-nuget-
# - name: Cache NuGet packages
# uses: actions/cache@v3
# with:
# path: ~/.nuget/packages
# key: ${{ runner.os }}-nuget-${{ hashFiles('src/**/*.csproj', 'src/**/packages.config') }}
# restore-keys: |
# ${{ runner.os }}-nuget-
# As per https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md#visual-studio-for-mac
- name: Switch to Visual Studio 2019
if: ${{ false }} # Not needed. We stay with the default 'Visual Studio 2022' of macos-12 runner.
run: |
mv "/Applications/Visual Studio.app" "/Applications/Visual Studio 2022.app"
mv "/Applications/Visual Studio 2019.app" "/Applications/Visual Studio.app"
# # As per https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md#visual-studio-for-mac
# - name: Switch to Visual Studio 2019
# if: ${{ false }} # Not needed. We stay with the default 'Visual Studio 2022' of macos-12 runner.
# run: |
# mv "/Applications/Visual Studio.app" "/Applications/Visual Studio 2022.app"
# mv "/Applications/Visual Studio 2019.app" "/Applications/Visual Studio.app"
# As of 2022-12-02, keepass2android doesn't build with Xamarin >= 12.1 because there is some issue with SamsungPass. Removing SamsungPass would make the build succeed.
- name: Set default Xamarin SDK versions
run: |
# If using the github runner 'macos-12'
#$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.3
#$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.0
#$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.1 # Build fails in this case, as of 2022-12-02 : Xamarin/Android/Xamarin.Android.D8.targets(79,5): error : java.lang.ArrayIndexOutOfBoundsException : Index 4 out of bounds for length 4
#$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.2 # Build fails in this case, as of 2022-12-02 : Xamarin/Android/Xamarin.Android.D8.targets(79,5): error : java.lang.ArrayIndexOutOfBoundsException : Index 4 out of bounds for length 4
#$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.3 # Build fails in this case, as of 2022-12-02
$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=13.1
# # As of 2022-12-02, keepass2android doesn't build with Xamarin >= 12.1 because there is some issue with SamsungPass. Removing SamsungPass would make the build succeed.
# - name: Set default Xamarin SDK versions
# run: |
# # If using the github runner 'macos-12'
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.3
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.0
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.1 # Build fails in this case, as of 2022-12-02 : Xamarin/Android/Xamarin.Android.D8.targets(79,5): error : java.lang.ArrayIndexOutOfBoundsException : Index 4 out of bounds for length 4
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.2 # Build fails in this case, as of 2022-12-02 : Xamarin/Android/Xamarin.Android.D8.targets(79,5): error : java.lang.ArrayIndexOutOfBoundsException : Index 4 out of bounds for length 4
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.3 # Build fails in this case, as of 2022-12-02
# $VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=13.1
# If using the github runner 'macos-11'
#$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.0
#$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.0
# # If using the github runner 'macos-11'
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.0
# #$VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=12.0
# If using the github runner 'macos-10.15'
# $VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.2
# # If using the github runner 'macos-10.15'
# # $VM_ASSETS/select-xamarin-sdk-v2.sh --mono=6.12 --android=11.2
- name: Switch to JDK-11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
# - name: Switch to JDK-11
# uses: actions/setup-java@v3
# with:
# java-version: '11'
# distribution: 'temurin'
- name: Display java version
run: java -version
# - name: Display java version
# run: java -version
# Some components of Keepass2Android currently target android API 26 which are not available on the runner
- name: Download android-26 API
run: $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --install "platforms;android-26"
# # Some components of Keepass2Android currently target android API 26 which are not available on the runner
# - name: Download android-26 API
# run: $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --install "platforms;android-26"
- name: Build native dependencies
run: make native
# - name: Build native dependencies
# run: make native
- name: Build java dependencies
run: make java
# - name: Build java dependencies
# run: make java
- name: Install NuGet dependencies (net)
run: make nuget Flavor=Net
# - name: Install NuGet dependencies (net)
# run: make nuget Flavor=Net
- name: Build keepass2android (net)
run: |
make msbuild Flavor=Net
# - name: Build keepass2android (net)
# run: |
# make msbuild Flavor=Net
- name: Build APK (net)
run: |
make apk Flavor=Net
# - name: Build APK (net)
# run: |
# make apk Flavor=Net
- name: Archive production artifacts (net)
uses: actions/upload-artifact@v3
with:
name: signed APK ('net' built on ${{ github.job }})
path: |
src/keepass2android/bin/*/*-Signed.apk
# - name: Archive production artifacts (net)
# uses: actions/upload-artifact@v3
# with:
# name: signed APK ('net' built on ${{ github.job }})
# path: |
# src/keepass2android/bin/*/*-Signed.apk
- name: Install NuGet dependencies (nonet)
run: make nuget Flavor=NoNet
# - name: Install NuGet dependencies (nonet)
# run: make nuget Flavor=NoNet
- name: Build keepass2android (nonet)
run: |
make msbuild Flavor=NoNet
# - name: Build keepass2android (nonet)
# run: |
# make msbuild Flavor=NoNet
- name: Build APK (nonet)
run: |
make apk Flavor=NoNet
# - name: Build APK (nonet)
# run: |
# make apk Flavor=NoNet
- name: Archive production artifacts (nonet)
uses: actions/upload-artifact@v3
with:
name: signed APK ('nonet' built on ${{ github.job }})
path: |
src/keepass2android/bin/*/*-Signed.apk
# - name: Archive production artifacts (nonet)
# uses: actions/upload-artifact@v3
# with:
# name: signed APK ('nonet' built on ${{ github.job }})
# path: |
# src/keepass2android/bin/*/*-Signed.apk
- name: Perform "make distclean"
run: make distclean
# - name: Perform "make distclean"
# run: make distclean
# linux:
# disabled.
@@ -330,6 +332,9 @@ jobs:
- name: Build keepass2android (nonet)
run: |
make msbuild Flavor=NoNet
- name: Test Autofill
working-directory: ./src/Kp2aAutofillParserTest
run: dotnet test
- name: Build APK (nonet)
run: |

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

@@ -18,6 +18,8 @@ Keepass2Android does not collect personal identifiable information. For debuggin
* **Internet** (Keepass2Android regular only): Required to allow the user to read/store password databases or key files on remote locations, e.g. Dropbox or via WebDav.
* **Contacts/Accounts** (Keepass2Android regular only): Required by the Google Drive SDK. If you want to access files on Google Drive, you are prompted to select one of the Google Accounts on your phone to use. The permission is required to query the list of Google accounts on the device. Keepass2Android does not access your personal contacts.
* **Storage**: Required to allow the user to read/store password databases or key files on the device locally.
* **Fingerprint**: Required if you want to use fingerprint unlock.
* **Fingerprint/Biometric**: Required if you want to use biometric unlock.
* **Vibrate**: Required by the built-in keyboard (vibrate on key press)
* **Camera**: Required for scanning OTP QR Codes
* **Foreground service**: Required to keep the app alive for QuickUnlock (so you don't need to enter your full master password repeatedly)

View File

@@ -25,6 +25,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCloudBindings", "PCloudBin
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "keepass2android-app", "keepass2android\keepass2android-app.csproj", "{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kp2aAutofillParser", "Kp2aAutofillParser\Kp2aAutofillParser.csproj", "{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kp2aAutofillParserTest", "Kp2aAutofillParserTest\Kp2aAutofillParserTest.csproj", "{3D1560FF-86BB-4CB4-8367-80BA13B81C38}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -283,6 +287,54 @@ Global
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{D4C32E0A-0193-4496-9DB4-02CC126FD9F3}.ReleaseNoNet|x64.Deploy.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Win32.ActiveCfg = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|Win32.Build.0 = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|x64.ActiveCfg = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Debug|x64.Build.0 = Debug|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Any CPU.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Win32.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|Win32.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|x64.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.Release|x64.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{39B12571-BAFE-4D3A-AEE2-4D74F14DFD96}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Win32.ActiveCfg = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|Win32.Build.0 = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|x64.ActiveCfg = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Debug|x64.Build.0 = Debug|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Any CPU.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Win32.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|Win32.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|x64.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.Release|x64.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|Win32.Build.0 = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU
{3D1560FF-86BB-4CB4-8367-80BA13B81C38}.ReleaseNoNet|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -58,12 +58,12 @@ namespace keepass2android
}
private static string LogFilename
public static string LogFilename
{
get { return Application.Context.FilesDir.CanonicalPath +"/keepass2android.log"; }
}
private static bool LogToFile
public static bool LogToFile
{
get
{

View File

@@ -0,0 +1,987 @@
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> HintMap { get; }
public string DatasetName { get; set; }
public FilledAutofillFieldCollection(Dictionary<string, FilledAutofillField> 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> BuildHintMap()
{
return new Dictionary<string, FilledAutofillField>(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 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.EMAIL,
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> ConvertToCanonicalLowerCaseHints(string[] supportedHints)
{
List<string> result = new List<string>();
foreach (string hint in supportedHints)
{
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++)
{
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
{
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(InputField inputField)
: this(inputField, inputField.AutofillHints)
{
}
public FilledAutofillField(InputField 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.ConvertToCanonicalLowerCaseHints(hintList.ToArray()).ToArray();
inputField.FillFilledAutofillValue(this);
}
public bool IsNull()
{
return TextValue == null && DateValue == null && ToggleValue == null;
}
public override bool Equals(object obj)
{
if (this == obj) return true;
if (obj == null || GetType() != obj.GetType()) return false;
FilledAutofillField that = (FilledAutofillField)obj;
if (!TextValue?.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 (or could be) an 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 abstract void FillFilledAutofillValue(FilledAutofillField filledField);
}
/// <summary>
/// Serializable structure defining the contents of the current view (from an autofill perspective)
/// </summary>
/// <typeparam name="TField"></typeparam>
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...");
if (LogAutofillView)
{
string debugInfo = JsonConvert.SerializeObject(autofillView, Newtonsoft.Json.Formatting.Indented);
_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).Select(AutofillHintsHelper.ToCanonicalHint).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.Select(AutofillHintsHelper.ToCanonicalHint).Intersect(_autofillHintsForLogin).Any())
{
AddFieldToHintMap(viewNode, viewHints.Select(AutofillHintsHelper.ToCanonicalHint).ToHashSet().ToArray());
}
}
}
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())
{
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.Where(IsEditText)
.TakeWhile(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)
AddFieldToHintMap(uf, new string[] { AutofillHintsHelper.AutofillHintUsername });
foreach (var pf in passwordFields.Except(usernameFields))
AddFieldToHintMap(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 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)
{
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", "user id" };
private static bool HasUsernameHint(InputField f)
{
return IsAny(f.IdEntry?.ToLower(), _usernameHints) ||
IsAny(f.Hint?.ToLower(), _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.FirstOrDefault() == "passwordAuto")
|| (f.HtmlInfoTypeAttribute == "password")
);
}
}
}

View File

@@ -0,0 +1,12 @@
<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>

View File

@@ -0,0 +1,145 @@
using Kp2aAutofillParser;
using Newtonsoft.Json;
using System.IO;
using System.Reflection;
using Xunit.Abstractions;
namespace Kp2aAutofillParserTest
{
public class AutofillTest
{
private readonly ITestOutputHelper _testOutputHelper;
public AutofillTest(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
class TestInputField: InputField
{
public string[] ExpectedAssignedHints { get; set; }
public override void FillFilledAutofillValue(FilledAutofillField filledField)
{
}
}
[Fact]
public void TestNotFocusedPasswordAutoIsNotFilled()
{
var resourceName = "Kp2aAutofillParserTest.com-servicenet-mobile-no-focus.json";
RunTestFromAutofillInput(resourceName, "com.servicenet.mobile");
}
[Fact]
public void TestCrashRegressionEmptySequence()
{
var resourceName = "Kp2aAutofillParserTest.imdb.json";
RunTestFromAutofillInput(resourceName, "com.vivaldi.browser", "m.imdb.com");
}
[Fact]
public void TestFocusedPasswordAutoIsFilled()
{
var resourceName = "Kp2aAutofillParserTest.com-servicenet-mobile-focused.json";
RunTestFromAutofillInput(resourceName, "com.servicenet.mobile");
}
[Fact]
public void TestMulitpleUnfocusedLoginsIsFilled()
{
var resourceName = "Kp2aAutofillParserTest.firefox-amazon-it.json";
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();
string input;
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
input = reader.ReadToEnd();
}
AutofillView<TestInputField>? autofillView =
JsonConvert.DeserializeObject<AutofillView<TestInputField>>(input);
StructureParserBase<TestInputField> parser =
new StructureParserBase<TestInputField>(new TestLogger(), new TestDalSourceTrustAll());
var result = parser.ParseForFill(false, autofillView);
if (expectedPackageName != null)
Assert.Equal(expectedPackageName, result.PackageName);
if (expectedWebDomain != null)
Assert.Equal(expectedWebDomain, result.WebDomain);
foreach (var field in autofillView.InputFields)
{
string[] expectedHints = field.ExpectedAssignedHints;
if (expectedHints == null)
expectedHints = new string[0];
string[] actualHints;
parser.FieldsMappedToHints.TryGetValue(field, out actualHints);
if (actualHints == null)
actualHints = new string[0];
if (actualHints.Any() || expectedHints.Any())
{
_testOutputHelper.WriteLine($"field = {field.IdEntry} {field.Hint} {string.Join(",", field.AutofillHints ?? new string[]{})}");
_testOutputHelper.WriteLine("actual Hints = " + string.Join(", ", actualHints));
_testOutputHelper.WriteLine("expected Hints = " + string.Join(", ", expectedHints));
}
Assert.Equal(expectedHints.Length, actualHints.Length);
Assert.Equal(expectedHints.OrderBy(x => x), actualHints.OrderBy(x => x));
}
}
}
public class TestDalSourceTrustAll : IKp2aDigitalAssetLinksDataSource
{
public bool IsTrustedApp(string packageName)
{
return true;
}
public bool IsTrustedLink(string domain, string targetPackage)
{
return true;
}
public bool IsEnabled()
{
return true;
}
}
public class TestLogger : ILogger
{
public void Log(string x)
{
Console.WriteLine(x);
}
}
}

View File

@@ -0,0 +1,62 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</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" />
<None Remove="imdb.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Kp2aAutofillParser\Kp2aAutofillParser.csproj" />
</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>
<EmbeddedResource Include="com-servicenet-mobile-focused.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="com-servicenet-mobile-no-focus.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="imdb.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1 @@
global using Xunit;

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

@@ -0,0 +1,121 @@
{
"InputFields": [
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": true,
"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": "content",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "username_text_input_layout",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "username",
"Hint": "Username",
"ClassName": "android.widget.EditText",
"AutofillHints": null,
"IsFocused": true,
"InputType": 97,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null,
"ExpectedAssignedHints": [ "username" ]
},
{
"IdEntry": "password_text_input_layout",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "password",
"Hint": "Password",
"ClassName": "android.widget.EditText",
"AutofillHints": [
"passwordAuto"
],
"IsFocused": false,
"InputType": 129,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null,
"ExpectedAssignedHints": [ "password" ]
},
{
"IdEntry": "login_button",
"Hint": null,
"ClassName": "android.widget.Button",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "progressBar",
"Hint": null,
"ClassName": "android.widget.ProgressBar",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "forgot_password",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
}
],
"PackageId": "com.servicenet.mobile",
"WebDomain": null
}

View File

@@ -0,0 +1,119 @@
{
"InputFields": [
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": true,
"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": "content",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "username_text_input_layout",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "username",
"Hint": "Username",
"ClassName": "android.widget.EditText",
"AutofillHints": null,
"IsFocused": false,
"InputType": 97,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "password_text_input_layout",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "password",
"Hint": "Password",
"ClassName": "android.widget.EditText",
"AutofillHints": [
"passwordAuto"
],
"IsFocused": false,
"InputType": 129,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null,
},
{
"IdEntry": "login_button",
"Hint": null,
"ClassName": "android.widget.Button",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "progressBar",
"Hint": null,
"ClassName": "android.widget.ProgressBar",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "forgot_password",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
}
],
"PackageId": "com.servicenet.mobile",
"WebDomain": null
}

View File

@@ -0,0 +1,469 @@
{
"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": "rootContainer",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "navigationToolbarStub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "gestureLayout",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "browserWindow",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "browserLayout",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "swipeRefresh",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "engineView",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": true,
"InputType": 0,
"HtmlInfoTag": "",
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": "form",
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": [
"password"
],
"IsFocused": false,
"InputType": 225,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "password",
"ExpectedAssignedHints": [ "password" ]
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": [
"emailAddress"
],
"IsFocused": false,
"InputType": 33,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "email",
"ExpectedAssignedHints": [ "emailAddress" ]
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "checkbox"
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "submit"
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": "form",
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": [
"password"
],
"IsFocused": false,
"InputType": 225,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "password",
"ExpectedAssignedHints": [ "password" ]
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": [
"emailAddress"
],
"IsFocused": false,
"InputType": 33,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "email",
"ExpectedAssignedHints": [ "emailAddress" ]
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "submit"
},
{
"IdEntry": "stubFindInPage",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "viewDynamicDownloadDialog",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "crash_reporter_view",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "toolbar",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_navigation_actions",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_origin_view",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_title_view",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_url_view",
"Hint": "Suche oder Adresse",
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_page_actions",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_browser_actions",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "counter_root",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "counter_text",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_menu",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_progress",
"Hint": null,
"ClassName": "android.widget.ProgressBar",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_container",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_edit_actions_start",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_edit_url_view",
"Hint": null,
"ClassName": "android.widget.EditText",
"AutofillHints": null,
"IsFocused": false,
"InputType": 17,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "mozac_browser_toolbar_edit_actions_end",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "readerViewControlsBar",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "addressSelectBar",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "creditCardSelectBar",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "loginSelectBar",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "tabPreview",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
}
],
"PackageId": "org.mozilla.firefox",
"WebDomain": "www.amazon.it"
}

View File

@@ -0,0 +1,728 @@
{
"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.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "custom_tabs_handle_view_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "coordinator",
"Hint": null,
"ClassName": "android.view.ViewGroup",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "compositor_view_holder",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": true,
"InputType": 0,
"HtmlInfoTag": "form",
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": "",
"ClassName": null,
"AutofillHints": [],
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "checkbox"
},
{
"IdEntry": null,
"Hint": "Search IMDb",
"ClassName": null,
"AutofillHints": [
"off"
],
"IsFocused": true,
"InputType": 0,
"HtmlInfoTag": "input",
"HtmlInfoTypeAttribute": "text"
},
{
"IdEntry": "main_tab_switcher",
"Hint": null,
"ClassName": "android.widget.RelativeLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "ar_view_holder",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "capture_overlay",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "overview_list_layout_holder",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "bottom_container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "keyboard_accessory_sheet_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "bottombar_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "tab_modal_dialog_container_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "tab_modal_dialog_container_sibling_view",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "omnibox_results_container_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "panel_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "search_engine_suggestion_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "action_bar_black_background",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "bottom_controls",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "bottom_controls_wrapper",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "tab_group_ui_bottom_container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "tab_group_ui_toolbar_view",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "bottom_container_slot",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "bottom_toolbar",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "bottom_toolbar_browsing",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "tab_switcher_tab_layout_toggle",
"Hint": null,
"ClassName": "android.widget.RelativeLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "tab_switcher_tab_layout",
"Hint": null,
"ClassName": "android.widget.HorizontalScrollView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": null,
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "control_container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "find_toolbar_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "find_toolbar_tablet_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "toolbar_container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "tab_group_ui_top_container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "toolbar",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "location_bar",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "location_bar_status_view_left_space",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "location_bar_status",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "location_bar_incognito_badge_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "location_bar_status_icon_view",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "location_bar_status_icon_frame",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "location_bar_status_icon_bg",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "location_bar_status_icon_holding_space",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "location_bar_verbose_status",
"Hint": null,
"ClassName": "android.widget.TextView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "location_bar_verbose_status_separator",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "location_bar_verbose_status_extra_space",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "location_bar_status_view_right_space",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "url_action_container",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "toolbar_buttons",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "optional_button_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "menu_button_wrapper",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "tab_switcher_toolbar_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "bottom_toolbar_tab_switcher_mode",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "grid_tab_switcher_view_holder_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "message_container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "status_indicator_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "empty_container_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "sheet_container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "survey_container",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "page_zoom_container",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "dialog_parent_view",
"Hint": null,
"ClassName": "android.widget.FrameLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "keyboard_accessory",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "accessory_bar_contents",
"Hint": null,
"ClassName": "android.widget.LinearLayout",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "tabs",
"Hint": null,
"ClassName": "android.widget.HorizontalScrollView",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "menu_anchor_stub",
"Hint": null,
"ClassName": "android.view.View",
"AutofillHints": null,
"IsFocused": false,
"InputType": 0,
"HtmlInfoTag": null,
"HtmlInfoTypeAttribute": null
},
{
"IdEntry": "navigation_popup_anchor_stub",
"Hint": null,
"ClassName": "android.view.View",
"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
}
],
"PackageId": "com.vivaldi.browser",
"WebDomain": "m.imdb.com"
}

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<Flavor>NoNet</Flavor>
<xFlavor>Net</xFlavor>
</PropertyGroup>

View File

@@ -0,0 +1,93 @@
package keepass2android.javafilestorage;
import android.util.Log;
import com.jcraft.jsch.Logger;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.Map;
public class Kp2aJSchLogger implements Logger {
private static final String PREFIX = "KP2AJFS[JSch]";
private interface ILogger {
void log(String message);
}
private static final class LogEntry {
private final String levelTag;
private final ILogger logger;
LogEntry(String levelTag, ILogger logger) {
this.levelTag = levelTag;
this.logger = logger;
}
}
private static final ILogger DEBUG = msg -> Log.d(PREFIX, msg);
private static final LogEntry DEBUG_ENTRY = new LogEntry("D", DEBUG);
private static final ILogger ERROR = msg -> Log.e(PREFIX, msg);
private static final LogEntry DEFAULT_ENTRY = DEBUG_ENTRY;
private static final Map<Integer, LogEntry> loggers = Map.of(
Logger.DEBUG, DEBUG_ENTRY,
Logger.INFO, new LogEntry("I", msg -> Log.i(PREFIX, msg)),
Logger.WARN, new LogEntry("W", msg -> Log.w(PREFIX, msg)),
Logger.ERROR, new LogEntry("E", ERROR),
Logger.FATAL, new LogEntry("F", msg -> Log.wtf(PREFIX, msg))
);
private final String logFilename;
public Kp2aJSchLogger(String logFilename) {
this.logFilename = logFilename;
}
@Override
public boolean isEnabled(int level) {
return true;
}
@Override
public void log(int level, String message) {
if (isEnabled(level))
getLogger(level).log(message);
}
private ILogger getLogger(int level) {
LogEntry entry = loggers.get(level);
if (entry == null)
entry = DEFAULT_ENTRY;
ILogger logger;
if (logFilename != null) {
logger = createFileLogger(entry);
} else {
logger = entry.logger;
}
return logger;
}
private ILogger createFileLogger(LogEntry entry) {
try {
final PrintWriter p = new PrintWriter(new FileWriter(logFilename, true));
return msg -> {
try {
String fullMsg = String.join(" ", entry.levelTag, PREFIX, msg);
p.println(fullMsg);
} catch (Exception e) {
ERROR.log(e.getMessage());
} finally {
p.close();
}
};
} catch (Exception e) {
ERROR.log(e.getMessage());
return entry.logger;
}
}
}

View File

@@ -27,6 +27,7 @@ import com.jcraft.jsch.UserInfo;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@SuppressWarnings("unused") // Exposed by JavaFileStorageBindings
public class SftpStorage extends JavaFileStorageBase {
@@ -423,6 +424,8 @@ public class SftpStorage extends JavaFileStorageBase {
ChannelSftp init(ConnectionInfo cInfo) throws JSchException, UnsupportedEncodingException {
jsch = new JSch();
Log.d("KP2AJFS", "init SFTP");
String base_dir = getBaseDir();
jsch.setKnownHosts(base_dir + "/known_hosts");
@@ -434,7 +437,9 @@ public class SftpStorage extends JavaFileStorageBase {
}
Log.e("KP2AJFS[thread]", "getting session...");
Session session = jsch.getSession(cInfo.username, cInfo.host, cInfo.port);
Log.e("KP2AJFS", "creating SftpUserInfo");
UserInfo ui = new SftpUserInfo(cInfo.password, cInfo.keyPassphrase, _appContext);
session.setUserInfo(ui);
@@ -475,12 +480,20 @@ public class SftpStorage extends JavaFileStorageBase {
return _keyUtils.createKeyPair(jsch);
}
@SuppressWarnings("unused") // Exposed by JavaFileStorageBindings
public void savePrivateKeyContent(String keyName, String keyContent) throws IOException, Exception {
_keyUtils.savePrivateKeyContent(keyName, keyContent);
}
@SuppressWarnings("unused") // Exposed by JavaFileStorageBindings
public void setJschLogging(boolean enabled, String logFilename) {
if (enabled) {
JSch.setLogger(new Kp2aJSchLogger(logFilename));
} else {
JSch.setLogger(null);
}
}
/**
* Exposed for testing purposes only.
* @param keyName

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 @@
org.gradle.jvmargs=-Xmx1536m

View File

@@ -19,4 +19,4 @@
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx2048m
org.gradle.jvmargs=-Xmx1536m

View File

@@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx1536m

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

@@ -21,7 +21,7 @@
<!-- Title for Latin keyboard -->
<string name="english_ime_name">Πληκτρολόγιο Keepass2Android</string>
<!-- Title for Latin keyboard settings activity / dialog -->
<string name="english_ime_settings">Ρυθμίσεις πληκτρολογίου Keepass2Android</string>
<string name="english_ime_settings">Ρυθμίσεις πληκτρολογίου Android</string>
<!-- Title for Latin keyboard input options dialog -->
<string name="english_ime_input_options">Επιλογές εισόδου</string>
<!-- Option to provide vibrate/haptic feedback on keypress -->
@@ -51,7 +51,7 @@
<!-- Dialog title for auto complete choices -->
<string name="auto_complete_dialog_title">Αυτόματη συμπλήρωση</string>
<!-- Option to enable text prediction in landscape -->
<string name="prediction_landscape">Αυξήστε το μέγεθος του πεδίου κειμένου</string>
<string name="prediction_landscape">Αύξηση μεγέθους του πεδίου κειμένου</string>
<!-- Description for text prediction -->
<string name="prediction_landscape_summary">Απόκρυψη υποδείξεων λέξεων στην οριζόντια προβολή</string>
<!-- Option to enable auto capitalization of sentences -->
@@ -77,7 +77,7 @@
<string name="prefs_settings_key">Εμφάνιση πλήκτρου ρυθμίσεων</string>
<!-- Array of the settings key mode values -->
<!-- Option to automatically decide to show/hide the settings key -->
<string name="settings_key_mode_auto_name">Αυτόματο</string>
<string name="settings_key_mode_auto_name">Αυτόματα</string>
<!-- Option to always show the settings key -->
<string name="settings_key_mode_always_show_name">Να εμφανίζεται πάντα</string>
<!-- Option to always hide the settings key -->
@@ -110,25 +110,25 @@
<!-- Tip to press ?123 to access numbers and symbols -->
<string name="tip_access_symbols">Πρόσβαση σε αριθμούς και σύμβολα</string>
<!-- Tip to long press on typed word to add to dictionary -->
<string name="tip_add_to_dictionary">Κρατήστε πατημένη τη λέξη στην άκρη αριστερά, για να την προσθέσετε στο λεξικό</string>
<string name="tip_add_to_dictionary">Παρατεταμένη επιλογή της λέξης στα αριστερά, την προσθέτει στο λεξικό</string>
<!-- Instruction to touch the bubble to continue -->
<string name="touch_to_continue">Αγγίξτε αυτή τη συμβουλή για να συνεχίσετε »</string>
<string name="touch_to_continue">Αγγίξτε αυτή την υπόδειξη για να συνεχίσετε »</string>
<!-- Instruction to touch the bubble to start typing -->
<string name="touch_to_finish">Αγγίξτε εδώ για να κλείσετε τη συμβουλή και να ξεκινήσετε την πληκτρολόγηση!</string>
<string name="touch_to_finish">Αγγίξτε εδώ για να κλείσετε την υπόδειξη και να ξεκινήσετε την πληκτρολόγηση!</string>
<!-- Tutorial tip 1 - The keyboard opens any time you touch a text field -->
<string name="tip_to_open_keyboard"><b>\"Το πληκτρολόγιο ανοίγει κάθε φορά που αγγίζετε ένα πεδίο κειμένου\"</b></string>
<string name="tip_to_open_keyboard"><b>Το πληκτρολόγιο ανοίγει κάθε φορά που αγγίζετε ένα πεδίο κειμένου</b></string>
<!-- Tutorial tip 2 - Touch and hold a key to view accents (examples) -->
<string name="tip_to_view_accents"><b>\"Αγγίξτε και κρατήστε κάποιο πλήκτρο για να προβάλετε τους τονισμένους χαρακτήρες\"\n\"(ø, ö, ô, ó κ.τ.λ.)\"</b></string>
<!-- Tutorial tip 3 - How to switch to number/symbol keyboard -->
<string name="tip_to_open_symbols"><b>\"Αλλαγή σε αριθμούς και σύμβολα με το πάτημα αυτού του πλήκτρου\"</b></string>
<string name="tip_to_open_symbols"><b>Αλλαγή σε αριθμούς και σύμβολα με το πάτημα αυτού του πλήκτρου</b></string>
<!-- Tutorial tip 4 - How to switch back to alphabet keyboard -->
<string name="tip_to_close_symbols"><b>\"Επιστρέψτε στα γράμματα αγγίζοντας ξανά αυτό το πλήκτρο\"</b></string>
<string name="tip_to_close_symbols"><b>Επιστρέψτε στα γράμματα αγγίζοντας ξανά αυτό το πλήκτρο</b></string>
<!-- Tutorial tip 5 - How to launch keyboard settings -->
<string name="tip_to_launch_settings"><b>\"Αγγίξτε και κρατήστε πατημένο αυτό το πληκτρολόγιο για να αλλάξετε τις ρυθμίσεις πληκτρολογίου, όπως η αυτόματη συμπλήρωση\"</b></string>
<string name="tip_to_launch_settings"><b>Αγγίξτε και κρατήστε πατημένο αυτό το πλήκτρο για να αλλάξετε τις ρυθμίσεις πληκτρολογίου, όπως η αυτόματη συμπλήρωση</b></string>
<!-- Tutorial tip 6 - Done with the tutorial -->
<string name="tip_to_start_typing"><b>\"Δοκιμάστε το!\"</b></string>
<string name="tip_to_start_typing"><b>Δοκιμάστε το!</b></string>
<!-- Label for soft enter key when it performs GO action. Must be short to fit on key! -->
<string name="label_go_key">Μετ.</string>
<string name="label_go_key">Μετάβαση</string>
<!-- Label for soft enter key when it performs NEXT action. Must be short to fit on key! -->
<string name="label_next_key">Επόμενο</string>
<!-- Label for soft enter key when it performs DONE action. Must be short to fit on key! -->

View File

@@ -21,7 +21,7 @@
<!-- Title for Latin keyboard -->
<string name="english_ime_name">== Keepass2Android 键盘 ==</string>
<!-- Title for Latin keyboard settings activity / dialog -->
<string name="english_ime_settings">USB 键盘设置</string>
<string name="english_ime_settings">键盘设置</string>
<!-- Title for Latin keyboard input options dialog -->
<string name="english_ime_input_options">输入选项</string>
<!-- Option to provide vibrate/haptic feedback on keypress -->
@@ -29,23 +29,23 @@
<!-- Option to play back sound on keypress in soft keyboard -->
<string name="sound_on_keypress">按键声音</string>
<!-- Option to pop up the character with a larger font above soft keyboard -->
<string name="popup_on_keypress">按键弹出放大</string>
<string name="popup_on_keypress">按键弹出显示</string>
<!-- Option to enable using nearby keys when correcting/predicting -->
<string name="hit_correction">智能纠错</string>
<!-- Description for hit_correction -->
<string name="hit_correction_summary">启用输入错误校正</string>
<!-- Option to enable using nearby keys when correcting/predicting in landscape-->
<string name="hit_correction_land">横屏输入错</string>
<string name="hit_correction_land">横屏输入</string>
<!-- Description for hit_correction in landscape -->
<string name="hit_correction_land_summary">启用输入错误校正</string>
<!-- Option to automatically correct word on hitting space -->
<string name="auto_correction">单词联想</string>
<string name="auto_correction">输入建议</string>
<!-- Description for auto_correction -->
<string name="auto_correction_summary">自动更正前一个单词</string>
<!-- Option to enable text prediction -->
<string name="prediction">单词联想</string>
<string name="prediction">输入建议</string>
<!-- Category title for text prediction -->
<string name="prediction_category"> 词语建议设置</string>
<string name="prediction_category">输入建议设置</string>
<!-- Description for text prediction -->
<string name="prediction_summary">在输入时启用自动补全</string>
<!-- Dialog title for auto complete choices -->
@@ -91,7 +91,7 @@
<!-- Option to enable bigram completion -->
<string name="bigram_suggestion">二元语法分词建议</string>
<!-- Description for auto completion -->
<string name="bigram_suggestion_summary">使用曾经使用过的词语改进建议</string>
<string name="bigram_suggestion_summary">使用前一个单词来改进建议</string>
<!-- Array of prediction modes -->
<string-array name="prediction_modes">
<item></item>

View File

@@ -1 +1 @@
org.gradle.jvmargs=-Xmx1024m
org.gradle.jvmargs=-Xmx1536m

View File

@@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx1536m

View File

@@ -1 +1 @@
org.gradle.jvmargs=-Xmx1024m
org.gradle.jvmargs=-Xmx1536m

View File

@@ -529,7 +529,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,2 +1,2 @@
org.gradle.jvmargs=-Xmx1024m
org.gradle.jvmargs=-Xmx1536m
android.useAndroidX=true

View File

@@ -43,7 +43,7 @@
</queries>
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<permission android:description="@string/permission_desc2" android:icon="@drawable/ic_notify_locked" android:label="KP2A entry search" android:name="keepass2android.keepass2android_debug.permission.KP2aInternalSearch" android:protectionLevel="signature" />
<permission android:description="@string/permission_desc3" android:icon="@drawable/ic_launcher" android:label="KP2A choose autofill dataset" android:name="keepass2android.keepass2android_debug.permission.Kp2aChooseAutofill" android:protectionLevel="signature" />
<application
@@ -55,7 +55,7 @@
<uses-library
android:name="org.apache.http.legacy"
android:required="false"/>
<activity android:name="microsoft.identity.client.BrowserTabActivity">
<activity android:name="microsoft.identity.client.BrowserTabActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
@@ -63,7 +63,7 @@
<data android:scheme="msal8374f801-0f55-407d-80cc-9a04fe86d9b2" android:host="auth" />
</intent-filter>
</activity>
<activity android:name="com.dropbox.core.android.AuthActivity" android:launchMode="singleTask" android:configChanges="orientation|keyboard">
<activity android:name="com.dropbox.core.android.AuthActivity" android:launchMode="singleTask" android:configChanges="orientation|keyboard" android:exported="true">
<intent-filter>
<data android:scheme="db-2gormiq7iq1jls1" />
<action android:name="android.intent.action.VIEW" />
@@ -83,13 +83,13 @@
<provider android:name="group.pals.android.lib.ui.filechooser.providers.history.HistoryProvider" android:authorities="keepass2android.keepass2android_debug.android-filechooser.history" android:exported="false" />
<activity android:name="group.pals.android.lib.ui.filechooser.FileChooserActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:screenOrientation="user" android:theme="@style/Afc.Theme.Light">
</activity>
<service android:name="keepass2android.softkeyboard.KP2AKeyboard" android:permission="android.permission.BIND_INPUT_METHOD">
<service android:name="keepass2android.softkeyboard.KP2AKeyboard" android:permission="android.permission.BIND_INPUT_METHOD" android:exported="true">
<intent-filter>
<action android:name="android.view.InputMethod" />
</intent-filter>
<meta-data android:name="android.view.im" android:resource="@xml/method" />
</service>
<activity android:name="keepass2android.softkeyboard.LatinIMESettings" android:label="@string/english_ime_settings">
<activity android:name="keepass2android.softkeyboard.LatinIMESettings" android:label="@string/english_ime_settings" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
@@ -99,7 +99,8 @@
</intent-filter>
</activity>
<activity android:name="keepass2android.softkeyboard.InputLanguageSelection"
android:label="@string/language_selection_title">
android:label="@string/language_selection_title"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="keepass2android.softkeyboard.INPUT_LANGUAGE_SELECTION"/>
@@ -107,7 +108,7 @@
</intent-filter>
</activity>
<activity android:configChanges="orientation|keyboard|keyboardHidden" android:label="@string/app_name" android:theme="@style/MyTheme_Blue" android:name="keepass2android.SelectCurrentDbActivity" android:windowSoftInputMode="adjustResize">
<activity android:configChanges="orientation|keyboard|keyboardHidden" android:label="@string/app_name" android:theme="@style/MyTheme_Blue" android:name="keepass2android.SelectCurrentDbActivity" android:windowSoftInputMode="adjustResize" android:exported="true">
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
@@ -252,13 +253,13 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" android:maxSdkVersion="22" />
<uses-permission android:name="keepass2android.keepass2android_debug.permission.KP2aInternalFileBrowsing" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<!-- Samsung Pass permission -->
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
<!-- READ_PHONE_STATE seems to come from some library or so, not clear where. We don't want to have it, remove it: -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
</manifest>

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="189"
android:versionName="1.09e-r1"
android:versionCode="197"
android:versionName="1.09e-r7"
package="keepass2android.keepass2android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto">
@@ -103,8 +103,8 @@
</intent-filter>
</activity>
<activity android:name="keepass2android.softkeyboard.InputLanguageSelection"
android:label="@string/language_selection_title"
android:exported="true">
android:label="@string/language_selection_title"
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="keepass2android.softkeyboard.INPUT_LANGUAGE_SELECTION"/>
@@ -272,5 +272,5 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<!-- Samsung Pass permission -->
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
</manifest>

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="189"
android:versionName="1.09e-r1"
android:versionCode="197"
android:versionName="1.09e-r7"
package="keepass2android.keepass2android_nonet"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto">
@@ -246,5 +246,6 @@ The scheme=file is still there for old OS devices. It's also queried by apps lik
<uses-permission android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalFileBrowsing" />
<uses-permission android:name="keepass2android.keepass2android_nonet.permission.KP2aInternalSearch" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
</manifest>

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"/>
<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"/>
<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"/>
<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"/>
<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_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 biometrisk oplåsning for den nye hovedadgangskode.</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="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>
@@ -330,7 +332,7 @@
<string name="QuickUnlockHideLength_title">Απόκρυψη μήκους QuickUnlock</string>
<string name="QuickUnlockHideLength_summary">Αν ενεργοποιηθεί, αποκρύπτει το μήκος του κωδικού QuickUnlock στη σχετική οθόνη.</string>
<string name="QuickUnlockKeyFromDatabase_title">Κλειδί QuickUnlock από τη βάση δεδομένων</string>
<string name="QuickUnlockKeyFromDatabase_summary">Εάν η ενεργή βάση δεδομένων περιέχει μια καταχώρηση με τίτλο QuickUnlock στην ομάδα ρίζας της, ο κωδικός πρόσβασης αυτής της καταχώρησης χρησιμοποιείται ως κωδικός QuickUnlock.</string>
<string name="QuickUnlockKeyFromDatabase_summary">Εάν η ενεργή βάση δεδομένων περιέχει μια καταχώριση με τίτλο QuickUnlock στην ομάδα ρίζας της, ο κωδικός πρόσβασης αυτής της καταχώρισης χρησιμοποιείται ως κωδικός QuickUnlock.</string>
<string name="QuickUnlock_fail">Αποτυχία QuickUnlock: λανθασμένο συνθηματικό!</string>
<string name="SaveAttachmentDialog_title">Αποθήκευση συνημμένου</string>
<string name="SaveAttachmentDialog_text">Επιλέξτε πού θα αποθηκεύσετε το συνημμένο.</string>
@@ -427,7 +429,7 @@
<string name="AskOverwriteBinary_no">Μετονομασία</string>
<string name="AttachFailed">Απέτυχε η προσθήκη του συνημμένου αρχείου.</string>
<string name="RecycleBin">Κάδος ανακύκλωσης</string>
<string name="AskDeletePermanentlyEntry">Θέλετε να διαγράψετε οριστικά αυτή την καταχώριση; Πατήστε όχι για να ανακύκλωση.</string>
<string name="AskDeletePermanentlyEntry">Θέλετε να διαγράψετε οριστικά αυτή την καταχώριση; Πατήστε όχι για ανακύκλωση.</string>
<string name="AskDeletePermanentlyGroup">Θέλετε να διαγράψετε οριστικά αυτή την ομάδα; Πατήστε όχι για ανακύκλωση.</string>
<string name="AskDeletePermanentlyItems">Θέλετε να διαγράψετε οριστικά τα επιλεγμένα στοιχεία; Πατήστε όχι για ανακύκλωση.</string>
<string name="AskDeletePermanentlyEntryNoRecycle">Θέλετε να διαγράψετε οριστικά αυτή την καταχώριση;</string>
@@ -540,7 +542,7 @@
<string name="help_master_password">Η βάση δεδομένων είναι κρυπτογραφημένη με το συνθηματικό που εισάγετε εδώ. Επιλέξτε ένα ισχυρό συνθηματικό προκειμένου να διατηρείτε τη βάση δεδομένων ασφαλή! Συμβουλή: Συνθέστε μια-δυο φράσεις και χρησιμοποιήστε τα πρώτα γράμματα των λέξεων ως συνθηματικό. Συμπεριλάβετε σημεία στίξης.</string>
<string name="hint_master_password">Επιλέξτε κύριο συνθηματικό για την προστασία της βάσης δεδομένων:</string>
<string name="key_file">Αρχείο κλειδιού</string>
<string name="help_key_file">Το αρχείο κλειδιού είναι βασικά ένα συνθηματικό αποθηκευμένο σε ένα αρχείο. Τα αρχείο κλειδιού είναισυνήθως ισχυρότερα από τα κύρια συνθηματικά, γιατί το κλειδί μπορεί να είναι πιο σύνθετο, ωστόσο είναι ε΄πίσης δυσκολότερο να διατηρηθούν μυστικά. Αν αποθηκεύεις τη βάση δεδομένων στο σύννεφο, μην αποθηκεύσεις και το αρχείο κλειδί εκεί! Αυτό θα το έκανε εντελώς άχρηστο! Σημαντικό: Μην αλλάζετε τα περιεχόμενο του αρχείου κλειδιού αφού δημιουργηθεί η βάση δεδομένων!</string>
<string name="help_key_file">Το αρχείο κλειδιού είναι βασικά ένα συνθηματικό αποθηκευμένο σε ένα αρχείο. Τα αρχεία κλειδιού είναι συνήθως ισχυρότερα από τα κύρια συνθηματικά, γιατί το κλειδί μπορεί να είναι πιο σύνθετο, ωστόσο είναι επίσης δυσκολότερο να διατηρηθούν μυστικά. Αν αποθηκεύεις τη βάση δεδομένων στο σύννεφο, μην αποθηκεύσεις και το αρχείο κλειδιού εκεί! Αυτό θα το έκανε εντελώς άχρηστο! Σημαντικό: Μην αλλάζετε τα περιεχόμενο του αρχείου κλειδιού αφού δημιουργηθεί η βάση δεδομένων!</string>
<string name="hint_key_file">Επιλέξτε αν θέλετε να χρησιμοποιήσετε ένα αρχείο κλειδιού, εκτός από το κύριο συνθηματικό:</string>
<string name="use_key_file">Χρήση αρχείου κλειδιού</string>
<string name="error_adding_keyfile">Παρουσιάστηκε σφάλμα κατά την προσθήκη του αρχείου κλειδιού!</string>
@@ -570,9 +572,9 @@
<string name="TrayTotp_SettingsField_title">Όνομα πεδίου στις ρυθμίσεις TOTP</string>
<string name="TrayTotp_SettingsField_summary">Εισάγετε όνομα πεδίου από ρυθμίσεις TrayTotp.</string>
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">Αρχείο καταχώρησης για αποσφαλματοποίηση</string>
<string name="DebugLog_title">Χρήση αρχείου καταχώρησης</string>
<string name="DebugLog_summary">Καταγραφή στοιχείων εφαρμογής σε τοπικό αρχείο καταχώρησης</string>
<string name="DebugLog_prefs_prefs">Αρχείο καταγραφών για αποσφαλμάτωση</string>
<string name="DebugLog_title">Χρήση αρχείου καταγραφών</string>
<string name="DebugLog_summary">Καταγραφή στοιχείων της εφαρμογής σε τοπικό αρχείο καταγραφών</string>
<string name="DebugLog_send">Αποστολή αρχείων καταγραφής εκσφαλμάτωσης...</string>
<string name="loading">Φόρτωση…</string>
<string name="plugins">Πρόσθετα</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</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>Υποστηρίζεται πλέον προβολή, διαγραφή και ανάκτηση των διαγραμμένων εγγραφών</item>
<item>Υλοποιήθηκε υποστήριξη για αποθήκευση στο νέφος MEGA </item>

View File

@@ -172,7 +172,9 @@
<string name="MaskedPassword">*****</string>
<string name="maskpass_title">Piilota salasana</string>
<string name="maskpass_summary">Piilota salasanat oletuksena</string>
<string name="masktotp_title">Peitä TOTP-kenttä</string>
<string name="masktotp_summary">Piilota TOTP-kenttä oletuksena</string>
<string name="NoAutofillDisabling_summary">Jos aktivoitu, sovellus ei näytä vaihtoehtoa poistaa automaattista täyttöä tiettyjen merkintöjen osalta.</string>
<string name="menu_about">Tietoja</string>
<string name="menu_change_key">Vaihda pääavain</string>
<string name="menu_copy_pass">Kopioi salasana</string>
@@ -285,6 +287,8 @@
<string name="please_note">Huomaa</string>
<string name="contributors">Kehittäjät ja avustajat</string>
<string name="regular_expression">Säännöllinen lauseke</string>
<string name="AlwaysMergeOnConflict_title">Yhdistä aina ristiriidan yhteydessä</string>
<string name="AlwaysMergeOnConflict_summary">Kun Keepass2Android havaitsee, että etätiedostoa on muokattu, yhdistetään paikalliset muutokset aina etämuutoksiin.</string>
<string name="TanExpiresOnUse_title">TAN käytön lopetus</string>
<string name="TanExpiresOnUse_summary">Merkitse TAN-merkinnät vanhentuneiksi käytön jälkeen</string>
<string name="ShowUsernameInList_title">Näytä käyttäjänimi</string>
@@ -323,6 +327,7 @@
<string name="QuickUnlockLength_summary">Pika-avauksessa käytettävien merkkien enimmäismäärä.</string>
<string name="QuickUnlockHideLength_title">Piilota pika-avauskoodin pituus</string>
<string name="QuickUnlockHideLength_summary">Jos käytössä, pika-avauskoodin pituutta ei näytetä pika-avausnäytössä.</string>
<string name="QuickUnlockKeyFromDatabase_title">QuickUnlock-avain tietokannan syötteestä</string>
<string name="QuickUnlock_fail">Pika-avaus epäonnistui: väärä salasana!</string>
<string name="SaveAttachmentDialog_title">Tallenna liite</string>
<string name="SaveAttachmentDialog_text">Valitse liitteen tallennuspaikka.</string>
@@ -946,7 +951,7 @@ Suojelee sinua Leikepöytään perustuvalta salasana sniffaukselta (Poista vanha
<item>Salasana + OTP salaus (Palautustila)</item>
<item>Salasana + Haastemenetelmä</item>
<item>Salasana + Haastemenetelmä (palautustila)</item>
<item>Salasana + Avaintiedosto + Haastemenetelmä Keepass XC:n kanssa</item>
<item>Salasana + Haastemenetelmä Keepass XC:n kanssa</item>
<item>Salasana + Avaintiedosto + Haastemenetelmä Keepass XC:n kanssa</item>
</string-array>
<string-array name="sftp_auth_modes">

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

@@ -301,6 +301,8 @@
<string name="NoDalVerification_summary">Disabilita il controllo se i pacchetti di dominio e applicazione corrispondono</string>
<string name="InlineSuggestions_title">Integra con la tastiera</string>
<string name="InlineSuggestions_summary">Mostra i suggerimenti di completamento automatico come opzioni in linea sulla tastiera (se supportato dal metodo di inserimento)</string>
<string name="LogAutofillView_title">Visualizza log autocompilazione</string>
<string name="LogAutofillView_summary">Scrivi i dettagli sulla visualizzazione del riempimento automatico per il log di debug (se il log di debug è abilitato). Questi dettagli possono essere inviati allo sviluppatore se il riempimento automatico non funziona come previsto.</string>
<string name="requires_android11">Richiede Android 11 o versioni successive</string>
<string name="kp2a_findUrl">Trova password</string>
<string name="excludeExpiredEntries">Escludi voci scadute</string>
@@ -683,6 +685,12 @@ Ecco alcuni suggerimenti che ti potrebbero aiutare a diagnosticare il problema:\
<string name="EntryChannel_desc">Notifica per semplificare l\'accesso alla voce attualmente selezionata.</string>
<string name="CloseDbAfterFailedAttempts">Chiudi il database dopo tre tentativi di sblocco biometrici falliti.</string>
<string name="WarnFingerprintInvalidated">Attenzione! L\'autenticazione biometrica può essere invalidata da Android, ad es. dopo aver aggiunto una nuova impronta digitale nelle impostazioni del dispositivo. Assicurati di sapere sempre come sbloccare con la tua password principale!</string>
<string-array name="ChangeLog_1_09e">
<item>Correzione bug per crash e logout non previsti</item>
<item>Passa alla nuova implementazione SFTP, supportando moderni algoritmi a chiave pubblica come rsa-sha2-256</item>
<item>Contrassegna le password come sensibili quando si copiano negli appunti (Android 13)</item>
<item>Migliorie Riempimento automatico</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>Aggiunto il supporto per la visualizzazione, la rimozione e il ripristino dei backup delle voci</item>
<item>Implementato supporto per l\'archiviazione su cloud MEGA</item>

View File

@@ -36,8 +36,8 @@
<string name="OfferSaveCredentials_title">アカウント情報の保存を提案</string>
<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_singular">1 件のエントリー</string>
<string name="Entry_plural">%1$d 件のエントリー</string>
<string name="IconSet_title">アイコンセット</string>
<string name="IconSet_install">さらに探す…</string>
<string name="security_prefs">セキュリティ</string>
@@ -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>
@@ -389,9 +391,9 @@
<string name="AcceptAllServerCertificates_summary">証明書の検証に失敗したときの動作を指定します。注意: 検証に失敗したときは、デバイスに証明書をインストールすることができます。</string>
<string name="ClearOfflineCache_title">キャッシュをクリアしますか?</string>
<string name="ClearOfflineCache_question">すべてのキャッシュされたデータベースファイルを削除します。元のデータベースにアクセスできない間に行った、同期されていない変更がすべて失われます! 続行しますか?</string>
<string name="CheckForFileChangesOnSave_title">変更をチェック</string>
<string name="CheckForFileChangesOnSave_title">変更を確認</string>
<string name="CheckForFileChangesOnSave_summary">変更を保存する前に、ファイルが外部から変更されているか確認します。</string>
<string name="CheckForDuplicateUuids_title">UUID の重複をチェック</string>
<string name="CheckForDuplicateUuids_title">UUID の重複を確認</string>
<string name="CheckForDuplicateUuids_summary">同じ ID で複数のエントリーがあるかによってデータベース ファイルの破損を確認します。これは予期しない動作が発生する可能性があります。</string>
<string name="ShowCopyToClipboardNotification_title">クリップボード通知</string>
<string name="ShowCopyToClipboardNotification_summary">ユーザー名とパスワードが、通知バーとクリップボードからアクセス可能になります。パスワード盗聴アプリに気をつけてください!</string>
@@ -482,7 +484,7 @@
<string name="otp_aux_file">OTP 補助ファイル</string>
<string name="ErrorOcurred">エラーが発生しました:</string>
<string name="DuplicateUuidsError">データベースが破損しています: 重複する ID が見つかりました。(Minikeepass で保存しましたか?) PC 用 Keepass 2 で新しいデータベースに再インポートして「新しい ID の作成」を選択してください。</string>
<string name="DuplicateUuidsErrorAdditional">設定/アプリ設定/ファイル処理/UUID の重複をチェック で、このエラーメッセージを無効にできます。予期しない動作が発生する可能性があることにご注意ください。データベースを修正することをお勧めします。</string>
<string name="DuplicateUuidsErrorAdditional">設定/アプリ設定/ファイル処理/UUID の重複を確認 で、このエラーメッセージを無効にできます。予期しない動作が発生する可能性があることにご注意ください。データベースを修正することをお勧めします。</string>
<string name="synchronize_database_menu">データベースを同期...</string>
<string name="CannotMoveGroupHere">このグループにグループを移動できません。</string>
<string name="donate_question">今日はオクトーバーフェスト! Keepass2Android を気に入ってくださったなら、今日は私にビールを買っていただくのにいい日ではありませんか?</string>
@@ -690,6 +692,7 @@
<item>クラッシュと突発的にログアウトするバグを修正</item>
<item>rsa-sha2-256 などの最新の公開鍵アルゴリズムをサポートする、新しい SFTP 実装に切り替え</item>
<item>パスワードをクリップボードにコピーしたとき、機密コンテンツとしてマークするよう変更 (Android 13)</item>
<item>自動入力の改善</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>エントリーバックアップの表示、削除、および復元に対応</item>

View File

@@ -301,6 +301,8 @@
<string name="NoDalVerification_summary">Schakelt controle of domein en app pakket overeenkomen uit </string>
<string name="InlineSuggestions_title">Integreer met toetsenbord</string>
<string name="InlineSuggestions_summary">Toont de suggesties voor automatisch aanvullen als inline-opties in het toetsenbord (indien ondersteund door de invoermethode)</string>
<string name="LogAutofillView_title">Automatisch invullen loggen</string>
<string name="LogAutofillView_summary">Schrijf details over de autofill weergave naar debug log (als debug logging is ingeschakeld). Deze details kunnen naar de ontwikkelaar worden verzonden als het automatisch invullen niet werkt zoals verwacht.</string>
<string name="requires_android11">Vereist Android 11 of hoger</string>
<string name="kp2a_findUrl">Zoek wachtwoord</string>
<string name="excludeExpiredEntries">Verlopen items uitsluiten</string>
@@ -687,6 +689,12 @@
<string name="EntryChannel_desc">Melding voor vereenvoudigde toegang van het nu geselecteerde item.</string>
<string name="CloseDbAfterFailedAttempts">Sluit de database na drie mislukte biometrische ontgrendelpogingen.</string>
<string name="WarnFingerprintInvalidated">Waarschuwing! Biometrische authenticatie wordt uitgeschakeld door Android, bijv. na het toevoegen van een nieuwe vingerafdruk in de instellingen van jouw apparaat. Zorg ervoor dat je de database altijd kunt ontgrendelen met het hoofdwachtwoord!</string>
<string-array name="ChangeLog_1_09e">
<item>Bug fix op crashes en onverwachte log-outs</item>
<item>Schakel over naar nieuwe SFTP-implementatie ter ondersteuning van moderne publieke sleutel algoritmen zoals rsa-sha2-256</item>
<item>Wachtwoorden als gevoelig markeren bij het kopiëren naar klembord (Android 13)</item>
<item>Autofill verbeteringen</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>Toegevoegde ondersteuning voor het bekijken, verwijderen en herstellen van invoer back-ups</item>
<item>Implementatie ondersteuning voor MEGA cloud opslag</item>

View File

@@ -687,9 +687,10 @@
<string name="CloseDbAfterFailedAttempts">Zamknij bazę danych po trzech nieudanych próbach odblokowania biometrycznego.</string>
<string name="WarnFingerprintInvalidated">Uwaga! Uwierzytelnienie biometryczne może zostać unieważnione przez Androida, np. po dodaniu nowego odcisku palca w ustawieniach urządzenia. Upewnij się, że zawsze wiesz, jak odblokować przy użyciu hasła głównego!</string>
<string-array name="ChangeLog_1_09e">
<item>Bug fix to crashes and unexpected log-outs</item>
<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>Melhorias no preenchimento automático</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

@@ -301,6 +301,8 @@
<string name="NoDalVerification_summary">Onemogoči preverjanje, ali se paket domen in aplikacije ujemata</string>
<string name="InlineSuggestions_title">Integracija s tipkovnico</string>
<string name="InlineSuggestions_summary">Prikaže predloge samodejnega izpolnjevanja kot vgrajene možnosti na tipkovnici (če to podpira način vnosa)</string>
<string name="LogAutofillView_title">Pregled podrobnosti samodejnega izpolnjevanja</string>
<string name="LogAutofillView_summary">Zapišite podrobnosti o pogledu samodejnega izpolnjevanja v dnevnik odpravljanja napak (če je omogočeno beleženje odpravljanja napak). Te podrobnosti lahko pošljete razvijalcu, če samodejno izpolnjevanje ne deluje po pričakovanjih.</string>
<string name="requires_android11">Zahteva Android 11 ali novejši</string>
<string name="kp2a_findUrl">Najdi geslo</string>
<string name="excludeExpiredEntries">Izključi potekle vnose</string>
@@ -690,6 +692,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>Izboljšave samodejnega izpolnjevanja</item>
</string-array>
<string-array name="ChangeLog_1_09d">
<item>Dodana podpora za ogled, odstranjevanje in obnavljanje varnostnih kopij vnosov</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>
@@ -690,6 +692,7 @@
<item>Виправлено помилку зі збоями та несподіваними виходами</item>
<item>Перехід на нову реалізацію SFTP з підтримкою сучасних алгоритмів публічного ключа, як-от rsa-sha2-256</item>
<item>Позначати паролі як вразливі під час копіювання до буфера обміну (Android 13)</item>
<item>Вдосконалене автозаповнення</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>
@@ -690,6 +692,7 @@
<item>修正當機和意外登出的錯誤</item>
<item>切換成新 SFTP 實作,支援現代公鑰演算法,如 rsa-sha2-256</item>
<item>複製密碼到剪貼簿時設為機密 (Android 13)</item>
<item>自動填入改進</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>自动填充改进</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>
@@ -97,6 +98,7 @@
<string name="TrayTotp_SeedField_key">TrayTotp_SeedField_key</string>
<string name="TrayTotp_prefs_key">TrayTotp_prefs_key</string>
<string name="DebugLog_key">DebugLog_key</string>
<string name="JSchDebug_key">JSchDebug_key</string>
<string name="DebugLog_prefs_key">DebugLog_prefs_key</string>
<string name="DebugLog_send_key">DebugLog_send</string>
<string name="AutofillDisabledQueriesPreference_key">AutofillDisabledQueriesPreference_key</string>

View File

@@ -333,6 +333,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>
@@ -692,6 +695,7 @@
<string name="TrayTotp_prefs">TrayTotp</string>
<string name="DebugLog_prefs_prefs">Log-File for Debugging</string>
<string name="DebugLog_title">Use log file</string>
<string name="JSchDebug_title">SFTP debug logging</string>
<string name="DebugLog_summary">Write app output to a local log file</string>
<string name="DebugLog_send">Send debug log...</string>
@@ -865,6 +869,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"
@@ -661,10 +670,17 @@
android:defaultValue="false"
android:title="@string/DebugLog_title"
android:key="@string/DebugLog_key" />
<Preference
android:enabled="true"
android:title="@string/DebugLog_send"
android:key="@string/DebugLog_send_key" />
<CheckBoxPreference
android:enabled="true"
android:persistent="true"
android:defaultValue="false"
android:title="@string/JSchDebug_title"
android:key="@string/JSchDebug_key" />
</PreferenceScreen>
</PreferenceScreen>

View File

@@ -475,9 +475,12 @@ namespace keepass2android
builder.SetMessage(activity.GetString(Resource.String.AskReloadFile));
bool buttonPressed = false;
builder.SetPositiveButton(activity.GetString(Android.Resource.String.Yes),
(dlgSender, dlgEvt) =>
{
buttonPressed = true;
CurrentDb.ReloadRequested = true;
activity.SetResult(KeePass.ExitReloadDb);
activity.Finish();
@@ -490,6 +493,7 @@ namespace keepass2android
builder.SetNegativeButton(activity.GetString(Android.Resource.String.No), (dlgSender, dlgEvt) =>
{
buttonPressed = true;
if (actionOnResult != null)
{
actionOnResult(false);
@@ -502,8 +506,16 @@ namespace keepass2android
dialog.SetOnDismissListener(new Util.DismissListener(() =>
{
//dismiss can be called when we're calling activity.Finish() during button press.
//don't do anything then.
if (buttonPressed)
return;
if (actionOnResult != null)
{
actionOnResult(false);
actionOnResult = null;
}
}));
dialog.Show();

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" InitialTargets="showFlavor">
<Target Name="showFlavor" AfterTargets="Build">
<Message Importance="high" Text="building flavor $(Flavor)">
<Message Importance="high" Text="building flavor $(Flavor) with Config $(Configuration)">
</Message>
</Target>
<Import Project="../build-properties.props" />
@@ -1944,6 +1944,10 @@
<Project>{545b4a6b-8bba-4fbe-92fc-4ac060122a54}</Project>
<Name>KeePassLib2Android</Name>
</ProjectReference>
<ProjectReference Include="..\Kp2aAutofillParser\Kp2aAutofillParser.csproj">
<Project>{39b12571-bafe-4d3a-aee2-4d74f14dfd96}</Project>
<Name>Kp2aAutofillParser</Name>
</ProjectReference>
<ProjectReference Include="..\Kp2aBusinessLogic\Kp2aBusinessLogic.csproj">
<Project>{53a9cb7f-6553-4bc0-b56b-9410bb2e59aa}</Project>
<Name>Kp2aBusinessLogic</Name>

View File

@@ -5,6 +5,7 @@ using Android.App.Assist;
using Android.Service.Autofill;
using Android.Views;
using Android.Views.Autofill;
using Kp2aAutofillParser;
namespace keepass2android.services.AutofillBase
{
@@ -38,9 +39,8 @@ 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());
}
void SetHints(string[] value)

View File

@@ -7,11 +7,12 @@ using Android.Runtime;
using Android.Service.Autofill;
using Android.Util;
using Android.Views;
using Android.Views.Autofill;
using Android.Widget;
using Android.Widget.Inline;
using AndroidX.AutoFill.Inline;
using AndroidX.AutoFill.Inline.V1;
using FilledAutofillFieldCollection = keepass2android.services.AutofillBase.model.FilledAutofillFieldCollection;
using Kp2aAutofillParser;
namespace keepass2android.services.AutofillBase
{
@@ -93,13 +94,9 @@ namespace keepass2android.services.AutofillBase
/// Wraps autofill data in a LoginCredential Dataset object which can then be sent back to the
/// client View.
/// </summary>
/// <returns>The dataset.</returns>
/// <param name="context">Context.</param>
/// <param name="autofillFields">Autofill fields.</param>
/// <param name="filledAutofillFieldCollection">Filled autofill field collection.</param>
public static Dataset NewDataset(Context context,
AutofillFieldMetadataCollection autofillFields,
FilledAutofillFieldCollection filledAutofillFieldCollection,
FilledAutofillFieldCollection<ViewNodeInputField> filledAutofillFieldCollection,
IAutofillIntentBuilder intentBuilder,
Android.Widget.Inline.InlinePresentationSpec inlinePresentationSpec)
{
@@ -108,21 +105,115 @@ namespace keepass2android.services.AutofillBase
var datasetBuilder = new Dataset.Builder(NewRemoteViews(context.PackageName, datasetName, intentBuilder.AppIconResource));
datasetBuilder.SetId(datasetName);
var setValueAtLeastOnce = filledAutofillFieldCollection.ApplyToFields(autofillFields, datasetBuilder);
var setValueAtLeastOnce = ApplyToFields(filledAutofillFieldCollection, autofillFields, datasetBuilder);
AddInlinePresentation(context, inlinePresentationSpec, datasetName, datasetBuilder, intentBuilder.AppIconResource, null);
if (setValueAtLeastOnce)
{
return datasetBuilder.Build();
}
else
/*else
{
Kp2aLog.Log("Failed to set at least one value. #fields=" + autofillFields.GetAutofillIds().Length + " " + autofillFields.FocusedAutofillCanonicalHints);
}
}*/
return null;
}
/// <summary>
/// Populates a Dataset.Builder with appropriate values for each AutofillId
/// in a AutofillFieldMetadataCollection.
///
/// In other words, it constructs an autofill Dataset.Builder
/// by applying saved values (from this FilledAutofillFieldCollection)
/// to Views specified in a AutofillFieldMetadataCollection, which represents the current
/// page the user is on.
/// </summary>
/// <returns><c>true</c>, if to fields was applyed, <c>false</c> otherwise.</returns>
/// <param name="filledAutofillFieldCollection"></param>
/// <param name="autofillFieldMetadataCollection">Autofill field metadata collection.</param>
/// <param name="datasetBuilder">Dataset builder.</param>
public static bool ApplyToFields(FilledAutofillFieldCollection<ViewNodeInputField> filledAutofillFieldCollection,
AutofillFieldMetadataCollection autofillFieldMetadataCollection, Dataset.Builder datasetBuilder)
{
bool setValueAtLeastOnce = false;
foreach (string hint in autofillFieldMetadataCollection.AllAutofillCanonicalHints)
{
foreach (AutofillFieldMetadata autofillFieldMetadata in autofillFieldMetadataCollection.GetFieldsForHint(hint))
{
FilledAutofillField filledAutofillField;
if (!filledAutofillFieldCollection.HintMap.TryGetValue(hint, out filledAutofillField) || (filledAutofillField == null))
{
continue;
}
var autofillId = autofillFieldMetadata.AutofillId;
var autofillType = autofillFieldMetadata.AutofillType;
switch (autofillType)
{
case AutofillType.List:
var listValue = autofillFieldMetadata.GetAutofillOptionIndex(filledAutofillField.TextValue);
if (listValue != -1)
{
datasetBuilder.SetValue(autofillId, AutofillValue.ForList(listValue));
setValueAtLeastOnce = true;
}
break;
case AutofillType.Date:
var dateValue = filledAutofillField.DateValue;
datasetBuilder.SetValue(autofillId, AutofillValue.ForDate((long)dateValue));
setValueAtLeastOnce = true;
break;
case AutofillType.Text:
var textValue = filledAutofillField.TextValue;
if (textValue != null)
{
datasetBuilder.SetValue(autofillId, AutofillValue.ForText(textValue));
setValueAtLeastOnce = true;
}
break;
case AutofillType.Toggle:
var toggleValue = filledAutofillField.ToggleValue;
if (toggleValue != null)
{
datasetBuilder.SetValue(autofillId, AutofillValue.ForToggle(toggleValue.Value));
setValueAtLeastOnce = true;
}
break;
default:
Log.Warn(CommonUtil.Tag, "Invalid autofill type - " + autofillType);
break;
}
}
}
/*
if (!setValueAtLeastOnce)
{
Kp2aLog.Log("No value set. Hint keys : " + string.Join(",", HintMap.Keys));
foreach (string hint in autofillFieldMetadataCollection.AllAutofillCanonicalHints)
{
Kp2aLog.Log("No value set. Hint = " + hint);
foreach (AutofillFieldMetadata autofillFieldMetadata in autofillFieldMetadataCollection
.GetFieldsForHint(hint))
{
Kp2aLog.Log("No value set. fieldForHint = " + autofillFieldMetadata.AutofillId.ToString());
FilledAutofillField filledAutofillField;
if (!HintMap.TryGetValue(hint, out filledAutofillField) || (filledAutofillField == null))
{
Kp2aLog.Log("No value set. Hint map does not contain value, " +
(filledAutofillField == null));
continue;
}
Kp2aLog.Log("autofill type=" + autofillFieldMetadata.AutofillType);
}
}
}*/
return setValueAtLeastOnce;
}
public static void AddInlinePresentation(Context context, InlinePresentationSpec inlinePresentationSpec,
string datasetName, Dataset.Builder datasetBuilder, int iconId, PendingIntent pendingIntent)
{

View File

@@ -14,256 +14,5 @@ using keepass2android.services.AutofillBase.model;
namespace keepass2android.services.AutofillBase
{
class AutofillHintsHelper
{
private static readonly HashSet<string> _allSupportedHints = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
View.AutofillHintCreditCardExpirationDate,
View.AutofillHintCreditCardExpirationDay,
View.AutofillHintCreditCardExpirationMonth,
View.AutofillHintCreditCardExpirationYear,
View.AutofillHintCreditCardNumber,
View.AutofillHintCreditCardSecurityCode,
View.AutofillHintEmailAddress,
View.AutofillHintPhone,
View.AutofillHintName,
View.AutofillHintPassword,
View.AutofillHintPostalAddress,
View.AutofillHintPostalCode,
View.AutofillHintUsername,
W3cHints.HONORIFIC_PREFIX,
W3cHints.NAME,
W3cHints.GIVEN_NAME,
W3cHints.ADDITIONAL_NAME,
W3cHints.FAMILY_NAME,
W3cHints.HONORIFIC_SUFFIX,
W3cHints.USERNAME,
W3cHints.NEW_PASSWORD,
W3cHints.CURRENT_PASSWORD,
W3cHints.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)
{
View.AutofillHintEmailAddress,
View.AutofillHintPhone,
View.AutofillHintName,
View.AutofillHintPassword,
View.AutofillHintUsername,
W3cHints.HONORIFIC_PREFIX,
W3cHints.NAME,
W3cHints.GIVEN_NAME,
W3cHints.ADDITIONAL_NAME,
W3cHints.FAMILY_NAME,
W3cHints.HONORIFIC_SUFFIX,
W3cHints.ORGANIZATION_TITLE,
W3cHints.ORGANIZATION,
W3cHints.LANGUAGE,
W3cHints.BDAY,
W3cHints.BDAY_DAY,
W3cHints.BDAY_MONTH,
W3cHints.BDAY_YEAR,
W3cHints.SEX,
W3cHints.URL,
W3cHints.PHOTO,
W3cHints.TEL,
W3cHints.TEL_COUNTRY_CODE,
W3cHints.TEL_NATIONAL,
W3cHints.TEL_AREA_CODE,
W3cHints.TEL_LOCAL,
W3cHints.TEL_LOCAL_PREFIX,
W3cHints.TEL_LOCAL_SUFFIX,
W3cHints.TEL_EXTENSION,
W3cHints.IMPP,
},
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
View.AutofillHintPostalAddress,
View.AutofillHintPostalCode,
W3cHints.STREET_ADDRESS,
W3cHints.ADDRESS_LINE1,
W3cHints.ADDRESS_LINE2,
W3cHints.ADDRESS_LINE3,
W3cHints.ADDRESS_LEVEL4,
W3cHints.ADDRESS_LEVEL3,
W3cHints.ADDRESS_LEVEL2,
W3cHints.ADDRESS_LEVEL1,
W3cHints.COUNTRY,
W3cHints.COUNTRY_NAME
},
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
View.AutofillHintCreditCardExpirationDate,
View.AutofillHintCreditCardExpirationDay,
View.AutofillHintCreditCardExpirationMonth,
View.AutofillHintCreditCardExpirationYear,
View.AutofillHintCreditCardNumber,
View.AutofillHintCreditCardSecurityCode,
W3cHints.CC_NAME,
W3cHints.CC_GIVEN_NAME,
W3cHints.CC_ADDITIONAL_NAME,
W3cHints.CC_FAMILY_NAME,
W3cHints.CC_TYPE,
W3cHints.TRANSACTION_CURRENCY,
W3cHints.TRANSACTION_AMOUNT,
},
};
private static readonly Dictionary<string, string> hintToCanonicalReplacement= new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{W3cHints.EMAIL, View.AutofillHintEmailAddress},
{W3cHints.USERNAME, View.AutofillHintUsername},
{W3cHints.CURRENT_PASSWORD, View.AutofillHintPassword},
{W3cHints.NEW_PASSWORD, View.AutofillHintPassword},
{W3cHints.CC_EXPIRATION_MONTH, View.AutofillHintCreditCardExpirationMonth },
{W3cHints.CC_EXPIRATION_YEAR, View.AutofillHintCreditCardExpirationYear },
{W3cHints.CC_EXPIRATION, View.AutofillHintCreditCardExpirationDate },
{W3cHints.CC_NUMBER, View.AutofillHintCreditCardNumber },
{W3cHints.CC_CSC, View.AutofillHintCreditCardSecurityCode },
{W3cHints.POSTAL_CODE, View.AutofillHintPostalCode },
};
public static bool IsSupportedHint(string hint)
{
return _allSupportedHints.Contains(hint);
}
public static string[] FilterForSupportedHints(string[] hints)
{
var filteredHints = new string[hints.Length];
int i = 0;
foreach (var hint in hints)
{
if (IsSupportedHint(hint))
{
filteredHints[i++] = hint;
}
else
{
CommonUtil.logd("Invalid autofill hint: " + 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 FilterForPartition(FilledAutofillFieldCollection autofillFields, int partitionIndex)
{
FilledAutofillFieldCollection filteredCollection =
new FilledAutofillFieldCollection {DatasetName = autofillFields.DatasetName};
if (partitionIndex == -1)
return filteredCollection;
foreach (var field in autofillFields.HintMap.Values.Distinct())
{
foreach (var hint in field.AutofillHints)
{
if (GetPartitionIndex(hint) == partitionIndex)
{
filteredCollection.Add(field);
break;
}
}
}
return filteredCollection;
}
public static FilledAutofillFieldCollection FilterForPartition(FilledAutofillFieldCollection filledAutofillFieldCollection, List<string> autofillFieldsFocusedAutofillCanonicalHints)
{
//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;
}
}
}

View File

@@ -20,6 +20,7 @@ using AndroidX.AutoFill.Inline;
using AndroidX.AutoFill.Inline.V1;
using Java.Util.Concurrent.Atomic;
using keepass2android.services.AutofillBase.model;
using Kp2aAutofillParser;
namespace keepass2android.services.AutofillBase
{
@@ -137,7 +138,7 @@ namespace keepass2android.services.AutofillBase
return;
}
AutofillFieldMetadataCollection autofillFields = parser.AutofillFields;
InlineSuggestionsRequest inlineSuggestionsRequest = null;
IList<InlinePresentationSpec> inlinePresentationSpecs = null;
if (((int) Build.VERSION.SdkInt >= 30)
@@ -149,7 +150,7 @@ namespace keepass2android.services.AutofillBase
}
var autofillIds = autofillFields.GetAutofillIds();
var autofillIds = parser.AutofillFields.GetAutofillIds();
if (autofillIds.Length != 0 && CanAutofill(query, isManual))
{
var responseBuilder = new FillResponse.Builder();
@@ -255,7 +256,7 @@ namespace keepass2android.services.AutofillBase
if (warning == DisplayWarning.None)
{
FilledAutofillFieldCollection partitionData =
FilledAutofillFieldCollection<ViewNodeInputField> partitionData =
AutofillHintsHelper.FilterForPartition(filledAutofillFieldCollection, parser.AutofillFields.FocusedAutofillCanonicalHints);
Kp2aLog.Log("AF: Add dataset");
@@ -299,7 +300,7 @@ namespace keepass2android.services.AutofillBase
}
protected abstract List<FilledAutofillFieldCollection> GetSuggestedEntries(string query);
protected abstract List<FilledAutofillFieldCollection<ViewNodeInputField>> GetSuggestedEntries(string query);
public enum DisplayWarning
{

View File

@@ -12,6 +12,7 @@ using Java.Util;
using keepass2android.services.AutofillBase.model;
using System.Linq;
using Android.Content.PM;
using Kp2aAutofillParser;
#if !NoNet
using Com.Dropbox.Core.V2.Teamlog;
#endif
@@ -173,7 +174,7 @@ namespace keepass2android.services.AutofillBase
ReplyIntent = null;
}
protected void OnSuccess(FilledAutofillFieldCollection clientFormDataMap, bool isManual)
protected void OnSuccess(FilledAutofillFieldCollection<ViewNodeInputField> clientFormDataMap, bool isManual)
{
var intent = Intent;
AssistStructure structure = (AssistStructure)intent.GetParcelableExtra(AutofillManager.ExtraAssistStructure);
@@ -229,7 +230,7 @@ namespace keepass2android.services.AutofillBase
/// <summary>
/// Creates the FilledAutofillFieldCollection from the intent returned from the query activity
/// </summary>
protected abstract FilledAutofillFieldCollection GetDataset();
protected abstract FilledAutofillFieldCollection<ViewNodeInputField> GetDataset();
public abstract IAutofillIntentBuilder IntentBuilder { get; }

View File

@@ -2,11 +2,13 @@
using System.Linq;
using Android.Content;
using Android.Preferences;
using Kp2aAutofillParser;
namespace keepass2android.services.AutofillBase
{
internal class Kp2aDigitalAssetLinksDataSource
internal class Kp2aDigitalAssetLinksDataSource : IKp2aDigitalAssetLinksDataSource
{
private const string Autofilltrustedapps = "AutoFillTrustedApps";
@@ -37,6 +39,11 @@ namespace keepass2android.services.AutofillBase
return trustedLinks.Contains(BuildLink(domain, targetPackage));
}
public bool IsEnabled()
{
return !PreferenceManager.GetDefaultSharedPreferences(_ctx).GetBoolean(_ctx.GetString(Resource.String.NoDalVerification_key), false);
}
public void RememberAsTrustedApp(string packageName)
{
var prefs = PreferenceManager.GetDefaultSharedPreferences(_ctx);

View File

@@ -1,267 +1,102 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Android.App.Assist;
using Android.Content;
using Android.Preferences;
using Android.Text;
using Android.Util;
using Android.Views;
using Android.Views.Autofill;
using Android.Views.InputMethods;
using DomainNameParser;
using keepass2android.services.AutofillBase.model;
using FilledAutofillFieldCollection = keepass2android.services.AutofillBase.model.FilledAutofillFieldCollection;
using Kp2aAutofillParser;
using Newtonsoft.Json;
namespace keepass2android.services.AutofillBase
{
public class ViewNodeInputField : Kp2aAutofillParser.InputField
{
public ViewNodeInputField(AssistStructure.ViewNode viewNode)
{
ViewNode = viewNode;
IdEntry = viewNode.IdEntry;
Hint = viewNode.Hint;
ClassName = viewNode.ClassName;
AutofillHints = viewNode.GetAutofillHints();
IsFocused = viewNode.IsFocused;
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 override void FillFilledAutofillValue(FilledAutofillField filledField)
{
AutofillValue autofillValue = ViewNode.AutofillValue;
if (autofillValue != null)
{
if (autofillValue.IsList)
{
string[] autofillOptions = ViewNode.GetAutofillOptions();
int index = autofillValue.ListValue;
if (autofillOptions != null && autofillOptions.Length > 0)
{
filledField.TextValue = autofillOptions[index];
}
}
else if (autofillValue.IsDate)
{
filledField.DateValue = autofillValue.DateValue;
}
else if (autofillValue.IsText)
{
filledField.TextValue = autofillValue.TextValue;
}
}
}
}
/// <summary>
/// Parser for an AssistStructure object. This is invoked when the Autofill Service receives an
/// AssistStructure from the client Activity, representing its View hierarchy. In this sample, it
/// parses the hierarchy and collects autofill metadata from {@link ViewNode}s along the way.
/// Converts an AssistStructure into a list of InputFields
/// </summary>
public sealed class StructureParser
class AutofillViewFromAssistStructureFinder
{
public Context mContext { get; }
public AutofillFieldMetadataCollection AutofillFields { get; set; }
AssistStructure Structure;
private List<AssistStructure.ViewNode> _editTextsWithoutHint = new List<AssistStructure.ViewNode>();
private readonly Context _context;
private readonly AssistStructure _structure;
private PublicSuffixRuleCache domainSuffixParserCache;
public FilledAutofillFieldCollection ClientFormData { get; set; }
public string PackageId { get; set; }
public StructureParser(Context context, AssistStructure structure)
public AutofillViewFromAssistStructureFinder(Context context, AssistStructure structure)
{
kp2aDigitalAssetLinksDataSource = new Kp2aDigitalAssetLinksDataSource(context);
mContext = context;
Structure = structure;
AutofillFields = new AutofillFieldMetadataCollection();
_context = context;
_structure = structure;
domainSuffixParserCache = new PublicSuffixRuleCache(context);
}
public class AutofillTargetId
public AutofillView<ViewNodeInputField> GetAutofillView(bool isManualRequest)
{
public string PackageName { get; set; }
AutofillView<ViewNodeInputField> autofillView = new AutofillView<ViewNodeInputField>();
public string PackageNameWithPseudoSchema
int nodeCount = _structure.WindowNodeCount;
for (int i = 0; i < nodeCount; i++)
{
get { return KeePass.AndroidAppScheme + PackageName; }
}
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)
{
return Parse(true, isManual);
}
public AutofillTargetId ParseForSave()
{
return Parse(false, true);
}
/// <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>
AutofillTargetId Parse(bool forFill, bool isManualRequest)
{
AutofillTargetId result = new AutofillTargetId();
CommonUtil.logd("Parsing structure for " + Structure.ActivityComponent);
var nodes = Structure.WindowNodeCount;
ClientFormData = new FilledAutofillFieldCollection();
String webDomain = null;
_editTextsWithoutHint.Clear();
for (int i = 0; i < nodes; i++)
{
var node = Structure.GetWindowNodeAt(i);
var node = _structure.GetWindowNodeAt(i);
var view = node.RootViewNode;
ParseLocked(forFill, isManualRequest, view, ref webDomain);
}
List<AssistStructure.ViewNode> passwordFields = new List<AssistStructure.ViewNode>();
List<AssistStructure.ViewNode> usernameFields = new List<AssistStructure.ViewNode>();
if (AutofillFields.Empty)
{
passwordFields = _editTextsWithoutHint.Where(IsPassword).ToList();
if (!passwordFields.Any())
{
passwordFields = _editTextsWithoutHint.Where(HasPasswordHint).ToList();
}
usernameFields = _editTextsWithoutHint.Where(HasUsernameHint).ToList();
if (usernameFields.Any() == false)
{
foreach (var passwordField in passwordFields)
{
var usernameField = _editTextsWithoutHint
.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
if (usernameField != null)
{
usernameFields.Add(usernameField);
}
}
}
if (usernameFields.Any() == false)
{
//for some pages with two-step login, we don't see a password field and don't display the autofill for non-manual requests. But if the user forces autofill,
//let's assume it is a username field:
if (isManualRequest && !passwordFields.Any() && _editTextsWithoutHint.Count == 1)
{
usernameFields.Add(_editTextsWithoutHint.First());
}
ParseRecursive(autofillView, view, isManualRequest);
}
return autofillView;
}
//force focused fields to be included in autofill fields when request was triggered manually. This allows to fill fields which are "off" or don't have a hint (in case there are hints)
if (isManualRequest)
{
foreach (AssistStructure.ViewNode editText in _editTextsWithoutHint)
{
if (editText.IsFocused)
{
if (IsPassword(editText) || HasPasswordHint(editText))
passwordFields.Add(editText);
else
usernameFields.Add(editText);
break;
}
}
}
if (forFill)
{
foreach (var uf in usernameFields)
AutofillFields.Add(new AutofillFieldMetadata(uf, new[] { View.AutofillHintUsername }));
foreach (var pf in passwordFields)
AutofillFields.Add(new AutofillFieldMetadata(pf, new[] { View.AutofillHintPassword }));
}
else
{
foreach (var uf in usernameFields)
ClientFormData.Add(new FilledAutofillField(uf, new[] { View.AutofillHintUsername }));
foreach (var pf in passwordFields)
ClientFormData.Add(new FilledAutofillField(pf, new[] { View.AutofillHintPassword }));
}
result.WebDomain = webDomain;
result.PackageName = Structure.ActivityComponent.PackageName;
if (!string.IsNullOrEmpty(webDomain) && !PreferenceManager.GetDefaultSharedPreferences(mContext).GetBoolean(mContext.GetString(Resource.String.NoDalVerification_key), false))
{
result.IncompatiblePackageAndDomain = !kp2aDigitalAssetLinksDataSource.IsTrustedLink(webDomain, result.PackageName);
if (result.IncompatiblePackageAndDomain)
{
CommonUtil.loge($"DAL verification failed for {result.PackageName}/{result.WebDomain}");
}
}
else
{
result.IncompatiblePackageAndDomain = false;
}
return result;
}
private static readonly HashSet<string> _passwordHints = new HashSet<string> { "password","passwort" };
private static bool HasPasswordHint(AssistStructure.ViewNode f)
{
return ContainsAny(f.IdEntry, _passwordHints) ||
ContainsAny(f.Hint, _passwordHints);
}
private static readonly HashSet<string> _usernameHints = new HashSet<string> { "email","e-mail","username" };
private Kp2aDigitalAssetLinksDataSource kp2aDigitalAssetLinksDataSource;
private static bool HasUsernameHint(AssistStructure.ViewNode f)
{
return ContainsAny(f.IdEntry, _usernameHints) ||
ContainsAny(f.Hint, _usernameHints);
}
private static bool ContainsAny(string value, IEnumerable<string> terms)
{
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
var lowerValue = value.ToLowerInvariant();
return terms.Any(t => lowerValue.Contains(t));
}
private static bool IsInputTypeClass(InputTypes inputType, InputTypes inputTypeClass)
{
if (!InputTypes.MaskClass.HasFlag(inputTypeClass))
throw new Exception("invalid inputTypeClas");
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");
bool result = (((int)inputType) & (int)InputTypes.MaskVariation) == (int)(inputTypeVariation);
if (result)
Kp2aLog.Log("found " + ((int)inputTypeVariation).ToString("X") + " in " + ((int)inputType).ToString("X"));
return result;
}
private static bool IsPassword(AssistStructure.ViewNode 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.HtmlInfo?.Attributes.Any(p => p.First.ToString() == "type" && p.Second.ToString() == "password") ?? false)
);
}
void ParseLocked(bool forFill, bool isManualRequest, AssistStructure.ViewNode viewNode, ref string validWebdomain)
void ParseRecursive(AutofillView<ViewNodeInputField> autofillView, AssistStructure.ViewNode viewNode, bool isManualRequest)
{
String webDomain = viewNode.WebDomain;
if ((PackageId == null) && (!string.IsNullOrWhiteSpace(viewNode.IdPackage)) &&
if ((autofillView.PackageId == null) && (!string.IsNullOrWhiteSpace(viewNode.IdPackage)) &&
(viewNode.IdPackage != "android"))
{
PackageId = viewNode.IdPackage;
autofillView.PackageId = viewNode.IdPackage;
}
DomainName outDomain;
@@ -272,63 +107,100 @@ namespace keepass2android.services.AutofillBase
if (webDomain != null)
{
if (!string.IsNullOrEmpty(validWebdomain))
if (!string.IsNullOrEmpty(autofillView.WebDomain))
{
if (webDomain != validWebdomain)
if (webDomain != autofillView.WebDomain)
{
throw new Java.Lang.SecurityException($"Found multiple web domains: valid= {validWebdomain}, child={webDomain}");
throw new Java.Lang.SecurityException($"Found multiple web domains: valid= {autofillView.WebDomain}, child={webDomain}");
}
}
else
{
validWebdomain = webDomain;
autofillView.WebDomain = webDomain;
}
}
string[] viewHints = viewNode.GetAutofillHints();
if (viewHints != null && viewHints.Length == 1 && viewHints.First() == "off" && viewNode.IsFocused &&
isManualRequest)
viewHints[0] = "on";
/*if (viewHints != null && viewHints.Any())
{
CommonUtil.logd("viewHints=" + viewHints);
CommonUtil.logd("class=" + viewNode.ClassName);
CommonUtil.logd("tag=" + (viewNode?.HtmlInfo?.Tag ?? "(null)"));
}*/
autofillView.InputFields.Add(new ViewNodeInputField(viewNode));
if (viewHints != null && viewHints.Length > 0 && viewHints.First() != "on" /*if hint is "on", treat as if there is no hint*/)
{
if (forFill)
{
AutofillFields.Add(new AutofillFieldMetadata(viewNode));
}
else
{
FilledAutofillField filledAutofillField = new FilledAutofillField(viewNode);
ClientFormData.Add(filledAutofillField);
}
}
else
{
if (viewNode.ClassName == "android.widget.EditText"
|| viewNode.ClassName == "android.widget.AutoCompleteTextView"
|| viewNode?.HtmlInfo?.Tag == "input")
{
_editTextsWithoutHint.Add(viewNode);
}
}
var childrenSize = viewNode.ChildCount;
if (childrenSize > 0)
{
for (int i = 0; i < childrenSize; i++)
{
ParseLocked(forFill, isManualRequest, viewNode.GetChildAt(i), ref validWebdomain);
ParseRecursive(autofillView, viewNode.GetChildAt(i), isManualRequest);
}
}
}
}
/// <summary>
/// Parser for an AssistStructure object. This is invoked when the Autofill Service receives an
/// AssistStructure from the client Activity, representing its View hierarchy. In this sample, it
/// parses the hierarchy and collects autofill metadata from {@link ViewNode}s along the way.
/// </summary>
public sealed class StructureParser: StructureParserBase<ViewNodeInputField>
{
private readonly AssistStructure _structure;
public Context _context { get; }
public AutofillFieldMetadataCollection AutofillFields { get; set; }
public FilledAutofillFieldCollection<ViewNodeInputField> ClientFormData { get; set; }
public string PackageId { get; set; }
public StructureParser(Context context, AssistStructure structure)
: base(new Kp2aLogger(), new Kp2aDigitalAssetLinksDataSource(context))
{
_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)
AutofillFields.Add(new AutofillFieldMetadata(p.Key.ViewNode, p.Value));
}
else
{
ClientFormData = new FilledAutofillFieldCollection<ViewNodeInputField>();
foreach (var p in FieldsMappedToHints)
ClientFormData.Add(new FilledAutofillField(p.Key, p.Value));
}
return result;
}
public AutofillTargetId ParseForSave()
{
var autofillView = new AutofillViewFromAssistStructureFinder(_context, _structure).GetAutofillView(true);
return Parse(false, true, autofillView);
}
public StructureParserBase<ViewNodeInputField>.AutofillTargetId ParseForFill(bool isManual)
{
var autofillView = new AutofillViewFromAssistStructureFinder(_context, _structure).GetAutofillView(isManual);
return Parse(true, isManual, autofillView);
}
}
public class Kp2aLogger : ILogger
{
public void Log(string x)
{
Kp2aLog.Log(x);
}
}
}

View File

@@ -2,154 +2,9 @@
using Android.App.Assist;
using Android.Views.Autofill;
using KeePassLib.Utility;
using Kp2aAutofillParser;
namespace keepass2android.services.AutofillBase.model
{
public class FilledAutofillField
{
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(AssistStructure.ViewNode viewNode)
: this(viewNode, viewNode.GetAutofillHints())
{
}
public FilledAutofillField(AssistStructure.ViewNode viewNode, 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];
CommonUtil.logd($"Hint is a W3C section prefix; using {hint} instead");
if (i < rawHints.Length - 1)
{
nextHint = rawHints[i + 1];
}
}
if (W3cHints.isW3cTypePrefix(hint) && nextHint != null && W3cHints.isW3cTypeHint(nextHint))
{
hint = nextHint;
i++;
CommonUtil.logd($"Hint is a W3C type prefix; using {hint} instead");
}
if (W3cHints.isW3cAddressType(hint) && nextHint != null)
{
hint = nextHint;
i++;
CommonUtil.logd($"Hint is a W3C address prefix; using {hint} instead");
}
// Then check if the "actual" hint is supported.
if (AutofillHintsHelper.IsSupportedHint(hint))
{
hintList.Add(hint);
}
else
{
CommonUtil.loge($"Invalid hint: {rawHints[i]}");
}
}
AutofillHints = AutofillHintsHelper.ConvertToCanonicalHints(hintList.ToArray()).ToArray();
AutofillValue autofillValue = viewNode.AutofillValue;
if (autofillValue != null)
{
if (autofillValue.IsList)
{
string[] autofillOptions = viewNode.GetAutofillOptions();
int index = autofillValue.ListValue;
if (autofillOptions != null && autofillOptions.Length > 0)
{
TextValue = autofillOptions[index];
}
}
else if (autofillValue.IsDate)
{
DateValue = autofillValue.DateValue;
}
else if (autofillValue.IsText)
{
TextValue = autofillValue.TextValue;
}
}
}
public bool IsNull()
{
return TextValue == null && DateValue == null && ToggleValue == null;
}
public override bool Equals(object obj)
{
if (this == obj) return true;
if (obj == null || GetType() != obj.GetType()) return false;
FilledAutofillField that = (FilledAutofillField)obj;
if (!TextValue?.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;
}
}
}
}

View File

@@ -7,164 +7,5 @@ using Android.Views.Autofill;
namespace keepass2android.services.AutofillBase.model
{
/// <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
{
public Dictionary<string, FilledAutofillField> HintMap { get; }
public string DatasetName { get; set; }
public FilledAutofillFieldCollection(Dictionary<string, FilledAutofillField> 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> BuildHintMap()
{
return new Dictionary<string, FilledAutofillField>(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 filledAutofillField)
{
foreach (string hint in filledAutofillField.AutofillHints)
{
if (AutofillHintsHelper.IsSupportedHint(hint))
{
HintMap.TryAdd(hint, filledAutofillField);
}
else
{
CommonUtil.loge($"Invalid hint: {hint}");
}
}
}
/// <summary>
/// Populates a Dataset.Builder with appropriate values for each AutofillId
/// in a AutofillFieldMetadataCollection.
///
/// In other words, it constructs an autofill Dataset.Builder
/// by applying saved values (from this FilledAutofillFieldCollection)
/// to Views specified in a AutofillFieldMetadataCollection, which represents the current
/// page the user is on.
/// </summary>
/// <returns><c>true</c>, if to fields was applyed, <c>false</c> otherwise.</returns>
/// <param name="autofillFieldMetadataCollection">Autofill field metadata collection.</param>
/// <param name="datasetBuilder">Dataset builder.</param>
public bool ApplyToFields(AutofillFieldMetadataCollection autofillFieldMetadataCollection, Dataset.Builder datasetBuilder)
{
bool setValueAtLeastOnce = false;
foreach (string hint in autofillFieldMetadataCollection.AllAutofillCanonicalHints)
{
foreach (AutofillFieldMetadata autofillFieldMetadata in autofillFieldMetadataCollection.GetFieldsForHint(hint))
{
FilledAutofillField filledAutofillField;
if (!HintMap.TryGetValue(hint, out filledAutofillField) || (filledAutofillField == null))
{
continue;
}
var autofillId = autofillFieldMetadata.AutofillId;
var autofillType = autofillFieldMetadata.AutofillType;
switch (autofillType)
{
case AutofillType.List:
var listValue = autofillFieldMetadata.GetAutofillOptionIndex(filledAutofillField.TextValue);
if (listValue != -1)
{
datasetBuilder.SetValue(autofillId, AutofillValue.ForList(listValue));
setValueAtLeastOnce = true;
}
break;
case AutofillType.Date:
var dateValue = filledAutofillField.DateValue;
datasetBuilder.SetValue(autofillId, AutofillValue.ForDate((long)dateValue));
setValueAtLeastOnce = true;
break;
case AutofillType.Text:
var textValue = filledAutofillField.TextValue;
if (textValue != null)
{
datasetBuilder.SetValue(autofillId, AutofillValue.ForText(textValue));
setValueAtLeastOnce = true;
}
break;
case AutofillType.Toggle:
var toggleValue = filledAutofillField.ToggleValue;
if (toggleValue != null)
{
datasetBuilder.SetValue(autofillId, AutofillValue.ForToggle(toggleValue.Value));
setValueAtLeastOnce = true;
}
break;
default:
Log.Warn(CommonUtil.Tag, "Invalid autofill type - " + autofillType);
break;
}
}
}
/*
if (!setValueAtLeastOnce)
{
Kp2aLog.Log("No value set. Hint keys : " + string.Join(",", HintMap.Keys));
foreach (string hint in autofillFieldMetadataCollection.AllAutofillCanonicalHints)
{
Kp2aLog.Log("No value set. Hint = " + hint);
foreach (AutofillFieldMetadata autofillFieldMetadata in autofillFieldMetadataCollection
.GetFieldsForHint(hint))
{
Kp2aLog.Log("No value set. fieldForHint = " + autofillFieldMetadata.AutofillId.ToString());
FilledAutofillField filledAutofillField;
if (!HintMap.TryGetValue(hint, out filledAutofillField) || (filledAutofillField == null))
{
Kp2aLog.Log("No value set. Hint map does not contain value, " +
(filledAutofillField == null));
continue;
}
Kp2aLog.Log("autofill type=" + autofillFieldMetadata.AutofillType);
}
}
}*/
return setValueAtLeastOnce;
}
/// <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;
}
}
}

View File

@@ -1,127 +1,6 @@
using Android.Util;
namespace keepass2android.services.AutofillBase.model
{
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;
}
Log.Warn(CommonUtil.Tag, "Invalid W3C type hint: " + hint);
return false;
}
}
}

View File

@@ -241,6 +241,7 @@ namespace keepass2android
.SetWhen(Java.Lang.JavaSystem.CurrentTimeMillis())
.SetTicker(entryName + ": " + desc)
.SetVisibility((int)Android.App.NotificationVisibility.Secret)
.SetAutoCancel(true)
.SetContentIntent(pending);
if (entryIcon != null)
builder.SetLargeIcon(entryIcon);
@@ -951,7 +952,9 @@ namespace keepass2android
{
CopyToClipboardService.CopyValueToClipboardWithTimeout(context, username, false);
}
context.SendBroadcast(new Intent(Intent.ActionCloseSystemDialogs)); //close notification drawer
CloseNotificationDrawer(context);
}
else if (action.Equals(Intents.CopyPassword))
{
@@ -960,7 +963,7 @@ namespace keepass2android
{
CopyToClipboardService.CopyValueToClipboardWithTimeout(context, password, true);
}
context.SendBroadcast(new Intent(Intent.ActionCloseSystemDialogs)); //close notification drawer
CloseNotificationDrawer(context);
}
else if (action.Equals(Intents.CopyTotp))
{
@@ -969,7 +972,7 @@ namespace keepass2android
{
CopyToClipboardService.CopyValueToClipboardWithTimeout(context, totp, true);
}
context.SendBroadcast(new Intent(Intent.ActionCloseSystemDialogs)); //close notification drawer
CloseNotificationDrawer(context);
}
else if (action.Equals(Intents.CheckKeyboard))
{
@@ -977,6 +980,11 @@ namespace keepass2android
}
}
private static void CloseNotificationDrawer(Context context)
{
if ((int)Build.VERSION.SdkInt < 31) //sending this intent is no longer allowed since Android 31
context.SendBroadcast(new Intent(Intent.ActionCloseSystemDialogs)); //close notification drawer
}
};
}

View File

@@ -15,6 +15,7 @@ using keepass2android.services.AutofillBase.model;
using Keepass2android.Pluginsdk;
using KeePassLib;
using KeePassLib.Utility;
using Kp2aAutofillParser;
namespace keepass2android.services.Kp2aAutofill
{
@@ -41,7 +42,7 @@ namespace keepass2android.services.Kp2aAutofill
protected override Result ExpectedActivityResult => KeePass.ExitCloseAfterTaskComplete;
protected override FilledAutofillFieldCollection GetDataset()
protected override FilledAutofillFieldCollection<ViewNodeInputField> GetDataset()
{
if (App.Kp2a.CurrentDb==null || (App.Kp2a.QuickLocked))
return null;
@@ -50,11 +51,11 @@ namespace keepass2android.services.Kp2aAutofill
return GetFilledAutofillFieldCollectionFromEntry(entryOutput, this);
}
public static FilledAutofillFieldCollection GetFilledAutofillFieldCollectionFromEntry(PwEntryOutput pwEntryOutput, Context context)
public static FilledAutofillFieldCollection<ViewNodeInputField> GetFilledAutofillFieldCollectionFromEntry(PwEntryOutput pwEntryOutput, Context context)
{
if (pwEntryOutput == null)
return null;
FilledAutofillFieldCollection fieldCollection = new FilledAutofillFieldCollection();
FilledAutofillFieldCollection<ViewNodeInputField> fieldCollection = new FilledAutofillFieldCollection<ViewNodeInputField>();
var pwEntry = pwEntryOutput.Entry;
foreach (string key in pwEntryOutput.OutputStrings.GetKeys())

View File

@@ -12,6 +12,7 @@ using Keepass2android.Pluginsdk;
using KeePassLib;
using KeePassLib.Collections;
using KeePassLib.Utility;
using Kp2aAutofillParser;
using Org.Json;
using AutofillServiceBase = keepass2android.services.AutofillBase.AutofillServiceBase;
@@ -33,10 +34,10 @@ namespace keepass2android.services
{
}
protected override List<FilledAutofillFieldCollection> GetSuggestedEntries(string query)
protected override List<FilledAutofillFieldCollection<ViewNodeInputField>> GetSuggestedEntries(string query)
{
if (!App.Kp2a.DatabaseIsUnlocked)
return new List<FilledAutofillFieldCollection>();
return new List<FilledAutofillFieldCollection<ViewNodeInputField>>();
var foundEntries = (ShareUrlResults.GetSearchResultsForUrl(query)?.Entries ?? new PwObjectList<PwEntry>())
.Select(e => new PwEntryOutput(e, App.Kp2a.FindDatabaseForElement(e)))
.ToList();

View File

@@ -176,6 +176,12 @@ namespace keepass2android
FindPreference(GetString(Resource.String.DebugLog_key)).PreferenceChange += OnDebugLogChanged;
FindPreference(GetString(Resource.String.DebugLog_send_key)).PreferenceClick += OnSendDebug;
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
FindPreference(GetString(Resource.String.JSchDebug_key)).PreferenceChange += OnJSchDebugChanged;
#else
FindPreference(GetString(Resource.String.JSchDebug_key)).Enabled = false;
#endif
HashSet<string> supportedLocales = new HashSet<string>() { "en", "af", "ar", "az", "be", "bg", "ca", "cs", "da", "de", "el", "es", "eu", "fa", "fi", "fr", "gl", "he", "hr", "hu", "id", "in", "it", "iw", "ja", "ko", "ml", "nb", "nl", "nn", "no", "pl", "pt", "ro", "ru", "si", "sk", "sl", "sr", "sv", "tr", "uk", "vi", "zh" };
ListPreference appLanguagePref = (ListPreference)FindPreference(GetString(Resource.String.app_language_pref_key));
@@ -418,16 +424,35 @@ namespace keepass2android
private void OnDebugLogChanged(object sender, Preference.PreferenceChangeEventArgs e)
{
if ((bool)e.NewValue)
{
Kp2aLog.CreateLogFile();
}
else
{
Kp2aLog.FinishLogFile();
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
bool jschLogEnable = PreferenceManager.GetDefaultSharedPreferences(Application.Context)
.GetBoolean(Application.Context.GetString(Resource.String.JSchDebug_key), false);
SetJSchLogging(jschLogEnable);
#endif
}
#if !EXCLUDE_JAVAFILESTORAGE && !NoNet
private void OnJSchDebugChanged(object sender, Preference.PreferenceChangeEventArgs e)
{
SetJSchLogging((bool)e.NewValue);
}
private void SetJSchLogging(bool enabled)
{
var sftpStorage = new Keepass2android.Javafilestorage.SftpStorage(Context);
string? logFilename = null;
if (Kp2aLog.LogToFile)
{
logFilename = Kp2aLog.LogFilename;
}
sftpStorage.SetJschLogging(enabled, logFilename);
}
#endif
private void AlgorithmPrefChange(object sender, Preference.PreferenceChangeEventArgs preferenceChangeEventArgs)
{
var db = App.Kp2a.CurrentDb;

View File

@@ -103,6 +103,10 @@ namespace keepass2android.view
private void PopulateView(View ev, PwEntry pw, int pos)
{
if (_groupBaseActivity.IsFinishing)
return;
_entry = pw;
_pos = pos;
ev.FindViewById(Resource.Id.icon).Visibility = ViewStates.Visible;

View File

@@ -77,8 +77,13 @@ namespace keepass2android.view
AddView(gv, lp);
}
private void PopulateView(View gv, PwGroup pw) {
private void PopulateView(View gv, PwGroup pw)
{
_pwGroup = pw;
Kp2aLog.Log($"Populating group view {_groupBaseActivity.IsFinishing} {pw.Name}");
if (_groupBaseActivity.IsFinishing)
return;
ImageView iv = (ImageView) gv.FindViewById(Resource.Id.icon);
Database db = App.Kp2a.FindDatabaseForElement(pw);