1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00

Compare commits

..

4 Commits

Author SHA1 Message Date
github-actions[bot]
39d275453e Bumped version to 2023.3.0 (#2423)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
(cherry picked from commit c6bdb67981)
2023-03-22 10:45:56 -04:00
André Bispo
e23ab78bd8 [PM-1497] Known device api error (#2418)
* [PM-1497] Ignore know device api error.
2023-03-17 15:08:29 +00:00
André Bispo
4ed5980cb5 [PM-1431] [Defect] [Android] New accounts are not able to log in (#2417)
* [PM-1431] Do not clear Master password if login is ongoing.
2023-03-16 12:50:26 +00:00
André Bispo
8c273f7d93 [PM-1078] Login with Device - Change mobile to not get fingerprint from API (#2390)
* [PM-1078] Fingerprint phrase gets calculated from pub key on AuthService instead of coming as a property from the api.

(cherry picked from commit ccd71202de)
2023-03-13 18:27:59 -04:00
254 changed files with 4270 additions and 18251 deletions

View File

@@ -1,8 +1,5 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: Customer Support
url: https://bitwarden.com/contact/
about: Please contact our customer support for account issues and general customer support.
- name: Report mobile autofill failure - name: Report mobile autofill failure
url: https://docs.google.com/forms/d/e/1FAIpQLScMopHyN7KGJs8hW562VTzbIGL4KcFnx0wJcsW0GYE1BnPiGA/viewform url: https://docs.google.com/forms/d/e/1FAIpQLScMopHyN7KGJs8hW562VTzbIGL4KcFnx0wJcsW0GYE1BnPiGA/viewform
about: We are aware of some situations where the Bitwarden mobile app will not autofill information correctly. This is something the Bitwarden team is actively working on but need your help as a community and active Bitwarden users! about: We are aware of some situations where the Bitwarden mobile app will not autofill information correctly. This is something the Bitwarden team is actively working on but need your help as a community and active Bitwarden users!
@@ -12,6 +9,9 @@ contact_links:
- name: Bitwarden Community Forums - name: Bitwarden Community Forums
url: https://community.bitwarden.com url: https://community.bitwarden.com
about: Please visit the community forums for general community discussion, support and the development roadmap. about: Please visit the community forums for general community discussion, support and the development roadmap.
- name: Customer Support
url: https://bitwarden.com/contact/
about: Please contact our customer support for account issues and general customer support.
- name: Security Issues - name: Security Issues
url: https://hackerone.com/bitwarden url: https://hackerone.com/bitwarden
about: We use HackerOne to manage security disclosures. about: We use HackerOne to manage security disclosures.

19
.github/labeler.yml vendored
View File

@@ -1,19 +0,0 @@
android:
- src/App/*
- src/Core/*
- src/Android/*
iOS:
- src/App/*
- src/Core/*
- lib/ios/*
- src/iOS/*
- 'src/iOS.Autofill/*'
- 'src/iOS.Core/*'
- 'src/iOS.Extension/*'
- 'src/iOS.ShareExtension/*'
- 'src/iOS.Widget/*'
- src/watchOS/*
watchOS:
- src/watchOS/*

57
.github/renovate.json vendored
View File

@@ -1,37 +1,22 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [ "extends": [
"config:base", "config:base",
":combinePatchMinorReleases", "schedule:monthly",
":dependencyDashboard", ":maintainLockFilesMonthly",
":maintainLockFilesWeekly", ":preserveSemverRanges",
":pinAllExceptPeerDependencies", ":rebaseStalePrs",
":prConcurrentLimit10", ":disableDependencyDashboard"
":rebaseStalePrs", ],
"schedule:weekends", "enabledManagers": [
":separateMajorReleases" "nuget"
], ],
"enabledManagers": ["cargo", "github-actions", "npm", "nuget"], "packageRules": [
"packageRules": [ {
{ "matchManagers": ["nuget"],
"groupName": "cargo minor", "groupName": "Nuget updates",
"matchManagers": ["cargo"], "groupSlug": "nuget",
"matchUpdateTypes": ["minor", "patch"] "separateMajorMinor": false
}, }
{ ]
"groupName": "gh minor", }
"matchManagers": ["github-actions"],
"matchUpdateTypes": ["minor", "patch"]
},
{
"groupName": "npm minor",
"matchManagers": ["npm"],
"matchUpdateTypes": ["minor", "patch"]
},
{
"groupName": "nuget minor",
"matchManagers": ["nuget"],
"matchUpdateTypes": ["minor", "patch"]
},
]
}

View File

@@ -14,7 +14,7 @@ jobs:
# Feature request # Feature request
- if: github.event.label.name == 'feature-request' - if: github.event.label.name == 'feature-request'
name: Feature request name: Feature request
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1 uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with: with:
comment: | comment: |
We use GitHub issues as a place to track bugs and other development related issues. The [Bitwarden Community Forums](https://community.bitwarden.com/) has a [Feature Requests](https://community.bitwarden.com/c/feature-requests) section for submitting, voting for, and discussing requests like this one. We use GitHub issues as a place to track bugs and other development related issues. The [Bitwarden Community Forums](https://community.bitwarden.com/) has a [Feature Requests](https://community.bitwarden.com/c/feature-requests) section for submitting, voting for, and discussing requests like this one.
@@ -25,7 +25,7 @@ jobs:
# Intended behavior # Intended behavior
- if: github.event.label.name == 'intended-behavior' - if: github.event.label.name == 'intended-behavior'
name: Intended behaviour name: Intended behaviour
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1 uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with: with:
comment: | comment: |
Your issue appears to be describing the intended behavior of the software. If you want this to be changed, it would be a feature request. Your issue appears to be describing the intended behavior of the software. If you want this to be changed, it would be a feature request.
@@ -38,7 +38,7 @@ jobs:
# Customer support request # Customer support request
- if: github.event.label.name == 'customer-support' - if: github.event.label.name == 'customer-support'
name: Customer Support request name: Customer Support request
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1 uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with: with:
comment: | comment: |
We use GitHub issues as a place to track bugs and other development related issues. Your issue appears to be a support request, or would otherwise be better handled by our dedicated Customer Success team. We use GitHub issues as a place to track bugs and other development related issues. Your issue appears to be a support request, or would otherwise be better handled by our dedicated Customer Success team.
@@ -49,14 +49,14 @@ jobs:
# Resolved # Resolved
- if: github.event.label.name == 'resolved' - if: github.event.label.name == 'resolved'
name: Resolved name: Resolved
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1 uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with: with:
comment: | comment: |
Weve closed this issue, as it appears the original problem has been resolved. If this happens again or continues to be an problem, please respond to this issue with any additional detail to assist with reproduction and root cause analysis. Weve closed this issue, as it appears the original problem has been resolved. If this happens again or continues to be an problem, please respond to this issue with any additional detail to assist with reproduction and root cause analysis.
# Stale # Stale
- if: github.event.label.name == 'stale' - if: github.event.label.name == 'stale'
name: Stale name: Stale
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1 uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with: with:
comment: | comment: |
As we havent heard from you about this problem in some time, this issue will now be closed. As we havent heard from you about this problem in some time, this issue will now be closed.

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Set up CLOC - name: Set up CLOC
run: | run: |
@@ -36,7 +36,7 @@ jobs:
hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }} hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }}
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
with: with:
submodules: 'true' submodules: 'true'
@@ -67,12 +67,12 @@ jobs:
variant: ["prod", "qa"] variant: ["prod", "qa"]
steps: steps:
- name: Setup NuGet - name: Setup NuGet
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0 uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
with: with:
nuget-version: 5.9.0 nuget-version: 5.9.0
- name: Set up MSBuild - name: Set up MSBuild
uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1 uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
- name: Setup Windows builder - name: Setup Windows builder
run: choco install checksum --no-progress run: choco install checksum --no-progress
@@ -105,7 +105,7 @@ jobs:
echo "GitHub event: $GITHUB_EVENT" echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Decrypt secrets - name: Decrypt secrets
@@ -157,7 +157,7 @@ jobs:
shell: pwsh shell: pwsh
- name: Report test results - name: Report test results
uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 # v1.6.0 uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226
if: always() if: always()
with: with:
name: Test Results name: Test Results
@@ -232,7 +232,7 @@ jobs:
shell: pwsh shell: pwsh
- name: Upload Prod .aab artifact - name: Upload Prod .aab artifact
if: ${{ matrix.variant == 'prod' }} if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with: with:
name: com.x8bit.bitwarden.aab name: com.x8bit.bitwarden.aab
path: ./com.x8bit.bitwarden.aab path: ./com.x8bit.bitwarden.aab
@@ -240,7 +240,7 @@ jobs:
- name: Upload Prod .apk artifact - name: Upload Prod .apk artifact
if: ${{ matrix.variant == 'prod' }} if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with: with:
name: com.x8bit.bitwarden.apk name: com.x8bit.bitwarden.apk
path: ./com.x8bit.bitwarden.apk path: ./com.x8bit.bitwarden.apk
@@ -248,7 +248,7 @@ jobs:
- name: Upload Other .apk artifact - name: Upload Other .apk artifact
if: ${{ matrix.variant != 'prod' }} if: ${{ matrix.variant != 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with: with:
name: com.x8bit.bitwarden.${{ matrix.variant }}.apk name: com.x8bit.bitwarden.${{ matrix.variant }}.apk
path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk
@@ -268,7 +268,7 @@ jobs:
- name: Upload .apk sha file for prod - name: Upload .apk sha file for prod
if: ${{ matrix.variant == 'prod' }} if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with: with:
name: bw-android-apk-sha256.txt name: bw-android-apk-sha256.txt
path: ./bw-android-apk-sha256.txt path: ./bw-android-apk-sha256.txt
@@ -276,7 +276,7 @@ jobs:
- name: Upload .apk sha file for other - name: Upload .apk sha file for other
if: ${{ matrix.variant != 'prod' }} if: ${{ matrix.variant != 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with: with:
name: bw-android-${{ matrix.variant }}-apk-sha256.txt name: bw-android-${{ matrix.variant }}-apk-sha256.txt
path: ./bw-android-${{ matrix.variant }}-apk-sha256.txt path: ./bw-android-${{ matrix.variant }}-apk-sha256.txt
@@ -303,12 +303,12 @@ jobs:
runs-on: windows-2022 runs-on: windows-2022
steps: steps:
- name: Setup NuGet - name: Setup NuGet
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0 uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
with: with:
nuget-version: 5.9.0 nuget-version: 5.9.0
- name: Set up MSBuild - name: Set up MSBuild
uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1 uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
- name: Setup Windows builder - name: Setup Windows builder
run: choco install checksum --no-progress run: choco install checksum --no-progress
@@ -342,7 +342,7 @@ jobs:
echo "GitHub event: $GITHUB_EVENT" echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Decrypt secrets - name: Decrypt secrets
env: env:
@@ -477,7 +477,7 @@ jobs:
shell: pwsh shell: pwsh
- name: Upload F-Droid .apk artifact - name: Upload F-Droid .apk artifact
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with: with:
name: com.x8bit.bitwarden-fdroid.apk name: com.x8bit.bitwarden-fdroid.apk
path: ./com.x8bit.bitwarden-fdroid.apk path: ./com.x8bit.bitwarden-fdroid.apk
@@ -489,7 +489,7 @@ jobs:
-t sha256 | Out-File -Encoding ASCII ./bw-fdroid-apk-sha256.txt -t sha256 | Out-File -Encoding ASCII ./bw-fdroid-apk-sha256.txt
- name: Upload F-Droid sha file - name: Upload F-Droid sha file
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with: with:
name: bw-fdroid-apk-sha256.txt name: bw-fdroid-apk-sha256.txt
path: ./bw-fdroid-apk-sha256.txt path: ./bw-fdroid-apk-sha256.txt
@@ -502,7 +502,7 @@ jobs:
needs: setup needs: setup
steps: steps:
- name: Setup NuGet - name: Setup NuGet
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0 uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
with: with:
nuget-version: 5.9.0 nuget-version: 5.9.0
@@ -515,19 +515,19 @@ jobs:
echo "GitHub event: $GITHUB_EVENT" echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
with: with:
submodules: 'true' submodules: 'true'
- name: Login to Azure - CI Subscription - name: Login to Azure - Prod Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3 uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
with: with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets - name: Retrieve secrets
id: retrieve-secrets id: retrieve-secrets
env: env:
KEYVAULT: bitwarden-ci KEYVAULT: bitwarden-prod-kv
SECRETS: | SECRETS: |
appcenter-ios-token appcenter-ios-token
run: | run: |
@@ -698,7 +698,7 @@ jobs:
shell: bash shell: bash
- name: Upload App Store .ipa & dSYMs artifacts - name: Upload App Store .ipa & dSYMs artifacts
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with: with:
name: Bitwarden iOS name: Bitwarden iOS
path: | path: |
@@ -771,17 +771,17 @@ jobs:
_CROWDIN_PROJECT_ID: "269690" _CROWDIN_PROJECT_ID: "269690"
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Login to Azure - CI Subscription - name: Login to Azure
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3 uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
with: with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets - name: Retrieve secrets
id: retrieve-secrets id: retrieve-secrets
env: env:
KEYVAULT: bitwarden-ci KEYVAULT: bitwarden-prod-kv
SECRETS: | SECRETS: |
crowdin-api-token crowdin-api-token
run: | run: |
@@ -793,7 +793,7 @@ jobs:
done done
- name: Upload Sources - name: Upload Sources
uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0 uses: crowdin/github-action@9237b4cb361788dfce63feb2e2f15c09e2fe7415
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
@@ -839,17 +839,17 @@ jobs:
exit 1 exit 1
fi fi
- name: Login to Azure - CI Subscription - name: Login to Azure - Prod Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3 uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
if: failure() if: failure()
with: with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets - name: Retrieve secrets
id: retrieve-secrets id: retrieve-secrets
if: failure() if: failure()
env: env:
KEYVAULT: bitwarden-ci KEYVAULT: bitwarden-prod-kv
SECRETS: | SECRETS: |
devops-alerts-slack-webhook-url devops-alerts-slack-webhook-url
run: | run: |
@@ -861,7 +861,7 @@ jobs:
done done
- name: Notify Slack on failure - name: Notify Slack on failure
uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 uses: act10ns/slack@da3191ebe2e67f49b46880b4633f5591a96d1d33
if: failure() if: failure()
env: env:
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}

View File

@@ -15,22 +15,22 @@ jobs:
_CROWDIN_PROJECT_ID: "269690" _CROWDIN_PROJECT_ID: "269690"
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
- name: Login to Azure - CI Subscription - name: Login to Azure
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3 uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
with: with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets - name: Retrieve secrets
id: retrieve-secrets id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@34ecb67b2a357795dc893549df0795e7383ff50f uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af
with: with:
keyvault: "bitwarden-ci" keyvault: "bitwarden-prod-kv"
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase" secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
- name: Download translations - name: Download translations
uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0 uses: crowdin/github-action@12143a68c213f3c6d9913c9e5023224f7231face
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- name: Enforce Label - name: Enforce Label
uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # 2.2.2 uses: yogevbd/enforce-label-action@8d1e1709b1011e6d90400a0e6cf7c0b77aa5efeb
with: with:
BANNED_LABELS: "hold,needs-qa" BANNED_LABELS: "hold,needs-qa"
BANNED_LABELS_DESCRIPTION: "PRs with the hold or needs-qa labels cannot be merged" BANNED_LABELS_DESCRIPTION: "PRs with the hold or needs-qa labels cannot be merged"

View File

@@ -1,17 +0,0 @@
---
name: "Pull Request Labeler"
on:
pull_request_target: {}
jobs:
labeler:
name: "Pull Request Labeler"
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-20.04
steps:
- uses: actions/labeler@ba790c862c380240c6d5e7427be5ace9a05c754b # v4.0.3
with:
sync-labels: true

View File

@@ -38,11 +38,11 @@ jobs:
fi fi
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
- name: Check Release Version - name: Check Release Version
id: version id: version
uses: bitwarden/gh-actions/release-version-check@34ecb67b2a357795dc893549df0795e7383ff50f uses: bitwarden/gh-actions/release-version-check@8f055ef543c7433c967a1b9b04a0f230923233bb
with: with:
release-type: ${{ github.event.inputs.release_type }} release-type: ${{ github.event.inputs.release_type }}
project-type: xamarin project-type: xamarin
@@ -56,7 +56,7 @@ jobs:
- name: Create GitHub deployment - name: Create GitHub deployment
if: ${{ github.event.inputs.release_type != 'Dry Run' }} if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5 uses: chrnorm/deployment-action@1b599fe41a0ef1f95191e7f2eec4743f2d7dfc48
id: deployment id: deployment
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
@@ -68,7 +68,7 @@ jobs:
- name: Download all artifacts - name: Download all artifacts
if: ${{ github.event.inputs.release_type != 'Dry Run' }} if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
with: with:
workflow: build.yml workflow: build.yml
workflow_conclusion: success workflow_conclusion: success
@@ -76,7 +76,7 @@ jobs:
- name: Dry Run - Download all artifacts - name: Dry Run - Download all artifacts
if: ${{ github.event.inputs.release_type == 'Dry Run' }} if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
with: with:
workflow: build.yml workflow: build.yml
workflow_conclusion: success workflow_conclusion: success
@@ -93,8 +93,8 @@ jobs:
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk, ./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk, ./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk,
./Bitwarden iOS.zip, ./Bitwarden iOS.zip,
./bw-android-apk-sha256.txt/bw-android-apk-sha256.txt, ./bw-android-apk-sha256.txt,
./bw-fdroid-apk-sha256.txt/bw-fdroid-apk-sha256.txt" ./bw-fdroid-apk-sha256.txt"
commit: ${{ github.sha }} commit: ${{ github.sha }}
tag: v${{ steps.version.outputs.version }} tag: v${{ steps.version.outputs.version }}
name: Version ${{ steps.version.outputs.version }} name: Version ${{ steps.version.outputs.version }}
@@ -104,7 +104,7 @@ jobs:
- name: Update deployment status to Success - name: Update deployment status to Success
if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }} if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
state: 'success' state: 'success'
@@ -112,7 +112,7 @@ jobs:
- name: Update deployment status to Failure - name: Update deployment status to Failure
if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }} if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
state: 'failure' state: 'failure'
@@ -126,11 +126,11 @@ jobs:
if: inputs.fdroid_publish if: inputs.fdroid_publish
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
- name: Download F-Droid .apk artifact - name: Download F-Droid .apk artifact
if: ${{ github.event.inputs.release_type != 'Dry Run' }} if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
with: with:
workflow: build.yml workflow: build.yml
workflow_conclusion: success workflow_conclusion: success
@@ -139,7 +139,7 @@ jobs:
- name: Dry Run - Download F-Droid .apk artifact - name: Dry Run - Download F-Droid .apk artifact
if: ${{ github.event.inputs.release_type == 'Dry Run' }} if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10
with: with:
workflow: build.yml workflow: build.yml
workflow_conclusion: success workflow_conclusion: success

View File

@@ -14,7 +14,7 @@ jobs:
version_number: ${{ steps.version.outputs.new-version }} version_number: ${{ steps.version.outputs.new-version }}
steps: steps:
- name: Checkout Branch - name: Checkout Branch
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
- name: Calculate bumped version - name: Calculate bumped version
id: version id: version
@@ -32,14 +32,32 @@ jobs:
echo "new-version=$NEW_VER" >> $GITHUB_OUTPUT echo "new-version=$NEW_VER" >> $GITHUB_OUTPUT
trigger_version_bump: trigger_version_bump:
name: "Version bump" name: "Trigger version bump workflow"
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: needs:
- setup - setup
steps: steps:
- name: Bump version to ${{ needs.setup.outputs.version_number }} - name: Login to Azure
uses: ./.github/workflows/version-bump.yml uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010
secrets:
AZURE_PROD_KV_CREDENTIALS: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
with: with:
version_number: ${{ needs.setup.outputs.version_number }} creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af
with:
keyvault: "bitwarden-prod-kv"
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
- name: Call GitHub API to trigger workflow bump
env:
TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
VERSION: ${{ needs.setup.outputs.version_number}}
run: |
JSON_STRING=$(printf '{"ref":"master", "inputs": { "version_number":"%s"}}' "$VERSION")
curl \
-X POST \
-i -u bitwarden-devops-bot:$TOKEN \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/bitwarden/mobile/actions/workflows/version-bump.yml/dispatches \
-d $JSON_STRING

View File

@@ -7,14 +7,6 @@ on:
version_number: version_number:
description: "New Version" description: "New Version"
required: true required: true
workflow_call:
inputs:
version_number:
required: true
type: string
secrets:
AZURE_PROD_KV_CREDENTIALS:
required: true
jobs: jobs:
bump_version: bump_version:
@@ -22,22 +14,22 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- name: Checkout Branch - name: Checkout Branch
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
- name: Login to Azure - CI Subscription - name: Login to Azure - Prod Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3 uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
with: with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets - name: Retrieve secrets
id: retrieve-secrets id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@34ecb67b2a357795dc893549df0795e7383ff50f uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af
with: with:
keyvault: "bitwarden-ci" keyvault: "bitwarden-prod-kv"
secrets: "github-gpg-private-key, github-gpg-private-key-passphrase" secrets: "github-gpg-private-key, github-gpg-private-key-passphrase"
- name: Import GPG key - name: Import GPG key
uses: crazy-max/ghaction-import-gpg@111c56156bcc6918c056dbef52164cfa583dc549 # v5.2.0 uses: crazy-max/ghaction-import-gpg@c8bb57c57e8df1be8c73ff3d59deab1dbc00e0d1
with: with:
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }} gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }} passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
@@ -48,31 +40,31 @@ jobs:
run: git switch -c version_bump_${{ github.event.inputs.version_number }} run: git switch -c version_bump_${{ github.event.inputs.version_number }}
- name: Bump Version - Android XML - name: Bump Version - Android XML
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
with: with:
version: ${{ github.event.inputs.version_number }} version: ${{ github.event.inputs.version_number }}
file_path: "./src/Android/Properties/AndroidManifest.xml" file_path: "./src/Android/Properties/AndroidManifest.xml"
- name: Bump Version - iOS.Autofill - name: Bump Version - iOS.Autofill
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
with: with:
version: ${{ github.event.inputs.version_number }} version: ${{ github.event.inputs.version_number }}
file_path: "./src/iOS.Autofill/Info.plist" file_path: "./src/iOS.Autofill/Info.plist"
- name: Bump Version - iOS.Extension - name: Bump Version - iOS.Extension
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
with: with:
version: ${{ github.event.inputs.version_number }} version: ${{ github.event.inputs.version_number }}
file_path: "./src/iOS.Extension/Info.plist" file_path: "./src/iOS.Extension/Info.plist"
- name: Bump Version - iOS.ShareExtension - name: Bump Version - iOS.ShareExtension
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
with: with:
version: ${{ github.event.inputs.version_number }} version: ${{ github.event.inputs.version_number }}
file_path: "./src/iOS.ShareExtension/Info.plist" file_path: "./src/iOS.ShareExtension/Info.plist"
- name: Bump Version - iOS - name: Bump Version - iOS
uses: bitwarden/gh-actions/version-bump@34ecb67b2a357795dc893549df0795e7383ff50f uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
with: with:
version: ${{ github.event.inputs.version_number }} version: ${{ github.event.inputs.version_number }}
file_path: "./src/iOS/Info.plist" file_path: "./src/iOS/Info.plist"

View File

@@ -8,4 +8,4 @@ on:
jobs: jobs:
call-workflow: call-workflow:
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@34ecb67b2a357795dc893549df0795e7383ff50f uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@master

View File

@@ -46,8 +46,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\i
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamariniOS17CredentialProviderBinding", "lib\ios\iOS17CredentialProvider\XamariniOS17CredentialProvider\XamariniOS17CredentialProviderBinding\XamariniOS17CredentialProviderBinding.csproj", "{63938BA1-0F72-4BFD-B7A4-823C146E43B4}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -448,36 +446,6 @@ Global
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.AppStore|Any CPU.Build.0 = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.AppStore|iPhone.Build.0 = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|iPhone.Build.0 = Debug|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.FDroid|Any CPU.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.FDroid|Any CPU.Build.0 = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.FDroid|iPhone.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.FDroid|iPhone.Build.0 = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|Any CPU.Build.0 = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|iPhone.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|iPhone.Build.0 = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -496,7 +464,6 @@ Global
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA} {8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {D10CA4A9-F866-40E1-B658-F69051236C71} {F8C3F648-EA5A-4719-8005-85D1690B1655} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71} {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{63938BA1-0F72-4BFD-B7A4-823C146E43B4} = {D10CA4A9-F866-40E1-B658-F69051236C71}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410} SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}

View File

@@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 25.0.1706.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamariniOS17CredentialProviderBinding", "XamariniOS17CredentialProviderBinding\XamariniOS17CredentialProviderBinding.csproj", "{63938BA1-0F72-4BFD-B7A4-823C146E43B4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63938BA1-0F72-4BFD-B7A4-823C146E43B4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5EE0FEF7-C01A-4CAF-BCF0-3A1B818D056A}
EndGlobalSection
EndGlobal

View File

@@ -1,141 +0,0 @@
using System;
using AuthenticationServices;
using Foundation;
using ObjCRuntime;
namespace XamariniOS17CredentialProviderBinding
{
interface IASCredentialProviderCompatDelegate { }
// @protocol ASCredentialProviderCompatDelegate
[Protocol, Model(AutoGeneratedName = true)]
interface ASCredentialProviderCompatDelegate
{
// @required -(void)prepareInterfaceToProvideCredentialCompatFor:(ASCredentialRequestCompat * _Nonnull)credentialRequest;
[iOS(17, 0)]
[Abstract]
[Export("prepareInterfaceToProvideCredentialCompatFor:")]
void PrepareInterfaceToProvideCredentialCompatFor(ASCredentialRequestCompat credentialRequest);
// @required -(void)provideCredentialWithoutUserInteractionCompatFor:(ASCredentialRequestCompat * _Nonnull)credentialRequest;
[iOS(17, 0)]
[Abstract]
[Export("provideCredentialWithoutUserInteractionCompatFor:")]
void ProvideCredentialWithoutUserInteractionCompatFor(ASCredentialRequestCompat credentialRequest);
// @required -(void)prepareInterfaceCompatForPasskeyRegistration:(ASCredentialRequestCompat * _Nonnull)registrationRequest;
[iOS(17, 0)]
[Abstract]
[Export("prepareInterfaceCompatForPasskeyRegistration:")]
void PrepareInterfaceCompatForPasskeyRegistration(ASCredentialRequestCompat registrationRequest);
}
// @interface ASCredentialRequestCompat : NSObject
[iOS(17, 0)]
[BaseType (typeof(NSObject))]
[DisableDefaultCtor]
interface ASCredentialRequestCompat
{
// @property (nonatomic) enum ASCredentialRequestCompatType type;
[Export("type", ArgumentSemantic.Assign)]
ASCredentialRequestCompatType Type { get; set; }
// @property (nonatomic, strong) id<ASCredentialIdentity> _Nonnull credentialIdentity;
//[Export("credentialIdentity", ArgumentSemantic.Strong)]
//ASCredentialIdentity CredentialIdentity { get; set; }
// @property (readonly, nonatomic, strong) ASPasswordCredentialIdentity * _Nullable passwordCredentialIdentity;
[iOS(17, 0)]
[NullAllowed, Export("passwordCredentialIdentity", ArgumentSemantic.Strong)]
ASPasswordCredentialIdentity PasswordCredentialIdentity { get; }
// @property (readonly, nonatomic, strong) ASPasskeyCredentialIdentityCompat * _Nullable passkeyCredentialIdentity;
[iOS(17, 0)]
[NullAllowed, Export("passkeyCredentialIdentity", ArgumentSemantic.Strong)]
ASPasskeyCredentialIdentityCompat PasskeyCredentialIdentity { get; }
}
// @interface ASPasskeyCredentialIdentityCompat : NSObject
[BaseType(typeof(NSObject))]
[DisableDefaultCtor]
interface ASPasskeyCredentialIdentityCompat
{
// @property (copy, nonatomic) NSString * _Nonnull relyingPartyIdentifier;
[Export("relyingPartyIdentifier")]
string RelyingPartyIdentifier { get; set; }
// @property (copy, nonatomic) NSString * _Nonnull userName;
[Export("userName")]
string UserName { get; set; }
// @property (copy, nonatomic) NSData * _Nonnull credentialID;
[Export("credentialID", ArgumentSemantic.Copy)]
NSData CredentialID { get; set; }
// @property (copy, nonatomic) NSData * _Nonnull userHandle;
[Export("userHandle", ArgumentSemantic.Copy)]
NSData UserHandle { get; set; }
// @property (copy, nonatomic) NSString * _Nullable recordIdentifier;
[NullAllowed, Export("recordIdentifier")]
string RecordIdentifier { get; set; }
// @property (nonatomic) NSInteger rank;
[Export("rank")]
nint Rank { get; set; }
}
// @interface BaseASCredentialProviderViewController : ASCredentialProviderViewController
[BaseType (typeof(ASCredentialProviderViewController))]
interface BaseASCredentialProviderViewController
{
// -(void)prepareInterfaceToProvideCredentialForRequest:(id<ASCredentialRequest> _Nonnull)credentialRequest;
//[Export ("prepareInterfaceToProvideCredentialForRequest:")]
//void PrepareInterfaceToProvideCredentialForRequest (ASCredentialRequest credentialRequest);
//// -(void)provideCredentialWithoutUserInteractionForRequest:(id<ASCredentialRequest> _Nonnull)credentialRequest;
//[Export ("provideCredentialWithoutUserInteractionForRequest:")]
//void ProvideCredentialWithoutUserInteractionForRequest (ASCredentialRequest credentialRequest);
//// -(void)prepareInterfaceForPasskeyRegistration:(id<ASCredentialRequest> _Nonnull)registrationRequest;
//[Export ("prepareInterfaceForPasskeyRegistration:")]
//void PrepareInterfaceForPasskeyRegistration (ASCredentialRequest registrationRequest);
// -(void)prepareInterfaceToProvideCredentialCompatFor:(ASCredentialRequestCompat * _Nonnull)credentialRequest;
//[Export ("prepareInterfaceToProvideCredentialCompatFor:")]
// [Abstract]
// void PrepareInterfaceToProvideCredentialCompatFor (ASCredentialRequestCompat credentialRequest);
//// -(void)provideCredentialWithoutUserInteractionCompatFor:(ASCredentialRequestCompat * _Nonnull)credentialRequest;
//[Export ("provideCredentialWithoutUserInteractionCompatFor:")]
// [Abstract]
// void ProvideCredentialWithoutUserInteractionCompatFor (ASCredentialRequestCompat credentialRequest);
//// -(void)prepareInterfaceCompatForPasskeyRegistration:(ASCredentialRequestCompat * _Nonnull)registrationRequest;
//[Export ("prepareInterfaceCompatForPasskeyRegistration:")]
// [Abstract]
// void PrepareInterfaceCompatForPasskeyRegistration (ASCredentialRequestCompat registrationRequest);
[iOS(17, 0)]
[Export("SetCompatDelegate:")]
void SetCompatDelegate(IASCredentialProviderCompatDelegate @delegate);
// -(instancetype _Nonnull)initWithNibName:(NSString * _Nullable)nibNameOrNil bundle:(NSBundle * _Nullable)nibBundleOrNil __attribute__((objc_designated_initializer));
[Export ("initWithNibName:bundle:")]
[DesignatedInitializer]
IntPtr Constructor ([NullAllowed] string nibNameOrNil, [NullAllowed] NSBundle nibBundleOrNil);
//// -(instancetype _Nullable)initWithCoder:(NSCoder * _Nonnull)coder __attribute__((objc_designated_initializer));
//[Export ("initWithCoder:")]
//[DesignatedInitializer]
// IntPtr Constructor (NSCoder coder);
}
// @interface MyTest : NSObject
[BaseType(typeof(NSObject))]
//[DisableDefaultCtor]
interface MyTest
{
}
}

View File

@@ -1,35 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using Foundation;
// This attribute allows you to mark your assemblies as “safe to link”.
// When the attribute is present, the linker—if enabled—will process the assembly
// even if youre using the “Link SDK assemblies only” option, which is the default for device builds.
[assembly: LinkerSafe]
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("NativeLibrary")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NativeLibrary")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

View File

@@ -1,11 +0,0 @@
using ObjCRuntime;
namespace XamariniOS17CredentialProviderBinding
{
[Native]
public enum ASCredentialRequestCompatType : long
{
word = 0,
keyAssertion = 1
}
}

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectTypeGuids>{8FFB629D-F513-41CE-95D2-7ECE97B6EEEC};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<ProjectGuid>{63938BA1-0F72-4BFD-B7A4-823C146E43B4}</ProjectGuid>
<TemplateGuid>{b6f3ff35-79b2-4f25-a2fc-60a7cf61013b}</TemplateGuid>
<OutputType>Library</OutputType>
<RootNamespace>NativeLibrary</RootNamespace>
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
<AssemblyName>NativeLibrary</AssemblyName>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="Xamarin.iOS" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ObjcBindingApiDefinition Include="ApiDefinitions.cs" />
</ItemGroup>
<ItemGroup>
<ObjcBindingCoreSource Include="StructsAndEnums.cs" />
</ItemGroup>
<ItemGroup>
<NativeReference Include="..\..\iOS17CredentialProvider\build\Release-fat\iOS17CredentialProvider.framework">
<Kind>Framework</Kind>
<SmartLink>True</SmartLink>
<ForceLoad>False</ForceLoad>
<WeakFrameworks>Foundation AuthenticationServices</WeakFrameworks>
</NativeReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.ObjCBinding.CSharp.targets" />
</Project>

View File

@@ -1,14 +0,0 @@
echo "Building framework"
xcodebuild -sdk iphonesimulator17.0 -project "iOS17CredentialProvider.xcodeproj" -configuration Release -arch x86_64
xcodebuild -sdk iphoneos17.0 -project "iOS17CredentialProvider.xcodeproj" -configuration Release
cd build
cp -R "Release-iphoneos" "Release-fat"
cp -R "Release-iphonesimulator/iOS17CredentialProvider.framework/Modules/iOS17CredentialProvider.swiftmodule/" "Release-fat/iOS17CredentialProvider.framework/Modules/iOS17CredentialProvider.swiftmodule/"
lipo -create -output "Release-fat/iOS17CredentialProvider.framework/iOS17CredentialProvider" "Release-iphoneos/iOS17CredentialProvider.framework/iOS17CredentialProvider" "Release-iphonesimulator/iOS17CredentialProvider.framework/iOS17CredentialProvider"
echo "Sharpie creating binding definitions"
sharpie bind --sdk=iphoneos17.0 --output="XamarinApiDef" --namespace="Binding" --scope="Release-iphoneos/iOS17CredentialProvider.framework/Headers/" "Release-iphoneos/iOS17CredentialProvider.framework/Headers/iOS17CredentialProvider-Swift.h"
echo "Done!"

View File

@@ -1,357 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
B72040A62A4E421C00E62648 /* iOS17CredentialProvider.docc in Sources */ = {isa = PBXBuildFile; fileRef = B72040A52A4E421C00E62648 /* iOS17CredentialProvider.docc */; };
B72040A72A4E421C00E62648 /* iOS17CredentialProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = B72040A42A4E421C00E62648 /* iOS17CredentialProvider.h */; settings = {ATTRIBUTES = (Public, ); }; };
B72040AE2A535D6500E62648 /* BaseASCredentialProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B72040AD2A535D6500E62648 /* BaseASCredentialProviderViewController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
B72040A12A4E421C00E62648 /* iOS17CredentialProvider.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = iOS17CredentialProvider.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B72040A42A4E421C00E62648 /* iOS17CredentialProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iOS17CredentialProvider.h; sourceTree = "<group>"; };
B72040A52A4E421C00E62648 /* iOS17CredentialProvider.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = iOS17CredentialProvider.docc; sourceTree = "<group>"; };
B72040AD2A535D6500E62648 /* BaseASCredentialProviderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseASCredentialProviderViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
B720409E2A4E421C00E62648 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
B72040972A4E421C00E62648 = {
isa = PBXGroup;
children = (
B72040A32A4E421C00E62648 /* iOS17CredentialProvider */,
B72040A22A4E421C00E62648 /* Products */,
);
sourceTree = "<group>";
};
B72040A22A4E421C00E62648 /* Products */ = {
isa = PBXGroup;
children = (
B72040A12A4E421C00E62648 /* iOS17CredentialProvider.framework */,
);
name = Products;
sourceTree = "<group>";
};
B72040A32A4E421C00E62648 /* iOS17CredentialProvider */ = {
isa = PBXGroup;
children = (
B72040A42A4E421C00E62648 /* iOS17CredentialProvider.h */,
B72040A52A4E421C00E62648 /* iOS17CredentialProvider.docc */,
B72040AD2A535D6500E62648 /* BaseASCredentialProviderViewController.swift */,
);
path = "iOS17CredentialProvider";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
B720409C2A4E421C00E62648 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
B72040A72A4E421C00E62648 /* iOS17CredentialProvider.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
B72040A02A4E421C00E62648 /* iOS17CredentialProvider */ = {
isa = PBXNativeTarget;
buildConfigurationList = B72040AA2A4E421C00E62648 /* Build configuration list for PBXNativeTarget "iOS17CredentialProvider" */;
buildPhases = (
B720409C2A4E421C00E62648 /* Headers */,
B720409D2A4E421C00E62648 /* Sources */,
B720409E2A4E421C00E62648 /* Frameworks */,
B720409F2A4E421C00E62648 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "iOS17CredentialProvider";
productName = "iOS17CredentialProvider";
productReference = B72040A12A4E421C00E62648 /* iOS17CredentialProvider.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
B72040982A4E421C00E62648 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastUpgradeCheck = 1500;
TargetAttributes = {
B72040A02A4E421C00E62648 = {
CreatedOnToolsVersion = 15.0;
};
};
};
buildConfigurationList = B720409B2A4E421C00E62648 /* Build configuration list for PBXProject "iOS17CredentialProvider" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = B72040972A4E421C00E62648;
productRefGroup = B72040A22A4E421C00E62648 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
B72040A02A4E421C00E62648 /* iOS17CredentialProvider */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B720409F2A4E421C00E62648 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
B720409D2A4E421C00E62648 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B72040A62A4E421C00E62648 /* iOS17CredentialProvider.docc in Sources */,
B72040AE2A535D6500E62648 /* BaseASCredentialProviderViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
B72040A82A4E421C00E62648 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
B72040A92A4E421C00E62648 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
B72040AB2A4E421C00E62648 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = LTZ2PFU5D6;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_MODULE_VERIFIER = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
PRODUCT_BUNDLE_IDENTIFIER = "com.8bit.bitwarden.iOS17CredentialProvider";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
B72040AC2A4E421C00E62648 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = LTZ2PFU5D6;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_MODULE_VERIFIER = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
PRODUCT_BUNDLE_IDENTIFIER = "com.8bit.bitwarden.iOS17CredentialProvider";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
B720409B2A4E421C00E62648 /* Build configuration list for PBXProject "iOS17CredentialProvider" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B72040A82A4E421C00E62648 /* Debug */,
B72040A92A4E421C00E62648 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B72040AA2A4E421C00E62648 /* Build configuration list for PBXNativeTarget "iOS17CredentialProvider" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B72040AB2A4E421C00E62648 /* Debug */,
B72040AC2A4E421C00E62648 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = B72040982A4E421C00E62648 /* Project object */;
}

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B72040A02A4E421C00E62648"
BuildableName = "iOS17CredentialProvider.framework"
BlueprintName = "iOS17CredentialProvider"
ReferencedContainer = "container:iOS17CredentialProvider.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B72040A02A4E421C00E62648"
BuildableName = "iOS17CredentialProvider.framework"
BlueprintName = "iOS17CredentialProvider"
ReferencedContainer = "container:iOS17CredentialProvider.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,188 +0,0 @@
//
// BaseASCredentialProviderViewController.swift
// iOS17CredentialProvider
//
// Created by Federico Maccaroni on 03/07/2023.
//
import Foundation
import AuthenticationServices
@objc(ASCredentialRequestCompatType)
public enum ASCredentialRequestCompatType : Int, @unchecked Sendable {
case password = 0
case passkeyAssertion = 1
}
@objc(ASPasskeyCredentialIdentityCompat)
public class ASPasskeyCredentialIdentityCompat : NSObject {
init(relyingPartyIdentifier: String, userName: String, credentialID: Data, userHandle: Data, recordIdentifier: String? = nil, rank: Int) {
self.relyingPartyIdentifier = relyingPartyIdentifier
self.userName = userName
self.credentialID = credentialID
self.userHandle = userHandle
self.recordIdentifier = recordIdentifier
self.rank = rank
}
/** The relying party identifier of this passkey credential.
@discussion This field is reported as the serviceIdentifier property of ASCredentialIdentity.
*/
@objc
public var relyingPartyIdentifier: String
/** The user name of this passkey credential.
@discussion This field is reported as the user property of ASCredentialIdentity.
*/
@objc
public var userName: String
/** The credential ID of this passkey credential.
@discussion This field is used to identify the correct credential to use based on relying party request parameters.
*/
@objc
public var credentialID: Data
/** The user handle of this passkey credential.
@discussion This field is used to identify the correct credential to use based on relying party request parameters.
*/
@objc
public var userHandle: Data
/** Get the record identifier.
@result The record identifier.
@discussion You can utilize the record identifier to uniquely identify the credential identity in your local database.
*/
@objc
public var recordIdentifier: String?
/** Get or set the rank of the credential identity object.
@discussion The system may utilize the rank to decide which credential identity precedes the other
if two identities have the same service identifier. A credential identity with a larger rank value
precedes one with a smaller value if both credential identities have the same service identifier.
The default value of this property is 0.
*/
@objc
public var rank: Int
}
@available(iOS 17.0, *)
@objc(ASCredentialRequestCompat)
public class ASCredentialRequestCompat : NSObject {
/** The type of credential used for this request.
*/
@objc
public var type: ASCredentialRequestCompatType
/** The credential identity selected by the user to authenticate.
*/
@available(iOS 17.0, *)
var credentialIdentity: ASCredentialIdentity
@available(iOS 17.0, *)
@objc
public var passwordCredentialIdentity: ASPasswordCredentialIdentity? {
guard type == .password, let password = credentialIdentity as? ASPasswordCredentialIdentity else {
return nil
}
return password
}
@available(iOS 17.0, *)
@objc
public var passkeyCredentialIdentity: ASPasskeyCredentialIdentityCompat? {
guard type == .passkeyAssertion, let passkey = credentialIdentity as? ASPasskeyCredentialIdentity else {
return nil
}
return ASPasskeyCredentialIdentityCompat(relyingPartyIdentifier: passkey.relyingPartyIdentifier, userName: passkey.userName, credentialID: passkey.credentialID, userHandle: passkey.userHandle, recordIdentifier: passkey.recordIdentifier, rank: passkey.rank)
}
@available(iOS 17.0, *)
init(type: ASCredentialRequestCompatType, credentialIdentity: ASCredentialIdentity) {
self.type = type
self.credentialIdentity = credentialIdentity
}
}
@objc(BaseASCredentialProviderViewController)
open class BaseASCredentialProviderViewController : ASCredentialProviderViewController
{
// MARK: iOS 17 new methods
@available(iOS 17.0, *)
override final public func prepareInterfaceToProvideCredential(for credentialRequest: ASCredentialRequest) {
guard let compatDelegate = compatDelegate else {
return
}
compatDelegate.prepareInterfaceToProvideCredentialCompat(for: convertRequestToCompat(from: credentialRequest))
}
@available(iOS 17.0, *)
override final public func provideCredentialWithoutUserInteraction(for credentialRequest: ASCredentialRequest) {
guard let compatDelegate = compatDelegate else {
return
}
compatDelegate.provideCredentialWithoutUserInteractionCompat(for: convertRequestToCompat(from: credentialRequest))
}
@available(iOS 17.0, *)
override final public func prepareInterface(forPasskeyRegistration registrationRequest: ASCredentialRequest) {
guard let compatDelegate = compatDelegate else {
return
}
compatDelegate.prepareInterfaceCompat(forPasskeyRegistration: convertRequestToCompat(from: registrationRequest))
}
// MARK: Compat
var compatDelegate: ASCredentialProviderCompatDelegate? = nil;
@objc
@available(iOS 17.0, *)
public func SetCompatDelegate(_ delegate: ASCredentialProviderCompatDelegate)
{
compatDelegate = delegate
}
@available(iOS 17.0, *)
func convertRequestToCompat(from credentialRequest: ASCredentialRequest) -> ASCredentialRequestCompat {
return ASCredentialRequestCompat(
type: credentialRequest.type == .password ? .password : .passkeyAssertion,
credentialIdentity: credentialRequest.credentialIdentity)
}
}
@objc(ASCredentialProviderCompatDelegate)
public protocol ASCredentialProviderCompatDelegate
{
@available(iOS 17.0, *)
@objc
func prepareInterfaceToProvideCredentialCompat(for credentialRequest: ASCredentialRequestCompat)
@available(iOS 17.0, *)
@objc
func provideCredentialWithoutUserInteractionCompat(for credentialRequest: ASCredentialRequestCompat)
@available(iOS 17.0, *)
@objc
func prepareInterfaceCompat(forPasskeyRegistration registrationRequest: ASCredentialRequestCompat)
}
@objc(MyTest)
public class MyTest : NSObject
{
var a: String
init(a: String) {
self.a = a
}
}

View File

@@ -1,13 +0,0 @@
# ``iOS17CredentialProvider``
<!--@START_MENU_TOKEN@-->Summary<!--@END_MENU_TOKEN@-->
## Overview
<!--@START_MENU_TOKEN@-->Text<!--@END_MENU_TOKEN@-->
## Topics
### <!--@START_MENU_TOKEN@-->Group<!--@END_MENU_TOKEN@-->
- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@-->

View File

@@ -1,18 +0,0 @@
//
// iOS17CredentialProvider.h
// iOS17CredentialProvider
//
// Created by Federico Maccaroni on 29/06/2023.
//
#import <Foundation/Foundation.h>
//! Project version number for iOS17CredentialProvider.
FOUNDATION_EXPORT double iOS17CredentialProviderVersionNumber;
//! Project version string for iOS17CredentialProvider.
FOUNDATION_EXPORT const unsigned char iOS17CredentialProviderVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <iOS17CredentialProvider/PublicHeader.h>

View File

@@ -78,20 +78,21 @@
<Version>1.9.0</Version> <Version>1.9.0</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.5.1.1" /> <PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.5.1.1" />
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.16" /> <PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.14" />
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.19" /> <PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.17" />
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.10.0" /> <PackageReference Include="Xamarin.AndroidX.Core" Version="1.9.0.1" />
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0.15" />
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.3.1.1" /> <PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.3.1.1" />
<PackageReference Include="Xamarin.Essentials"> <PackageReference Include="Xamarin.Essentials">
<Version>1.7.5</Version> <Version>1.7.3</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Xamarin.Firebase.Messaging"> <PackageReference Include="Xamarin.Firebase.Messaging">
<Version>123.1.1.1</Version> <Version>123.0.8</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.8.0" /> <PackageReference Include="Xamarin.Google.Android.Material" Version="1.6.1.1" />
<PackageReference Include="Xamarin.Google.Dagger" Version="2.44.2.1" /> <PackageReference Include="Xamarin.Google.Dagger" Version="2.41.0.2" />
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet"> <PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
<Version>118.0.1.3</Version> <Version>118.0.1.2</Version>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -159,7 +160,6 @@
<Compile Include="Constants.cs" /> <Compile Include="Constants.cs" />
<Compile Include="Effects\RemoveFontPaddingEffect.cs" /> <Compile Include="Effects\RemoveFontPaddingEffect.cs" />
<Compile Include="Services\WatchDeviceService.cs" /> <Compile Include="Services\WatchDeviceService.cs" />
<Compile Include="Renderers\CustomLabelRenderer.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<AndroidAsset Include="Assets\bwi-font.ttf" /> <AndroidAsset Include="Assets\bwi-font.ttf" />

View File

@@ -12,7 +12,7 @@ namespace Bit.Droid.Autofill
private List<Field> _passwordFields = null; private List<Field> _passwordFields = null;
private List<Field> _usernameFields = null; private List<Field> _usernameFields = null;
private HashSet<string> _ignoreSearchTerms = new HashSet<string> { "search", "find", "recipient", "edit" }; private HashSet<string> _ignoreSearchTerms = new HashSet<string> { "search", "find", "recipient", "edit" };
private HashSet<string> _usernameTerms = new HashSet<string> { "email", "phone", "username" }; private HashSet<string> _usernameTerms = new HashSet<string> { "email", "phone", "username"};
private HashSet<string> _passwordTerms = new HashSet<string> { "password", "pswd" }; private HashSet<string> _passwordTerms = new HashSet<string> { "password", "pswd" };
public List<AutofillId> AutofillIds { get; private set; } = new List<AutofillId>(); public List<AutofillId> AutofillIds { get; private set; } = new List<AutofillId>();
@@ -54,14 +54,15 @@ namespace Bit.Droid.Autofill
if (HintToFieldsMap.ContainsKey(View.AutofillHintPassword)) if (HintToFieldsMap.ContainsKey(View.AutofillHintPassword))
{ {
_passwordFields.AddRange(HintToFieldsMap[View.AutofillHintPassword]); _passwordFields.AddRange(HintToFieldsMap[View.AutofillHintPassword]);
return _passwordFields;
} }
} }
else
_passwordFields = Fields.Where(f => FieldIsPassword(f)).ToList();
if (!_passwordFields.Any())
{ {
_passwordFields = Fields.Where(f => FieldHasPasswordTerms(f)).ToList(); _passwordFields = Fields.Where(f => FieldIsPassword(f)).ToList();
if (!_passwordFields.Any())
{
_passwordFields = Fields.Where(f => FieldHasPasswordTerms(f)).ToList();
}
} }
return _passwordFields; return _passwordFields;
} }
@@ -86,25 +87,23 @@ namespace Bit.Droid.Autofill
{ {
_usernameFields.AddRange(HintToFieldsMap[View.AutofillHintUsername]); _usernameFields.AddRange(HintToFieldsMap[View.AutofillHintUsername]);
} }
if (_usernameFields.Any())
{
return _usernameFields;
}
} }
else
foreach (var passwordField in PasswordFields)
{ {
var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId) foreach (var passwordField in PasswordFields)
.LastOrDefault();
if (usernameField != null)
{ {
_usernameFields.Add(usernameField); var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId)
.LastOrDefault();
if (usernameField != null)
{
_usernameFields.Add(usernameField);
}
} }
}
if (!_usernameFields.Any()) if (!_usernameFields.Any())
{ {
_usernameFields = Fields.Where(f => FieldIsUsername(f)).ToList(); _usernameFields = Fields.Where(f => FieldIsUsername(f)).ToList();
}
} }
return _usernameFields; return _usernameFields;
} }

View File

@@ -21,7 +21,6 @@ using Bit.App.Utilities;
using Bit.App.Pages; using Bit.App.Pages;
using Bit.App.Utilities.AccountManagement; using Bit.App.Utilities.AccountManagement;
using Bit.App.Controls; using Bit.App.Controls;
using Bit.Core.Enums;
#if !FDROID #if !FDROID
using Android.Gms.Security; using Android.Gms.Security;
#endif #endif
@@ -82,8 +81,7 @@ namespace Bit.Droid
ServiceContainer.Resolve<IAuthService>("authService"), ServiceContainer.Resolve<IAuthService>("authService"),
ServiceContainer.Resolve<ILogger>("logger"), ServiceContainer.Resolve<ILogger>("logger"),
ServiceContainer.Resolve<IMessagingService>("messagingService"), ServiceContainer.Resolve<IMessagingService>("messagingService"),
ServiceContainer.Resolve<IWatchDeviceService>(), ServiceContainer.Resolve<IWatchDeviceService>());
ServiceContainer.Resolve<IConditionedAwaiterManager>());
ServiceContainer.Register<IAccountsManager>("accountsManager", accountsManager); ServiceContainer.Register<IAccountsManager>("accountsManager", accountsManager);
} }
#if !FDROID #if !FDROID
@@ -148,7 +146,7 @@ namespace Bit.Droid
var storageMediatorService = new StorageMediatorService(mobileStorageService, secureStorageService, preferencesStorage); var storageMediatorService = new StorageMediatorService(mobileStorageService, secureStorageService, preferencesStorage);
var stateService = new StateService(mobileStorageService, secureStorageService, storageMediatorService, messagingService); var stateService = new StateService(mobileStorageService, secureStorageService, storageMediatorService, messagingService);
var stateMigrationService = var stateMigrationService =
new StateMigrationService(DeviceType.Android, liteDbStorage, preferencesStorage, secureStorageService); new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
var clipboardService = new ClipboardService(stateService); var clipboardService = new ClipboardService(stateService);
var deviceActionService = new DeviceActionService(stateService, messagingService); var deviceActionService = new DeviceActionService(stateService, messagingService);
var fileService = new FileService(stateService, broadcasterService); var fileService = new FileService(stateService, broadcasterService);
@@ -156,7 +154,7 @@ namespace Bit.Droid
messagingService, broadcasterService); messagingService, broadcasterService);
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService, var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService,
platformUtilsService, new LazyResolve<IEventService>()); platformUtilsService, new LazyResolve<IEventService>());
var biometricService = new BiometricService(stateService); var biometricService = new BiometricService();
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService); var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
var cryptoService = new CryptoService(stateService, cryptoFunctionService); var cryptoService = new CryptoService(stateService, cryptoFunctionService);
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService); var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.5.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.3.0" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.NFC" />

View File

@@ -1,31 +0,0 @@
using System;
using Bit.App.Controls;
using System.ComponentModel;
using Xamarin.Forms.Platform.Android;
using Android.Content;
using Xamarin.Forms;
using Bit.Droid.Renderers;
[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRenderer))]
namespace Bit.Droid.Renderers
{
public class CustomLabelRenderer : LabelRenderer
{
public CustomLabelRenderer(Context context)
: base(context)
{ }
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var label = sender as CustomLabel;
switch (e.PropertyName)
{
case nameof(CustomLabel.AutomationId):
Control.ContentDescription = label.AutomationId;
break;
}
base.OnElementPropertyChanged(sender, e);
}
}
}

View File

@@ -4,6 +4,7 @@ using Android.OS;
using Android.Security.Keystore; using Android.Security.Keystore;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities;
using Java.Security; using Java.Security;
using Javax.Crypto; using Javax.Crypto;
@@ -11,8 +12,6 @@ namespace Bit.Droid.Services
{ {
public class BiometricService : IBiometricService public class BiometricService : IBiometricService
{ {
private readonly IStateService _stateService;
private const string KeyName = "com.8bit.bitwarden.biometric_integrity"; private const string KeyName = "com.8bit.bitwarden.biometric_integrity";
private const string KeyStoreName = "AndroidKeyStore"; private const string KeyStoreName = "AndroidKeyStore";
@@ -24,28 +23,28 @@ namespace Bit.Droid.Services
private readonly KeyStore _keystore; private readonly KeyStore _keystore;
public BiometricService(IStateService stateService) public BiometricService()
{ {
_stateService = stateService;
_keystore = KeyStore.GetInstance(KeyStoreName); _keystore = KeyStore.GetInstance(KeyStoreName);
_keystore.Load(null); _keystore.Load(null);
} }
public async Task<bool> SetupBiometricAsync(string bioIntegritySrcKey = null) public Task<bool> SetupBiometricAsync(string bioIntegrityKey = null)
{ {
// bioIntegrityKey used in iOS only
if (Build.VERSION.SdkInt >= BuildVersionCodes.M) if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{ {
await CreateKeyAsync(bioIntegritySrcKey); CreateKey();
} }
return true; return Task.FromResult(true);
} }
public async Task<bool> IsSystemBiometricIntegrityValidAsync(string bioIntegritySrcKey = null) public Task<bool> ValidateIntegrityAsync(string bioIntegrityKey = null)
{ {
if (Build.VERSION.SdkInt < BuildVersionCodes.M) if (Build.VERSION.SdkInt < BuildVersionCodes.M)
{ {
return true; return Task.FromResult(true);
} }
try try
@@ -56,7 +55,7 @@ namespace Bit.Droid.Services
if (key == null || cipher == null) if (key == null || cipher == null)
{ {
return true; return Task.FromResult(true);
} }
cipher.Init(CipherMode.EncryptMode, key); cipher.Init(CipherMode.EncryptMode, key);
@@ -64,32 +63,25 @@ namespace Bit.Droid.Services
catch (KeyPermanentlyInvalidatedException e) catch (KeyPermanentlyInvalidatedException e)
{ {
// Biometric has changed // Biometric has changed
await ClearStateAsync(bioIntegritySrcKey); return Task.FromResult(false);
return false;
} }
catch (UnrecoverableKeyException e) catch (UnrecoverableKeyException e)
{ {
// Biometric was disabled and re-enabled // Biometric was disabled and re-enabled
await ClearStateAsync(bioIntegritySrcKey); return Task.FromResult(false);
return false;
} }
catch (InvalidKeyException e) catch (InvalidKeyException e)
{ {
// Fallback for old bitwarden users without a key // Fallback for old bitwarden users without a key
LoggerHelper.LogEvenIfCantBeResolved(e); LoggerHelper.LogEvenIfCantBeResolved(e);
await CreateKeyAsync(bioIntegritySrcKey); CreateKey();
} }
return true; return Task.FromResult(true);
} }
private async Task CreateKeyAsync(string bioIntegritySrcKey = null) private void CreateKey()
{ {
bioIntegritySrcKey ??= Core.Constants.BiometricIntegritySourceKey;
await _stateService.SetSystemBiometricIntegrityState(bioIntegritySrcKey,
await GetStateAsync(bioIntegritySrcKey));
await _stateService.SetAccountBiometricIntegrityValidAsync(bioIntegritySrcKey);
try try
{ {
var keyGen = KeyGenerator.GetInstance(KeyAlgorithm, KeyStoreName); var keyGen = KeyGenerator.GetInstance(KeyAlgorithm, KeyStoreName);
@@ -109,16 +101,5 @@ namespace Bit.Droid.Services
LoggerHelper.LogEvenIfCantBeResolved(e); LoggerHelper.LogEvenIfCantBeResolved(e);
} }
} }
private async Task<string> GetStateAsync(string bioIntegritySrcKey)
{
return await _stateService.GetSystemBiometricIntegrityState(bioIntegritySrcKey) ??
Guid.NewGuid().ToString();
}
private async Task ClearStateAsync(string bioIntegritySrcKey)
{
await _stateService.SetSystemBiometricIntegrityState(bioIntegritySrcKey, null);
}
} }
} }

View File

@@ -14,11 +14,11 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Plugin.Fingerprint" Version="2.1.5" /> <PackageReference Include="Plugin.Fingerprint" Version="2.1.5" />
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.88.3" /> <PackageReference Include="SkiaSharp.Views.Forms" Version="2.88.2" />
<PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.6" /> <PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.5" />
<PackageReference Include="Xamarin.Essentials" Version="1.7.5" /> <PackageReference Include="Xamarin.Essentials" Version="1.7.3" />
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" /> <PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2578" /> <PackageReference Include="Xamarin.Forms" Version="5.0.0.2515" />
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" /> <PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" /> <PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
<PackageReference Include="MessagePack" Version="2.4.59" /> <PackageReference Include="MessagePack" Version="2.4.59" />
@@ -145,7 +145,6 @@
<Folder Include="Controls\DateTime\" /> <Folder Include="Controls\DateTime\" />
<Folder Include="Controls\IconLabelButton\" /> <Folder Include="Controls\IconLabelButton\" />
<Folder Include="Controls\PasswordStrengthProgressBar\" /> <Folder Include="Controls\PasswordStrengthProgressBar\" />
<Folder Include="Utilities\Automation\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -441,6 +440,5 @@
<None Remove="MessagePack" /> <None Remove="MessagePack" />
<None Remove="MessagePack.MSBuild.Tasks" /> <None Remove="MessagePack.MSBuild.Tasks" />
<None Remove="Controls\PasswordStrengthProgressBar\" /> <None Remove="Controls\PasswordStrengthProgressBar\" />
<None Remove="Utilities\Automation\" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -38,7 +38,6 @@ namespace Bit.App
private readonly IFileService _fileService; private readonly IFileService _fileService;
private readonly IAccountsManager _accountsManager; private readonly IAccountsManager _accountsManager;
private readonly IPushNotificationService _pushNotificationService; private readonly IPushNotificationService _pushNotificationService;
private readonly IConfigService _configService;
private static bool _isResumed; private static bool _isResumed;
// these variables are static because the app is launching new activities on notification click, creating new instances of App. // these variables are static because the app is launching new activities on notification click, creating new instances of App.
private static bool _pendingCheckPasswordlessLoginRequests; private static bool _pendingCheckPasswordlessLoginRequests;
@@ -62,7 +61,6 @@ namespace Bit.App
_fileService = ServiceContainer.Resolve<IFileService>(); _fileService = ServiceContainer.Resolve<IFileService>();
_accountsManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager"); _accountsManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>(); _pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>();
_configService = ServiceContainer.Resolve<IConfigService>();
_accountsManager.Init(() => Options, this); _accountsManager.Init(() => Options, this);
@@ -163,18 +161,6 @@ namespace Bit.App
new NavigationPage(new RemoveMasterPasswordPage())); new NavigationPage(new RemoveMasterPasswordPage()));
}); });
} }
else if (message.Command == Constants.ForceUpdatePassword)
{
Device.BeginInvokeOnMainThread(async () =>
{
await Application.Current.MainPage.Navigation.PushModalAsync(
new NavigationPage(new UpdateTempPasswordPage()));
});
}
else if (message.Command == "syncCompleted")
{
await _configService.GetAsync(true);
}
else if (message.Command == Constants.PasswordlessLoginRequestKey else if (message.Command == Constants.PasswordlessLoginRequestKey
|| message.Command == "unlocked" || message.Command == "unlocked"
|| message.Command == AccountsManagerMessageCommands.ACCOUNT_SWITCH_COMPLETED) || message.Command == AccountsManagerMessageCommands.ACCOUNT_SWITCH_COMPLETED)
@@ -299,8 +285,6 @@ namespace Bit.App
// Reset delay on every start // Reset delay on every start
_vaultTimeoutService.DelayLockAndLogoutMs = null; _vaultTimeoutService.DelayLockAndLogoutMs = null;
} }
await _configService.GetAsync();
_messagingService.Send("startEventTimer"); _messagingService.Send("startEventTimer");
} }

View File

@@ -30,15 +30,13 @@
BackgroundColor="{DynamicResource BackgroundColor}" BackgroundColor="{DynamicResource BackgroundColor}"
VerticalOptions="Start" VerticalOptions="Start"
RowHeight="{Binding AccountListRowHeight, Source={x:Reference _mainOverlay}}" RowHeight="{Binding AccountListRowHeight, Source={x:Reference _mainOverlay}}"
effects:ScrollViewContentInsetAdjustmentBehaviorEffect.ContentInsetAdjustmentBehavior="Never" effects:ScrollViewContentInsetAdjustmentBehaviorEffect.ContentInsetAdjustmentBehavior="Never">
AutomationId="AccountListView">
<ListView.ItemTemplate> <ListView.ItemTemplate>
<DataTemplate x:DataType="view:AccountView"> <DataTemplate x:DataType="view:AccountView">
<controls:AccountViewCell <controls:AccountViewCell
Account="{Binding .}" Account="{Binding .}"
SelectAccountCommand="{Binding SelectAccountCommand, Source={x:Reference _mainOverlay}}" SelectAccountCommand="{Binding SelectAccountCommand, Source={x:Reference _mainOverlay}}"
LongPressAccountCommand="{Binding LongPressAccountCommand, Source={x:Reference _mainOverlay}}" LongPressAccountCommand="{Binding LongPressAccountCommand, Source={x:Reference _mainOverlay}}"
AutomationId="AccountViewCell"
/> />
</DataTemplate> </DataTemplate>
</ListView.ItemTemplate> </ListView.ItemTemplate>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms" <ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit" xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
@@ -60,23 +60,20 @@
Text="{Binding AccountView.Email}" Text="{Binding AccountView.Email}"
IsVisible="{Binding IsActive}" IsVisible="{Binding IsActive}"
StyleClass="accountlist-title, accountlist-title-platform" StyleClass="accountlist-title, accountlist-title-platform"
LineBreakMode="TailTruncation" LineBreakMode="TailTruncation" />
AutomationId="AccountEmailLabel" />
<Label <Label
Grid.Row="0" Grid.Row="0"
Text="{Binding AccountView.Email}" Text="{Binding AccountView.Email}"
IsVisible="{Binding IsActive, Converter={StaticResource inverseBool}}" IsVisible="{Binding IsActive, Converter={StaticResource inverseBool}}"
StyleClass="accountlist-title, accountlist-title-platform" StyleClass="accountlist-title, accountlist-title-platform"
TextColor="{DynamicResource MutedColor}" TextColor="{DynamicResource MutedColor}"
LineBreakMode="TailTruncation" LineBreakMode="TailTruncation" />
AutomationId="AccountEmailLabel" />
<Label <Label
Grid.Row="1" Grid.Row="1"
IsVisible="{Binding ShowHostname}" IsVisible="{Binding ShowHostname}"
Text="{Binding AccountView.Hostname}" Text="{Binding AccountView.Hostname}"
StyleClass="accountlist-sub, accountlist-sub-platform" StyleClass="accountlist-sub, accountlist-sub-platform"
LineBreakMode="TailTruncation" LineBreakMode="TailTruncation" />
AutomationId="AccountHostUrlLabel" />
<Label <Label
Grid.Row="2" Grid.Row="2"
Text="{u:I18n AccountUnlocked}" Text="{u:I18n AccountUnlocked}"
@@ -84,8 +81,7 @@
StyleClass="accountlist-sub, accountlist-sub-platform" StyleClass="accountlist-sub, accountlist-sub-platform"
FontAttributes="Italic" FontAttributes="Italic"
TextTransform="Lowercase" TextTransform="Lowercase"
LineBreakMode="TailTruncation" LineBreakMode="TailTruncation" />
AutomationId="AccountStatusLabel" />
<Label <Label
Grid.Row="2" Grid.Row="2"
Text="{u:I18n AccountLocked}" Text="{u:I18n AccountLocked}"
@@ -93,8 +89,7 @@
StyleClass="accountlist-sub, accountlist-sub-platform" StyleClass="accountlist-sub, accountlist-sub-platform"
FontAttributes="Italic" FontAttributes="Italic"
TextTransform="Lowercase" TextTransform="Lowercase"
LineBreakMode="TailTruncation" LineBreakMode="TailTruncation" />
AutomationId="AccountStatusLabel" />
<Label <Label
Grid.Row="2" Grid.Row="2"
Text="{u:I18n AccountLoggedOut}" Text="{u:I18n AccountLoggedOut}"
@@ -102,8 +97,7 @@
StyleClass="accountlist-sub, accountlist-sub-platform" StyleClass="accountlist-sub, accountlist-sub-platform"
FontAttributes="Italic" FontAttributes="Italic"
TextTransform="Lowercase" TextTransform="Lowercase"
LineBreakMode="TailTruncation" LineBreakMode="TailTruncation" />
AutomationId="AccountStatusLabel" />
</Grid> </Grid>
<controls:IconLabel <controls:IconLabel
@@ -113,8 +107,7 @@
Margin="12,0" Margin="12,0"
HorizontalOptions="Center" HorizontalOptions="Center"
VerticalOptions="Center" VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform" StyleClass="list-icon, list-icon-platform" />
AutomationId="InactiveVaultIcon" />
<controls:IconLabel <controls:IconLabel
Grid.Column="2" Grid.Column="2"
Text="{Binding AuthStatusIconActive}" Text="{Binding AuthStatusIconActive}"
@@ -123,8 +116,7 @@
HorizontalOptions="Center" HorizontalOptions="Center"
VerticalOptions="Center" VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform" StyleClass="list-icon, list-icon-platform"
TextColor="{DynamicResource TextColor}" TextColor="{DynamicResource TextColor}"/>
AutomationId="ActiveVaultIcon" />
</Grid> </Grid>
<Grid <Grid
@@ -155,8 +147,7 @@
StyleClass="accountlist-title, accountlist-title-platform" StyleClass="accountlist-title, accountlist-title-platform"
LineBreakMode="TailTruncation" LineBreakMode="TailTruncation"
VerticalOptions="Center" VerticalOptions="Center"
Grid.Column="1" Grid.Column="1" />
AutomationId="AddAccountButton" />
</Grid> </Grid>
</Grid> </Grid>
</ViewCell> </ViewCell>

View File

@@ -9,8 +9,7 @@
StyleClass="list-row, list-row-platform" StyleClass="list-row, list-row-platform"
RowSpacing="0" RowSpacing="0"
ColumnSpacing="0" ColumnSpacing="0"
x:DataType="controls:CipherViewCellViewModel" x:DataType="controls:CipherViewCellViewModel">
AutomationId="CipherCell">
<Grid.Resources> <Grid.Resources>
<u:IconGlyphConverter x:Key="iconGlyphConverter"/> <u:IconGlyphConverter x:Key="iconGlyphConverter"/>
@@ -37,8 +36,7 @@
IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}" IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}"
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}" Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
ShouldUpdateFontSizeDynamicallyForAccesibility="True" ShouldUpdateFontSizeDynamicallyForAccesibility="True"
AutomationProperties.IsInAccessibleTree="False" AutomationProperties.IsInAccessibleTree="False" />
AutomationId="CipherTypeIcon" />
<ff:CachedImage <ff:CachedImage
x:Name="_iconImage" x:Name="_iconImage"
@@ -54,8 +52,7 @@
Aspect="AspectFit" Aspect="AspectFit"
IsVisible="{Binding ShowIconImage}" IsVisible="{Binding ShowIconImage}"
Source="{Binding IconImageSource, Mode=OneTime}" Source="{Binding IconImageSource, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="False" AutomationProperties.IsInAccessibleTree="False" />
AutomationId="CipherWebsiteIcon" />
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7"> <Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
<Grid.RowDefinitions> <Grid.RowDefinitions>
@@ -74,8 +71,7 @@
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="0"
StyleClass="list-title, list-title-platform" StyleClass="list-title, list-title-platform"
Text="{Binding Cipher.Name}" Text="{Binding Cipher.Name}" />
AutomationId="CipherNameLabel" />
<Label <Label
LineBreakMode="TailTruncation" LineBreakMode="TailTruncation"
Grid.Column="0" Grid.Column="0"
@@ -84,8 +80,7 @@
StyleClass="list-subtitle, list-subtitle-platform" StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Cipher.SubTitle}" Text="{Binding Cipher.SubTitle}"
IsVisible="{Binding Source={RelativeSource Self}, Path=Text, IsVisible="{Binding Source={RelativeSource Self}, Path=Text,
Converter={StaticResource stringHasValueConverter}}" Converter={StaticResource stringHasValueConverter}}"/>
AutomationId="CipherSubTitleLabel" />
<controls:IconLabel <controls:IconLabel
Grid.Column="1" Grid.Column="1"
Grid.Row="0" Grid.Row="0"
@@ -96,8 +91,7 @@
Text="{Binding Source={x:Static core:BitwardenIcons.Collection}}" Text="{Binding Source={x:Static core:BitwardenIcons.Collection}}"
IsVisible="{Binding Cipher.Shared, Mode=OneTime}" IsVisible="{Binding Cipher.Shared, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Shared}" AutomationProperties.Name="{u:I18n Shared}" />
AutomationId="CipherInCollectionIcon" />
<controls:IconLabel <controls:IconLabel
Grid.Column="2" Grid.Column="2"
Grid.Row="0" Grid.Row="0"
@@ -108,8 +102,7 @@
Text="{Binding Source={x:Static core:BitwardenIcons.Paperclip}}" Text="{Binding Source={x:Static core:BitwardenIcons.Paperclip}}"
IsVisible="{Binding Cipher.HasAttachments, Mode=OneTime}" IsVisible="{Binding Cipher.HasAttachments, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Attachments}" AutomationProperties.Name="{u:I18n Attachments}" />
AutomationId="CipherWithAttachmentsIcon" />
</Grid> </Grid>
<controls:MiButton <controls:MiButton
@@ -121,7 +114,6 @@
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalOptions="EndAndExpand" HorizontalOptions="EndAndExpand"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" AutomationProperties.Name="{u:I18n Options}" />
AutomationId="CipherOptionsButton" />
</controls:ExtendedGrid> </controls:ExtendedGrid>

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Windows.Input;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@@ -19,7 +18,7 @@ namespace Bit.App.Controls
nameof(WebsiteIconsEnabled), typeof(bool?), typeof(CipherViewCell)); nameof(WebsiteIconsEnabled), typeof(bool?), typeof(CipherViewCell));
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create( public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
nameof(ButtonCommand), typeof(ICommand), typeof(CipherViewCell)); nameof(ButtonCommand), typeof(Command<CipherView>), typeof(CipherViewCell));
public CipherViewCell() public CipherViewCell()
{ {
@@ -43,9 +42,9 @@ namespace Bit.App.Controls
set => SetValue(CipherProperty, value); set => SetValue(CipherProperty, value);
} }
public ICommand ButtonCommand public Command<CipherView> ButtonCommand
{ {
get => GetValue(ButtonCommandProperty) as ICommand; get => GetValue(ButtonCommandProperty) as Command<CipherView>;
set => SetValue(ButtonCommandProperty, value); set => SetValue(ButtonCommandProperty, value);
} }

View File

@@ -1,13 +0,0 @@
using System;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class CustomLabel : Label
{
public CustomLabel()
{
}
}
}

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms" <controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.SendViewCell" x:Class="Bit.App.Controls.SendViewCell"
@@ -54,16 +54,14 @@
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="0"
StyleClass="list-title, list-title-platform" StyleClass="list-title, list-title-platform"
Text="{Binding Send.Name}" Text="{Binding Send.Name}" />
AutomationId="SendNameLabel" />
<Label <Label
LineBreakMode="TailTruncation" LineBreakMode="TailTruncation"
Grid.Column="0" Grid.Column="0"
Grid.Row="1" Grid.Row="1"
Grid.ColumnSpan="6" Grid.ColumnSpan="6"
StyleClass="list-subtitle, list-subtitle-platform" StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Send.DisplayDate}" Text="{Binding Send.DisplayDate}" />
AutomationId="SendDateLabel" />
<controls:IconLabel <controls:IconLabel
Grid.Column="1" Grid.Column="1"
Grid.Row="0" Grid.Row="0"
@@ -74,8 +72,7 @@
Text="{Binding Source={x:Static core:BitwardenIcons.ExclamationTriangle}}" Text="{Binding Source={x:Static core:BitwardenIcons.ExclamationTriangle}}"
IsVisible="{Binding Send.Disabled, Mode=OneTime}" IsVisible="{Binding Send.Disabled, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Disabled}" AutomationProperties.Name="{u:I18n Disabled}" />
AutomationId="DisabledSendLabel" />
<controls:IconLabel <controls:IconLabel
Grid.Column="2" Grid.Column="2"
Grid.Row="0" Grid.Row="0"
@@ -86,8 +83,7 @@
Text="{Binding Source={x:Static core:BitwardenIcons.Key}}" Text="{Binding Source={x:Static core:BitwardenIcons.Key}}"
IsVisible="{Binding Send.HasPassword, Mode=OneTime}" IsVisible="{Binding Send.HasPassword, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Password}" AutomationProperties.Name="{u:I18n Password}" />
AutomationId="PasswordProtectedSendLabel" />
<controls:IconLabel <controls:IconLabel
Grid.Column="3" Grid.Column="3"
Grid.Row="0" Grid.Row="0"
@@ -98,8 +94,7 @@
Text="{Binding Source={x:Static core:BitwardenIcons.Ban}}" Text="{Binding Source={x:Static core:BitwardenIcons.Ban}}"
IsVisible="{Binding Send.MaxAccessCountReached, Mode=OneTime}" IsVisible="{Binding Send.MaxAccessCountReached, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n MaxAccessCountReached}" AutomationProperties.Name="{u:I18n MaxAccessCountReached}" />
AutomationId="SendMaxAccessCountReachedLabel" />
<controls:IconLabel <controls:IconLabel
Grid.Column="4" Grid.Column="4"
Grid.Row="0" Grid.Row="0"
@@ -110,8 +105,7 @@
Text="{Binding Source={x:Static core:BitwardenIcons.Clock}}" Text="{Binding Source={x:Static core:BitwardenIcons.Clock}}"
IsVisible="{Binding Send.Expired, Mode=OneTime}" IsVisible="{Binding Send.Expired, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Expired}" AutomationProperties.Name="{u:I18n Expired}" />
AutomationId="ExpiredSendLabel" />
<controls:IconLabel <controls:IconLabel
Grid.Column="5" Grid.Column="5"
Grid.Row="0" Grid.Row="0"
@@ -122,8 +116,7 @@
Text="{Binding Source={x:Static core:BitwardenIcons.Trash}}" Text="{Binding Source={x:Static core:BitwardenIcons.Trash}}"
IsVisible="{Binding Send.PendingDelete, Mode=OneTime}" IsVisible="{Binding Send.PendingDelete, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n PendingDelete}" AutomationProperties.Name="{u:I18n PendingDelete}" />
AutomationId="SendWithPendingDeletionLabel" />
</Grid> </Grid>
<controls:MiButton <controls:MiButton
@@ -136,7 +129,6 @@
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalOptions="EndAndExpand" HorizontalOptions="EndAndExpand"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" AutomationProperties.Name="{u:I18n Options}" />
AutomationId="SendOptionsButton" />
</controls:ExtendedGrid> </controls:ExtendedGrid>

View File

@@ -33,8 +33,7 @@
StyleClass="box-label" StyleClass="box-label"
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0"
IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" />
AutomationId="BooleanCustomFieldNameLabel" />
<Label <Label
Text="{Binding Field.Name, Mode=OneWay}" Text="{Binding Field.Name, Mode=OneWay}"
IsVisible="{Binding IsEditing}" IsVisible="{Binding IsEditing}"
@@ -50,15 +49,13 @@
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="0, 5, 0, 0" Margin="0, 5, 0, 0"
IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" />
AutomationId="BooleanCustomFieldValueLabel" />
<Switch <Switch
IsToggled="{Binding BooleanValue}" IsToggled="{Binding BooleanValue}"
IsVisible="{Binding IsEditing}" IsVisible="{Binding IsEditing}"
Grid.Row="0" Grid.Row="0"
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2" />
AutomationId="BooleanCustomFieldValueToggle" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Cog}}" Text="{Binding Source={x:Static core:BitwardenIcons.Cog}}"

View File

@@ -31,8 +31,7 @@
Text="{Binding Field.Name, Mode=OneWay}" Text="{Binding Field.Name, Mode=OneWay}"
StyleClass="box-label" StyleClass="box-label"
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0" />
AutomationId="HiddenCustomFieldNameLabel" />
<StackLayout <StackLayout
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
@@ -40,8 +39,7 @@
<controls:MonoLabel <controls:MonoLabel
Text="{Binding ValueText, Mode=OneWay}" Text="{Binding ValueText, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
IsVisible="{Binding ShowHiddenValue}" IsVisible="{Binding ShowHiddenValue}" />
AutomationId="HiddenCustomFieldValueLabel" />
<controls:MonoLabel <controls:MonoLabel
Text="{Binding Field.MaskedValue, Mode=OneWay}" Text="{Binding Field.MaskedValue, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
@@ -56,10 +54,7 @@
IsPassword="{Binding ShowHiddenValue, Converter={StaticResource inverseBool}}" IsPassword="{Binding ShowHiddenValue, Converter={StaticResource inverseBool}}"
IsEnabled="{Binding ShowViewHidden}" IsEnabled="{Binding ShowViewHidden}"
IsSpellCheckEnabled="False" IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False" IsTextPredictionEnabled="False">
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{Binding Field.Name}"
AutomationId="HiddenCustomFieldValueEntry">
<Entry.Keyboard> <Entry.Keyboard>
<Keyboard x:FactoryMethod="Create"> <Keyboard x:FactoryMethod="Create">
<x:Arguments> <x:Arguments>
@@ -77,8 +72,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}" />
AutomationId="HiddenCustomFieldShowValueButton" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}" Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}"

View File

@@ -29,15 +29,13 @@
Text="{Binding Field.Name, Mode=OneWay}" Text="{Binding Field.Name, Mode=OneWay}"
StyleClass="box-label" StyleClass="box-label"
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0" />
AutomationId="LinkedCustomFieldNameLabel" />
<controls:IconLabel <controls:IconLabel
Text="{Binding ValueText, Mode=OneWay}" Text="{Binding ValueText, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" />
AutomationId="LinkedCustomFieldValueLabel" />
<StackLayout <StackLayout
StyleClass="box-row, box-row-input" StyleClass="box-row, box-row-input"
IsVisible="{Binding IsEditing}"> IsVisible="{Binding IsEditing}">
@@ -46,8 +44,7 @@
ItemsSource="{Binding LinkedFieldOptions, Mode=OneTime}" ItemsSource="{Binding LinkedFieldOptions, Mode=OneTime}"
SelectedIndex="{Binding LinkedFieldOptionSelectedIndex}" SelectedIndex="{Binding LinkedFieldOptionSelectedIndex}"
ItemDisplayBinding="{Binding Key}" ItemDisplayBinding="{Binding Key}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="LinkedCustomFieldValuePicker" />
</StackLayout> </StackLayout>
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
@@ -58,8 +55,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" AutomationProperties.Name="{u:I18n Options}" />
AutomationId="LinkedCustomFieldOptionsButton" />
</Grid> </Grid>
<BoxView StyleClass="box-row-separator" IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" /> <BoxView StyleClass="box-row-separator" IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" />
</StackLayout> </StackLayout>

View File

@@ -29,24 +29,19 @@
Text="{Binding Field.Name, Mode=OneWay}" Text="{Binding Field.Name, Mode=OneWay}"
StyleClass="box-label" StyleClass="box-label"
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0" />
AutomationId="TextCustomFieldNameLabel" />
<Label <Label
Text="{Binding ValueText, Mode=OneWay}" Text="{Binding ValueText, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" />
AutomationId="TextCustomFieldValueLabel" />
<Entry <Entry
Text="{Binding Field.Value}" Text="{Binding Field.Value}"
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
IsVisible="{Binding IsEditing}" IsVisible="{Binding IsEditing}" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{Binding Field.Name}"
AutomationId="TextCustomFieldValueEntry" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}" Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}"
@@ -56,8 +51,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Copy}" AutomationProperties.Name="{u:I18n Copy}" />
AutomationId="TextCustomFieldCopyValue" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Cog}}" Text="{Binding Source={x:Static core:BitwardenIcons.Cog}}"
@@ -67,8 +61,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" AutomationProperties.Name="{u:I18n Options}" />
AutomationId="TextCustomFieldOptionsButton" />
</Grid> </Grid>
<BoxView StyleClass="box-row-separator" IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" /> <BoxView StyleClass="box-row-separator" IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" />
</StackLayout> </StackLayout>

View File

@@ -1,7 +1,10 @@
using System.Text; using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core; using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
@@ -70,13 +73,13 @@ namespace Bit.App.Pages
set => SetProperty(ref _policy, value); set => SetProperty(ref _policy, value);
} }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public string ShowPasswordIcon => ShowPassword ? "" : "";
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow; public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
public string MasterPassword { get; set; } public string MasterPassword { get; set; }
public string ConfirmMasterPassword { get; set; } public string ConfirmMasterPassword { get; set; }
public string Hint { get; set; } public string Hint { get; set; }
public virtual async Task InitAsync(bool forceSync = false) public async Task InitAsync(bool forceSync = false)
{ {
if (forceSync) if (forceSync)
{ {

View File

@@ -34,8 +34,7 @@
Placeholder="ex. https://bitwarden.company.com" Placeholder="ex. https://bitwarden.company.com"
StyleClass="box-value" StyleClass="box-value"
ReturnType="Go" ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" ReturnCommand="{Binding SubmitCommand}" />
AutomationId="ServerUrlEntry"/>
</StackLayout> </StackLayout>
<Label <Label
Text="{u:I18n SelfHostedEnvironmentFooter}" Text="{u:I18n SelfHostedEnvironmentFooter}"
@@ -54,8 +53,7 @@
x:Name="_webVaultEntry" x:Name="_webVaultEntry"
Text="{Binding WebVaultUrl}" Text="{Binding WebVaultUrl}"
Keyboard="Url" Keyboard="Url"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="WebVaultUrlEntry"/>
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row"> <StackLayout StyleClass="box-row">
<Label <Label
@@ -65,8 +63,7 @@
x:Name="_apiEntry" x:Name="_apiEntry"
Text="{Binding ApiUrl}" Text="{Binding ApiUrl}"
Keyboard="Url" Keyboard="Url"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ApiUrlEntry"/>
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row"> <StackLayout StyleClass="box-row">
<Label <Label
@@ -76,8 +73,7 @@
x:Name="_identityEntry" x:Name="_identityEntry"
Text="{Binding IdentityUrl}" Text="{Binding IdentityUrl}"
Keyboard="Url" Keyboard="Url"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="IdentityUrlEntry"/>
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row"> <StackLayout StyleClass="box-row">
<Label <Label
@@ -89,8 +85,7 @@
Keyboard="Url" Keyboard="Url"
StyleClass="box-value" StyleClass="box-value"
ReturnType="Go" ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" ReturnCommand="{Binding SubmitCommand}" />
AutomationId="IconsUrlEntry"/>
</StackLayout> </StackLayout>
<Label <Label
Text="{u:I18n CustomEnvironmentFooter}" Text="{u:I18n CustomEnvironmentFooter}"

View File

@@ -3,7 +3,6 @@ using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Models.Data;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
@@ -19,8 +18,7 @@ namespace Bit.App.Pages
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService"); _environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
PageTitle = AppResources.Settings; PageTitle = AppResources.Settings;
BaseUrl = _environmentService.BaseUrl == EnvironmentUrlData.DefaultEU.Base || EnvironmentUrlData.DefaultUS.Base == _environmentService.BaseUrl ? BaseUrl = _environmentService.BaseUrl;
string.Empty : _environmentService.BaseUrl;
WebVaultUrl = _environmentService.WebVaultUrl; WebVaultUrl = _environmentService.WebVaultUrl;
ApiUrl = _environmentService.ApiUrl; ApiUrl = _environmentService.ApiUrl;
IdentityUrl = _environmentService.IdentityUrl; IdentityUrl = _environmentService.IdentityUrl;

View File

@@ -23,9 +23,12 @@
Priority="-1" Priority="-1"
UseOriginalImage="True" UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" AutomationProperties.Name="{u:I18n Account}" />
AutomationId="AccountIconButton" />
<ToolbarItem x:Name="_closeButton" Text="{u:I18n Close}" Command="{Binding CloseCommand}" Order="Primary" Priority="-1"/> <ToolbarItem x:Name="_closeButton" Text="{u:I18n Close}" Command="{Binding CloseCommand}" Order="Primary" Priority="-1"/>
<ToolbarItem
Icon="cog_environment.png" Clicked="Environment_Clicked" Order="Primary"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
</ContentPage.ToolbarItems> </ContentPage.ToolbarItems>
<ContentPage.Resources> <ContentPage.Resources>
@@ -50,9 +53,7 @@
Keyboard="Email" Keyboard="Email"
StyleClass="box-value" StyleClass="box-value"
ReturnType="Go" ReturnType="Go"
ReturnCommand="{Binding ContinueCommand}" ReturnCommand="{Binding ContinueCommand}">
AutomationId="EmailAddressEntry"
>
<VisualStateManager.VisualStateGroups> <VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates"> <VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled"> <VisualState x:Name="Disabled">
@@ -65,28 +66,7 @@
</Entry> </Entry>
<StackLayout <StackLayout
Orientation="Horizontal" Orientation="Horizontal"
Margin="0, 6, 0 ,0"> Margin="0, 16, 0 ,0">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding ShowEnvironmentPickerCommand}" />
</StackLayout.GestureRecognizers>
<Label
Text="{Binding RegionText}"
FontSize="13"
TextColor="{DynamicResource MutedColor}"
VerticalOptions="Center"
VerticalTextAlignment="Center"/>
<controls:IconLabel
Text="{Binding SelectedEnvironmentName}"
FontSize="13"
TextColor="{DynamicResource PrimaryColor}"
VerticalOptions="Center"
VerticalTextAlignment="Center"
AutomationId="RegionSelectorDropdown"/>
</StackLayout>
<StackLayout
Orientation="Horizontal"
Margin="0, 20, 0 ,0">
<StackLayout.GestureRecognizers> <StackLayout.GestureRecognizers>
<TapGestureRecognizer <TapGestureRecognizer
Command="{Binding RememberEmailCommand}" /> Command="{Binding RememberEmailCommand}" />
@@ -96,27 +76,21 @@
StyleClass="text-sm" StyleClass="text-sm"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
VerticalOptions="Center" VerticalOptions="Center"
VerticalTextAlignment="Center" VerticalTextAlignment="Center"/>
/>
<Switch <Switch
Scale="0.8" Scale="0.8"
IsToggled="{Binding RememberEmail}" IsToggled="{Binding RememberEmail}"
VerticalOptions="Center" VerticalOptions="Center"/>
AutomationId="RememberMeSwitch"
/>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
<Button Text="{u:I18n Continue}" <Button Text="{u:I18n Continue}"
StyleClass="btn-primary" StyleClass="btn-primary"
IsEnabled="{Binding CanContinue}" IsEnabled="{Binding CanContinue}"
Command="{Binding ContinueCommand}" Command="{Binding ContinueCommand}" />
AutomationId="ContinueButton"
/>
<Label FormattedText="{Binding CreateAccountText}" <Label FormattedText="{Binding CreateAccountText}"
Margin="0, 10" Margin="0, 10"
StyleClass="box-footer-label" StyleClass="box-footer-label">
AutomationId="CreateAccountLabel">
<Label.GestureRecognizers> <Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CreateAccountCommand}" /> <TapGestureRecognizer Command="{Binding CreateAccountCommand}" />
</Label.GestureRecognizers> </Label.GestureRecognizers>
@@ -142,4 +116,5 @@
MainPage="{Binding Source={x:Reference _page}}" MainPage="{Binding Source={x:Reference _page}}"
BindingContext="{Binding AccountSwitchingOverlayViewModel}"/> BindingContext="{Binding AccountSwitchingOverlayViewModel}"/>
</AbsoluteLayout> </AbsoluteLayout>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -15,8 +15,6 @@ namespace Bit.App.Pages
private readonly AppOptions _appOptions; private readonly AppOptions _appOptions;
private IBroadcasterService _broadcasterService; private IBroadcasterService _broadcasterService;
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
public HomePage(AppOptions appOptions = null) public HomePage(AppOptions appOptions = null)
{ {
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
@@ -72,14 +70,6 @@ namespace Bit.App.Pages
}); });
} }
}); });
try
{
await _vm.UpdateEnvironment();
}
catch (Exception ex)
{
_logger.Value?.Exception(ex);
}
} }
protected override bool OnBackButtonPressed() protected override bool OnBackButtonPressed()
@@ -138,6 +128,14 @@ namespace Bit.App.Pages
await Navigation.PushModalAsync(new NavigationPage(page)); await Navigation.PushModalAsync(new NavigationPage(page));
} }
private void Environment_Clicked(object sender, EventArgs e)
{
if (DoOnce())
{
_vm.StartEnvironmentAction();
}
}
private async Task StartEnvironmentAsync() private async Task StartEnvironmentAsync()
{ {
await _accountListOverlay.HideAsync(); await _accountListOverlay.HideAsync();

View File

@@ -4,10 +4,7 @@ using Bit.App.Abstractions;
using Bit.App.Controls; using Bit.App.Controls;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Models.Data;
using Bit.Core.Models.Response;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
@@ -20,19 +17,16 @@ namespace Bit.App.Pages
{ {
private readonly IStateService _stateService; private readonly IStateService _stateService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly ILogger _logger;
private readonly IEnvironmentService _environmentService;
private readonly IAccountsManager _accountManager;
private readonly IConfigService _configService;
private bool _showCancelButton; private bool _showCancelButton;
private bool _rememberEmail; private bool _rememberEmail;
private string _email; private string _email;
private string _selectedEnvironmentName;
private bool _isEmailEnabled; private bool _isEmailEnabled;
private bool _canLogin; private bool _canLogin;
private bool _displayEuEnvironment; private IPlatformUtilsService _platformUtilsService;
private ILogger _logger;
private IEnvironmentService _environmentService;
private IAccountsManager _accountManager;
public HomeViewModel() public HomeViewModel()
{ {
@@ -42,7 +36,6 @@ namespace Bit.App.Pages
_logger = ServiceContainer.Resolve<ILogger>(); _logger = ServiceContainer.Resolve<ILogger>();
_environmentService = ServiceContainer.Resolve<IEnvironmentService>(); _environmentService = ServiceContainer.Resolve<IEnvironmentService>();
_accountManager = ServiceContainer.Resolve<IAccountsManager>(); _accountManager = ServiceContainer.Resolve<IAccountsManager>();
_configService = ServiceContainer.Resolve<IConfigService>();
PageTitle = AppResources.Bitwarden; PageTitle = AppResources.Bitwarden;
@@ -56,8 +49,6 @@ namespace Bit.App.Pages
onException: _logger.Exception, allowsMultipleExecutions: false); onException: _logger.Exception, allowsMultipleExecutions: false);
CloseCommand = new AsyncCommand(async () => await Device.InvokeOnMainThreadAsync(CloseAction), CloseCommand = new AsyncCommand(async () => await Device.InvokeOnMainThreadAsync(CloseAction),
onException: _logger.Exception, allowsMultipleExecutions: false); onException: _logger.Exception, allowsMultipleExecutions: false);
ShowEnvironmentPickerCommand = new AsyncCommand(ShowEnvironmentPickerAsync,
onException: _logger.Exception, allowsMultipleExecutions: false);
InitAsync().FireAndForget(); InitAsync().FireAndForget();
} }
@@ -80,13 +71,6 @@ namespace Bit.App.Pages
additionalPropertyNames: new[] { nameof(CanContinue) }); additionalPropertyNames: new[] { nameof(CanContinue) });
} }
public string SelectedEnvironmentName
{
get => $"{_selectedEnvironmentName} {BitwardenIcons.AngleDown}";
set => SetProperty(ref _selectedEnvironmentName, value);
}
public string RegionText => $"{AppResources.Region}:";
public bool CanContinue => !string.IsNullOrEmpty(Email); public bool CanContinue => !string.IsNullOrEmpty(Email);
public FormattedString CreateAccountText public FormattedString CreateAccountText
@@ -117,13 +101,11 @@ namespace Bit.App.Pages
public AsyncCommand ContinueCommand { get; } public AsyncCommand ContinueCommand { get; }
public AsyncCommand CloseCommand { get; } public AsyncCommand CloseCommand { get; }
public AsyncCommand CreateAccountCommand { get; } public AsyncCommand CreateAccountCommand { get; }
public AsyncCommand ShowEnvironmentPickerCommand { get; }
public async Task InitAsync() public async Task InitAsync()
{ {
Email = await _stateService.GetRememberedEmailAsync(); Email = await _stateService.GetRememberedEmailAsync();
RememberEmail = !string.IsNullOrEmpty(Email); RememberEmail = !string.IsNullOrEmpty(Email);
_displayEuEnvironment = await _configService.GetFeatureFlagBoolAsync(Constants.DisplayEuEnvironmentFlag, forceRefresh: true);
} }
public async Task ContinueToLoginStepAsync() public async Task ContinueToLoginStepAsync()
@@ -162,59 +144,5 @@ namespace Bit.App.Pages
await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage, AppResources.AnErrorHasOccurred, AppResources.Ok); await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage, AppResources.AnErrorHasOccurred, AppResources.Ok);
} }
} }
public async Task ShowEnvironmentPickerAsync()
{
_displayEuEnvironment = await _configService.GetFeatureFlagBoolAsync(Constants.DisplayEuEnvironmentFlag);
var options = _displayEuEnvironment
? new string[] { AppResources.US, AppResources.EU, AppResources.SelfHosted }
: new string[] { AppResources.US, AppResources.SelfHosted };
await Device.InvokeOnMainThreadAsync(async () =>
{
var result = await Page.DisplayActionSheet(AppResources.DataRegion, AppResources.Cancel, null, options);
if (result is null || result == AppResources.Cancel)
{
return;
}
if (result == AppResources.SelfHosted)
{
StartEnvironmentAction?.Invoke();
return;
}
await _environmentService.SetUrlsAsync(result == AppResources.EU ? EnvironmentUrlData.DefaultEU : EnvironmentUrlData.DefaultUS);
await _configService.GetAsync(true);
SelectedEnvironmentName = result;
});
}
public async Task UpdateEnvironment()
{
var environmentsSaved = await _stateService.GetPreAuthEnvironmentUrlsAsync();
if (environmentsSaved == null || environmentsSaved.IsEmpty)
{
await _environmentService.SetUrlsAsync(EnvironmentUrlData.DefaultUS);
environmentsSaved = EnvironmentUrlData.DefaultUS;
SelectedEnvironmentName = AppResources.US;
return;
}
if (environmentsSaved.Base == EnvironmentUrlData.DefaultUS.Base)
{
SelectedEnvironmentName = AppResources.US;
}
else if (environmentsSaved.Base == EnvironmentUrlData.DefaultEU.Base)
{
SelectedEnvironmentName = AppResources.EU;
}
else
{
await _configService.GetAsync(true);
SelectedEnvironmentName = AppResources.SelfHosted;
}
}
} }
} }

View File

@@ -24,8 +24,7 @@
Priority="-1" Priority="-1"
UseOriginalImage="True" UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" AutomationProperties.Name="{u:I18n Account}" />
AutomationId="AccountIconButton" />
</ContentPage.ToolbarItems> </ContentPage.ToolbarItems>
<ContentPage.Resources> <ContentPage.Resources>
@@ -72,8 +71,7 @@
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
ReturnType="Go" ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" ReturnCommand="{Binding SubmitCommand}" />
AutomationId="PinEntry" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}" Text="{Binding ShowPasswordIcon}"
@@ -83,8 +81,7 @@
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
AutomationId="PinVisibilityToggle" />
</Grid> </Grid>
<Grid <Grid
x:Name="_passwordGrid" x:Name="_passwordGrid"
@@ -114,8 +111,7 @@
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
ReturnType="Go" ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" ReturnCommand="{Binding SubmitCommand}" />
AutomationId="MasterPasswordEntry" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}" Text="{Binding ShowPasswordIcon}"
@@ -125,9 +121,7 @@
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
AutomationId="PasswordVisibilityToggle"
/>
</Grid> </Grid>
<StackLayout <StackLayout
StyleClass="box-row" StyleClass="box-row"
@@ -143,7 +137,7 @@
</StackLayout> </StackLayout>
<StackLayout Padding="10, 0"> <StackLayout Padding="10, 0">
<Label <Label
Text="{u:I18n AccountBiometricInvalidated}" Text="{u:I18n BiometricInvalidated}"
StyleClass="box-footer-label,text-danger,text-bold" StyleClass="box-footer-label,text-danger,text-bold"
IsVisible="{Binding BiometricIntegrityValid, Converter={StaticResource inverseBool}}" /> IsVisible="{Binding BiometricIntegrityValid, Converter={StaticResource inverseBool}}" />
<Button Text="{Binding BiometricButtonText}" Clicked="Biometric_Clicked" <Button Text="{Binding BiometricButtonText}" Clicked="Biometric_Clicked"
@@ -153,8 +147,7 @@
x:Name="_unlockButton" x:Name="_unlockButton"
Text="{u:I18n Unlock}" Text="{u:I18n Unlock}"
StyleClass="btn-primary" StyleClass="btn-primary"
Clicked="Unlock_Clicked" Clicked="Unlock_Clicked" />
AutomationId="UnlockVaultButton" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>

View File

@@ -31,8 +31,6 @@ namespace Bit.App.Pages
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IWatchDeviceService _watchDeviceService; private readonly IWatchDeviceService _watchDeviceService;
private readonly WeakEventManager<int?> _secretEntryFocusWeakEventManager = new WeakEventManager<int?>(); private readonly WeakEventManager<int?> _secretEntryFocusWeakEventManager = new WeakEventManager<int?>();
private readonly IPolicyService _policyService;
private readonly IPasswordGenerationService _passwordGenerationService;
private string _email; private string _email;
private string _masterPassword; private string _masterPassword;
@@ -63,8 +61,6 @@ namespace Bit.App.Pages
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService"); _keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
_logger = ServiceContainer.Resolve<ILogger>("logger"); _logger = ServiceContainer.Resolve<ILogger>("logger");
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>(); _watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
_policyService = ServiceContainer.Resolve<IPolicyService>();
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>();
PageTitle = AppResources.VerifyMasterPassword; PageTitle = AppResources.VerifyMasterPassword;
TogglePasswordCommand = new Command(TogglePassword); TogglePasswordCommand = new Command(TogglePassword);
@@ -209,7 +205,7 @@ namespace Bit.App.Pages
if (BiometricLock) if (BiometricLock)
{ {
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync(); BiometricIntegrityValid = await _biometricService.ValidateIntegrityAsync();
if (!_biometricIntegrityValid) if (!_biometricIntegrityValid)
{ {
BiometricButtonVisible = false; BiometricButtonVisible = false;
@@ -298,7 +294,6 @@ namespace Bit.App.Pages
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdfConfig); var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdfConfig);
var storedKeyHash = await _cryptoService.GetKeyHashAsync(); var storedKeyHash = await _cryptoService.GetKeyHashAsync();
var passwordValid = false; var passwordValid = false;
MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null;
if (storedKeyHash != null) if (storedKeyHash != null)
{ {
@@ -310,11 +305,9 @@ namespace Bit.App.Pages
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization); var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
var request = new PasswordVerificationRequest(); var request = new PasswordVerificationRequest();
request.MasterPasswordHash = keyHash; request.MasterPasswordHash = keyHash;
try try
{ {
var response = await _apiService.PostAccountVerifyPasswordAsync(request); await _apiService.PostAccountVerifyPasswordAsync(request);
enforcedMasterPasswordOptions = response.MasterPasswordPolicy;
passwordValid = true; passwordValid = true;
var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization); var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
await _cryptoService.SetKeyHashAsync(localKeyHash); await _cryptoService.SetKeyHashAsync(localKeyHash);
@@ -335,14 +328,6 @@ namespace Bit.App.Pages
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, kdfConfig); var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, kdfConfig);
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey)); await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
} }
if (await RequirePasswordChangeAsync(enforcedMasterPasswordOptions))
{
// Save the ForcePasswordResetReason to force a password reset after unlock
await _stateService.SetForcePasswordResetReasonAsync(
ForcePasswordResetReason.WeakMasterPasswordOnLogin);
}
MasterPassword = string.Empty; MasterPassword = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync(); await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(key); await SetKeyAndContinueAsync(key);
@@ -367,37 +352,6 @@ namespace Bit.App.Pages
} }
} }
/// <summary>
/// Checks if the master password requires updating to meet the enforced policy requirements
/// </summary>
/// <param name="options"></param>
private async Task<bool> RequirePasswordChangeAsync(MasterPasswordPolicyOptions options = null)
{
// If no policy options are provided, attempt to load them from the policy service
var enforcedOptions = options ?? await _policyService.GetMasterPasswordPolicyOptions();
// No policy to enforce on login/unlock
if (!(enforcedOptions is { EnforceOnLogin: true }))
{
return false;
}
var strength = _passwordGenerationService.PasswordStrength(
MasterPassword, _passwordGenerationService.GetPasswordStrengthUserInput(_email))?.Score;
if (!strength.HasValue)
{
_logger.Error("Unable to evaluate master password strength during unlock");
return false;
}
return !await _policyService.EvaluateMasterPassword(
strength.Value,
MasterPassword,
enforcedOptions
);
}
public async Task LogOutAsync() public async Task LogOutAsync()
{ {
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.LogoutConfirmation, var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.LogoutConfirmation,
@@ -431,8 +385,7 @@ namespace Bit.App.Pages
public async Task PromptBiometricAsync() public async Task PromptBiometricAsync()
{ {
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync(); BiometricIntegrityValid = await _biometricService.ValidateIntegrityAsync();
BiometricButtonVisible = BiometricIntegrityValid;
if (!BiometricLock || !BiometricIntegrityValid) if (!BiometricLock || !BiometricIntegrityValid)
{ {
return; return;

View File

@@ -9,23 +9,23 @@
xmlns:u="clr-namespace:Bit.App.Utilities" xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:LoginPageViewModel" x:DataType="pages:LoginPageViewModel"
x:Name="_page" x:Name="_page"
Title="{Binding PageTitle}" Title="{Binding PageTitle}">
AutomationId="PageTitleLabel">
<ContentPage.BindingContext> <ContentPage.BindingContext>
<pages:LoginPageViewModel /> <pages:LoginPageViewModel />
</ContentPage.BindingContext> </ContentPage.BindingContext>
<ContentPage.ToolbarItems> <ContentPage.ToolbarItems>
<controls:ExtendedToolbarItem <controls:ExtendedToolbarItem
x:Name="_accountAvatar" x:Name="_accountAvatar"
x:Key="accountAvatar"
IconImageSource="{Binding AvatarImageSource}" IconImageSource="{Binding AvatarImageSource}"
Command="{Binding Source={x:Reference _accountListOverlay}, Path=ToggleVisibililtyCommand}" Command="{Binding Source={x:Reference _accountListOverlay}, Path=ToggleVisibililtyCommand}"
Order="Primary" Order="Primary"
Priority="-1" Priority="-1"
UseOriginalImage="True" UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" AutomationProperties.Name="{u:I18n Account}" />
AutomationId="AccountIconButton" />
</ContentPage.ToolbarItems> </ContentPage.ToolbarItems>
<ContentPage.Resources> <ContentPage.Resources>
@@ -34,8 +34,7 @@
<ToolbarItem Icon="more_vert.png" Clicked="More_Clicked" Order="Primary" <ToolbarItem Icon="more_vert.png" Clicked="More_Clicked" Order="Primary"
x:Name="_moreItem" x:Key="moreItem" x:Name="_moreItem" x:Key="moreItem"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" AutomationProperties.Name="{u:I18n Options}" />
AutomationId="OptionsButton" />
<ToolbarItem Text="{u:I18n GetPasswordHint}" <ToolbarItem Text="{u:I18n GetPasswordHint}"
x:Key="getPasswordHint" x:Key="getPasswordHint"
x:Name="_getPasswordHint" x:Name="_getPasswordHint"
@@ -76,9 +75,7 @@
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
ReturnType="Go" ReturnType="Go"
ReturnCommand="{Binding LogInCommand}" ReturnCommand="{Binding LogInCommand}" />
AutomationId="MasterPasswordEntry"
/>
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}" Text="{Binding ShowPasswordIcon}"
@@ -87,7 +84,6 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="1" Grid.RowSpan="1"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationId="PasswordVisibilityToggle"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/> AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
<Label <Label
@@ -97,9 +93,7 @@
Padding="0,5,0,0" Padding="0,5,0,0"
Grid.Row="2" Grid.Row="2"
Grid.Column="0" Grid.Column="0"
Grid.ColumnSpan="2" Grid.ColumnSpan="2">
AutomationId="GetMasterPasswordHintLabel"
>
<Label.GestureRecognizers> <Label.GestureRecognizers>
<TapGestureRecognizer Tapped="Hint_Clicked" /> <TapGestureRecognizer Tapped="Hint_Clicked" />
</Label.GestureRecognizers> </Label.GestureRecognizers>
@@ -110,24 +104,19 @@
<Button x:Name="_loginWithMasterPassword" <Button x:Name="_loginWithMasterPassword"
Text="{u:I18n LogInWithMasterPassword}" Text="{u:I18n LogInWithMasterPassword}"
StyleClass="btn-primary" StyleClass="btn-primary"
Clicked="LogIn_Clicked" Clicked="LogIn_Clicked" />
AutomationId="LogInWithMasterPasswordButton"
/>
<controls:IconLabelButton <controls:IconLabelButton
HorizontalOptions="Fill" HorizontalOptions="Fill"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
Icon="{Binding Source={x:Static core:BitwardenIcons.Device}}" Icon="{Binding Source={x:Static core:BitwardenIcons.Device}}"
Label="{u:I18n LogInWithAnotherDevice}" Label="{u:I18n LogInWithAnotherDevice}"
ButtonCommand="{Binding LogInWithDeviceCommand}" ButtonCommand="{Binding LogInWithDeviceCommand}"
IsVisible="{Binding IsKnownDevice}" IsVisible="{Binding IsKnownDevice}"/>
AutomationId="LogInWithAnotherDeviceButton"
/>
<controls:IconLabelButton <controls:IconLabelButton
HorizontalOptions="Fill" HorizontalOptions="Fill"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
Icon="{Binding Source={x:Static core:BitwardenIcons.Suitcase}}" Icon="{Binding Source={x:Static core:BitwardenIcons.Suitcase}}"
Label="{u:I18n LogInSso}" Label="{u:I18n LogInSso}">
AutomationId="LogInWithSsoButton">
<controls:IconLabelButton.GestureRecognizers> <controls:IconLabelButton.GestureRecognizers>
<TapGestureRecognizer Tapped="LogInSSO_Clicked" /> <TapGestureRecognizer Tapped="LogInSSO_Clicked" />
</controls:IconLabelButton.GestureRecognizers> </controls:IconLabelButton.GestureRecognizers>
@@ -135,15 +124,12 @@
<Label <Label
Text="{Binding LoggingInAsText}" Text="{Binding LoggingInAsText}"
StyleClass="text-sm" StyleClass="text-sm"
Margin="0,40,0,0" Margin="0,40,0,0"/>
AutomationId="LoggingInAsLabel"
/>
<Label <Label
Text="{u:I18n NotYou}" Text="{u:I18n NotYou}"
StyleClass="text-md" StyleClass="text-md"
HorizontalOptions="Start" HorizontalOptions="Start"
TextColor="{DynamicResource HyperlinkColor}" TextColor="{DynamicResource HyperlinkColor}">
AutomationId="NotYouLabel">
<Label.GestureRecognizers> <Label.GestureRecognizers>
<TapGestureRecognizer Tapped="Cancel_Clicked" /> <TapGestureRecognizer Tapped="Cancel_Clicked" />
</Label.GestureRecognizers> </Label.GestureRecognizers>

View File

@@ -41,7 +41,6 @@ namespace Bit.App.Pages
private bool _isEmailEnabled; private bool _isEmailEnabled;
private bool _isKnownDevice; private bool _isKnownDevice;
private bool _isExecutingLogin; private bool _isExecutingLogin;
private string _environmentHostName;
public LoginPageViewModel() public LoginPageViewModel()
{ {
@@ -116,16 +115,6 @@ namespace Bit.App.Pages
set => SetProperty(ref _isKnownDevice, value); set => SetProperty(ref _isKnownDevice, value);
} }
public string EnvironmentDomainName
{
get => _environmentHostName;
set => SetProperty(ref _environmentHostName, value,
additionalPropertyNames: new string[]
{
nameof(LoggingInAsText)
});
}
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; } public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
public Command LogInCommand { get; } public Command LogInCommand { get; }
public Command TogglePasswordCommand { get; } public Command TogglePasswordCommand { get; }
@@ -133,7 +122,7 @@ namespace Bit.App.Pages
public ICommand LogInWithDeviceCommand { get; } public ICommand LogInWithDeviceCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow; public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
public string LoggingInAsText => string.Format(AppResources.LoggingInAsXOnY, Email, EnvironmentDomainName); public string LoggingInAsText => string.Format(AppResources.LoggingInAsX, Email);
public bool IsIosExtension { get; set; } public bool IsIosExtension { get; set; }
public bool CanRemoveAccount { get; set; } public bool CanRemoveAccount { get; set; }
public Action StartTwoFactorAction { get; set; } public Action StartTwoFactorAction { get; set; }
@@ -162,7 +151,6 @@ namespace Bit.App.Pages
Email = await _stateService.GetRememberedEmailAsync(); Email = await _stateService.GetRememberedEmailAsync();
} }
CanRemoveAccount = await _stateService.GetActiveUserEmailAsync() != Email; CanRemoveAccount = await _stateService.GetActiveUserEmailAsync() != Email;
EnvironmentDomainName = CoreHelpers.GetDomain((await _stateService.GetPreAuthEnvironmentUrlsAsync())?.Base);
IsKnownDevice = await _apiService.GetKnownDeviceAsync(Email, await _appIdService.GetAppIdAsync()); IsKnownDevice = await _apiService.GetKnownDeviceAsync(Email, await _appIdService.GetAppIdAsync());
} }
catch (ApiException apiEx) when (apiEx.Error.StatusCode == System.Net.HttpStatusCode.Unauthorized) catch (ApiException apiEx) when (apiEx.Error.StatusCode == System.Net.HttpStatusCode.Unauthorized)

View File

@@ -32,8 +32,7 @@
<Label <Label
Text="{Binding LogInAttemptByLabel}" Text="{Binding LogInAttemptByLabel}"
FontSize="Small" FontSize="Small"
Margin="0,0,0,24" Margin="0,0,0,24"/>
AutomationId="LogInAttemptByLabel" />
<Label <Label
Text="{u:I18n FingerprintPhrase}" Text="{u:I18n FingerprintPhrase}"
FontSize="Small" FontSize="Small"
@@ -42,8 +41,7 @@
FormattedText="{Binding LoginRequest.FingerprintPhrase}" FormattedText="{Binding LoginRequest.FingerprintPhrase}"
FontSize="Medium" FontSize="Medium"
TextColor="{DynamicResource FingerprintPhrase}" TextColor="{DynamicResource FingerprintPhrase}"
Margin="0,0,0,27" Margin="0,0,0,27"/>
AutomationId="FingerprintValueLabel" />
<Label <Label
Text="{u:I18n DeviceType}" Text="{u:I18n DeviceType}"
FontSize="Small" FontSize="Small"
@@ -51,8 +49,7 @@
<Label <Label
Text="{Binding LoginRequest.DeviceType}" Text="{Binding LoginRequest.DeviceType}"
FontSize="Small" FontSize="Small"
Margin="0,0,0,21" Margin="0,0,0,21"/>
AutomationId="DeviceTypeValueLabel" />
<Label <Label
Text="{u:I18n IpAddress}" Text="{u:I18n IpAddress}"
IsVisible="{Binding ShowIpAddress}" IsVisible="{Binding ShowIpAddress}"
@@ -62,8 +59,7 @@
Text="{Binding LoginRequest.IpAddress}" Text="{Binding LoginRequest.IpAddress}"
IsVisible="{Binding ShowIpAddress}" IsVisible="{Binding ShowIpAddress}"
FontSize="Small" FontSize="Small"
Margin="0,0,0,21" Margin="0,0,0,21"/>
AutomationId="IpAddressValueLabel" />
<Label <Label
Text="{u:I18n Time}" Text="{u:I18n Time}"
FontSize="Small" FontSize="Small"
@@ -71,8 +67,7 @@
<Label <Label
Text="{Binding TimeOfRequestText}" Text="{Binding TimeOfRequestText}"
FontSize="Small" FontSize="Small"
Margin="0,0,0,57" Margin="0,0,0,57"/>
AutomationId="TimeOfRequestValueLabel" />
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
@@ -80,13 +75,11 @@
Text="{u:I18n ConfirmLogIn}" Text="{u:I18n ConfirmLogIn}"
Command="{Binding AcceptRequestCommand}" Command="{Binding AcceptRequestCommand}"
Margin="0,0,0,17" Margin="0,0,0,17"
StyleClass="btn-primary" StyleClass="btn-primary"/>
AutomationId="ConfirmLoginButton" />
<Button <Button
Text="{u:I18n DenyLogIn}" Text="{u:I18n DenyLogIn}"
Command="{Binding RejectRequestCommand}" Command="{Binding RejectRequestCommand}"
StyleClass="btn-secundary" StyleClass="btn-secundary"/>
AutomationId="DenyLoginButton" />
</StackLayout> </StackLayout>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -24,8 +24,7 @@
Text="{u:I18n LogInInitiated}" Text="{u:I18n LogInInitiated}"
FontSize="Title" FontSize="Title"
FontAttributes="Bold" FontAttributes="Bold"
Margin="0,14,0,21" Margin="0,14,0,21"/>
AutomationId="LogInInitiatedLabel" />
<Label <Label
Text="{u:I18n ANotificationHasBeenSentToYourDevice}" Text="{u:I18n ANotificationHasBeenSentToYourDevice}"
FontSize="Small" FontSize="Small"
@@ -41,15 +40,13 @@
<controls:MonoLabel <controls:MonoLabel
FormattedText="{Binding FingerprintPhrase}" FormattedText="{Binding FingerprintPhrase}"
FontSize="Medium" FontSize="Medium"
TextColor="{DynamicResource FingerprintPhrase}" TextColor="{DynamicResource FingerprintPhrase}"/>
AutomationId="FingerprintPhraseValue" />
<Label <Label
Text="{u:I18n ResendNotification}" Text="{u:I18n ResendNotification}"
StyleClass="text-md" StyleClass="text-md"
HorizontalOptions="Start" HorizontalOptions="Start"
Margin="0,40,0,0" Margin="0,40,0,0"
TextColor="{DynamicResource HyperlinkColor}" TextColor="{DynamicResource HyperlinkColor}">
AutomationId="ResendNotificationButton">
<Label.GestureRecognizers> <Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CreatePasswordlessLoginCommand}" /> <TapGestureRecognizer Command="{Binding CreatePasswordlessLoginCommand}" />
</Label.GestureRecognizers> </Label.GestureRecognizers>
@@ -67,8 +64,7 @@
VerticalTextAlignment="End" VerticalTextAlignment="End"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
Margin="5, 0" Margin="5, 0"
TextColor="{DynamicResource HyperlinkColor}" TextColor="{DynamicResource HyperlinkColor}">
AutomationId="ViewAllLoginOptionsButton">
<Label.GestureRecognizers> <Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CloseCommand}" /> <TapGestureRecognizer Command="{Binding CloseCommand}" />
</Label.GestureRecognizers> </Label.GestureRecognizers>

View File

@@ -121,8 +121,9 @@ namespace Bit.App.Pages
var ssoToken = response.Token; var ssoToken = response.Token;
var passwordOptions = PasswordGenerationOptions.CreateDefault
.WithLength(64); var passwordOptions = new PasswordGenerationOptions(true);
passwordOptions.Length = 64;
var codeVerifier = await _passwordGenerationService.GeneratePasswordAsync(passwordOptions); var codeVerifier = await _passwordGenerationService.GeneratePasswordAsync(passwordOptions);
var codeVerifierHash = await _cryptoFunctionService.HashAsync(codeVerifier, CryptoHashAlgorithm.Sha256); var codeVerifierHash = await _cryptoFunctionService.HashAsync(codeVerifier, CryptoHashAlgorithm.Sha256);

View File

@@ -35,8 +35,7 @@
x:Name="_email" x:Name="_email"
Text="{Binding Email}" Text="{Binding Email}"
Keyboard="Email" Keyboard="Email"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="EmailAddressEntry"/>
</StackLayout> </StackLayout>
<Grid StyleClass="box-row"> <Grid StyleClass="box-row">
<Grid.RowDefinitions> <Grid.RowDefinitions>
@@ -60,8 +59,7 @@
IsTextPredictionEnabled="False" IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}" IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0" />
AutomationId="MasterPasswordEntry"/>
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}" Text="{Binding ShowPasswordIcon}"
@@ -71,8 +69,7 @@
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
AutomationId="PasswordVisibilityToggle"/>
</Grid> </Grid>
<Label <Label
StyleClass="box-sub-label" StyleClass="box-sub-label"
@@ -112,8 +109,7 @@
IsTextPredictionEnabled="False" IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}" IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0" />
AutomationId="ConfirmMasterPasswordEntry"/>
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}" Text="{Binding ShowPasswordIcon}"
@@ -122,7 +118,6 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationId="ConfirmPasswordVisibilityToggle"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" /> AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
</Grid> </Grid>
@@ -135,8 +130,7 @@
Text="{Binding Hint}" Text="{Binding Hint}"
StyleClass="box-value" StyleClass="box-value"
ReturnType="Go" ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" ReturnCommand="{Binding SubmitCommand}" />
AutomationId="MasterPasswordHintLabel" />
</StackLayout> </StackLayout>
<Label <Label
Text="{u:I18n MasterPasswordHintDescription}" Text="{u:I18n MasterPasswordHintDescription}"
@@ -148,8 +142,7 @@
IsToggled="{Binding CheckExposedMasterPassword}" IsToggled="{Binding CheckExposedMasterPassword}"
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="Start" HorizontalOptions="Start"
Margin="0, 0, 10, 0" Margin="0, 0, 10, 0"/>
AutomationId="CheckExposedMasterPasswordToggle"/>
<Label <Label
Text="{u:I18n CheckKnownDataBreachesForThisPassword}" Text="{u:I18n CheckKnownDataBreachesForThisPassword}"
StyleClass="box-footer-label" StyleClass="box-footer-label"
@@ -161,8 +154,7 @@
IsToggled="{Binding AcceptPolicies}" IsToggled="{Binding AcceptPolicies}"
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="Start" HorizontalOptions="Start"
Margin="0, 0, 10, 0" Margin="0, 0, 10, 0"/>
AutomationId="AcceptPoliciesToggle"/>
<Label StyleClass="box-footer-label" <Label StyleClass="box-footer-label"
HorizontalOptions="Fill"> HorizontalOptions="Fill">
<Label.FormattedText> <Label.FormattedText>

View File

@@ -46,7 +46,7 @@
BackgroundColor="Transparent" BackgroundColor="Transparent"
BorderColor="{DynamicResource PrimaryColor}"> BorderColor="{DynamicResource PrimaryColor}">
<Label <Label
Text="{Binding UpdateMasterPasswordWarningText }" Text="{u:I18n UpdateMasterPasswordWarning}"
StyleClass="text-muted, text-sm, text-bold" StyleClass="text-muted, text-sm, text-bold"
HorizontalTextAlignment="Center" /> HorizontalTextAlignment="Center" />
</Frame> </Frame>
@@ -74,40 +74,6 @@
HorizontalTextAlignment="Start" /> HorizontalTextAlignment="Start" />
</Frame> </Frame>
</Grid> </Grid>
<Grid StyleClass="box-row" IsVisible="{Binding RequireCurrentPassword }">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Text="{u:I18n CurrentMasterPassword}"
StyleClass="box-label"
Grid.Row="0"
Grid.Column="0" />
<controls:MonoEntry
x:Name="_currentMasterPassword"
Text="{Binding CurrentMasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0" />
<controls:IconButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
</Grid>
<Grid StyleClass="box-row"> <Grid StyleClass="box-row">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />

View File

@@ -1,68 +1,27 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Request; using Bit.Core.Models.Request;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
public class UpdateTempPasswordPageViewModel : BaseChangePasswordViewModel public class UpdateTempPasswordPageViewModel : BaseChangePasswordViewModel
{ {
private readonly IUserVerificationService _userVerificationService;
private ForcePasswordResetReason _reason = ForcePasswordResetReason.AdminForcePasswordReset;
public UpdateTempPasswordPageViewModel() public UpdateTempPasswordPageViewModel()
{ {
PageTitle = AppResources.UpdateMasterPassword; PageTitle = AppResources.UpdateMasterPassword;
TogglePasswordCommand = new Command(TogglePassword); TogglePasswordCommand = new Command(TogglePassword);
ToggleConfirmPasswordCommand = new Command(ToggleConfirmPassword); ToggleConfirmPasswordCommand = new Command(ToggleConfirmPassword);
SubmitCommand = new AsyncCommand(SubmitAsync, SubmitCommand = new Command(async () => await SubmitAsync());
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
_userVerificationService = ServiceContainer.Resolve<IUserVerificationService>();
} }
public AsyncCommand SubmitCommand { get; } public Command SubmitCommand { get; }
public Command TogglePasswordCommand { get; } public Command TogglePasswordCommand { get; }
public Command ToggleConfirmPasswordCommand { get; } public Command ToggleConfirmPasswordCommand { get; }
public Action UpdateTempPasswordSuccessAction { get; set; } public Action UpdateTempPasswordSuccessAction { get; set; }
public Action LogOutAction { get; set; } public Action LogOutAction { get; set; }
public string CurrentMasterPassword { get; set; }
public override async Task InitAsync(bool forceSync = false)
{
await base.InitAsync(forceSync);
var forcePasswordResetReason = await _stateService.GetForcePasswordResetReasonAsync();
if (forcePasswordResetReason.HasValue)
{
_reason = forcePasswordResetReason.Value;
}
}
public bool RequireCurrentPassword
{
get => _reason == ForcePasswordResetReason.WeakMasterPasswordOnLogin;
}
public string UpdateMasterPasswordWarningText
{
get
{
return _reason == ForcePasswordResetReason.WeakMasterPasswordOnLogin
? AppResources.UpdateWeakMasterPasswordWarning
: AppResources.UpdateMasterPasswordWarning;
}
}
public void TogglePassword() public void TogglePassword()
{ {
@@ -83,12 +42,6 @@ namespace Bit.App.Pages
return; return;
} }
if (RequireCurrentPassword &&
!await _userVerificationService.VerifyUser(CurrentMasterPassword, VerificationType.MasterPassword))
{
return;
}
// Retrieve details for key generation // Retrieve details for key generation
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile)); var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
var email = await _stateService.GetEmailAsync(); var email = await _stateService.GetEmailAsync();
@@ -100,29 +53,21 @@ namespace Bit.App.Pages
// Create new encKey for the User // Create new encKey for the User
var newEncKey = await _cryptoService.RemakeEncKeyAsync(key); var newEncKey = await _cryptoService.RemakeEncKeyAsync(key);
// Create request
var request = new UpdateTempPasswordRequest
{
Key = newEncKey.Item2.EncryptedString,
NewMasterPasswordHash = masterPasswordHash,
MasterPasswordHint = Hint
};
// Initiate API action // Initiate API action
try try
{ {
await _deviceActionService.ShowLoadingAsync(AppResources.UpdatingPassword); await _deviceActionService.ShowLoadingAsync(AppResources.UpdatingPassword);
await _apiService.PutUpdateTempPasswordAsync(request);
switch (_reason)
{
case ForcePasswordResetReason.AdminForcePasswordReset:
await UpdateTempPasswordAsync(masterPasswordHash, newEncKey.Item2.EncryptedString);
break;
case ForcePasswordResetReason.WeakMasterPasswordOnLogin:
await UpdatePasswordAsync(masterPasswordHash, newEncKey.Item2.EncryptedString);
break;
default:
throw new ArgumentOutOfRangeException();
}
await _deviceActionService.HideLoadingAsync(); await _deviceActionService.HideLoadingAsync();
// Clear the force reset password reason
await _stateService.SetForcePasswordResetReasonAsync(null);
_platformUtilsService.ShowToast(null, null, AppResources.UpdatedMasterPassword);
UpdateTempPasswordSuccessAction?.Invoke(); UpdateTempPasswordSuccessAction?.Invoke();
} }
catch (ApiException e) catch (ApiException e)
@@ -140,32 +85,5 @@ namespace Bit.App.Pages
} }
} }
} }
private async Task UpdateTempPasswordAsync(string newMasterPasswordHash, string newEncKey)
{
var request = new UpdateTempPasswordRequest
{
Key = newEncKey,
NewMasterPasswordHash = newMasterPasswordHash,
MasterPasswordHint = Hint
};
await _apiService.PutUpdateTempPasswordAsync(request);
}
private async Task UpdatePasswordAsync(string newMasterPasswordHash, string newEncKey)
{
var currentPasswordHash = await _cryptoService.HashPasswordAsync(CurrentMasterPassword, null);
var request = new PasswordRequest
{
MasterPasswordHash = currentPasswordHash,
Key = newEncKey,
NewMasterPasswordHash = newMasterPasswordHash,
MasterPasswordHint = Hint
};
await _apiService.PostPasswordAsync(request);
}
} }
} }

View File

@@ -27,8 +27,7 @@
Clicked="Clear_Clicked" Clicked="Clear_Clicked"
Order="Secondary" Order="Secondary"
x:Name="_clearItem" x:Name="_clearItem"
x:Key="clearItem" x:Key="clearItem" />
AutomationId="ClearPasswordList" />
<ToolbarItem Icon="more_vert.png" <ToolbarItem Icon="more_vert.png"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" AutomationProperties.Name="{u:I18n Options}"
@@ -44,8 +43,7 @@
Margin="20, 0" Margin="20, 0"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center"></Label>
AutomationId="NoPasswordsDisplayedLabel"></Label>
<controls:ExtendedCollectionView <controls:ExtendedCollectionView
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}" IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
ItemsSource="{Binding History}" ItemsSource="{Binding History}"
@@ -58,8 +56,7 @@
StyleClass="list-row, list-row-platform" StyleClass="list-row, list-row-platform"
Padding="10" Padding="10"
RowSpacing="0" RowSpacing="0"
ColumnSpacing="10" ColumnSpacing="10">
AutomationId="GeneratedPasswordRow">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -74,14 +71,12 @@
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="0"
StyleClass="list-title, list-title-platform, text-html" StyleClass="list-title, list-title-platform, text-html"
Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" />
AutomationId="GeneratedPasswordValue" />
<Label LineBreakMode="TailTruncation" <Label LineBreakMode="TailTruncation"
Grid.Column="0" Grid.Column="0"
Grid.Row="1" Grid.Row="1"
StyleClass="list-subtitle, list-subtitle-platform" StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Date, Mode=OneWay, Converter={StaticResource dateTime}}" Text="{Binding Date, Mode=OneWay, Converter={StaticResource dateTime}}" />
AutomationId="GeneratedPasswordDateLabel" />
<controls:IconButton <controls:IconButton
StyleClass="list-row-button, list-row-button-platform" StyleClass="list-row-button, list-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Paste}}" Text="{Binding Source={x:Static core:BitwardenIcons.Paste}}"
@@ -91,8 +86,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyPassword}" AutomationProperties.Name="{u:I18n CopyPassword}" />
AutomationId="CopyPasswordValueButton" />
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</CollectionView.ItemTemplate> </CollectionView.ItemTemplate>

View File

@@ -71,8 +71,7 @@
<Label <Label
Text="{u:I18n PasswordGeneratorPolicyInEffect}" Text="{u:I18n PasswordGeneratorPolicyInEffect}"
StyleClass="text-muted, text-sm, text-bold" StyleClass="text-muted, text-sm, text-bold"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center" />
AutomationId="PasswordGeneratorPolicyInEffectLabel" />
</Frame> </Frame>
</Grid> </Grid>
<Grid IsVisible="{Binding IsUsername, Converter={StaticResource inverseBool}}" <Grid IsVisible="{Binding IsUsername, Converter={StaticResource inverseBool}}"
@@ -83,24 +82,21 @@
x:Name="lblPassword" x:Name="lblPassword"
StyleClass="text-lg, text-html" StyleClass="text-lg, text-html"
Text="{Binding ColoredPassword, Mode=OneWay}" Text="{Binding ColoredPassword, Mode=OneWay}"
Margin="0, 20" Margin="0, 20" />
AutomationId="GeneratedPasswordLabel" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}" Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}"
Command="{Binding CopyCommand}" Command="{Binding CopyCommand}"
Grid.Column="1" Grid.Column="1"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyPassword}" AutomationProperties.Name="{u:I18n CopyPassword}" />
AutomationId="CopyValueButton" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Generate}}" Text="{Binding Source={x:Static core:BitwardenIcons.Generate}}"
Command="{Binding RegenerateCommand}" Command="{Binding RegenerateCommand}"
Grid.Column="2" Grid.Column="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n GeneratePassword}" AutomationProperties.Name="{u:I18n GeneratePassword}" />
AutomationId="RegenerateValueButton" />
</Grid> </Grid>
<Grid IsVisible="{Binding IsUsername}" <Grid IsVisible="{Binding IsUsername}"
StyleClass="box-row" StyleClass="box-row"
@@ -111,24 +107,21 @@
StyleClass="text-lg, text-html" StyleClass="text-lg, text-html"
Text="{Binding ColoredUsername, Mode=OneWay}" Text="{Binding ColoredUsername, Mode=OneWay}"
Margin="0, 20" Margin="0, 20"
HorizontalOptions="Start" HorizontalOptions="Start" />
AutomationId="GeneratedPasswordLabel" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}" Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}"
Command="{Binding CopyCommand}" Command="{Binding CopyCommand}"
Grid.Column="1" Grid.Column="1"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyUsername}" AutomationProperties.Name="{u:I18n CopyUsername}" />
AutomationId="CopyValueButton" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Generate}}" Text="{Binding Source={x:Static core:BitwardenIcons.Generate}}"
Command="{Binding RegenerateUsernameCommand}" Command="{Binding RegenerateUsernameCommand}"
Grid.Column="2" Grid.Column="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n GenerateUsername}" AutomationProperties.Name="{u:I18n GenerateUsername}" />
AutomationId="RegenerateValueButton" />
</Grid> </Grid>
<BoxView StyleClass="box-row-separator"/> <BoxView StyleClass="box-row-separator"/>
<StackLayout StyleClass="box" <StackLayout StyleClass="box"
@@ -142,8 +135,7 @@
ItemsSource="{Binding GeneratorTypeOptions, Mode=OneTime}" ItemsSource="{Binding GeneratorTypeOptions, Mode=OneTime}"
SelectedItem="{Binding GeneratorTypeSelected}" SelectedItem="{Binding GeneratorTypeSelected}"
ItemDisplayBinding="{Binding ., Converter={StaticResource localizableEnum}}" ItemDisplayBinding="{Binding ., Converter={StaticResource localizableEnum}}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="GeneratorTypePicker" />
</StackLayout> </StackLayout>
<Label Text="{u:I18n Options, Header=True}" <Label Text="{u:I18n Options, Header=True}"
StyleClass="box-header, box-header-platform" StyleClass="box-header, box-header-platform"
@@ -169,8 +161,7 @@
ItemsSource="{Binding UsernameTypeOptions, Mode=OneTime}" ItemsSource="{Binding UsernameTypeOptions, Mode=OneTime}"
SelectedItem="{Binding UsernameTypeSelected}" SelectedItem="{Binding UsernameTypeSelected}"
ItemDisplayBinding="{Binding ., Converter={StaticResource localizableEnum}}" ItemDisplayBinding="{Binding ., Converter={StaticResource localizableEnum}}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="UsernameTypePicker" />
<Label <Label
StyleClass="box-footer-label" StyleClass="box-footer-label"
Text="{Binding UsernameTypeDescriptionLabel}" /> Text="{Binding UsernameTypeDescriptionLabel}" />
@@ -181,8 +172,7 @@
StyleClass="box-label" /> StyleClass="box-label" />
<Entry x:Name="_plusAddressedEmailEntry" <Entry x:Name="_plusAddressedEmailEntry"
Text="{Binding PlusAddressedEmail}" Text="{Binding PlusAddressedEmail}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="PlusAddressedEmailEntry" />
<Label IsVisible="{Binding ShowUsernameEmailType}" <Label IsVisible="{Binding ShowUsernameEmailType}"
Text="{u:I18n EmailType}" Text="{u:I18n EmailType}"
StyleClass="box-label" StyleClass="box-label"
@@ -213,8 +203,7 @@
<Entry <Entry
x:Name="_catchAllEmailDomainNameEntry" x:Name="_catchAllEmailDomainNameEntry"
Text="{Binding CatchAllEmailDomain}" Text="{Binding CatchAllEmailDomain}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="CatchAllEmailDomainEntry" />
<Label IsVisible="{Binding ShowUsernameEmailType}" <Label IsVisible="{Binding ShowUsernameEmailType}"
Text="{u:I18n EmailType}" Text="{u:I18n EmailType}"
StyleClass="box-label" StyleClass="box-label"
@@ -247,27 +236,26 @@
ItemsSource="{Binding ForwardedEmailServiceTypeOptions, Mode=OneTime}" ItemsSource="{Binding ForwardedEmailServiceTypeOptions, Mode=OneTime}"
SelectedItem="{Binding ForwardedEmailServiceSelected}" SelectedItem="{Binding ForwardedEmailServiceSelected}"
ItemDisplayBinding="{Binding ., Converter={StaticResource localizableEnum}}" ItemDisplayBinding="{Binding ., Converter={StaticResource localizableEnum}}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ServiceTypePicker" /> <!--ANONADDY OPTIONS-->
<Grid <Grid IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.AnonAddy}}"
Grid.RowDefinitions="Auto,*" Grid.RowDefinitions="Auto,*"
Grid.ColumnDefinitions="*,Auto"> Grid.ColumnDefinitions="*,Auto">
<Label <Label
Margin="0,10,0,0" Margin="0,10,0,0"
Text="{Binding ForwardedEmailApiSecretLabel}" Text="{u:I18n APIAccessToken}"
StyleClass="box-label"/> StyleClass="box-label"/>
<Entry <Entry
Text="{Binding ForwardedEmailApiSecret}" x:Name="_anonAddyApiAccessTokenEntry"
IsPassword="{Binding ShowForwardedEmailApiSecret, Converter={StaticResource inverseBool}}" Text="{Binding AnonAddyApiAccessToken}"
Grid.Row="1" IsPassword="{Binding ShowAnonAddyApiAccessToken, Converter={StaticResource inverseBool}}"
AutomationId="ForwardedEmailApiSecretEntry" /> Grid.Row="1"/>
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowForwardedEmailApiSecret, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}" Text="{Binding ShowAnonAddyApiAccessToken, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
Command="{Binding ToggleForwardedEmailHiddenValueCommand}" Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"/>
AutomationId="ShowForwardedEmailApiSecretButton" />
</Grid> </Grid>
<Label IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.AnonAddy}}" <Label IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.AnonAddy}}"
Text="{u:I18n DomainNameRequiredParenthesis}" Text="{u:I18n DomainNameRequiredParenthesis}"
@@ -276,8 +264,91 @@
<Entry IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.AnonAddy}}" <Entry IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.AnonAddy}}"
x:Name="_anonAddyDomainNameEntry" x:Name="_anonAddyDomainNameEntry"
Text="{Binding AnonAddyDomainName}" Text="{Binding AnonAddyDomainName}"
StyleClass="box-value" StyleClass="box-value"/>
AutomationId="AnonAddyDomainNameEntry" /> <!--FIREFOX RELAY OPTIONS-->
<Grid StyleClass="box-row, box-row-input"
IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.FirefoxRelay}}"
Grid.RowDefinitions="Auto,*"
Grid.ColumnDefinitions="*,Auto">
<Label
Text="{u:I18n APIAccessToken}"
StyleClass="box-label"/>
<Entry
x:Name="_firefoxRelayApiAccessTokenEntry"
Text="{Binding FirefoxRelayApiAccessToken}"
StyleClass="box-value"
Grid.Row="1"
IsPassword="{Binding ShowFirefoxRelayApiAccessToken, Converter={StaticResource inverseBool}}"/>
<controls:IconButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowFirefoxRelayApiAccessToken, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
Grid.Row="1"
Grid.Column="1"/>
</Grid>
<!--SIMPLELOGIN OPTIONS-->
<Grid StyleClass="box-row, box-row-input"
IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.SimpleLogin}}"
Grid.RowDefinitions="Auto,*"
Grid.ColumnDefinitions="*,Auto">
<Label
Text="{u:I18n APIKeyRequiredParenthesis}"
StyleClass="box-label"/>
<Entry
x:Name="_simpleLoginApiKeyEntry"
Text="{Binding SimpleLoginApiKey}"
StyleClass="box-value"
Grid.Row="1"
IsPassword="{Binding ShowSimpleLoginApiKey, Converter={StaticResource inverseBool}}"/>
<controls:IconButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowSimpleLoginApiKey, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
Grid.Row="1"
Grid.Column="1"/>
</Grid>
<!--DUCKDUCKGO OPTIONS-->
<Grid StyleClass="box-row, box-row-input"
IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.DuckDuckGo}}"
Grid.RowDefinitions="Auto,*"
Grid.ColumnDefinitions="*,Auto">
<Label
Text="{u:I18n APIKeyRequiredParenthesis}"
StyleClass="box-label"/>
<Entry
x:Name="_duckDuckGoApiAccessTokenEntry"
Text="{Binding DuckDuckGoApiKey}"
StyleClass="box-value"
Grid.Row="1"
IsPassword="{Binding ShowDuckDuckGoApiKey, Converter={StaticResource inverseBool}}"/>
<controls:IconButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowDuckDuckGoApiKey, Converter={StaticResource inverseBool, iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
Grid.Row="1"
Grid.Column="1"/>
</Grid>
<!--FASTMAIL OPTIONS-->
<Grid StyleClass="box-row, box-row-input"
IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.Fastmail}}"
Grid.RowDefinitions="Auto,*"
Grid.ColumnDefinitions="*,Auto">
<Label
Text="{u:I18n APIKeyRequiredParenthesis}"
StyleClass="box-label"/>
<Entry
x:Name="_fastmailApiAccessTokenEntry"
Text="{Binding FastmailApiKey}"
StyleClass="box-value"
Grid.Row="1"
IsPassword="{Binding ShowFastmailApiKey, Converter={StaticResource inverseBool}}"/>
<controls:IconButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowFastmailApiKey, Converter={StaticResource iconGlyphConverter}, ConverterParameter={x:Static u:BooleanGlyphType.Eye}}"
Command="{Binding ToggleForwardedEmailHiddenValueCommand}"
Grid.Row="1"
Grid.Column="1"/>
</Grid>
</StackLayout> </StackLayout>
<!--RANDOM WORD OPTIONS--> <!--RANDOM WORD OPTIONS-->
<Grid IsVisible="{Binding UsernameTypeSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:UsernameType.RandomWord}}"> <Grid IsVisible="{Binding UsernameTypeSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:UsernameType.RandomWord}}">
@@ -288,8 +359,7 @@
<Switch <Switch
IsToggled="{Binding CapitalizeRandomWordUsername}" IsToggled="{Binding CapitalizeRandomWordUsername}"
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End" />
AutomationId="CapitalizeRandomWordUsernameToggle" />
</Grid> </Grid>
<BoxView IsVisible="{Binding UsernameTypeSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:UsernameType.RandomWord}}" <BoxView IsVisible="{Binding UsernameTypeSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:UsernameType.RandomWord}}"
StyleClass="box-row-separator" /> StyleClass="box-row-separator" />
@@ -301,8 +371,7 @@
<Switch <Switch
IsToggled="{Binding IncludeNumberRandomWordUsername}" IsToggled="{Binding IncludeNumberRandomWordUsername}"
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End" />
AutomationId="IncludeNumberRandomWordUsernameToggle" />
</Grid> </Grid>
<BoxView IsVisible="{Binding UsernameTypeSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:UsernameType.RandomWord}}" <BoxView IsVisible="{Binding UsernameTypeSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:UsernameType.RandomWord}}"
StyleClass="box-row-separator" /> StyleClass="box-row-separator" />
@@ -317,8 +386,7 @@
x:Name="_passwordTypePicker" x:Name="_passwordTypePicker"
ItemsSource="{Binding PasswordTypeOptions, Mode=OneTime}" ItemsSource="{Binding PasswordTypeOptions, Mode=OneTime}"
SelectedIndex="{Binding PasswordTypeSelectedIndex}" SelectedIndex="{Binding PasswordTypeSelectedIndex}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="PasswordTypePicker" />
</StackLayout> </StackLayout>
<StackLayout Spacing="0" <StackLayout Spacing="0"
Padding="0" Padding="0"
@@ -335,14 +403,12 @@
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
HorizontalTextAlignment="End" HorizontalTextAlignment="End"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center" VerticalTextAlignment="Center" />
AutomationId="NumberOfWordsLabel" />
<controls:ExtendedStepper <controls:ExtendedStepper
Value="{Binding NumWords}" Value="{Binding NumWords}"
Maximum="20" Maximum="20"
Minimum="3" Minimum="3"
Increment="1" Increment="1" />
AutomationId="NumberOfWordsStepper" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
@@ -353,8 +419,7 @@
Text="{Binding WordSeparator}" Text="{Binding WordSeparator}"
IsSpellCheckEnabled="False" IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False" IsTextPredictionEnabled="False"
StyleClass="box-value" StyleClass="box-value">
AutomationId="WordSeparatorEntry">
<Entry.Effects> <Entry.Effects>
<effects:NoEmojiKeyboardEffect /> <effects:NoEmojiKeyboardEffect />
</Entry.Effects> </Entry.Effects>
@@ -370,8 +435,7 @@
IsEnabled="{Binding EnforcedPolicyOptions.Capitalize, IsEnabled="{Binding EnforcedPolicyOptions.Capitalize,
Converter={StaticResource inverseBool}}" Converter={StaticResource inverseBool}}"
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End" />
AutomationId="CapitalizePassphraseToggle" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
<StackLayout StyleClass="box-row, box-row-switch"> <StackLayout StyleClass="box-row, box-row-switch">
@@ -384,8 +448,7 @@
IsEnabled="{Binding EnforcedPolicyOptions.IncludeNumber, IsEnabled="{Binding EnforcedPolicyOptions.IncludeNumber,
Converter={StaticResource inverseBool}}" Converter={StaticResource inverseBool}}"
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End" />
AutomationId="IncludeNumbersToggle" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
<StackLayout Spacing="0" Padding="0" IsVisible="{Binding IsPassword}"> <StackLayout Spacing="0" Padding="0" IsVisible="{Binding IsPassword}">
@@ -399,8 +462,7 @@
StyleClass="box-sub-label" StyleClass="box-sub-label"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalTextAlignment="End" HorizontalTextAlignment="End"
WidthRequest="50" WidthRequest="50" />
AutomationId="PasswordLengthLabel" />
<controls:ExtendedSlider <controls:ExtendedSlider
DragCompleted="LengthSlider_DragCompleted" DragCompleted="LengthSlider_DragCompleted"
Value="{Binding Length}" Value="{Binding Length}"
@@ -409,8 +471,7 @@
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
Maximum="128" Maximum="128"
Minimum="5" Minimum="5" />
AutomationId="PasswordLengthSlider" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
<StackLayout StyleClass="box-row, box-row-switch"> <StackLayout StyleClass="box-row, box-row-switch">
@@ -427,8 +488,7 @@
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n UppercaseAtoZ}" AutomationProperties.Name="{u:I18n UppercaseAtoZ}"/>
AutomationId="UppercaseAtoZToggle" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
<StackLayout StyleClass="box-row, box-row-switch"> <StackLayout StyleClass="box-row, box-row-switch">
@@ -445,8 +505,7 @@
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n LowercaseAtoZ}" AutomationProperties.Name="{u:I18n LowercaseAtoZ}"/>
AutomationId="LowercaseAtoZToggle" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
<StackLayout StyleClass="box-row, box-row-switch"> <StackLayout StyleClass="box-row, box-row-switch">
@@ -463,8 +522,7 @@
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n NumbersZeroToNine}" AutomationProperties.Name="{u:I18n NumbersZeroToNine}"/>
AutomationId="NumbersZeroToNineToggle" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
<StackLayout StyleClass="box-row, box-row-switch"> <StackLayout StyleClass="box-row, box-row-switch">
@@ -481,8 +539,7 @@
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n SpecialCharacters}" AutomationProperties.Name="{u:I18n SpecialCharacters}"/>
AutomationId="SpecialCharactersToggle" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
<StackLayout StyleClass="box-row, box-row-stepper"> <StackLayout StyleClass="box-row, box-row-stepper">
@@ -497,14 +554,12 @@
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
HorizontalTextAlignment="End" HorizontalTextAlignment="End"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center" VerticalTextAlignment="Center" />
AutomationId="MinNumberValueLabel" />
<controls:ExtendedStepper <controls:ExtendedStepper
Value="{Binding MinNumber}" Value="{Binding MinNumber}"
Maximum="5" Maximum="5"
Minimum="0" Minimum="0"
Increment="1" Increment="1" />
AutomationId="MinNumberStepper" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
<StackLayout StyleClass="box-row, box-row-stepper"> <StackLayout StyleClass="box-row, box-row-stepper">
@@ -519,14 +574,12 @@
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
HorizontalTextAlignment="End" HorizontalTextAlignment="End"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center" VerticalTextAlignment="Center" />
AutomationId="MinSpecialValueLabel" />
<controls:ExtendedStepper <controls:ExtendedStepper
Value="{Binding MinSpecial}" Value="{Binding MinSpecial}"
Maximum="5" Maximum="5"
Minimum="0" Minimum="0"
Increment="1" Increment="1" />
AutomationId="MinSpecialStepper" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
<StackLayout StyleClass="box-row, box-row-switch"> <StackLayout StyleClass="box-row, box-row-switch">
@@ -537,8 +590,7 @@
<Switch <Switch
IsToggled="{Binding AvoidAmbiguousChars}" IsToggled="{Binding AvoidAmbiguousChars}"
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End" />
AutomationId="AvoidAmbiguousCharsToggle" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
</StackLayout> </StackLayout>

View File

@@ -8,7 +8,6 @@ using Bit.App.Utilities;
using Bit.Core; using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
@@ -24,7 +23,7 @@ namespace Bit.App.Pages
private readonly IUsernameGenerationService _usernameGenerationService; private readonly IUsernameGenerationService _usernameGenerationService;
private readonly ITokenService _tokenService; private readonly ITokenService _tokenService;
private readonly IDeviceActionService _deviceActionService; private readonly IDeviceActionService _deviceActionService;
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>(); readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
private PasswordGenerationOptions _options; private PasswordGenerationOptions _options;
private UsernameGenerationOptions _usernameOptions; private UsernameGenerationOptions _usernameOptions;
@@ -50,7 +49,11 @@ namespace Bit.App.Pages
private bool _doneIniting; private bool _doneIniting;
private bool _showTypePicker; private bool _showTypePicker;
private string _emailWebsite; private string _emailWebsite;
private bool _showForwardedEmailApiSecret; private bool _showFirefoxRelayApiAccessToken;
private bool _showAnonAddyApiAccessToken;
private bool _showSimpleLoginApiKey;
private bool _showDuckDuckGoApiKey;
private bool _showFastmailApiKey;
private bool _editMode; private bool _editMode;
public GeneratorPageViewModel() public GeneratorPageViewModel()
@@ -93,7 +96,7 @@ namespace Bit.App.Pages
UsernameTypePromptHelpCommand = new Command(UsernameTypePromptHelp); UsernameTypePromptHelpCommand = new Command(UsernameTypePromptHelp);
RegenerateCommand = new AsyncCommand(RegenerateAsync, onException: ex => OnSubmitException(ex), allowsMultipleExecutions: false); RegenerateCommand = new AsyncCommand(RegenerateAsync, onException: ex => OnSubmitException(ex), allowsMultipleExecutions: false);
RegenerateUsernameCommand = new AsyncCommand(RegenerateUsernameAsync, onException: ex => OnSubmitException(ex), allowsMultipleExecutions: false); RegenerateUsernameCommand = new AsyncCommand(RegenerateUsernameAsync, onException: ex => OnSubmitException(ex), allowsMultipleExecutions: false);
ToggleForwardedEmailHiddenValueCommand = new Command(() => ShowForwardedEmailApiSecret = !ShowForwardedEmailApiSecret); ToggleForwardedEmailHiddenValueCommand = new AsyncCommand(ToggleForwardedEmailHiddenValueAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false);
CopyCommand = new AsyncCommand(CopyAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false); CopyCommand = new AsyncCommand(CopyAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false);
CloseCommand = new AsyncCommand(CloseAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false); CloseCommand = new AsyncCommand(CloseAsync, onException: ex => _logger.Value.Exception(ex), allowsMultipleExecutions: false);
} }
@@ -412,6 +415,7 @@ namespace Bit.App.Pages
public string UsernameTypeDescriptionLabel => GetUsernameTypeLabelDescription(UsernameTypeSelected); public string UsernameTypeDescriptionLabel => GetUsernameTypeLabelDescription(UsernameTypeSelected);
public ForwardedEmailServiceType ForwardedEmailServiceSelected public ForwardedEmailServiceType ForwardedEmailServiceSelected
{ {
get => _usernameOptions.ServiceType; get => _usernameOptions.ServiceType;
@@ -421,11 +425,7 @@ namespace Bit.App.Pages
{ {
_usernameOptions.ServiceType = value; _usernameOptions.ServiceType = value;
Username = Constants.DefaultUsernameGenerated; Username = Constants.DefaultUsernameGenerated;
TriggerPropertyChanged(nameof(ForwardedEmailServiceSelected), new string[] TriggerPropertyChanged(nameof(ForwardedEmailServiceSelected));
{
nameof(ForwardedEmailApiSecret),
nameof(ForwardedEmailApiSecretLabel)
});
SaveUsernameOptionsAsync(false).FireAndForget(); SaveUsernameOptionsAsync(false).FireAndForget();
} }
} }
@@ -445,104 +445,27 @@ namespace Bit.App.Pages
} }
} }
public string ForwardedEmailApiSecret public string AnonAddyApiAccessToken
{ {
get get => _usernameOptions.AnonAddyApiAccessToken;
{
switch (ForwardedEmailServiceSelected)
{
case ForwardedEmailServiceType.AnonAddy:
return _usernameOptions.AnonAddyApiAccessToken;
case ForwardedEmailServiceType.DuckDuckGo:
return _usernameOptions.DuckDuckGoApiKey;
case ForwardedEmailServiceType.Fastmail:
return _usernameOptions.FastMailApiKey;
case ForwardedEmailServiceType.FirefoxRelay:
return _usernameOptions.FirefoxRelayApiAccessToken;
case ForwardedEmailServiceType.SimpleLogin:
return _usernameOptions.SimpleLoginApiKey;
default:
return null;
}
}
set set
{ {
bool changed = false; if (_usernameOptions.AnonAddyApiAccessToken != value)
switch (ForwardedEmailServiceSelected)
{ {
case ForwardedEmailServiceType.AnonAddy: _usernameOptions.AnonAddyApiAccessToken = value;
if (_usernameOptions.AnonAddyApiAccessToken != value) TriggerPropertyChanged(nameof(AnonAddyApiAccessToken));
{
_usernameOptions.AnonAddyApiAccessToken = value;
changed = true;
}
break;
case ForwardedEmailServiceType.DuckDuckGo:
if (_usernameOptions.DuckDuckGoApiKey != value)
{
_usernameOptions.DuckDuckGoApiKey = value;
changed = true;
}
break;
case ForwardedEmailServiceType.Fastmail:
if (_usernameOptions.FastMailApiKey != value)
{
_usernameOptions.FastMailApiKey = value;
changed = true;
}
break;
case ForwardedEmailServiceType.FirefoxRelay:
if (_usernameOptions.FirefoxRelayApiAccessToken != value)
{
_usernameOptions.FirefoxRelayApiAccessToken = value;
changed = true;
}
break;
case ForwardedEmailServiceType.SimpleLogin:
if (_usernameOptions.SimpleLoginApiKey != value)
{
_usernameOptions.SimpleLoginApiKey = value;
changed = true;
}
break;
default:
break;
}
if (changed)
{
TriggerPropertyChanged(nameof(ForwardedEmailApiSecret));
SaveUsernameOptionsAsync(false).FireAndForget(); SaveUsernameOptionsAsync(false).FireAndForget();
} }
} }
} }
public string ForwardedEmailApiSecretLabel public bool ShowAnonAddyApiAccessToken
{ {
get get
{ {
switch (ForwardedEmailServiceSelected) return _showAnonAddyApiAccessToken;
{
case ForwardedEmailServiceType.AnonAddy:
case ForwardedEmailServiceType.FirefoxRelay:
return AppResources.APIAccessToken;
case ForwardedEmailServiceType.DuckDuckGo:
case ForwardedEmailServiceType.Fastmail:
case ForwardedEmailServiceType.SimpleLogin:
return AppResources.APIKeyRequiredParenthesis;
default:
return null;
}
} }
} set => SetProperty(ref _showAnonAddyApiAccessToken, value);
public bool ShowForwardedEmailApiSecret
{
get
{
return _showForwardedEmailApiSecret;
}
set => SetProperty(ref _showForwardedEmailApiSecret, value);
} }
public string AnonAddyDomainName public string AnonAddyDomainName
@@ -559,6 +482,99 @@ namespace Bit.App.Pages
} }
} }
public string FirefoxRelayApiAccessToken
{
get => _usernameOptions.FirefoxRelayApiAccessToken;
set
{
if (_usernameOptions.FirefoxRelayApiAccessToken != value)
{
_usernameOptions.FirefoxRelayApiAccessToken = value;
TriggerPropertyChanged(nameof(FirefoxRelayApiAccessToken));
SaveUsernameOptionsAsync(false).FireAndForget();
}
}
}
public bool ShowFirefoxRelayApiAccessToken
{
get
{
return _showFirefoxRelayApiAccessToken;
}
set => SetProperty(ref _showFirefoxRelayApiAccessToken, value);
}
public string SimpleLoginApiKey
{
get => _usernameOptions.SimpleLoginApiKey;
set
{
if (_usernameOptions.SimpleLoginApiKey != value)
{
_usernameOptions.SimpleLoginApiKey = value;
TriggerPropertyChanged(nameof(SimpleLoginApiKey));
SaveUsernameOptionsAsync(false).FireAndForget();
}
}
}
public bool ShowSimpleLoginApiKey
{
get
{
return _showSimpleLoginApiKey;
}
set => SetProperty(ref _showSimpleLoginApiKey, value);
}
public string DuckDuckGoApiKey
{
get => _usernameOptions.DuckDuckGoApiKey;
set
{
if (_usernameOptions.DuckDuckGoApiKey != value)
{
_usernameOptions.DuckDuckGoApiKey = value;
TriggerPropertyChanged(nameof(DuckDuckGoApiKey));
SaveUsernameOptionsAsync(false).FireAndForget();
}
}
}
public bool ShowDuckDuckGoApiKey
{
get
{
return _showDuckDuckGoApiKey;
}
set => SetProperty(ref _showDuckDuckGoApiKey, value);
}
public string FastmailApiKey
{
get => _usernameOptions.FastMailApiKey;
set
{
if (_usernameOptions.FastMailApiKey != value)
{
_usernameOptions.FastMailApiKey = value;
TriggerPropertyChanged(nameof(FastmailApiKey));
SaveUsernameOptionsAsync(false).FireAndForget();
}
}
}
public bool ShowFastmailApiKey
{
get
{
return _showFastmailApiKey;
}
set => SetProperty(ref _showFastmailApiKey, value);
}
public bool CapitalizeRandomWordUsername public bool CapitalizeRandomWordUsername
{ {
get => _usernameOptions.CapitalizeRandomWordUsername; get => _usernameOptions.CapitalizeRandomWordUsername;
@@ -791,9 +807,12 @@ namespace Bit.App.Pages
TriggerPropertyChanged(nameof(PlusAddressedEmailTypeSelected)); TriggerPropertyChanged(nameof(PlusAddressedEmailTypeSelected));
TriggerPropertyChanged(nameof(IncludeNumberRandomWordUsername)); TriggerPropertyChanged(nameof(IncludeNumberRandomWordUsername));
TriggerPropertyChanged(nameof(CapitalizeRandomWordUsername)); TriggerPropertyChanged(nameof(CapitalizeRandomWordUsername));
TriggerPropertyChanged(nameof(ForwardedEmailApiSecret)); TriggerPropertyChanged(nameof(SimpleLoginApiKey));
TriggerPropertyChanged(nameof(ForwardedEmailApiSecretLabel)); TriggerPropertyChanged(nameof(FirefoxRelayApiAccessToken));
TriggerPropertyChanged(nameof(AnonAddyDomainName)); TriggerPropertyChanged(nameof(AnonAddyDomainName));
TriggerPropertyChanged(nameof(AnonAddyApiAccessToken));
TriggerPropertyChanged(nameof(DuckDuckGoApiKey));
TriggerPropertyChanged(nameof(FastmailApiKey));
TriggerPropertyChanged(nameof(CatchAllEmailDomain)); TriggerPropertyChanged(nameof(CatchAllEmailDomain));
TriggerPropertyChanged(nameof(ForwardedEmailServiceSelected)); TriggerPropertyChanged(nameof(ForwardedEmailServiceSelected));
TriggerPropertyChanged(nameof(UsernameTypeSelected)); TriggerPropertyChanged(nameof(UsernameTypeSelected));
@@ -808,7 +827,7 @@ namespace Bit.App.Pages
private void SetOptions() private void SetOptions()
{ {
_options.AllowAmbiguousChar = AllowAmbiguousChars; _options.AllowAmbiguousChar = AllowAmbiguousChars;
_options.Type = PasswordTypeSelectedIndex == 1 ? PasswordGenerationOptions.TYPE_PASSPHRASE : PasswordGenerationOptions.TYPE_PASSWORD; _options.Type = PasswordTypeSelectedIndex == 1 ? "passphrase" : "password";
_options.MinNumber = MinNumber; _options.MinNumber = MinNumber;
_options.MinSpecial = MinSpecial; _options.MinSpecial = MinSpecial;
_options.Special = Special; _options.Special = Special;
@@ -826,23 +845,15 @@ namespace Bit.App.Pages
{ {
_logger.Value.Exception(ex); _logger.Value.Exception(ex);
string message = AppResources.GenericErrorMessage;
if (IsUsername && UsernameTypeSelected == UsernameType.ForwardedEmailAlias) if (IsUsername && UsernameTypeSelected == UsernameType.ForwardedEmailAlias)
{ {
if (ex is ForwardedEmailInvalidSecretException) await Device.InvokeOnMainThreadAsync(() => Page.DisplayAlert(
{ AppResources.AnErrorHasOccurred, string.Format(AppResources.UnknownXErrorMessage, ForwardedEmailServiceSelected), AppResources.Ok));
message = ForwardedEmailServiceSelected == ForwardedEmailServiceType.AnonAddy || ForwardedEmailServiceSelected == ForwardedEmailServiceType.FirefoxRelay }
? AppResources.InvalidAPIToken else
: AppResources.InvalidAPIKey; {
} await Device.InvokeOnMainThreadAsync(() => Page.DisplayAlert(AppResources.AnErrorHasOccurred, AppResources.GenericErrorMessage, AppResources.Ok));
else
{
message = string.Format(AppResources.UnknownXErrorMessage, ForwardedEmailServiceSelected);
}
} }
await Device.InvokeOnMainThreadAsync(() => Page.DisplayAlert(AppResources.AnErrorHasOccurred, message, AppResources.Ok));
} }
private string GetUsernameTypeLabelDescription(UsernameType value) private string GetUsernameTypeLabelDescription(UsernameType value)
@@ -859,5 +870,27 @@ namespace Bit.App.Pages
return string.Empty; return string.Empty;
} }
} }
private async Task ToggleForwardedEmailHiddenValueAsync()
{
switch (ForwardedEmailServiceSelected)
{
case ForwardedEmailServiceType.AnonAddy:
ShowAnonAddyApiAccessToken = !ShowAnonAddyApiAccessToken;
break;
case ForwardedEmailServiceType.FirefoxRelay:
ShowFirefoxRelayApiAccessToken = !ShowFirefoxRelayApiAccessToken;
break;
case ForwardedEmailServiceType.SimpleLogin:
ShowSimpleLoginApiKey = !ShowSimpleLoginApiKey;
break;
case ForwardedEmailServiceType.DuckDuckGo:
ShowDuckDuckGoApiKey = !ShowDuckDuckGoApiKey;
break;
case ForwardedEmailServiceType.Fastmail:
ShowFastmailApiKey = !ShowFastmailApiKey;
break;
}
}
} }
} }

View File

@@ -71,8 +71,7 @@
<Label <Label
Text="{u:I18n SendDisabledWarning}" Text="{u:I18n SendDisabledWarning}"
StyleClass="text-muted, text-sm, text-bold" StyleClass="text-muted, text-sm, text-bold"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center" />
AutomationId="SendDisabledWarningMessageLabel" />
</Frame> </Frame>
<Frame <Frame
IsVisible="{Binding SendOptionsPolicyInEffect}" IsVisible="{Binding SendOptionsPolicyInEffect}"
@@ -84,8 +83,7 @@
<Label <Label
Text="{u:I18n SendOptionsPolicyInEffect}" Text="{u:I18n SendOptionsPolicyInEffect}"
StyleClass="text-muted, text-sm, text-bold" StyleClass="text-muted, text-sm, text-bold"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center" />
AutomationId="SendOptionsPolicyInEffectLabel" />
</Frame> </Frame>
<StackLayout StyleClass="box-row"> <StackLayout StyleClass="box-row">
<Label <Label
@@ -95,8 +93,7 @@
x:Name="_nameEntry" x:Name="_nameEntry"
Text="{Binding Send.Name}" Text="{Binding Send.Name}"
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="SendNameEntry" />
<Label <Label
Text="{u:I18n NameInfo}" Text="{u:I18n NameInfo}"
StyleClass="box-footer-label" StyleClass="box-footer-label"
@@ -126,7 +123,6 @@
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n File}" AutomationProperties.Name="{u:I18n File}"
AutomationProperties.HelpText="{Binding FileTypeAccessibilityLabel}" AutomationProperties.HelpText="{Binding FileTypeAccessibilityLabel}"
AutomationId="SendFileButton"
Grid.Column="0"> Grid.Column="0">
</Button> </Button>
<Button <Button
@@ -139,7 +135,6 @@
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Text}" AutomationProperties.Name="{u:I18n Text}"
AutomationProperties.HelpText="{Binding TextTypeAccessibilityLabel}" AutomationProperties.HelpText="{Binding TextTypeAccessibilityLabel}"
AutomationId="SendTextButton"
Grid.Column="1"> Grid.Column="1">
</Button> </Button>
</Grid> </Grid>
@@ -157,14 +152,12 @@
Text="{Binding Send.File.FileName, Mode=OneWay}" Text="{Binding Send.File.FileName, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
VerticalTextAlignment="Center" VerticalTextAlignment="Center"
HorizontalOptions="StartAndExpand" HorizontalOptions="StartAndExpand" />
AutomationId="SendFileNameLabel" />
<Label <Label
Text="{Binding Send.File.SizeName, Mode=OneWay}" Text="{Binding Send.File.SizeName, Mode=OneWay}"
StyleClass="box-sub-label" StyleClass="box-sub-label"
HorizontalTextAlignment="End" HorizontalTextAlignment="End"
VerticalTextAlignment="Center" VerticalTextAlignment="Center" />
AutomationId="SendFileSizeLabel" />
</StackLayout> </StackLayout>
<StackLayout <StackLayout
IsVisible="{Binding EditMode, Converter={StaticResource inverseBool}}" IsVisible="{Binding EditMode, Converter={StaticResource inverseBool}}"
@@ -175,23 +168,20 @@
LineBreakMode="CharacterWrap" LineBreakMode="CharacterWrap"
StyleClass="text-sm, text-muted" StyleClass="text-sm, text-muted"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center" />
AutomationId="SendNoFileChosenLabel" />
<Label <Label
IsVisible="{Binding FileName, Converter={StaticResource notNull}}" IsVisible="{Binding FileName, Converter={StaticResource notNull}}"
Text="{Binding FileName}" Text="{Binding FileName}"
LineBreakMode="CharacterWrap" LineBreakMode="CharacterWrap"
StyleClass="text-sm, text-muted" StyleClass="text-sm, text-muted"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center" />
AutomationId="SendCurrentFileNameLabel" />
<Button <Button
Text="{u:I18n ChooseFile}" Text="{u:I18n ChooseFile}"
IsVisible="{Binding IsAddFromShare, Converter={StaticResource inverseBool}}" IsVisible="{Binding IsAddFromShare, Converter={StaticResource inverseBool}}"
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
StyleClass="box-button-row" StyleClass="box-button-row"
Clicked="ChooseFile_Clicked" Clicked="ChooseFile_Clicked" />
AutomationId="SendChooseFileButton" />
<Label <Label
Margin="0, 5, 0, 0" Margin="0, 5, 0, 0"
Text="{u:I18n MaxFileSize}" Text="{u:I18n MaxFileSize}"
@@ -218,7 +208,6 @@
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
StyleClass="box-value" StyleClass="box-value"
Margin="{Binding EditorMargins}" Margin="{Binding EditorMargins}"
AutomationId="SendTextContentEntry"
effects:ScrollEnabledEffect.IsScrollEnabled="false" > effects:ScrollEnabledEffect.IsScrollEnabled="false" >
<Editor.Behaviors> <Editor.Behaviors>
<behaviors:EditorPreventAutoBottomScrollingOnFocusedBehavior ParentScrollView="{x:Reference _scrollView}" /> <behaviors:EditorPreventAutoBottomScrollingOnFocusedBehavior ParentScrollView="{x:Reference _scrollView}" />
@@ -246,8 +235,7 @@
IsToggled="{Binding Send.Text.Hidden}" IsToggled="{Binding Send.Text.Hidden}"
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
HorizontalOptions="End" HorizontalOptions="End"
Margin="10,0,0,0" Margin="10,0,0,0" />
AutomationId="SendHideTextByDefaultToggle" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
<StackLayout <StackLayout
@@ -261,8 +249,7 @@
IsToggled="{Binding ShareOnSave}" IsToggled="{Binding ShareOnSave}"
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
HorizontalOptions="End" HorizontalOptions="End"
Margin="10,0,0,0" Margin="10,0,0,0" />
AutomationId="SendShareSendAfterSaveToggle" />
</StackLayout> </StackLayout>
<StackLayout <StackLayout
Orientation="Horizontal" Orientation="Horizontal"
@@ -276,24 +263,21 @@
StyleClass="box-row-button" StyleClass="box-row-button"
TextColor="{DynamicResource PrimaryColor}" TextColor="{DynamicResource PrimaryColor}"
Margin="0" Margin="0"
AutomationProperties.IsInAccessibleTree="False" AutomationProperties.IsInAccessibleTree="False"/>
AutomationId="SendShowHideOptionsButton" />
<controls:IconButton <controls:IconButton
x:Name="_btnOptionsUp" x:Name="_btnOptionsUp"
Text="{Binding Source={x:Static core:BitwardenIcons.ChevronUp}}" Text="{Binding Source={x:Static core:BitwardenIcons.ChevronUp}}"
StyleClass="box-row-button" StyleClass="box-row-button"
TextColor="{DynamicResource PrimaryColor}" TextColor="{DynamicResource PrimaryColor}"
IsVisible="{Binding ShowOptions}" IsVisible="{Binding ShowOptions}"
AutomationProperties.IsInAccessibleTree="False" AutomationProperties.IsInAccessibleTree="False"/>
AutomationId="SendOptionsDisplayed" />
<controls:IconButton <controls:IconButton
x:Name="_btnOptionsDown" x:Name="_btnOptionsDown"
Text="{Binding Source={x:Static core:BitwardenIcons.AngleDown}}" Text="{Binding Source={x:Static core:BitwardenIcons.AngleDown}}"
StyleClass="box-row-button" StyleClass="box-row-button"
TextColor="{DynamicResource PrimaryColor}" TextColor="{DynamicResource PrimaryColor}"
IsVisible="{Binding ShowOptions, Converter={StaticResource inverseBool}}" IsVisible="{Binding ShowOptions, Converter={StaticResource inverseBool}}"
AutomationProperties.IsInAccessibleTree="False" AutomationProperties.IsInAccessibleTree="False"/>
AutomationId="SendOptionsHidden" />
</StackLayout> </StackLayout>
<StackLayout IsVisible="{Binding ShowOptions}"> <StackLayout IsVisible="{Binding ShowOptions}">
<StackLayout <StackLayout
@@ -310,8 +294,7 @@
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
StyleClass="box-value" StyleClass="box-value"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n DeletionTime}" AutomationProperties.Name="{u:I18n DeletionTime}" />
AutomationId="SendDeletionOptionsPicker" />
<Grid <Grid
IsVisible="{Binding ShowDeletionCustomPickers}" IsVisible="{Binding ShowDeletionCustomPickers}"
Margin="0,5,0,0"> Margin="0,5,0,0">
@@ -325,16 +308,14 @@
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n DeletionDate}" AutomationProperties.Name="{u:I18n DeletionDate}"
Grid.Column="0" Grid.Column="0" />
AutomationId="SendCustomDeletionDatePicker" />
<controls:ExtendedTimePicker <controls:ExtendedTimePicker
NullableTime="{Binding DeletionDateTimeViewModel.Time, Mode=TwoWay}" NullableTime="{Binding DeletionDateTimeViewModel.Time, Mode=TwoWay}"
Format="t" Format="t"
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n DeletionTime}" AutomationProperties.Name="{u:I18n DeletionTime}"
Grid.Column="1" Grid.Column="1" />
AutomationId="SendCustomDeletionTimePicker" />
</Grid> </Grid>
<Label <Label
Text="{u:I18n DeletionDateInfo}" Text="{u:I18n DeletionDateInfo}"
@@ -353,8 +334,7 @@
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
StyleClass="box-value" StyleClass="box-value"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ExpirationTime}" AutomationProperties.Name="{u:I18n ExpirationTime}" />
AutomationId="SendExpirationOptionsPicker" />
<Grid <Grid
IsVisible="{Binding ShowExpirationCustomPickers}" IsVisible="{Binding ShowExpirationCustomPickers}"
Margin="0,5,0,0"> Margin="0,5,0,0">
@@ -369,8 +349,7 @@
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ExpirationDate}" AutomationProperties.Name="{u:I18n ExpirationDate}"
Grid.Column="0" Grid.Column="0" />
AutomationId="SendCustomExpirationDatePicker" />
<controls:ExtendedTimePicker <controls:ExtendedTimePicker
NullableTime="{Binding ExpirationDateTimeViewModel.Time, Mode=TwoWay}" NullableTime="{Binding ExpirationDateTimeViewModel.Time, Mode=TwoWay}"
PlaceHolder="--:-- --" PlaceHolder="--:-- --"
@@ -378,8 +357,7 @@
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ExpirationTime}" AutomationProperties.Name="{u:I18n ExpirationTime}"
Grid.Column="1" Grid.Column="1" />
AutomationId="SendCustomExpirationTimePicker" />
</Grid> </Grid>
<StackLayout <StackLayout
Orientation="Horizontal" Orientation="Horizontal"
@@ -396,8 +374,7 @@
FontSize="{Binding SegmentedButtonFontSize}" FontSize="{Binding SegmentedButtonFontSize}"
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
StyleClass="box-row-button" StyleClass="box-row-button"
Clicked="ClearExpirationDate_Clicked" Clicked="ClearExpirationDate_Clicked" />
AutomationId="SendClearExpirationDateButton" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
<StackLayout <StackLayout
@@ -416,15 +393,13 @@
Keyboard="Numeric" Keyboard="Numeric"
MaxLength="9" MaxLength="9"
TextChanged="OnMaxAccessCountTextChanged" TextChanged="OnMaxAccessCountTextChanged"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" />
AutomationId="SendMaxAccessCountEntry" />
<controls:ExtendedStepper <controls:ExtendedStepper
x:Name="_maxAccessCountStepper" x:Name="_maxAccessCountStepper"
Value="{Binding MaxAccessCount}" Value="{Binding MaxAccessCount}"
Maximum="999999999" Maximum="999999999"
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
Margin="10,0,0,0" Margin="10,0,0,0" />
AutomationId="SendMaxAccessCountStepper" />
</StackLayout> </StackLayout>
<Label <Label
Text="{u:I18n MaximumAccessCountInfo}" Text="{u:I18n MaximumAccessCountInfo}"
@@ -444,8 +419,7 @@
<Label <Label
Text="{Binding Send.AccessCount, Mode=OneWay}" Text="{Binding Send.AccessCount, Mode=OneWay}"
StyleClass="box-label" StyleClass="box-label"
VerticalTextAlignment="Center" VerticalTextAlignment="Center" />
AutomationId="SendCurrentAccessCountLabel" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
<StackLayout <StackLayout
@@ -462,8 +436,7 @@
StyleClass="box-value" StyleClass="box-value"
IsSpellCheckEnabled="False" IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False" IsTextPredictionEnabled="False"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" />
AutomationId="SendNewPasswordEntry" />
<controls:IconButton <controls:IconButton
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
@@ -472,8 +445,7 @@
Margin="10,0,0,0" Margin="10,0,0,0"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
AutomationId="SendShowHidePasswordButton" />
</StackLayout> </StackLayout>
<Label <Label
Text="{u:I18n PasswordInfo}" Text="{u:I18n PasswordInfo}"
@@ -492,8 +464,7 @@
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
StyleClass="box-value" StyleClass="box-value"
Margin="{Binding EditorMargins}" Margin="{Binding EditorMargins}"
effects:ScrollEnabledEffect.IsScrollEnabled="false" effects:ScrollEnabledEffect.IsScrollEnabled="false" >
AutomationId="SendNotesEntry">
<Editor.Behaviors> <Editor.Behaviors>
<behaviors:EditorPreventAutoBottomScrollingOnFocusedBehavior ParentScrollView="{x:Reference _scrollView}" /> <behaviors:EditorPreventAutoBottomScrollingOnFocusedBehavior ParentScrollView="{x:Reference _scrollView}" />
</Editor.Behaviors> </Editor.Behaviors>
@@ -521,8 +492,7 @@
IsToggled="{Binding Send.HideEmail}" IsToggled="{Binding Send.HideEmail}"
IsEnabled="{Binding DisableHideEmailControl, Converter={StaticResource inverseBool}}" IsEnabled="{Binding DisableHideEmailControl, Converter={StaticResource inverseBool}}"
HorizontalOptions="End" HorizontalOptions="End"
Margin="10,0,0,0" Margin="10,0,0,0" />
AutomationId="SendHideEmailSwitch" />
</StackLayout> </StackLayout>
<StackLayout <StackLayout
StyleClass="box-row, box-row-switch" StyleClass="box-row, box-row-switch"
@@ -536,8 +506,7 @@
IsToggled="{Binding Send.Disabled}" IsToggled="{Binding Send.Disabled}"
IsEnabled="{Binding SendEnabled}" IsEnabled="{Binding SendEnabled}"
HorizontalOptions="End" HorizontalOptions="End"
Margin="10,0,0,0" Margin="10,0,0,0" />
AutomationId="SendDeactivateSwitch" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>

View File

@@ -25,8 +25,7 @@
Priority="-2" Priority="-2"
UseOriginalImage="True" UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" AutomationProperties.Name="{u:I18n Account}" />
AutomationId="AccountIconButton" />
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" x:Name="_closeItem" /> <ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" x:Name="_closeItem" />
<ToolbarItem Text="{u:I18n Save}" Clicked="Save_Clicked" Order="Primary" x:Name="_saveItem"/> <ToolbarItem Text="{u:I18n Save}" Clicked="Save_Clicked" Order="Primary" x:Name="_saveItem"/>
</ContentPage.ToolbarItems> </ContentPage.ToolbarItems>

View File

@@ -44,15 +44,13 @@
<controls:SendViewCell <controls:SendViewCell
Send="{Binding Send}" Send="{Binding Send}"
ButtonCommand="{Binding BindingContext.SendOptionsCommand, Source={x:Reference _page}}" ButtonCommand="{Binding BindingContext.SendOptionsCommand, Source={x:Reference _page}}"
ShowOptions="{Binding BindingContext.SendEnabled, Source={x:Reference _page}}" ShowOptions="{Binding BindingContext.SendEnabled, Source={x:Reference _page}}" />
AutomationId="SendCell" />
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="sendGroupTemplate" <DataTemplate x:Key="sendGroupTemplate"
x:DataType="pages:SendGroupingsPageListItem"> x:DataType="pages:SendGroupingsPageListItem">
<controls:ExtendedStackLayout Orientation="Horizontal" <controls:ExtendedStackLayout Orientation="Horizontal"
StyleClass="list-row, list-row-platform" StyleClass="list-row, list-row-platform">
AutomationId="{Binding AutomationId}">
<controls:IconLabel Text="{Binding Icon, Mode=OneWay}" <controls:IconLabel Text="{Binding Icon, Mode=OneWay}"
HorizontalOptions="Start" HorizontalOptions="Start"
VerticalOptions="Center" VerticalOptions="Center"
@@ -66,14 +64,12 @@
LineBreakMode="TailTruncation" LineBreakMode="TailTruncation"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
StyleClass="list-title" StyleClass="list-title" />
AutomationId="SendFilterNameLabel" />
<Label Text="{Binding ItemCount, Mode=OneWay}" <Label Text="{Binding ItemCount, Mode=OneWay}"
HorizontalOptions="End" HorizontalOptions="End"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalTextAlignment="End" HorizontalTextAlignment="End"
StyleClass="list-sub" StyleClass="list-sub" />
AutomationId="SendFilterCountLabel" />
</controls:ExtendedStackLayout> </controls:ExtendedStackLayout>
</DataTemplate> </DataTemplate>

View File

@@ -66,27 +66,5 @@ namespace Bit.App.Pages
return _icon; return _icon;
} }
} }
public string AutomationId
{
get
{
if (_name != null)
{
return "SendItem";
}
if (Type != null)
{
switch (Type.Value)
{
case SendType.Text:
return "SendTextFilter";
case SendType.File:
return "SendFileFilter";
}
}
return null;
}
}
} }
} }

View File

@@ -59,8 +59,7 @@
Margin="20, 0" Margin="20, 0"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center" />
AutomationId="NoSendDisplayedLabel" />
<controls:ExtendedCollectionView <controls:ExtendedCollectionView
IsVisible="{Binding ShowList}" IsVisible="{Binding ShowList}"
ItemsSource="{Binding Sends}" ItemsSource="{Binding Sends}"
@@ -68,15 +67,13 @@
SelectionMode="Single" SelectionMode="Single"
SelectionChanged="RowSelected" SelectionChanged="RowSelected"
StyleClass="list, list-platform" StyleClass="list, list-platform"
ExtraDataForLogging="Sends Page" ExtraDataForLogging="Sends Page">
AutomationId="SendCellList">
<CollectionView.ItemTemplate> <CollectionView.ItemTemplate>
<DataTemplate x:DataType="views:SendView"> <DataTemplate x:DataType="views:SendView">
<controls:SendViewCell <controls:SendViewCell
Send="{Binding .}" Send="{Binding .}"
ButtonCommand="{Binding BindingContext.SendOptionsCommand, Source={x:Reference _page}}" ButtonCommand="{Binding BindingContext.SendOptionsCommand, Source={x:Reference _page}}"
ShowOptions="{Binding BindingContext.SendEnabled, Source={x:Reference _page}}" ShowOptions="{Binding BindingContext.SendEnabled, Source={x:Reference _page}}" />
AutomationId="SendCell" />
</DataTemplate> </DataTemplate>
</CollectionView.ItemTemplate> </CollectionView.ItemTemplate>
</controls:ExtendedCollectionView> </controls:ExtendedCollectionView>

View File

@@ -13,13 +13,8 @@
</ContentPage.BindingContext> </ContentPage.BindingContext>
<ContentPage.ToolbarItems> <ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Cancel}" <ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
Clicked="Close_Clicked" <ToolbarItem Text="{u:I18n Save}" Clicked="Save_Clicked" Order="Primary" />
Order="Primary"
Priority="-1" />
<ToolbarItem Text="{u:I18n Save}"
Clicked="Save_Clicked"
Order="Primary" />
<ToolbarItem Text="{u:I18n Delete}" <ToolbarItem Text="{u:I18n Delete}"
Clicked="Delete_Clicked" Clicked="Delete_Clicked"
Order="Secondary" Order="Secondary"
@@ -48,8 +43,7 @@
StyleClass="box-value" StyleClass="box-value"
x:Name="_nameEntry" x:Name="_nameEntry"
ReturnType="Go" ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" ReturnCommand="{Binding SubmitCommand}" />
AutomationId="FolderNameEntry" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>

View File

@@ -31,8 +31,7 @@
Margin="20, 0" Margin="20, 0"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center"></Label>
AutomationId="NoFoldersLabel"></Label>
<controls:ExtendedCollectionView <controls:ExtendedCollectionView
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}" IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
ItemsSource="{Binding Folders}" ItemsSource="{Binding Folders}"
@@ -45,12 +44,10 @@
<DataTemplate x:DataType="views:FolderView"> <DataTemplate x:DataType="views:FolderView">
<controls:ExtendedStackLayout <controls:ExtendedStackLayout
StyleClass="list-row, list-row-platform" StyleClass="list-row, list-row-platform"
Padding="10" Padding="10">
AutomationId="FolderCell">
<Label LineBreakMode="TailTruncation" <Label LineBreakMode="TailTruncation"
StyleClass="list-title, list-title-platform" StyleClass="list-title, list-title-platform"
Text="{Binding Name, Mode=OneWay}" Text="{Binding Name, Mode=OneWay}" />
AutomationId="FolderName" />
</controls:ExtendedStackLayout> </controls:ExtendedStackLayout>
</DataTemplate> </DataTemplate>
</CollectionView.ItemTemplate> </CollectionView.ItemTemplate>

View File

@@ -27,8 +27,7 @@
x:Name="_themePicker" x:Name="_themePicker"
ItemsSource="{Binding ThemeOptions, Mode=OneTime}" ItemsSource="{Binding ThemeOptions, Mode=OneTime}"
SelectedIndex="{Binding ThemeSelectedIndex}" SelectedIndex="{Binding ThemeSelectedIndex}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ThemeSelectorPicker" />
</StackLayout> </StackLayout>
<Label <Label
StyleClass="box-footer-label" StyleClass="box-footer-label"
@@ -45,8 +44,7 @@
x:Name="_autoDarkThemePicker" x:Name="_autoDarkThemePicker"
ItemsSource="{Binding AutoDarkThemeOptions, Mode=OneTime}" ItemsSource="{Binding AutoDarkThemeOptions, Mode=OneTime}"
SelectedIndex="{Binding AutoDarkThemeSelectedIndex}" SelectedIndex="{Binding AutoDarkThemeSelectedIndex}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="DefaultDarkThemePicker" />
</StackLayout> </StackLayout>
<Label <Label
StyleClass="box-footer-label" StyleClass="box-footer-label"
@@ -61,8 +59,7 @@
x:Name="_uriMatchPicker" x:Name="_uriMatchPicker"
ItemsSource="{Binding UriMatchOptions, Mode=OneTime}" ItemsSource="{Binding UriMatchOptions, Mode=OneTime}"
SelectedIndex="{Binding UriMatchSelectedIndex}" SelectedIndex="{Binding UriMatchSelectedIndex}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="DefaultUriMatchDetectionPicker" />
</StackLayout> </StackLayout>
<Label <Label
Text="{u:I18n DefaultUriMatchDetectionDescription}" Text="{u:I18n DefaultUriMatchDetectionDescription}"
@@ -77,8 +74,7 @@
x:Name="_clearClipboardPicker" x:Name="_clearClipboardPicker"
ItemsSource="{Binding ClearClipboardOptions, Mode=OneTime}" ItemsSource="{Binding ClearClipboardOptions, Mode=OneTime}"
SelectedIndex="{Binding ClearClipboardSelectedIndex}" SelectedIndex="{Binding ClearClipboardSelectedIndex}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ClearClipboardPicker" />
</StackLayout> </StackLayout>
<Label <Label
Text="{u:I18n ClearClipboardDescription}" Text="{u:I18n ClearClipboardDescription}"
@@ -94,8 +90,7 @@
ItemsSource="{Binding LocalesOptions, Mode=OneTime}" ItemsSource="{Binding LocalesOptions, Mode=OneTime}"
SelectedItem="{Binding SelectedLocale}" SelectedItem="{Binding SelectedLocale}"
ItemDisplayBinding="{Binding Value}" ItemDisplayBinding="{Binding Value}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="LanguagePicker" />
</StackLayout> </StackLayout>
<Label <Label
Text="{u:I18n LanguageChangeRequiresAppRestart}" Text="{u:I18n LanguageChangeRequiresAppRestart}"
@@ -110,8 +105,7 @@
<Switch <Switch
IsToggled="{Binding AutoTotpCopy}" IsToggled="{Binding AutoTotpCopy}"
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End" />
AutomationId="CopyTotpAutomaticallyToggle" />
</StackLayout> </StackLayout>
<Label <Label
Text="{u:I18n CopyTotpAutomaticallyDescription}" Text="{u:I18n CopyTotpAutomaticallyDescription}"
@@ -126,8 +120,7 @@
<Switch <Switch
IsToggled="{Binding Favicon}" IsToggled="{Binding Favicon}"
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End" />
AutomationId="ShowWebsiteIconsToggle" />
</StackLayout> </StackLayout>
<Label <Label
Text="{u:I18n ShowWebsiteIconsDescription}" Text="{u:I18n ShowWebsiteIconsDescription}"

View File

@@ -32,21 +32,19 @@
StyleClass="text-muted, text-sm, text-bold" StyleClass="text-muted, text-sm, text-bold"
HorizontalTextAlignment="Center" /> HorizontalTextAlignment="Center" />
</Frame> </Frame>
<controls:CustomLabel IsVisible="{Binding UseFrame, Converter={StaticResource inverseBool}}" <Label IsVisible="{Binding UseFrame, Converter={StaticResource inverseBool}}"
Text="{Binding Name, Mode=OneWay}" Text="{Binding Name, Mode=OneWay}"
LineBreakMode="{Binding LineBreakMode}" LineBreakMode="{Binding LineBreakMode}"
HorizontalOptions="StartAndExpand" HorizontalOptions="StartAndExpand"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
StyleClass="list-title" StyleClass="list-title"/>
AutomationId="{Binding AutomationIdSettingName}" /> <Label Text="{Binding SubLabel, Mode=OneWay}"
<controls:CustomLabel Text="{Binding SubLabel, Mode=OneWay}"
IsVisible="{Binding ShowSubLabel}" IsVisible="{Binding ShowSubLabel}"
HorizontalOptions="End" HorizontalOptions="End"
HorizontalTextAlignment="End" HorizontalTextAlignment="End"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
TextColor="{Binding SubLabelColor}" TextColor="{Binding SubLabelColor}"
StyleClass="list-sub" StyleClass="list-sub" />
AutomationId="{Binding AutomationIdSettingStatus}" />
</controls:ExtendedStackLayout> </controls:ExtendedStackLayout>
</DataTemplate> </DataTemplate>
<DataTemplate <DataTemplate
@@ -59,8 +57,7 @@
Padding="10" Padding="10"
HasShadow="False" HasShadow="False"
BackgroundColor="Transparent" BackgroundColor="Transparent"
BorderColor="{DynamicResource PrimaryColor}" BorderColor="{DynamicResource PrimaryColor}">
AutomationId="SettingActivePolicyTextLabel">
<Label <Label
Text="{Binding Name, Mode=OneWay}" Text="{Binding Name, Mode=OneWay}"
StyleClass="text-muted, text-sm, text-bold" StyleClass="text-muted, text-sm, text-bold"
@@ -78,8 +75,7 @@
VerticalOptions="Center" VerticalOptions="Center"
FontSize="Small" FontSize="Small"
TextColor="{Binding SubLabelColor}" TextColor="{Binding SubLabelColor}"
StyleClass="list-sub" Margin="-5" StyleClass="list-sub" Margin="-5"/>
AutomationId="SettingCustomVaultTimeoutPicker" />
<controls:ExtendedStackLayout.GestureRecognizers> <controls:ExtendedStackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="ActivateTimePicker"/> <TapGestureRecognizer Tapped="ActivateTimePicker"/>
</controls:ExtendedStackLayout.GestureRecognizers> </controls:ExtendedStackLayout.GestureRecognizers>

View File

@@ -1,9 +1,7 @@
using System; using System;
using System.Globalization;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.App.Utilities.Automation;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@@ -24,29 +22,5 @@ namespace Bit.App.Pages
public Color SubLabelColor => SubLabelTextEnabled ? public Color SubLabelColor => SubLabelTextEnabled ?
ThemeManager.GetResourceColor("SuccessColor") : ThemeManager.GetResourceColor("SuccessColor") :
ThemeManager.GetResourceColor("MutedColor"); ThemeManager.GetResourceColor("MutedColor");
public string AutomationIdSettingName
{
get
{
return AutomationIdsHelper.AddSuffixFor(
UseFrame ? "EnabledPolicy"
: AutomationIdsHelper.ToEnglishTitleCase(Name)
, SuffixType.Cell);
}
}
public string AutomationIdSettingStatus
{
get
{
if (UseFrame)
{
return null;
}
return AutomationIdsHelper.AddSuffixFor(AutomationIdsHelper.ToEnglishTitleCase(Name), SuffixType.SettingValue);
}
}
} }
} }

View File

@@ -7,7 +7,10 @@ using Bit.App.Pages.Accounts;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models;
using Bit.Core.Models.Domain; using Bit.Core.Models.Domain;
using Bit.Core.Models.Response;
using Bit.Core.Models.View;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel; using Xamarin.CommunityToolkit.ObjectModel;
@@ -48,7 +51,7 @@ namespace Bit.App.Pages
private bool _reportLoggingEnabled; private bool _reportLoggingEnabled;
private bool _approvePasswordlessLoginRequests; private bool _approvePasswordlessLoginRequests;
private bool _shouldConnectToWatch; private bool _shouldConnectToWatch;
private readonly static List<KeyValuePair<string, int?>> VaultTimeoutOptions = private List<KeyValuePair<string, int?>> _vaultTimeouts =
new List<KeyValuePair<string, int?>> new List<KeyValuePair<string, int?>>
{ {
new KeyValuePair<string, int?>(AppResources.Immediately, 0), new KeyValuePair<string, int?>(AppResources.Immediately, 0),
@@ -62,7 +65,7 @@ namespace Bit.App.Pages
new KeyValuePair<string, int?>(AppResources.Never, null), new KeyValuePair<string, int?>(AppResources.Never, null),
new KeyValuePair<string, int?>(AppResources.Custom, CustomVaultTimeoutValue), new KeyValuePair<string, int?>(AppResources.Custom, CustomVaultTimeoutValue),
}; };
private readonly static List<KeyValuePair<string, VaultTimeoutAction>> VaultTimeoutActionOptions = private List<KeyValuePair<string, VaultTimeoutAction>> _vaultTimeoutActions =
new List<KeyValuePair<string, VaultTimeoutAction>> new List<KeyValuePair<string, VaultTimeoutAction>>
{ {
new KeyValuePair<string, VaultTimeoutAction>(AppResources.Lock, VaultTimeoutAction.Lock), new KeyValuePair<string, VaultTimeoutAction>(AppResources.Lock, VaultTimeoutAction.Lock),
@@ -71,8 +74,6 @@ namespace Bit.App.Pages
private Policy _vaultTimeoutPolicy; private Policy _vaultTimeoutPolicy;
private int? _vaultTimeout; private int? _vaultTimeout;
private List<KeyValuePair<string, int?>> _vaultTimeoutOptions = VaultTimeoutOptions;
private List<KeyValuePair<string, VaultTimeoutAction>> _vaultTimeoutActionOptions = VaultTimeoutActionOptions;
public SettingsPageViewModel() public SettingsPageViewModel()
{ {
@@ -116,28 +117,20 @@ namespace Bit.App.Pages
_localizeService.GetLocaleShortTime(lastSync.Value)); _localizeService.GetLocaleShortTime(lastSync.Value));
} }
_vaultTimeoutPolicy = null;
_vaultTimeoutOptions = VaultTimeoutOptions;
_vaultTimeoutActionOptions = VaultTimeoutActionOptions;
_vaultTimeout = await _vaultTimeoutService.GetVaultTimeout();
_vaultTimeoutDisplayValue = _vaultTimeoutOptions.FirstOrDefault(o => o.Value == _vaultTimeout).Key;
_vaultTimeoutDisplayValue ??= _vaultTimeoutOptions.Where(o => o.Value == CustomVaultTimeoutValue).First().Key;
var action = await _vaultTimeoutService.GetVaultTimeoutAction() ?? VaultTimeoutAction.Lock;
_vaultTimeoutActionDisplayValue = _vaultTimeoutActionOptions.FirstOrDefault(o => o.Value == action).Key;
if (await _policyService.PolicyAppliesToUser(PolicyType.MaximumVaultTimeout)) if (await _policyService.PolicyAppliesToUser(PolicyType.MaximumVaultTimeout))
{ {
// if we have a vault timeout policy, we need to filter the timeout options
_vaultTimeoutPolicy = (await _policyService.GetAll(PolicyType.MaximumVaultTimeout)).First(); _vaultTimeoutPolicy = (await _policyService.GetAll(PolicyType.MaximumVaultTimeout)).First();
var policyMinutes = _vaultTimeoutPolicy.GetInt(Policy.MINUTES_KEY); var minutes = _policyService.GetPolicyInt(_vaultTimeoutPolicy, "minutes").GetValueOrDefault();
_vaultTimeoutOptions = _vaultTimeoutOptions.Where(t => _vaultTimeouts = _vaultTimeouts.Where(t =>
t.Value <= policyMinutes && t.Value <= minutes &&
(t.Value > 0 || t.Value == CustomVaultTimeoutValue) && (t.Value > 0 || t.Value == CustomVaultTimeoutValue) &&
t.Value != null).ToList(); t.Value != null).ToList();
} }
_vaultTimeout = await _vaultTimeoutService.GetVaultTimeout();
_vaultTimeoutDisplayValue = _vaultTimeouts.FirstOrDefault(o => o.Value == _vaultTimeout).Key;
var action = await _stateService.GetVaultTimeoutActionAsync() ?? VaultTimeoutAction.Lock;
_vaultTimeoutActionDisplayValue = _vaultTimeoutActions.FirstOrDefault(o => o.Value == action).Key;
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync(); var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
_pin = pinSet.Item1 || pinSet.Item2; _pin = pinSet.Item1 || pinSet.Item2;
_biometric = await _vaultTimeoutService.IsBiometricLockSetAsync(); _biometric = await _vaultTimeoutService.IsBiometricLockSetAsync();
@@ -273,7 +266,7 @@ namespace Bit.App.Pages
{ {
var oldTimeout = _vaultTimeout; var oldTimeout = _vaultTimeout;
var options = _vaultTimeoutOptions.Select( var options = _vaultTimeouts.Select(
o => o.Key == _vaultTimeoutDisplayValue ? $"✓ {o.Key}" : o.Key).ToArray(); o => o.Key == _vaultTimeoutDisplayValue ? $"✓ {o.Key}" : o.Key).ToArray();
if (promptOptions) if (promptOptions)
{ {
@@ -284,7 +277,7 @@ namespace Bit.App.Pages
return; return;
} }
var cleanSelection = selection.Replace("✓ ", string.Empty); var cleanSelection = selection.Replace("✓ ", string.Empty);
var selectionOption = _vaultTimeoutOptions.FirstOrDefault(o => o.Key == cleanSelection); var selectionOption = _vaultTimeouts.FirstOrDefault(o => o.Key == cleanSelection);
// Check if the selected Timeout action is "Never" and if it's different from the previous selected value // Check if the selected Timeout action is "Never" and if it's different from the previous selected value
if (selectionOption.Value == null && selectionOption.Value != oldTimeout) if (selectionOption.Value == null && selectionOption.Value != oldTimeout)
@@ -302,13 +295,13 @@ namespace Bit.App.Pages
if (_vaultTimeoutPolicy != null) if (_vaultTimeoutPolicy != null)
{ {
var maximumTimeout = _vaultTimeoutPolicy.GetInt(Policy.MINUTES_KEY); var maximumTimeout = _policyService.GetPolicyInt(_vaultTimeoutPolicy, "minutes");
if (newTimeout > maximumTimeout) if (newTimeout > maximumTimeout)
{ {
await _platformUtilsService.ShowDialogAsync(AppResources.VaultTimeoutToLarge, AppResources.Warning); await _platformUtilsService.ShowDialogAsync(AppResources.VaultTimeoutToLarge, AppResources.Warning);
var timeout = await _vaultTimeoutService.GetVaultTimeout(); var timeout = await _vaultTimeoutService.GetVaultTimeout();
_vaultTimeoutDisplayValue = _vaultTimeoutOptions.FirstOrDefault(o => o.Value == timeout).Key ?? _vaultTimeoutDisplayValue = _vaultTimeouts.FirstOrDefault(o => o.Value == timeout).Key ??
AppResources.Custom; AppResources.Custom;
return; return;
} }
@@ -381,13 +374,7 @@ namespace Bit.App.Pages
public async Task VaultTimeoutActionAsync() public async Task VaultTimeoutActionAsync()
{ {
if (_vaultTimeoutPolicy != null && var options = _vaultTimeoutActions.Select(o =>
!string.IsNullOrEmpty(_vaultTimeoutPolicy.GetString(Policy.ACTION_KEY)))
{
// do nothing if we have a policy set
return;
}
var options = _vaultTimeoutActionOptions.Select(o =>
o.Key == _vaultTimeoutActionDisplayValue ? $"✓ {o.Key}" : o.Key).ToArray(); o.Key == _vaultTimeoutActionDisplayValue ? $"✓ {o.Key}" : o.Key).ToArray();
var selection = await Page.DisplayActionSheet(AppResources.VaultTimeoutAction, var selection = await Page.DisplayActionSheet(AppResources.VaultTimeoutAction,
AppResources.Cancel, null, options); AppResources.Cancel, null, options);
@@ -406,7 +393,7 @@ namespace Bit.App.Pages
cleanSelection = AppResources.Lock; cleanSelection = AppResources.Lock;
} }
} }
var selectionOption = _vaultTimeoutActionOptions.FirstOrDefault(o => o.Key == cleanSelection); var selectionOption = _vaultTimeoutActions.FirstOrDefault(o => o.Key == cleanSelection);
var changed = _vaultTimeoutActionDisplayValue != selectionOption.Key; var changed = _vaultTimeoutActionDisplayValue != selectionOption.Key;
_vaultTimeoutActionDisplayValue = selectionOption.Key; _vaultTimeoutActionDisplayValue = selectionOption.Key;
await _vaultTimeoutService.SetVaultTimeoutOptionsAsync(_vaultTimeout, await _vaultTimeoutService.SetVaultTimeoutOptionsAsync(_vaultTimeout,
@@ -610,36 +597,14 @@ namespace Bit.App.Pages
} }
if (_vaultTimeoutPolicy != null) if (_vaultTimeoutPolicy != null)
{ {
var policyMinutes = _vaultTimeoutPolicy.GetInt(Policy.MINUTES_KEY); var maximumTimeout = _policyService.GetPolicyInt(_vaultTimeoutPolicy, "minutes").GetValueOrDefault();
var policyAction = _vaultTimeoutPolicy.GetString(Policy.ACTION_KEY); securityItems.Insert(0, new SettingsPageListItem
if (policyMinutes.HasValue || !string.IsNullOrWhiteSpace(policyAction))
{ {
string policyAlert; Name = string.Format(AppResources.VaultTimeoutPolicyInEffect,
if (policyMinutes.HasValue && string.IsNullOrWhiteSpace(policyAction)) Math.Floor((float)maximumTimeout / 60),
{ maximumTimeout % 60),
policyAlert = string.Format(AppResources.VaultTimeoutPolicyInEffect, UseFrame = true,
Math.Floor((float)policyMinutes / 60), });
policyMinutes % 60);
}
else if (!policyMinutes.HasValue && !string.IsNullOrWhiteSpace(policyAction))
{
policyAlert = string.Format(AppResources.VaultTimeoutActionPolicyInEffect,
policyAction == Policy.ACTION_LOCK ? AppResources.Lock : AppResources.LogOut);
}
else
{
policyAlert = string.Format(AppResources.VaultTimeoutPolicyWithActionInEffect,
Math.Floor((float)policyMinutes / 60),
policyMinutes % 60,
policyAction == Policy.ACTION_LOCK ? AppResources.Lock : AppResources.LogOut);
}
securityItems.Insert(0, new SettingsPageListItem
{
Name = policyAlert,
UseFrame = true,
});
}
} }
if (Device.RuntimePlatform == Device.Android) if (Device.RuntimePlatform == Device.Android)
{ {
@@ -827,12 +792,12 @@ namespace Bit.App.Pages
private VaultTimeoutAction GetVaultTimeoutActionFromKey(string key) private VaultTimeoutAction GetVaultTimeoutActionFromKey(string key)
{ {
return _vaultTimeoutActionOptions.FirstOrDefault(o => o.Key == key).Value; return _vaultTimeoutActions.FirstOrDefault(o => o.Key == key).Value;
} }
private int? GetVaultTimeoutFromKey(string key) private int? GetVaultTimeoutFromKey(string key)
{ {
return _vaultTimeoutOptions.FirstOrDefault(o => o.Key == key).Value; return _vaultTimeouts.FirstOrDefault(o => o.Key == key).Value;
} }
private string CreateSelectableOption(string option, bool selected) => selected ? $"✓ {option}" : option; private string CreateSelectableOption(string option, bool selected) => selected ? $"✓ {option}" : option;

View File

@@ -3,7 +3,6 @@ using System.Threading.Tasks;
using Bit.App.Effects; using Bit.App.Effects;
using Bit.App.Models; using Bit.App.Models;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@@ -16,7 +15,6 @@ namespace Bit.App.Pages
private readonly IBroadcasterService _broadcasterService; private readonly IBroadcasterService _broadcasterService;
private readonly IMessagingService _messagingService; private readonly IMessagingService _messagingService;
private readonly IKeyConnectorService _keyConnectorService; private readonly IKeyConnectorService _keyConnectorService;
private readonly IStateService _stateService;
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger"); private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>("logger");
private NavigationPage _groupingsPage; private NavigationPage _groupingsPage;
@@ -28,7 +26,6 @@ namespace Bit.App.Pages
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService"); _messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService"); _keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
_stateService = ServiceContainer.Resolve<IStateService>();
_groupingsPage = new NavigationPage(new GroupingsPage(true, previousPage: previousPage)) _groupingsPage = new NavigationPage(new GroupingsPage(true, previousPage: previousPage))
{ {
@@ -98,13 +95,6 @@ namespace Bit.App.Pages
{ {
_messagingService.Send("convertAccountToKeyConnector"); _messagingService.Send("convertAccountToKeyConnector");
} }
var forcePasswordResetReason = await _stateService.GetForcePasswordResetReasonAsync();
if (forcePasswordResetReason.HasValue)
{
_messagingService.Send(Constants.ForceUpdatePassword);
}
} }
protected override void OnDisappearing() protected override void OnDisappearing()

View File

@@ -33,25 +33,23 @@
<StackLayout StyleClass="box"> <StackLayout StyleClass="box">
<StackLayout StyleClass="box-row" Padding="10, 20" <StackLayout StyleClass="box-row" Padding="10, 20"
IsVisible="{Binding HasAttachments, Converter={StaticResource inverseBool}}"> IsVisible="{Binding HasAttachments, Converter={StaticResource inverseBool}}">
<Label Text="{u:I18n NoAttachments}" HorizontalTextAlignment="Center" AutomationId="NoAttachmentsLabel" /> <Label Text="{u:I18n NoAttachments}" HorizontalTextAlignment="Center" />
</StackLayout> </StackLayout>
<controls:RepeaterView ItemsSource="{Binding Attachments}" IsVisible="{Binding HasAttachments}" AutomationId="AttachmentsList"> <controls:RepeaterView ItemsSource="{Binding Attachments}" IsVisible="{Binding HasAttachments}">
<controls:RepeaterView.ItemTemplate> <controls:RepeaterView.ItemTemplate>
<DataTemplate x:DataType="views:AttachmentView"> <DataTemplate x:DataType="views:AttachmentView">
<StackLayout Spacing="0" Padding="0"> <StackLayout Spacing="0" Padding="0">
<StackLayout Orientation="Horizontal" StyleClass="box-row" Spacing="10" AutomationId="AttachmentRow"> <StackLayout Orientation="Horizontal" StyleClass="box-row" Spacing="10">
<Label <Label
Text="{Binding FileName, Mode=OneWay}" Text="{Binding FileName, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
VerticalTextAlignment="Center" VerticalTextAlignment="Center"
HorizontalOptions="StartAndExpand" HorizontalOptions="StartAndExpand" />
AutomationId="AttachmentFileNameLabel" />
<Label <Label
Text="{Binding SizeName, Mode=OneWay}" Text="{Binding SizeName, Mode=OneWay}"
StyleClass="box-sub-label" StyleClass="box-sub-label"
HorizontalTextAlignment="End" HorizontalTextAlignment="End"
VerticalTextAlignment="Center" VerticalTextAlignment="Center" />
AutomationId="AttachmentFileSizeLabel" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Trash}}" Text="{Binding Source={x:Static core:BitwardenIcons.Trash}}"
@@ -59,8 +57,7 @@
CommandParameter="{Binding .}" CommandParameter="{Binding .}"
VerticalOptions="Center" VerticalOptions="Center"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Delete}" AutomationProperties.Name="{u:I18n Delete}" />
AutomationId="AttachmentDeleteButton" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
</StackLayout> </StackLayout>
@@ -80,20 +77,17 @@
LineBreakMode="CharacterWrap" LineBreakMode="CharacterWrap"
StyleClass="text-sm, text-muted" StyleClass="text-sm, text-muted"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center" />
AutomationId="NoFileChosenLabel" />
<Label <Label
IsVisible="{Binding FileName, Converter={StaticResource notNull}}" IsVisible="{Binding FileName, Converter={StaticResource notNull}}"
Text="{Binding FileName}" Text="{Binding FileName}"
LineBreakMode="CharacterWrap" LineBreakMode="CharacterWrap"
StyleClass="text-sm, text-muted" StyleClass="text-sm, text-muted"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center" />
AutomationId="NewAttachmentNameLabel" />
</StackLayout> </StackLayout>
<Button Text="{u:I18n ChooseFile}" StyleClass="box-button-row" <Button Text="{u:I18n ChooseFile}" StyleClass="box-button-row"
Clicked="ChooseFile_Clicked" Clicked="ChooseFile_Clicked"></Button>
AutomationId="ChooseFileButton"></Button>
<Label <Label
Margin="0, 10, 0, 0" Margin="0, 10, 0, 0"
Text="{u:I18n MaxFileSize}" Text="{u:I18n MaxFileSize}"

View File

@@ -57,16 +57,16 @@
x:Key="deleteItem" /> x:Key="deleteItem" />
<DataTemplate x:Key="TextCustomFieldDataTemplate"> <DataTemplate x:Key="TextCustomFieldDataTemplate">
<il:TextCustomFieldItemLayout AutomationId="TextCustomFieldItem" /> <il:TextCustomFieldItemLayout />
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="BooleanCustomFieldDataTemplate"> <DataTemplate x:Key="BooleanCustomFieldDataTemplate">
<il:BooleanCustomFieldItemLayout AutomationId="BooleanCustomFieldItem" /> <il:BooleanCustomFieldItemLayout />
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="HiddenCustomFieldDataTemplate"> <DataTemplate x:Key="HiddenCustomFieldDataTemplate">
<il:HiddenCustomFieldItemLayout AutomationId="HiddenCustomFieldItem" /> <il:HiddenCustomFieldItemLayout />
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="LinkedCustomFieldDataTemplate"> <DataTemplate x:Key="LinkedCustomFieldDataTemplate">
<il:LinkedCustomFieldItemLayout AutomationId="LinkedCustomFieldItem" /> <il:LinkedCustomFieldItemLayout />
</DataTemplate> </DataTemplate>
<dts:CustomFieldItemTemplateSelector x:Key="CustomFieldItemTemplateSelector" <dts:CustomFieldItemTemplateSelector x:Key="CustomFieldItemTemplateSelector"
@@ -100,8 +100,7 @@
<Label <Label
Text="{u:I18n PersonalOwnershipPolicyInEffect}" Text="{u:I18n PersonalOwnershipPolicyInEffect}"
StyleClass="text-muted, text-sm, text-bold" StyleClass="text-muted, text-sm, text-bold"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center" />
AutomationId="PersonalOwnershipPolicyLabel"/>
</Frame> </Frame>
</Grid> </Grid>
<StackLayout StyleClass="box-row-header"> <StackLayout StyleClass="box-row-header">
@@ -117,8 +116,7 @@
x:Name="_typePicker" x:Name="_typePicker"
ItemsSource="{Binding TypeOptions, Mode=OneTime}" ItemsSource="{Binding TypeOptions, Mode=OneTime}"
SelectedIndex="{Binding TypeSelectedIndex}" SelectedIndex="{Binding TypeSelectedIndex}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemTypePicker" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -127,10 +125,7 @@
<Entry <Entry
x:Name="_nameEntry" x:Name="_nameEntry"
Text="{Binding Cipher.Name}" Text="{Binding Cipher.Name}"
StyleClass="box-value" StyleClass="box-value" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Name}"
AutomationId="ItemNameEntry" />
</StackLayout> </StackLayout>
<StackLayout IsVisible="{Binding IsLogin}" Spacing="0" Padding="0"> <StackLayout IsVisible="{Binding IsLogin}" Spacing="0" Padding="0">
<Grid StyleClass="box-row, box-row-input" <Grid StyleClass="box-row, box-row-input"
@@ -143,10 +138,7 @@
x:Name="_loginUsernameEntry" x:Name="_loginUsernameEntry"
Text="{Binding Cipher.Login.Username}" Text="{Binding Cipher.Login.Username}"
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"/>
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Username}"
AutomationId="LoginUsernameEntry" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Generate}}" Text="{Binding Source={x:Static core:BitwardenIcons.Generate}}"
@@ -154,8 +146,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n GenerateUsername}" AutomationProperties.Name="{u:I18n GenerateUsername}" />
AutomationId="GenerateUsernameButton" />
</Grid> </Grid>
<Grid StyleClass="box-row, box-row-input"> <Grid StyleClass="box-row, box-row-input">
<Grid.RowDefinitions> <Grid.RowDefinitions>
@@ -183,10 +174,7 @@
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}" IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
IsSpellCheckEnabled="False" IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False" IsTextPredictionEnabled="False"
IsEnabled="{Binding Cipher.ViewPassword}" IsEnabled="{Binding Cipher.ViewPassword}"/>
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Password}"
AutomationId="LoginPasswordEntry" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.CheckCircle}}" Text="{Binding Source={x:Static core:BitwardenIcons.CheckCircle}}"
@@ -196,8 +184,7 @@
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CheckPassword}" AutomationProperties.Name="{u:I18n CheckPassword}"
IsVisible="{Binding Cipher.ViewPassword}" IsVisible="{Binding Cipher.ViewPassword}" />
AutomationId="CheckPasswordButton" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}" Text="{Binding ShowPasswordIcon}"
@@ -208,8 +195,7 @@
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
IsVisible="{Binding Cipher.ViewPassword}" IsVisible="{Binding Cipher.ViewPassword}" />
AutomationId="ViewPasswordButton" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Generate}}" Text="{Binding Source={x:Static core:BitwardenIcons.Generate}}"
@@ -219,8 +205,7 @@
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n GeneratePassword}" AutomationProperties.Name="{u:I18n GeneratePassword}"
IsVisible="{Binding Cipher.ViewPassword}" IsVisible="{Binding Cipher.ViewPassword}" />
AutomationId="RegeneratePasswordButton" />
</Grid> </Grid>
<Grid StyleClass="box-row, box-row-input"> <Grid StyleClass="box-row, box-row-input">
@@ -256,8 +241,7 @@
Padding="0,15" Padding="0,15"
HorizontalOptions="Center" HorizontalOptions="Center"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center" VerticalTextAlignment="Center" />
AutomationId="SetupTotpButton" />
</Frame> </Frame>
<controls:MonoEntry <controls:MonoEntry
x:Name="_loginTotpEntry" x:Name="_loginTotpEntry"
@@ -270,10 +254,7 @@
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Grid.ColumnSpan="{Binding TotpColumnSpan}" Grid.ColumnSpan="{Binding TotpColumnSpan}" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n AuthenticatorKey}"
AutomationId="LoginTotpEntry" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}" Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}"
@@ -281,10 +262,7 @@
IsVisible="{Binding HasTotpValue}" IsVisible="{Binding HasTotpValue}"
Grid.Row="0" Grid.Row="0"
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyTotp}"
AutomationId="CopyTotpValueButton" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Camera}}" Text="{Binding Source={x:Static core:BitwardenIcons.Camera}}"
@@ -294,8 +272,7 @@
Grid.RowSpan="2" Grid.RowSpan="2"
IsVisible="{Binding HasTotpValue}" IsVisible="{Binding HasTotpValue}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ScanQrTitle}" AutomationProperties.Name="{u:I18n ScanQrTitle}" />
/>
</Grid> </Grid>
</StackLayout> </StackLayout>
<StackLayout IsVisible="{Binding IsCard}" Spacing="0" Padding="0"> <StackLayout IsVisible="{Binding IsCard}" Spacing="0" Padding="0">
@@ -306,8 +283,7 @@
<Entry <Entry
x:Name="_cardholderNameEntry" x:Name="_cardholderNameEntry"
Text="{Binding Cipher.Card.CardholderName}" Text="{Binding Cipher.Card.CardholderName}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="CardholderNameEntry" />
</StackLayout> </StackLayout>
<Grid StyleClass="box-row, box-row-input"> <Grid StyleClass="box-row, box-row-input">
<Grid.RowDefinitions> <Grid.RowDefinitions>
@@ -331,10 +307,7 @@
Grid.Column="0" Grid.Column="0"
IsPassword="{Binding ShowCardNumber, Converter={StaticResource inverseBool}}" IsPassword="{Binding ShowCardNumber, Converter={StaticResource inverseBool}}"
IsSpellCheckEnabled="False" IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False" IsTextPredictionEnabled="False" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Number}"
AutomationId="CardNumberEntry" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowCardNumberIcon}" Text="{Binding ShowCardNumberIcon}"
@@ -343,8 +316,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}" />
AutomationId="ShowCardNumberButton" />
</Grid> </Grid>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -354,8 +326,7 @@
x:Name="_cardBrandPicker" x:Name="_cardBrandPicker"
ItemsSource="{Binding CardBrandOptions, Mode=OneTime}" ItemsSource="{Binding CardBrandOptions, Mode=OneTime}"
SelectedIndex="{Binding CardBrandSelectedIndex}" SelectedIndex="{Binding CardBrandSelectedIndex}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="CardBrandPicker" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -365,8 +336,7 @@
x:Name="_cardExpMonthPicker" x:Name="_cardExpMonthPicker"
ItemsSource="{Binding CardExpMonthOptions, Mode=OneTime}" ItemsSource="{Binding CardExpMonthOptions, Mode=OneTime}"
SelectedIndex="{Binding CardExpMonthSelectedIndex}" SelectedIndex="{Binding CardExpMonthSelectedIndex}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="CardExpirationMonthPicker" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -376,10 +346,7 @@
x:Name="_cardExpYearEntry" x:Name="_cardExpYearEntry"
Text="{Binding Cipher.Card.ExpYear}" Text="{Binding Cipher.Card.ExpYear}"
StyleClass="box-value" StyleClass="box-value"
Keyboard="Numeric" Keyboard="Numeric" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ExpirationYear}"
AutomationId="CardExpirationYearEntry" />
</StackLayout> </StackLayout>
<Grid StyleClass="box-row, box-row-input"> <Grid StyleClass="box-row, box-row-input">
<Grid.RowDefinitions> <Grid.RowDefinitions>
@@ -404,10 +371,7 @@
Keyboard="Numeric" Keyboard="Numeric"
IsPassword="{Binding ShowCardCode, Converter={StaticResource inverseBool}}" IsPassword="{Binding ShowCardCode, Converter={StaticResource inverseBool}}"
IsSpellCheckEnabled="False" IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False" IsTextPredictionEnabled="False" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n SecurityCode}"
AutomationId="CardSecurityCodeEntry" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowCardCodeIcon}" Text="{Binding ShowCardCodeIcon}"
@@ -416,8 +380,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}" />
AutomationId="CardShowSecurityCodeButton" />
</Grid> </Grid>
</StackLayout> </StackLayout>
<StackLayout IsVisible="{Binding IsIdentity}" Spacing="0" Padding="0"> <StackLayout IsVisible="{Binding IsIdentity}" Spacing="0" Padding="0">
@@ -429,8 +392,7 @@
x:Name="_identityTitlePicker" x:Name="_identityTitlePicker"
ItemsSource="{Binding IdentityTitleOptions, Mode=OneTime}" ItemsSource="{Binding IdentityTitleOptions, Mode=OneTime}"
SelectedIndex="{Binding IdentityTitleSelectedIndex}" SelectedIndex="{Binding IdentityTitleSelectedIndex}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="IdentityTitlePicker" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -439,10 +401,7 @@
<Entry <Entry
x:Name="_identityFirstNameEntry" x:Name="_identityFirstNameEntry"
Text="{Binding Cipher.Identity.FirstName}" Text="{Binding Cipher.Identity.FirstName}"
StyleClass="box-value,capitalize-word-input" StyleClass="box-value,capitalize-word-input"/>
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n FirstName}"
AutomationId="IdentityFirstNameEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -451,10 +410,7 @@
<Entry <Entry
x:Name="_identityMiddleNameEntry" x:Name="_identityMiddleNameEntry"
Text="{Binding Cipher.Identity.MiddleName}" Text="{Binding Cipher.Identity.MiddleName}"
StyleClass="box-value,capitalize-word-input" StyleClass="box-value,capitalize-word-input" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n MiddleName}"
AutomationId="IdentityMiddleNameEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -463,10 +419,7 @@
<Entry <Entry
x:Name="_identityLastNameEntry" x:Name="_identityLastNameEntry"
Text="{Binding Cipher.Identity.LastName}" Text="{Binding Cipher.Identity.LastName}"
StyleClass="box-value,capitalize-word-input" StyleClass="box-value,capitalize-word-input" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n LastName}"
AutomationId="IdentityLastNameEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -475,10 +428,7 @@
<Entry <Entry
x:Name="_identityUsernameEntry" x:Name="_identityUsernameEntry"
Text="{Binding Cipher.Identity.Username}" Text="{Binding Cipher.Identity.Username}"
StyleClass="box-value" StyleClass="box-value" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Username}"
AutomationId="IdentityUsernameEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -487,10 +437,7 @@
<Entry <Entry
x:Name="_identityCompanyEntry" x:Name="_identityCompanyEntry"
Text="{Binding Cipher.Identity.Company}" Text="{Binding Cipher.Identity.Company}"
StyleClass="box-value" StyleClass="box-value" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Company}"
AutomationId="IdentityCompanyEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -499,10 +446,7 @@
<Entry <Entry
x:Name="_identitySsnEntry" x:Name="_identitySsnEntry"
Text="{Binding Cipher.Identity.SSN}" Text="{Binding Cipher.Identity.SSN}"
StyleClass="box-value" StyleClass="box-value" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n SSN}"
AutomationId="IdentitySsnEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -511,10 +455,7 @@
<Entry <Entry
x:Name="_identityPassportNumberEntry" x:Name="_identityPassportNumberEntry"
Text="{Binding Cipher.Identity.PassportNumber}" Text="{Binding Cipher.Identity.PassportNumber}"
StyleClass="box-value" StyleClass="box-value" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n PassportNumber}"
AutomationId="IdentityPassportNumberEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -523,10 +464,7 @@
<Entry <Entry
x:Name="_identityLicenseNumberEntry" x:Name="_identityLicenseNumberEntry"
Text="{Binding Cipher.Identity.LicenseNumber}" Text="{Binding Cipher.Identity.LicenseNumber}"
StyleClass="box-value" StyleClass="box-value" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n LicenseNumber}"
AutomationId="IdentityLicenseNumberEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -536,10 +474,7 @@
x:Name="_identityEmailEntry" x:Name="_identityEmailEntry"
Keyboard="Email" Keyboard="Email"
Text="{Binding Cipher.Identity.Email}" Text="{Binding Cipher.Identity.Email}"
StyleClass="box-value" StyleClass="box-value" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Email}"
AutomationId="IdentityEmailEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -549,10 +484,7 @@
x:Name="_identityPhoneEntry" x:Name="_identityPhoneEntry"
Text="{Binding Cipher.Identity.Phone}" Text="{Binding Cipher.Identity.Phone}"
Keyboard="Telephone" Keyboard="Telephone"
StyleClass="box-value" StyleClass="box-value" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Phone}"
AutomationId="IdentityPhoneEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -561,10 +493,7 @@
<Entry <Entry
x:Name="_identityAddress1Entry" x:Name="_identityAddress1Entry"
Text="{Binding Cipher.Identity.Address1}" Text="{Binding Cipher.Identity.Address1}"
StyleClass="box-value" StyleClass="box-value" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Address1}"
AutomationId="IdentityAddressOneEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -573,10 +502,7 @@
<Entry <Entry
x:Name="_identityAddress2Entry" x:Name="_identityAddress2Entry"
Text="{Binding Cipher.Identity.Address2}" Text="{Binding Cipher.Identity.Address2}"
StyleClass="box-value" StyleClass="box-value" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Address2}"
AutomationId="IdentityAddressTwoEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -585,10 +511,7 @@
<Entry <Entry
x:Name="_identityAddress3Entry" x:Name="_identityAddress3Entry"
Text="{Binding Cipher.Identity.Address3}" Text="{Binding Cipher.Identity.Address3}"
StyleClass="box-value" StyleClass="box-value" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Address3}"
AutomationId="IdentityAddressThreeEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -597,10 +520,7 @@
<Entry <Entry
x:Name="_identityCityEntry" x:Name="_identityCityEntry"
Text="{Binding Cipher.Identity.City}" Text="{Binding Cipher.Identity.City}"
StyleClass="box-value,capitalize-sentence-input" StyleClass="box-value,capitalize-sentence-input" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CityTown}"
AutomationId="IdentityCityEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -609,10 +529,7 @@
<Entry <Entry
x:Name="_identityStateEntry" x:Name="_identityStateEntry"
Text="{Binding Cipher.Identity.State}" Text="{Binding Cipher.Identity.State}"
StyleClass="box-value,capitalize-sentence-input" StyleClass="box-value,capitalize-sentence-input" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n StateProvince}"
AutomationId="IdentityStateEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -621,10 +538,7 @@
<Entry <Entry
x:Name="_identityPostalCodeEntry" x:Name="_identityPostalCodeEntry"
Text="{Binding Cipher.Identity.PostalCode}" Text="{Binding Cipher.Identity.PostalCode}"
StyleClass="box-value" StyleClass="box-value" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ZipPostalCode}"
AutomationId="IdentityPostalCodeEntry" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-input"> <StackLayout StyleClass="box-row, box-row-input">
<Label <Label
@@ -633,10 +547,7 @@
<Entry <Entry
x:Name="_identityCountryEntry" x:Name="_identityCountryEntry"
Text="{Binding Cipher.Identity.Country}" Text="{Binding Cipher.Identity.Country}"
StyleClass="box-value,capitalize-sentence-input" StyleClass="box-value,capitalize-sentence-input" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Country}"
AutomationId="IdentityCountryEntry" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
@@ -648,7 +559,7 @@
<controls:RepeaterView ItemsSource="{Binding Uris}"> <controls:RepeaterView ItemsSource="{Binding Uris}">
<controls:RepeaterView.ItemTemplate> <controls:RepeaterView.ItemTemplate>
<DataTemplate x:DataType="views:LoginUriView"> <DataTemplate x:DataType="views:LoginUriView">
<Grid StyleClass="box-row, box-row-input" AutomationId="UriListGrid" > <Grid StyleClass="box-row, box-row-input">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -667,10 +578,7 @@
Keyboard="Url" Keyboard="Url"
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0" />
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n URI}"
AutomationId="LoginUriEntry" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Cog}}" Text="{Binding Source={x:Static core:BitwardenIcons.Cog}}"
@@ -680,15 +588,13 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" AutomationProperties.Name="{u:I18n Options}" />
AutomationId="LoginUriOptionsButton" />
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</controls:RepeaterView.ItemTemplate> </controls:RepeaterView.ItemTemplate>
</controls:RepeaterView> </controls:RepeaterView>
<Button Text="{u:I18n NewUri}" StyleClass="box-button-row" <Button Text="{u:I18n NewUri}" StyleClass="box-button-row"
Clicked="NewUri_Clicked" Clicked="NewUri_Clicked"></Button>
AutomationId="LoginAddNewUriButton"></Button>
</StackLayout> </StackLayout>
<StackLayout StyleClass="box"> <StackLayout StyleClass="box">
<StackLayout StyleClass="box-row-header"> <StackLayout StyleClass="box-row-header">
@@ -703,8 +609,7 @@
x:Name="_folderPicker" x:Name="_folderPicker"
ItemsSource="{Binding FolderOptions, Mode=OneTime}" ItemsSource="{Binding FolderOptions, Mode=OneTime}"
SelectedIndex="{Binding FolderSelectedIndex}" SelectedIndex="{Binding FolderSelectedIndex}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="FolderPicker" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row, box-row-switch"> <StackLayout StyleClass="box-row, box-row-switch">
<Label <Label
@@ -714,8 +619,7 @@
<Switch <Switch
IsToggled="{Binding Cipher.Favorite}" IsToggled="{Binding Cipher.Favorite}"
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End" />
AutomationId="ItemFavoriteToggle" />
</StackLayout> </StackLayout>
<StackLayout x:Name="_passwordPrompt" StyleClass="box-row, box-row-switch"> <StackLayout x:Name="_passwordPrompt" StyleClass="box-row, box-row-switch">
<Label <Label
@@ -727,14 +631,13 @@
Command="{Binding PasswordPromptHelpCommand}" Command="{Binding PasswordPromptHelpCommand}"
TextColor="{DynamicResource MutedColor}" TextColor="{DynamicResource MutedColor}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n MasterPasswordRePromptHelp}" AutomationProperties.Name="{u:I18n ToggleVisibility}"
HorizontalOptions="StartAndExpand" /> HorizontalOptions="StartAndExpand" />
<Switch <Switch
IsToggled="{Binding PasswordPrompt}" IsToggled="{Binding PasswordPrompt}"
Toggled="PasswordPrompt_Toggled" Toggled="PasswordPrompt_Toggled"
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End" />
AutomationId="MasterPasswordRepromptToggle" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
</StackLayout> </StackLayout>
@@ -749,10 +652,7 @@
AutoSize="TextChanges" AutoSize="TextChanges"
StyleClass="box-value" StyleClass="box-value"
effects:ScrollEnabledEffect.IsScrollEnabled="false" effects:ScrollEnabledEffect.IsScrollEnabled="false"
Text="{Binding Cipher.Notes}" Text="{Binding Cipher.Notes}">
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Notes}"
AutomationId="ItemNotesEntry">
<Editor.Behaviors> <Editor.Behaviors>
<behaviors:EditorPreventAutoBottomScrollingOnFocusedBehavior ParentScrollView="{x:Reference _scrollView}" /> <behaviors:EditorPreventAutoBottomScrollingOnFocusedBehavior ParentScrollView="{x:Reference _scrollView}" />
</Editor.Behaviors> </Editor.Behaviors>
@@ -771,11 +671,9 @@
<StackLayout <StackLayout
Spacing="0" Spacing="0"
BindableLayout.ItemsSource="{Binding Fields}" BindableLayout.ItemsSource="{Binding Fields}"
BindableLayout.ItemTemplateSelector="{StaticResource CustomFieldItemTemplateSelector}" BindableLayout.ItemTemplateSelector="{StaticResource CustomFieldItemTemplateSelector}" />
AutomationId="CustomFieldsList" />
<Button Text="{u:I18n NewCustomField}" StyleClass="box-button-row" <Button Text="{u:I18n NewCustomField}" StyleClass="box-button-row"
Clicked="NewField_Clicked" Clicked="NewField_Clicked"></Button>
AutomationId="NewCustomFieldButton"></Button>
</StackLayout> </StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding ShowOwnershipOptions}"> <StackLayout StyleClass="box" IsVisible="{Binding ShowOwnershipOptions}">
<StackLayout StyleClass="box-row-header"> <StackLayout StyleClass="box-row-header">
@@ -790,8 +688,7 @@
x:Name="_ownershipPicker" x:Name="_ownershipPicker"
ItemsSource="{Binding OwnershipOptions, Mode=OneTime}" ItemsSource="{Binding OwnershipOptions, Mode=OneTime}"
SelectedIndex="{Binding OwnershipSelectedIndex}" SelectedIndex="{Binding OwnershipSelectedIndex}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemOwnershipPicker" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding ShowCollections}"> <StackLayout StyleClass="box" IsVisible="{Binding ShowCollections}">
@@ -802,8 +699,7 @@
<StackLayout Spacing="0" Padding="0" <StackLayout Spacing="0" Padding="0"
IsVisible="{Binding HasCollections, Converter={StaticResource inverseBool}}"> IsVisible="{Binding HasCollections, Converter={StaticResource inverseBool}}">
<StackLayout StyleClass="box-row, box-row-switch"> <StackLayout StyleClass="box-row, box-row-switch">
<Label Text="{u:I18n NoCollectionsToList}" <Label Text="{u:I18n NoCollectionsToList}" />
AutomationId="NoCollectionsToListLabel" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
</StackLayout> </StackLayout>
@@ -813,17 +709,15 @@
<controls:RepeaterView.ItemTemplate> <controls:RepeaterView.ItemTemplate>
<DataTemplate x:DataType="pages:CollectionViewModel"> <DataTemplate x:DataType="pages:CollectionViewModel">
<StackLayout Spacing="0" Padding="0"> <StackLayout Spacing="0" Padding="0">
<StackLayout StyleClass="box-row, box-row-switch" AutomationId="CollectionItemCell"> <StackLayout StyleClass="box-row, box-row-switch">
<Label <Label
Text="{Binding Collection.Name}" Text="{Binding Collection.Name}"
StyleClass="box-label-regular" StyleClass="box-label-regular"
HorizontalOptions="StartAndExpand" HorizontalOptions="StartAndExpand" />
AutomationId="CollectionItemNameLabel" />
<Switch <Switch
IsToggled="{Binding Checked}" IsToggled="{Binding Checked}"
StyleClass="box-value" StyleClass="box-value"
HorizontalOptions="End" HorizontalOptions="End" />
AutomationId="CollectionItemSwitch" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
</StackLayout> </StackLayout>
@@ -833,4 +727,5 @@
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -121,7 +121,6 @@ namespace Bit.App.Pages
new KeyValuePair<string, string>("JCB", "JCB"), new KeyValuePair<string, string>("JCB", "JCB"),
new KeyValuePair<string, string>("Maestro", "Maestro"), new KeyValuePair<string, string>("Maestro", "Maestro"),
new KeyValuePair<string, string>("UnionPay", "UnionPay"), new KeyValuePair<string, string>("UnionPay", "UnionPay"),
new KeyValuePair<string, string>("RuPay", "RuPay"),
new KeyValuePair<string, string>(AppResources.Other, "Other") new KeyValuePair<string, string>(AppResources.Other, "Other")
}; };
CardExpMonthOptions = new List<KeyValuePair<string, string>> CardExpMonthOptions = new List<KeyValuePair<string, string>>

View File

@@ -49,16 +49,16 @@
x:Name="_cloneItem" x:Key="cloneItem" /> x:Name="_cloneItem" x:Key="cloneItem" />
<DataTemplate x:Key="TextCustomFieldDataTemplate"> <DataTemplate x:Key="TextCustomFieldDataTemplate">
<il:TextCustomFieldItemLayout AutomationId="TextCustomFieldItem" /> <il:TextCustomFieldItemLayout />
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="BooleanCustomFieldDataTemplate"> <DataTemplate x:Key="BooleanCustomFieldDataTemplate">
<il:BooleanCustomFieldItemLayout AutomationId="BooleanCustomFieldItem" /> <il:BooleanCustomFieldItemLayout />
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="HiddenCustomFieldDataTemplate"> <DataTemplate x:Key="HiddenCustomFieldDataTemplate">
<il:HiddenCustomFieldItemLayout AutomationId="HiddenCustomFieldItem" /> <il:HiddenCustomFieldItemLayout />
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="LinkedCustomFieldDataTemplate"> <DataTemplate x:Key="LinkedCustomFieldDataTemplate">
<il:LinkedCustomFieldItemLayout AutomationId="LinkedCustomFieldItem" /> <il:LinkedCustomFieldItemLayout />
</DataTemplate> </DataTemplate>
<dts:CustomFieldItemTemplateSelector x:Key="CustomFieldItemTemplateSelector" <dts:CustomFieldItemTemplateSelector x:Key="CustomFieldItemTemplateSelector"
@@ -69,26 +69,23 @@
<ScrollView x:Key="scrollView" x:Name="_scrollView"> <ScrollView x:Key="scrollView" x:Name="_scrollView">
<StackLayout Spacing="20" x:Name="_mainLayout"> <StackLayout Spacing="20" x:Name="_mainLayout">
<StackLayout StyleClass="box" AutomationId="ItemInformationSection"> <StackLayout StyleClass="box">
<StackLayout StyleClass="box-row-header"> <StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n ItemInformation, Header=True}" <Label Text="{u:I18n ItemInformation, Header=True}"
StyleClass="box-header, box-header-platform" /> StyleClass="box-header, box-header-platform" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row" AutomationId="ItemRow"> <StackLayout StyleClass="box-row">
<Label <Label
Text="{u:I18n Name}" Text="{u:I18n Name}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Name, Mode=OneWay}" Text="{Binding Cipher.Name, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemValue" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
<StackLayout IsVisible="{Binding IsLogin}" Spacing="0" Padding="0"> <StackLayout IsVisible="{Binding IsLogin}" Spacing="0" Padding="0">
<Grid StyleClass="box-row" <Grid StyleClass="box-row"
IsVisible="{Binding Cipher.Login.Username, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Login.Username, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -101,14 +98,12 @@
Text="{u:I18n Username}" Text="{u:I18n Username}"
StyleClass="box-label" StyleClass="box-label"
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Login.Username, Mode=OneWay}" Text="{Binding Cipher.Login.Username, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0" />
AutomationId="ItemValue" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}" Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}"
@@ -118,14 +113,12 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyUsername}" AutomationProperties.Name="{u:I18n CopyUsername}" />
AutomationId="CopyValueButton" />
</Grid> </Grid>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Login.Username, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Login.Username, Converter={StaticResource stringHasValue}}" />
<Grid StyleClass="box-row" <Grid StyleClass="box-row"
IsVisible="{Binding Cipher.Login.Password, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Login.Password, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -140,23 +133,20 @@
Text="{u:I18n Password}" Text="{u:I18n Password}"
StyleClass="box-label" StyleClass="box-label"
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0" />
AutomationId="ItemName" />
<controls:MonoLabel <controls:MonoLabel
Text="{Binding Cipher.Login.MaskedPassword, Mode=OneWay}" Text="{Binding Cipher.Login.MaskedPassword, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
IsVisible="{Binding ShowPassword, Converter={StaticResource inverseBool}}" IsVisible="{Binding ShowPassword, Converter={StaticResource inverseBool}}" />
AutomationId="ItemValue" />
<controls:MonoLabel <controls:MonoLabel
Text="{Binding ColoredPassword, Mode=OneWay}" Text="{Binding ColoredPassword, Mode=OneWay}"
StyleClass="box-value, text-html" StyleClass="box-value, text-html"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
LineBreakMode="CharacterWrap" LineBreakMode="CharacterWrap"
IsVisible="{Binding ShowPassword}" IsVisible="{Binding ShowPassword}" />
AutomationId="ItemValue" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.CheckCircle}}" Text="{Binding Source={x:Static core:BitwardenIcons.CheckCircle}}"
@@ -166,8 +156,7 @@
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CheckPassword}" AutomationProperties.Name="{u:I18n CheckPassword}"
IsVisible="{Binding Cipher.ViewPassword}" IsVisible="{Binding Cipher.ViewPassword}" />
AutomationId="CheckPasswordButton" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}" Text="{Binding ShowPasswordIcon}"
@@ -178,8 +167,7 @@
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
IsVisible="{Binding Cipher.ViewPassword}" IsVisible="{Binding Cipher.ViewPassword}" />
AutomationId="ViewValueButton" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}" Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}"
@@ -190,14 +178,11 @@
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyPassword}" AutomationProperties.Name="{u:I18n CopyPassword}"
IsVisible="{Binding Cipher.ViewPassword}" IsVisible="{Binding Cipher.ViewPassword}" />
AutomationId="CopyValueButton" />
</Grid> </Grid>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Login.Password, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Login.Password, Converter={StaticResource stringHasValue}}" />
<Grid StyleClass="box-row" <Grid StyleClass="box-row" IsVisible="{Binding ShowTotp}">
IsVisible="{Binding ShowTotp}"
AutomationId="ItemRow">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -212,8 +197,7 @@
Text="{u:I18n VerificationCodeTotp}" Text="{u:I18n VerificationCodeTotp}"
StyleClass="box-label" StyleClass="box-label"
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0" />
AutomationId="ItemName" />
<controls:MonoLabel <controls:MonoLabel
Text="{Binding TotpCodeFormatted, Mode=OneWay}" Text="{Binding TotpCodeFormatted, Mode=OneWay}"
IsVisible="{Binding ShowUpgradePremiumTotpText, Converter={StaticResource inverseBool}}" IsVisible="{Binding ShowUpgradePremiumTotpText, Converter={StaticResource inverseBool}}"
@@ -221,16 +205,14 @@
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
VerticalTextAlignment="Start" VerticalTextAlignment="Start"
VerticalOptions="Start" VerticalOptions="Start" />
AutomationId="ItemValue" />
<controls:CircularProgressbarView <controls:CircularProgressbarView
Progress="{Binding TotpProgress}" Progress="{Binding TotpProgress}"
Grid.Row="0" Grid.Row="0"
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
AutomationId="LoginTotpProgressBar" />
<Label <Label
Text="{Binding TotpSec, Mode=OneWay}" Text="{Binding TotpSec, Mode=OneWay}"
Style="{DynamicResource textTotp}" Style="{DynamicResource textTotp}"
@@ -252,8 +234,7 @@
Grid.Column="2" Grid.Column="2"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyTotp}" AutomationProperties.Name="{u:I18n CopyTotp}" />
AutomationId="CopyValueButton" />
<Label <Label
Text="{u:I18n PremiumSubscriptionRequired}" Text="{u:I18n PremiumSubscriptionRequired}"
StyleClass="box-footer-label" StyleClass="box-footer-label"
@@ -261,29 +242,24 @@
Margin="0,5,0,2" Margin="0,5,0,2"
Grid.Column="0" Grid.Column="0"
Grid.Row="1" Grid.Row="1"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" />
AutomationId="ShowUpgradePremiumTotpLabel" />
</Grid> </Grid>
<BoxView StyleClass="box-row-separator" IsVisible="{Binding ShowTotp}" /> <BoxView StyleClass="box-row-separator" IsVisible="{Binding ShowTotp}" />
</StackLayout> </StackLayout>
<StackLayout IsVisible="{Binding IsCard}" Spacing="0" Padding="0"> <StackLayout IsVisible="{Binding IsCard}" Spacing="0" Padding="0">
<StackLayout StyleClass="box-row" <StackLayout StyleClass="box-row"
IsVisible="{Binding Cipher.Card.CardholderName, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Card.CardholderName, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Label <Label
Text="{u:I18n CardholderName}" Text="{u:I18n CardholderName}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Card.CardholderName, Mode=OneWay}" Text="{Binding Cipher.Card.CardholderName, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemValue" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Card.CardholderName, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Card.CardholderName, Converter={StaticResource stringHasValue}}" />
<Grid StyleClass="box-row" <Grid StyleClass="box-row"
IsVisible="{Binding Cipher.Card.Number, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Card.Number, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -297,22 +273,19 @@
Text="{u:I18n Number}" Text="{u:I18n Number}"
StyleClass="box-label" StyleClass="box-label"
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0" />
AutomationId="ItemName" />
<controls:MonoLabel <controls:MonoLabel
Text="{Binding Cipher.Card.MaskedNumber, Mode=OneWay}" Text="{Binding Cipher.Card.MaskedNumber, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
IsVisible="{Binding ShowCardNumber, Converter={StaticResource inverseBool}}" IsVisible="{Binding ShowCardNumber, Converter={StaticResource inverseBool}}" />
AutomationId="ItemValue" />
<controls:MonoLabel <controls:MonoLabel
Text="{Binding Cipher.Card.Number, Mode=OneWay}" Text="{Binding Cipher.Card.Number, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
IsVisible="{Binding ShowCardNumber}" IsVisible="{Binding ShowCardNumber}" />
AutomationId="ItemValue" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowCardNumberIcon}" Text="{Binding ShowCardNumberIcon}"
@@ -321,8 +294,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}" />
AutomationId="ShowValueButton" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}" Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}"
@@ -332,42 +304,34 @@
Grid.Column="2" Grid.Column="2"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyNumber}" AutomationProperties.Name="{u:I18n CopyNumber}" />
AutomationId="CopyValueButton" />
</Grid> </Grid>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Card.Number, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Card.Number, Converter={StaticResource stringHasValue}}" />
<StackLayout StyleClass="box-row" <StackLayout StyleClass="box-row"
IsVisible="{Binding Cipher.Card.Brand, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Card.Brand, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Label <Label
Text="{u:I18n Brand}" Text="{u:I18n Brand}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Card.Brand, Mode=OneWay}" Text="{Binding Cipher.Card.Brand, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemValue" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Card.Brand, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Card.Brand, Converter={StaticResource stringHasValue}}" />
<StackLayout StyleClass="box-row" <StackLayout StyleClass="box-row"
IsVisible="{Binding Cipher.Card.Expiration, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Card.Expiration, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Label <Label
Text="{u:I18n Expiration}" Text="{u:I18n Expiration}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Card.Expiration, Mode=OneWay}" Text="{Binding Cipher.Card.Expiration, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemValue" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Card.Expiration, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Card.Expiration, Converter={StaticResource stringHasValue}}" />
<Grid StyleClass="box-row" <Grid StyleClass="box-row"
IsVisible="{Binding Cipher.Card.Code, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Card.Code, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -381,22 +345,19 @@
Text="{u:I18n SecurityCode}" Text="{u:I18n SecurityCode}"
StyleClass="box-label" StyleClass="box-label"
Grid.Row="0" Grid.Row="0"
Grid.Column="0" Grid.Column="0" />
AutomationId="ItemName" />
<controls:MonoLabel <controls:MonoLabel
Text="{Binding Cipher.Card.MaskedCode, Mode=OneWay}" Text="{Binding Cipher.Card.MaskedCode, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
IsVisible="{Binding ShowCardCode, Converter={StaticResource inverseBool}}" IsVisible="{Binding ShowCardCode, Converter={StaticResource inverseBool}}" />
AutomationId="ItemValue" />
<controls:MonoLabel <controls:MonoLabel
Text="{Binding Cipher.Card.Code, Mode=OneWay}" Text="{Binding Cipher.Card.Code, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
IsVisible="{Binding ShowCardCode}" IsVisible="{Binding ShowCardCode}" />
AutomationId="ItemValue" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowCardCodeIcon}" Text="{Binding ShowCardCodeIcon}"
@@ -405,8 +366,7 @@
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" AutomationProperties.Name="{u:I18n ToggleVisibility}" />
AutomationId="ShowValueButton" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}" Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}"
@@ -416,156 +376,124 @@
Grid.Column="2" Grid.Column="2"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopySecurityCode}" AutomationProperties.Name="{u:I18n CopySecurityCode}" />
AutomationId="CopyValueButton" />
</Grid> </Grid>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Card.Code, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Card.Code, Converter={StaticResource stringHasValue}}" />
</StackLayout> </StackLayout>
<StackLayout IsVisible="{Binding IsIdentity}" Spacing="0" Padding="0"> <StackLayout IsVisible="{Binding IsIdentity}" Spacing="0" Padding="0">
<StackLayout StyleClass="box-row" <StackLayout StyleClass="box-row"
IsVisible="{Binding Cipher.Identity.FullName, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.FullName, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Label <Label
Text="{u:I18n IdentityName}" Text="{u:I18n IdentityName}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Identity.FullName, Mode=OneWay}" Text="{Binding Cipher.Identity.FullName, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemValue" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Identity.FullName, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Identity.FullName, Converter={StaticResource stringHasValue}}" />
<StackLayout StyleClass="box-row" <StackLayout StyleClass="box-row"
IsVisible="{Binding Cipher.Identity.Username, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.Username, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Label <Label
Text="{u:I18n Username}" Text="{u:I18n Username}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Identity.Username, Mode=OneWay}" Text="{Binding Cipher.Identity.Username, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemValue" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Identity.Username, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Identity.Username, Converter={StaticResource stringHasValue}}" />
<StackLayout StyleClass="box-row" <StackLayout StyleClass="box-row"
IsVisible="{Binding Cipher.Identity.Company, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.Company, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Label <Label
Text="{u:I18n Company}" Text="{u:I18n Company}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Identity.Company, Mode=OneWay}" Text="{Binding Cipher.Identity.Company, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemValue" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Identity.Company, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Identity.Company, Converter={StaticResource stringHasValue}}" />
<StackLayout StyleClass="box-row" <StackLayout StyleClass="box-row"
IsVisible="{Binding Cipher.Identity.SSN, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.SSN, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Label <Label
Text="{u:I18n SSN}" Text="{u:I18n SSN}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Identity.SSN, Mode=OneWay}" Text="{Binding Cipher.Identity.SSN, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemValue" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Identity.SSN, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Identity.SSN, Converter={StaticResource stringHasValue}}" />
<StackLayout StyleClass="box-row" <StackLayout StyleClass="box-row"
IsVisible="{Binding Cipher.Identity.PassportNumber, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.PassportNumber, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Label <Label
Text="{u:I18n PassportNumber}" Text="{u:I18n PassportNumber}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Identity.PassportNumber, Mode=OneWay}" Text="{Binding Cipher.Identity.PassportNumber, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemValue" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Identity.PassportNumber, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Identity.PassportNumber, Converter={StaticResource stringHasValue}}" />
<StackLayout StyleClass="box-row" <StackLayout StyleClass="box-row"
IsVisible="{Binding Cipher.Identity.LicenseNumber, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.LicenseNumber, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Label <Label
Text="{u:I18n LicenseNumber}" Text="{u:I18n LicenseNumber}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Identity.LicenseNumber, Mode=OneWay}" Text="{Binding Cipher.Identity.LicenseNumber, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemValue" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Identity.LicenseNumber, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Identity.LicenseNumber, Converter={StaticResource stringHasValue}}" />
<StackLayout StyleClass="box-row" <StackLayout StyleClass="box-row"
IsVisible="{Binding Cipher.Identity.Email, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.Email, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow">
<Label <Label
Text="{u:I18n Email}" Text="{u:I18n Email}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Identity.Email, Mode=OneWay}" Text="{Binding Cipher.Identity.Email, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemValue" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Identity.Email, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Identity.Email, Converter={StaticResource stringHasValue}}" />
<StackLayout StyleClass="box-row" <StackLayout StyleClass="box-row"
IsVisible="{Binding Cipher.Identity.Phone, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.Phone, Converter={StaticResource stringHasValue}}">
AutomationId="ItemRow" >
<Label <Label
Text="{u:I18n Phone}" Text="{u:I18n Phone}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Identity.Phone, Mode=OneWay}" Text="{Binding Cipher.Identity.Phone, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="ItemValue" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" <BoxView StyleClass="box-row-separator"
IsVisible="{Binding Cipher.Identity.Phone, Converter={StaticResource stringHasValue}}" /> IsVisible="{Binding Cipher.Identity.Phone, Converter={StaticResource stringHasValue}}" />
<StackLayout StyleClass="box-row" IsVisible="{Binding ShowIdentityAddress}" <StackLayout StyleClass="box-row" IsVisible="{Binding ShowIdentityAddress}">
AutomationId="ItemRow">
<Label <Label
Text="{u:I18n Address}" Text="{u:I18n Address}"
StyleClass="box-label" StyleClass="box-label" />
AutomationId="ItemName" />
<Label <Label
Text="{Binding Cipher.Identity.Address1, Mode=OneWay}" Text="{Binding Cipher.Identity.Address1, Mode=OneWay}"
IsVisible="{Binding Cipher.Identity.Address1, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.Address1, Converter={StaticResource stringHasValue}}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="IdentityAddressOneLabel" />
<Label <Label
Text="{Binding Cipher.Identity.Address2, Mode=OneWay}" Text="{Binding Cipher.Identity.Address2, Mode=OneWay}"
IsVisible="{Binding Cipher.Identity.Address2, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.Address2, Converter={StaticResource stringHasValue}}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="IdentityAddressTwoLabel" />
<Label <Label
Text="{Binding Cipher.Identity.Address3, Mode=OneWay}" Text="{Binding Cipher.Identity.Address3, Mode=OneWay}"
IsVisible="{Binding Cipher.Identity.Address3, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.Address3, Converter={StaticResource stringHasValue}}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="IdentityAddressThreeLabel" />
<Label <Label
Text="{Binding Cipher.Identity.FullAddressPart2, Mode=OneWay}" Text="{Binding Cipher.Identity.FullAddressPart2, Mode=OneWay}"
IsVisible="{Binding Cipher.Identity.FullAddressPart2, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.FullAddressPart2, Converter={StaticResource stringHasValue}}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="IdentityFullAddressPartTwoLabel" />
<Label <Label
Text="{Binding Cipher.Identity.Country, Mode=OneWay}" Text="{Binding Cipher.Identity.Country, Mode=OneWay}"
IsVisible="{Binding Cipher.Identity.Country, Converter={StaticResource stringHasValue}}" IsVisible="{Binding Cipher.Identity.Country, Converter={StaticResource stringHasValue}}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="IdentityCountryLabel" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" IsVisible="{Binding ShowIdentityAddress}" /> <BoxView StyleClass="box-row-separator" IsVisible="{Binding ShowIdentityAddress}" />
</StackLayout> </StackLayout>
@@ -575,11 +503,11 @@
<Label Text="{u:I18n URIs, Header=True}" <Label Text="{u:I18n URIs, Header=True}"
StyleClass="box-header, box-header-platform" /> StyleClass="box-header, box-header-platform" />
</StackLayout> </StackLayout>
<controls:RepeaterView ItemsSource="{Binding Cipher.Login.Uris}" AutomationId="CipherUriContainer"> <controls:RepeaterView ItemsSource="{Binding Cipher.Login.Uris}">
<controls:RepeaterView.ItemTemplate> <controls:RepeaterView.ItemTemplate>
<DataTemplate x:DataType="views:LoginUriView"> <DataTemplate x:DataType="views:LoginUriView">
<StackLayout Spacing="0" Padding="0"> <StackLayout Spacing="0" Padding="0">
<Grid StyleClass="box-row" AutomationId="UriRow"> <Grid StyleClass="box-row">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -605,8 +533,7 @@
Text="{Binding HostOrUri, Mode=OneWay}" Text="{Binding HostOrUri, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0" />
AutomationId="UriValue" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.ShareSquare}}" Text="{Binding Source={x:Static core:BitwardenIcons.ShareSquare}}"
@@ -617,8 +544,7 @@
Grid.RowSpan="2" Grid.RowSpan="2"
IsVisible="{Binding CanLaunch, Mode=OneWay}" IsVisible="{Binding CanLaunch, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Launch}" AutomationProperties.Name="{u:I18n Launch}" />
AutomationId="LaunchUriButton" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}" Text="{Binding Source={x:Static core:BitwardenIcons.Clone}}"
@@ -628,8 +554,7 @@
Grid.Column="2" Grid.Column="2"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Copy}" AutomationProperties.Name="{u:I18n Copy}" />
AutomationId="CopyUriButton" />
</Grid> </Grid>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
</StackLayout> </StackLayout>
@@ -643,15 +568,14 @@
<Label Text="{u:I18n Notes, Header=True}" <Label Text="{u:I18n Notes, Header=True}"
StyleClass="box-header, box-header-platform" /> StyleClass="box-header, box-header-platform" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-row" AutomationId="NotesRow"> <StackLayout StyleClass="box-row">
<controls:SelectableLabel <controls:SelectableLabel
Text="{Binding Cipher.Notes, Mode=OneWay}" Text="{Binding Cipher.Notes, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value" />
AutomationId="CipherNotesLabel" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
</StackLayout> </StackLayout>
<StackLayout StyleClass="box" IsVisible="{Binding Cipher.HasFields}" AutomationId="CustomFieldsContainer"> <StackLayout StyleClass="box" IsVisible="{Binding Cipher.HasFields}">
<StackLayout StyleClass="box-row-header"> <StackLayout StyleClass="box-row-header">
<Label Text="{u:I18n CustomFields, Header=True}" <Label Text="{u:I18n CustomFields, Header=True}"
StyleClass="box-header, box-header-platform" /> StyleClass="box-header, box-header-platform" />
@@ -666,23 +590,21 @@
<Label Text="{u:I18n Attachments, Header=True}" <Label Text="{u:I18n Attachments, Header=True}"
StyleClass="box-header, box-header-platform" /> StyleClass="box-header, box-header-platform" />
</StackLayout> </StackLayout>
<controls:RepeaterView ItemsSource="{Binding Cipher.Attachments}" AutomationId="CipherAttachmentsContainer"> <controls:RepeaterView ItemsSource="{Binding Cipher.Attachments}">
<controls:RepeaterView.ItemTemplate> <controls:RepeaterView.ItemTemplate>
<DataTemplate x:DataType="views:AttachmentView"> <DataTemplate x:DataType="views:AttachmentView">
<StackLayout Spacing="0" Padding="0"> <StackLayout Spacing="0" Padding="0">
<StackLayout Orientation="Horizontal" StyleClass="box-row" Spacing="10" AutomationId="CipherAttachment"> <StackLayout Orientation="Horizontal" StyleClass="box-row" Spacing="10">
<Label <Label
Text="{Binding FileName, Mode=OneWay}" Text="{Binding FileName, Mode=OneWay}"
StyleClass="box-value" StyleClass="box-value"
VerticalTextAlignment="Center" VerticalTextAlignment="Center"
HorizontalOptions="StartAndExpand" HorizontalOptions="StartAndExpand" />
AutomationId="CipherAttachmentFileNameLabel" />
<Label <Label
Text="{Binding SizeName, Mode=OneWay}" Text="{Binding SizeName, Mode=OneWay}"
StyleClass="box-sub-label" StyleClass="box-sub-label"
HorizontalTextAlignment="End" HorizontalTextAlignment="End"
VerticalTextAlignment="Center" VerticalTextAlignment="Center" />
AutomationId="CipherAttachmentFileSizeLabel" />
<controls:IconButton <controls:IconButton
StyleClass="box-row-button, box-row-button-platform" StyleClass="box-row-button, box-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Download}}" Text="{Binding Source={x:Static core:BitwardenIcons.Download}}"
@@ -690,8 +612,7 @@
CommandParameter="{Binding .}" CommandParameter="{Binding .}"
VerticalOptions="Center" VerticalOptions="Center"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Download}" AutomationProperties.Name="{u:I18n Download}" />
AutomationId="CipherAttachmentDownloadButton" />
</StackLayout> </StackLayout>
<BoxView StyleClass="box-row-separator" /> <BoxView StyleClass="box-row-separator" />
</StackLayout> </StackLayout>
@@ -701,20 +622,17 @@
</StackLayout> </StackLayout>
<StackLayout StyleClass="box-bottom"> <StackLayout StyleClass="box-bottom">
<Label FormattedText="{Binding UpdatedText}" <Label FormattedText="{Binding UpdatedText}"
StyleClass="box-footer-label" StyleClass="box-footer-label" />
AutomationId="CipherUpdatedDateLabel" />
<Label FormattedText="{Binding PasswordUpdatedText}" <Label FormattedText="{Binding PasswordUpdatedText}"
StyleClass="box-footer-label" StyleClass="box-footer-label"
IsVisible="{Binding Cipher.PasswordRevisionDisplayDate, Converter={StaticResource notNull}}" IsVisible="{Binding Cipher.PasswordRevisionDisplayDate, Converter={StaticResource notNull}}">
AutomationId="CipherUpdatedPasswordDateLabel">
<Label.GestureRecognizers> <Label.GestureRecognizers>
<TapGestureRecognizer Tapped="PasswordHistory_Tapped" /> <TapGestureRecognizer Tapped="PasswordHistory_Tapped" />
</Label.GestureRecognizers> </Label.GestureRecognizers>
</Label> </Label>
<Label FormattedText="{Binding PasswordHistoryText}" <Label FormattedText="{Binding PasswordHistoryText}"
StyleClass="box-footer-label" StyleClass="box-footer-label"
IsVisible="{Binding Cipher.HasPasswordHistory}" IsVisible="{Binding Cipher.HasPasswordHistory}">
AutomationId="CipherPasswordHistoryLabel">
<Label.GestureRecognizers> <Label.GestureRecognizers>
<TapGestureRecognizer Tapped="PasswordHistory_Tapped" /> <TapGestureRecognizer Tapped="PasswordHistory_Tapped" />
</Label.GestureRecognizers> </Label.GestureRecognizers>
@@ -744,7 +662,6 @@
AbsoluteLayout.LayoutBounds="1, 1, AutoSize, AutoSize" AbsoluteLayout.LayoutBounds="1, 1, AutoSize, AutoSize"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n EditItem}" AutomationProperties.Name="{u:I18n EditItem}"
AutomationId="CipherEditButton"
IsVisible="{Binding CanEdit}"> IsVisible="{Binding CanEdit}">
<Button.Effects> <Button.Effects>
<effects:FabShadowEffect /> <effects:FabShadowEffect />

View File

@@ -16,11 +16,10 @@
IconImageSource="{Binding AvatarImageSource}" IconImageSource="{Binding AvatarImageSource}"
Command="{Binding Source={x:Reference _accountListOverlay}, Path=ToggleVisibililtyCommand}" Command="{Binding Source={x:Reference _accountListOverlay}, Path=ToggleVisibililtyCommand}"
Order="Primary" Order="Primary"
Priority="-1" Priority="-2"
UseOriginalImage="True" UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" AutomationProperties.Name="{u:I18n Account}" />
AutomationId="AccountIconButton" />
<ToolbarItem IconImageSource="search.png" Clicked="Search_Clicked" <ToolbarItem IconImageSource="search.png" Clicked="Search_Clicked"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Search}" /> AutomationProperties.Name="{u:I18n Search}" />

View File

@@ -69,16 +69,14 @@ namespace Bit.App.Pages
return; return;
} }
try // TODO: There's currently an issue on iOS where the toolbar item is not getting updated
// as the others somehow. Removing this so at least we get the circle with ".." instead
// of a white circle
if (Device.RuntimePlatform != Device.iOS)
{ {
// don't crash the app if the avatar can't be loaded, just log the ex
_accountAvatar?.OnAppearing(); _accountAvatar?.OnAppearing();
_vm.AvatarImageSource = await GetAvatarImageSourceAsync(); _vm.AvatarImageSource = await GetAvatarImageSourceAsync();
} }
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
_broadcasterService.Subscribe(nameof(CipherSelectionPage), async (message) => _broadcasterService.Subscribe(nameof(CipherSelectionPage), async (message) =>
{ {

View File

@@ -41,8 +41,7 @@
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
TextChanged="SearchBar_TextChanged" TextChanged="SearchBar_TextChanged"
SearchButtonPressed="SearchBar_SearchButtonPressed" SearchButtonPressed="SearchBar_SearchButtonPressed"
Placeholder="{Binding PageTitle}" Placeholder="{Binding PageTitle}" />
AutomationId="SearchBar" />
</StackLayout> </StackLayout>
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" <BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform"
x:Name="_separator" x:Key="separator" /> x:Name="_separator" x:Key="separator" />
@@ -92,8 +91,7 @@
Source="empty_items_state" /> Source="empty_items_state" />
<Label <Label
Text="{u:I18n ThereAreNoItemsThatMatchTheSearch}" Text="{u:I18n ThereAreNoItemsThatMatchTheSearch}"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center" />
AutomationId="NoSearchResultsLabel" />
<Button <Button
Text="{u:I18n AddAnItem}" Text="{u:I18n AddAnItem}"
Command="{Binding AddCipherCommand}" Command="{Binding AddCipherCommand}"
@@ -106,8 +104,7 @@
SelectionMode="Single" SelectionMode="Single"
SelectionChanged="RowSelected" SelectionChanged="RowSelected"
StyleClass="list, list-platform" StyleClass="list, list-platform"
ExtraDataForLogging="Ciphers Page" ExtraDataForLogging="Ciphers Page">
AutomationId="CipherList">
<CollectionView.ItemTemplate> <CollectionView.ItemTemplate>
<DataTemplate x:DataType="views:CipherView"> <DataTemplate x:DataType="views:CipherView">
<controls:CipherViewCell <controls:CipherViewCell

View File

@@ -25,8 +25,7 @@
Priority="-1" Priority="-1"
UseOriginalImage="True" UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" AutomationProperties.Name="{u:I18n Account}" />
AutomationId="AccountIconButton" />
<ToolbarItem Icon="search.png" Clicked="Search_Clicked" <ToolbarItem Icon="search.png" Clicked="Search_Clicked"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Search}" /> AutomationProperties.Name="{u:I18n Search}" />
@@ -60,14 +59,13 @@
<controls:AuthenticatorViewCell <controls:AuthenticatorViewCell
Cipher="{Binding Cipher}" Cipher="{Binding Cipher}"
WebsiteIconsEnabled="{Binding BindingContext.WebsiteIconsEnabled, Source={x:Reference _page}}" WebsiteIconsEnabled="{Binding BindingContext.WebsiteIconsEnabled, Source={x:Reference _page}}"
TotpSec="{Binding TotpSec}" /> TotpSec="{Binding TotpSec}"/>
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="groupTemplate" <DataTemplate x:Key="groupTemplate"
x:DataType="pages:GroupingsPageListItem"> x:DataType="pages:GroupingsPageListItem">
<controls:ExtendedStackLayout Orientation="Horizontal" <controls:ExtendedStackLayout Orientation="Horizontal"
StyleClass="list-row, list-row-platform" StyleClass="list-row, list-row-platform">
AutomationId="{Binding AutomationId}">
<controls:IconLabel Text="{Binding Icon, Mode=OneWay}" <controls:IconLabel Text="{Binding Icon, Mode=OneWay}"
HorizontalOptions="Start" HorizontalOptions="Start"
VerticalOptions="Center" VerticalOptions="Center"
@@ -81,14 +79,12 @@
LineBreakMode="TailTruncation" LineBreakMode="TailTruncation"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
StyleClass="list-title" StyleClass="list-title"/>
AutomationId="ItemNameLabel" />
<Label Text="{Binding ItemCount, Mode=OneWay}" <Label Text="{Binding ItemCount, Mode=OneWay}"
HorizontalOptions="End" HorizontalOptions="End"
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalTextAlignment="End" HorizontalTextAlignment="End"
StyleClass="list-sub" StyleClass="list-sub"/>
AutomationId="ItemCountLabel" />
</controls:ExtendedStackLayout> </controls:ExtendedStackLayout>
</DataTemplate> </DataTemplate>
@@ -99,8 +95,7 @@
Spacing="0" Spacing="0"
Padding="0" Padding="0"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform" StyleClass="list-row-header-container, list-row-header-container-platform">
AutomationId="{Binding AutomationId}">
<BoxView <BoxView
StyleClass="list-section-separator-top, list-section-separator-top-platform" /> StyleClass="list-section-separator-top, list-section-separator-top-platform" />
<StackLayout StyleClass="list-row-header, list-row-header-platform"> <StackLayout StyleClass="list-row-header, list-row-header-platform">
@@ -109,8 +104,7 @@
StyleClass="list-header, list-header-platform" /> StyleClass="list-header, list-header-platform" />
<Label <Label
Text="{Binding ItemCount}" Text="{Binding ItemCount}"
StyleClass="list-header-sub" StyleClass="list-header-sub" />
AutomationId="SectionItemCount" />
</StackLayout> </StackLayout>
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" /> <BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
</StackLayout> </StackLayout>
@@ -153,8 +147,7 @@
IsVisible="{Binding ShowNoData}"> IsVisible="{Binding ShowNoData}">
<Label <Label
Text="{Binding NoDataText}" Text="{Binding NoDataText}"
HorizontalTextAlignment="Center" HorizontalTextAlignment="Center"></Label>
AutomationId="NoDataDisplayed"></Label>
<Button <Button
Text="{u:I18n AddAnItem}" Text="{u:I18n AddAnItem}"
Clicked="AddButton_Clicked" Clicked="AddButton_Clicked"

View File

@@ -1,6 +1,4 @@
using Bit.App.Utilities.Automation; namespace Bit.App.Pages
namespace Bit.App.Pages
{ {
public class GroupingsPageHeaderListItem : IGroupingsPageListItem public class GroupingsPageHeaderListItem : IGroupingsPageListItem
{ {
@@ -12,12 +10,5 @@ namespace Bit.App.Pages
public string Title { get; } public string Title { get; }
public string ItemCount { get; set; } public string ItemCount { get; set; }
public string AutomationId
{
get
{
return AutomationIdsHelper.AddSuffixFor(AutomationIdsHelper.ToEnglishTitleCase(Title), SuffixType.Header);
}
}
} }
} }

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using Bit.App.Utilities.Automation;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
@@ -33,6 +32,5 @@ namespace Bit.App.Pages
public string Name { get; set; } public string Name { get; set; }
public string NameShort => string.IsNullOrWhiteSpace(Name) || Name.Length == 0 ? "-" : Name[0].ToString(); public string NameShort => string.IsNullOrWhiteSpace(Name) || Name.Length == 0 ? "-" : Name[0].ToString();
public string ItemCount { get; set; } public string ItemCount { get; set; }
public string AutomationId => AutomationIdsHelper.AddSuffixFor(NameShort, SuffixType.ListGroup);
} }
} }

View File

@@ -1,5 +1,4 @@
using Bit.App.Resources; using Bit.App.Resources;
using Bit.App.Utilities.Automation;
using Bit.Core; using Bit.Core;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.View; using Bit.Core.Models.View;
@@ -116,38 +115,5 @@ namespace Bit.App.Pages
return _icon; return _icon;
} }
} }
public string AutomationId
{
get
{
if (Type != null)
{
return AutomationIdsHelper.AddSuffixFor(System.Enum.GetName(typeof(CipherType), Type.Value), SuffixType.Filter);
}
if (IsTrash)
{
return AutomationIdsHelper.AddSuffixFor("Trash", SuffixType.Filter);
}
if (Folder != null)
{
return AutomationIdsHelper.AddSuffixFor("Folder", SuffixType.Filter);
}
if (Collection != null)
{
return AutomationIdsHelper.AddSuffixFor("Collection", SuffixType.Filter);
}
if (IsTotpCode)
{
return AutomationIdsHelper.AddSuffixFor("TOTP", SuffixType.ListItem);
}
return null;
}
}
} }
} }

View File

@@ -78,9 +78,7 @@ namespace Bit.App.Pages
Refreshing = true; Refreshing = true;
await LoadAsync(); await LoadAsync();
}); });
CipherOptionsCommand = new AsyncCommand<CipherView>(cipher => AppHelpers.CipherListOptions(Page, cipher, _passwordRepromptService), CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
onException: ex => _logger.Exception(ex),
allowsMultipleExecutions: false);
VaultFilterCommand = new AsyncCommand(VaultFilterOptionsAsync, VaultFilterCommand = new AsyncCommand(VaultFilterOptionsAsync,
onException: ex => _logger.Exception(ex), onException: ex => _logger.Exception(ex),
allowsMultipleExecutions: false); allowsMultipleExecutions: false);
@@ -170,7 +168,7 @@ namespace Bit.App.Pages
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; } public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
public ObservableRangeCollection<IGroupingsPageListItem> GroupedItems { get; set; } public ObservableRangeCollection<IGroupingsPageListItem> GroupedItems { get; set; }
public Command RefreshCommand { get; set; } public Command RefreshCommand { get; set; }
public ICommand CipherOptionsCommand { get; } public Command<CipherView> CipherOptionsCommand { get; set; }
public bool LoadedOnce { get; set; } public bool LoadedOnce { get; set; }
public async Task LoadAsync() public async Task LoadAsync()
@@ -711,5 +709,13 @@ namespace Bit.App.Pages
var folders = decFolders.Where(f => _allCiphers.Any(c => c.FolderId == f.Id)).ToList(); var folders = decFolders.Where(f => _allCiphers.Any(c => c.FolderId == f.Id)).ToList();
return folders.Any() ? folders : null; return folders.Any() ? folders : null;
} }
private async void CipherOptionsAsync(CipherView cipher)
{
if ((Page as BaseContentPage).DoOnce())
{
await AppHelpers.CipherListOptions(Page, cipher, _passwordRepromptService);
}
}
} }
} }

View File

@@ -202,24 +202,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Biometric unlock for this account is disabled pending verification of master password..
/// </summary>
public static string AccountBiometricInvalidated {
get {
return ResourceManager.GetString("AccountBiometricInvalidated", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Autofill biometric unlock for this account is disabled pending verification of master password..
/// </summary>
public static string AccountBiometricInvalidatedExtension {
get {
return ResourceManager.GetString("AccountBiometricInvalidatedExtension", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Your new account has been created! You may now log in.. /// Looks up a localized string similar to Your new account has been created! You may now log in..
/// </summary> /// </summary>
@@ -985,6 +967,24 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Biometric unlock disabled pending verification of master password..
/// </summary>
public static string BiometricInvalidated {
get {
return ResourceManager.GetString("BiometricInvalidated", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Biometric unlock for autofill disabled pending verification of master password..
/// </summary>
public static string BiometricInvalidatedExtension {
get {
return ResourceManager.GetString("BiometricInvalidatedExtension", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Biometrics. /// Looks up a localized string similar to Biometrics.
/// </summary> /// </summary>
@@ -1687,15 +1687,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Current master password.
/// </summary>
public static string CurrentMasterPassword {
get {
return ResourceManager.GetString("CurrentMasterPassword", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Custom. /// Looks up a localized string similar to Custom.
/// </summary> /// </summary>
@@ -1750,15 +1741,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Data region.
/// </summary>
public static string DataRegion {
get {
return ResourceManager.GetString("DataRegion", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Password updated. /// Looks up a localized string similar to Password updated.
/// </summary> /// </summary>
@@ -2335,15 +2317,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to EU.
/// </summary>
public static string EU {
get {
return ResourceManager.GetString("EU", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Exact. /// Looks up a localized string similar to Exact.
/// </summary> /// </summary>
@@ -3262,24 +3235,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Invalid API key.
/// </summary>
public static string InvalidAPIKey {
get {
return ResourceManager.GetString("InvalidAPIKey", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid API token.
/// </summary>
public static string InvalidAPIToken {
get {
return ResourceManager.GetString("InvalidAPIToken", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Invalid email address.. /// Looks up a localized string similar to Invalid email address..
/// </summary> /// </summary>
@@ -3605,11 +3560,11 @@ namespace Bit.App.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Logging in as {0} on {1}. /// Looks up a localized string similar to Logging in as {0}.
/// </summary> /// </summary>
public static string LoggingInAsXOnY { public static string LoggingInAsX {
get { get {
return ResourceManager.GetString("LoggingInAsXOnY", resourceCulture); return ResourceManager.GetString("LoggingInAsX", resourceCulture);
} }
} }
@@ -3912,15 +3867,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Master password re-prompt help.
/// </summary>
public static string MasterPasswordRePromptHelp {
get {
return ResourceManager.GetString("MasterPasswordRePromptHelp", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Match detection. /// Looks up a localized string similar to Match detection.
/// </summary> /// </summary>
@@ -5174,15 +5120,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Region.
/// </summary>
public static string Region {
get {
return ResourceManager.GetString("Region", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Remember me. /// Looks up a localized string similar to Remember me.
/// </summary> /// </summary>
@@ -5516,15 +5453,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Self-hosted.
/// </summary>
public static string SelfHosted {
get {
return ResourceManager.GetString("SelfHosted", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Self-hosted environment. /// Looks up a localized string similar to Self-hosted environment.
/// </summary> /// </summary>
@@ -6452,15 +6380,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve..
/// </summary>
public static string UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve {
get {
return ResourceManager.GetString("UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Unlock vault. /// Looks up a localized string similar to Unlock vault.
/// </summary> /// </summary>
@@ -6533,15 +6452,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour..
/// </summary>
public static string UpdateWeakMasterPasswordWarning {
get {
return ResourceManager.GetString("UpdateWeakMasterPasswordWarning", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Updating password. /// Looks up a localized string similar to Updating password.
/// </summary> /// </summary>
@@ -6596,15 +6506,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to US.
/// </summary>
public static string US {
get {
return ResourceManager.GetString("US", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Use another two-step login method. /// Looks up a localized string similar to Use another two-step login method.
/// </summary> /// </summary>
@@ -6776,15 +6677,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Your organization policies have set your vault timeout action to {0}..
/// </summary>
public static string VaultTimeoutActionPolicyInEffect {
get {
return ResourceManager.GetString("VaultTimeoutActionPolicyInEffect", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Logging out will remove all access to your vault and requires online authentication after the timeout period. Are you sure you want to use this setting?. /// Looks up a localized string similar to Logging out will remove all access to your vault and requires online authentication after the timeout period. Are you sure you want to use this setting?.
/// </summary> /// </summary>
@@ -6795,7 +6687,7 @@ namespace Bit.App.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Your organization policies have set your maximum allowed vault timeout to {0} hour(s) and {1} minute(s).. /// Looks up a localized string similar to Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is {0} hour(s) and {1} minute(s).
/// </summary> /// </summary>
public static string VaultTimeoutPolicyInEffect { public static string VaultTimeoutPolicyInEffect {
get { get {
@@ -6803,15 +6695,6 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is {0} hour(s) and {1} minute(s). Your vault timeout action is set to {2}..
/// </summary>
public static string VaultTimeoutPolicyWithActionInEffect {
get {
return ResourceManager.GetString("VaultTimeoutPolicyWithActionInEffect", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Your vault timeout exceeds the restrictions set by your organization.. /// Looks up a localized string similar to Your vault timeout exceeds the restrictions set by your organization..
/// </summary> /// </summary>

View File

@@ -1752,11 +1752,11 @@ Skandering gebeur outomaties.</value>
<value>Wil u dit regtig na die asblik stuur?</value> <value>Wil u dit regtig na die asblik stuur?</value>
<comment>Confirmation alert message when soft-deleting a cipher.</comment> <comment>Confirmation alert message when soft-deleting a cipher.</comment>
</data> </data>
<data name="AccountBiometricInvalidated" xml:space="preserve"> <data name="BiometricInvalidated" xml:space="preserve">
<value>Biometriese ontgrendeling vir hierdie rekening is gedeaktiveer hangende bevestiging van hoofwagwoord.</value> <value>Biometriese ontgrendeling gedeaktiveer hangende bevestiging van hoofwagwoord.</value>
</data> </data>
<data name="AccountBiometricInvalidatedExtension" xml:space="preserve"> <data name="BiometricInvalidatedExtension" xml:space="preserve">
<value>Outovul-biometriese ontgrendeline vir hierdie rekening is gedeaktiveer hangende bevestiging van hoofwagwoord.</value> <value>Biometriese ontgrendeling vir outovul gedeaktiveer hangende bevestiging van hoofwagwoord.</value>
</data> </data>
<data name="EnableSyncOnRefresh" xml:space="preserve"> <data name="EnableSyncOnRefresh" xml:space="preserve">
<value>Aktiveer sinchronisering by verfrissing</value> <value>Aktiveer sinchronisering by verfrissing</value>
@@ -2142,12 +2142,6 @@ Skandering gebeur outomaties.</value>
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve"> <data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
<value>U organisasiebeleide beïnvloed u kluisuitelling. Maksimum toegelate kluisuittelling is {0} uur en {1} minuut(e).</value> <value>U organisasiebeleide beïnvloed u kluisuitelling. Maksimum toegelate kluisuittelling is {0} uur en {1} minuut(e).</value>
</data> </data>
<data name="VaultTimeoutPolicyWithActionInEffect" xml:space="preserve">
<value>Jou organisasie se beleid affekteer jou kluis-uittel tyd. Maksimum toelaatbare kluis-uittel tyd is {0} uur(ure) en {1} minuut(minute). Jou kluis-uittel aksie is gestel na {2}.</value>
</data>
<data name="VaultTimeoutActionPolicyInEffect" xml:space="preserve">
<value>Jou organisasie se beleid het jou kluis-uittel aksie na {0} verander.</value>
</data>
<data name="VaultTimeoutToLarge" xml:space="preserve"> <data name="VaultTimeoutToLarge" xml:space="preserve">
<value>U kluisuittelling oorskry die beperkinge wat deur u organisasie daargestel is.</value> <value>U kluisuittelling oorskry die beperkinge wat deur u organisasie daargestel is.</value>
</data> </data>
@@ -2495,8 +2489,8 @@ Wil u na die rekening omskakel?</value>
<data name="GetMasterPasswordwordHint" xml:space="preserve"> <data name="GetMasterPasswordwordHint" xml:space="preserve">
<value>Kry hoofwagwoord wenk</value> <value>Kry hoofwagwoord wenk</value>
</data> </data>
<data name="LoggingInAsXOnY" xml:space="preserve"> <data name="LoggingInAsX" xml:space="preserve">
<value>Teken aan as {0} op {1}</value> <value>Teken in as {0}</value>
</data> </data>
<data name="NotYou" xml:space="preserve"> <data name="NotYou" xml:space="preserve">
<value>Nie jy nie?</value> <value>Nie jy nie?</value>
@@ -2598,48 +2592,15 @@ Wil u na die rekening omskakel?</value>
<value>Organisasie SSO identifiseerder vereis.</value> <value>Organisasie SSO identifiseerder vereis.</value>
</data> </data>
<data name="AddTheKeyToAnExistingOrNewItem" xml:space="preserve"> <data name="AddTheKeyToAnExistingOrNewItem" xml:space="preserve">
<value>Voeg die sleutel by 'n huidige of nuwe item</value> <value>Add the key to an existing or new item</value>
</data> </data>
<data name="ThereAreNoItemsInYourVaultThatMatchX" xml:space="preserve"> <data name="ThereAreNoItemsInYourVaultThatMatchX" xml:space="preserve">
<value>Daar is geen items in jou kluis wat met "{0}" ooreenstem nie</value> <value>There are no items in your vault that match "{0}"</value>
</data> </data>
<data name="SearchForAnItemOrAddANewItem" xml:space="preserve"> <data name="SearchForAnItemOrAddANewItem" xml:space="preserve">
<value>Soek vir 'n item of voeg 'n nuwe een by</value> <value>Search for an item or add a new item</value>
</data> </data>
<data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve"> <data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve">
<value>Daar is geen items wat met die soekterm ooreenstem nie</value> <value>There are no items that match the search</value>
</data>
<data name="US" xml:space="preserve">
<value>VS</value>
</data>
<data name="EU" xml:space="preserve">
<value>EU</value>
</data>
<data name="SelfHosted" xml:space="preserve">
<value>Selghehuisves</value>
</data>
<data name="DataRegion" xml:space="preserve">
<value>Datastreek</value>
</data>
<data name="Region" xml:space="preserve">
<value>Streek</value>
</data>
<data name="UpdateWeakMasterPasswordWarning" xml:space="preserve">
<value>U hoofwagwoord voldoen nie aan een of meer van die organisasiebeleide nie. Om toegang tot die kluis te kry, moet u nou u hoofwagwoord bywerk. Deur voort te gaan sal u van u huidige sessie afgeteken word, en u sal weer moet aanteken. Aktiewe sessies op ander toestelle kan vir tot een uur aktief bly.</value>
</data>
<data name="CurrentMasterPassword" xml:space="preserve">
<value>Huidige hoofwagwoord</value>
</data>
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
<value>Master password re-prompt help</value>
</data>
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
<value>Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve.</value>
</data>
<data name="InvalidAPIKey" xml:space="preserve">
<value>Invalid API key</value>
</data>
<data name="InvalidAPIToken" xml:space="preserve">
<value>Invalid API token</value>
</data> </data>
</root> </root>

View File

@@ -1752,11 +1752,11 @@
<value>هل تريد حقاً أن ترسل إلى سلة المهملات؟</value> <value>هل تريد حقاً أن ترسل إلى سلة المهملات؟</value>
<comment>Confirmation alert message when soft-deleting a cipher.</comment> <comment>Confirmation alert message when soft-deleting a cipher.</comment>
</data> </data>
<data name="AccountBiometricInvalidated" xml:space="preserve"> <data name="BiometricInvalidated" xml:space="preserve">
<value>تم تعطيل فتح القفل الحيوي لهذا الحساب في انتظار التحقق من كلمة المرور الرئيسية.</value> <value>تم تعطيل فتح القفل الحيوي في انتظار التحقق من كلمة المرور الرئيسية.</value>
</data> </data>
<data name="AccountBiometricInvalidatedExtension" xml:space="preserve"> <data name="BiometricInvalidatedExtension" xml:space="preserve">
<value>إلغاء القفل الحيوي للملء التلقائي لهذا الحساب معطل في انتظار التحقق من كلمة المرور الرئيسية.</value> <value>تم تعطيل إلغاء القفل البيومتري للملء التلقائي في انتظار التحقق من كلمة المرور الرئيسية.</value>
</data> </data>
<data name="EnableSyncOnRefresh" xml:space="preserve"> <data name="EnableSyncOnRefresh" xml:space="preserve">
<value>تمكين المزامنة عند التحديث</value> <value>تمكين المزامنة عند التحديث</value>
@@ -2143,12 +2143,6 @@
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve"> <data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
<value>سياسات مؤسستك تؤثر على مهلة الخزنة الخاص بك. الحد الأقصى المسموح به لمهلة الخزنة هو {0} ساعة و {1} دقيقة</value> <value>سياسات مؤسستك تؤثر على مهلة الخزنة الخاص بك. الحد الأقصى المسموح به لمهلة الخزنة هو {0} ساعة و {1} دقيقة</value>
</data> </data>
<data name="VaultTimeoutPolicyWithActionInEffect" xml:space="preserve">
<value>سياسات مؤسستك تؤثر على مهلة خزنتك. الحد الأقصى المسموح به لمهلة الخزنة هو {0} ساعة(ساعات) و {1} دقيقة(دقائق). يتم تعيين إجراء مهلة المخزن الخاص بك إلى {2}.</value>
</data>
<data name="VaultTimeoutActionPolicyInEffect" xml:space="preserve">
<value>سياسات مؤسستك قامت بتعيين إجراء مهلة خزنتك إلى {0}.</value>
</data>
<data name="VaultTimeoutToLarge" xml:space="preserve"> <data name="VaultTimeoutToLarge" xml:space="preserve">
<value>مهلة خزنتك تتجاوز القيود التي تضعها مؤسستك.</value> <value>مهلة خزنتك تتجاوز القيود التي تضعها مؤسستك.</value>
</data> </data>
@@ -2496,8 +2490,8 @@
<data name="GetMasterPasswordwordHint" xml:space="preserve"> <data name="GetMasterPasswordwordHint" xml:space="preserve">
<value>احصل على تلميح كلمة المرور الرئيسية</value> <value>احصل على تلميح كلمة المرور الرئيسية</value>
</data> </data>
<data name="LoggingInAsXOnY" xml:space="preserve"> <data name="LoggingInAsX" xml:space="preserve">
<value>تسجيل الدخول كـ {0} في {1}</value> <value>تسجيل الدخول كـ {0}</value>
</data> </data>
<data name="NotYou" xml:space="preserve"> <data name="NotYou" xml:space="preserve">
<value>ليس أنت؟</value> <value>ليس أنت؟</value>
@@ -2548,16 +2542,16 @@
<value>تمكين إذن الكاميرا لاستخدام الماسح الضوئي</value> <value>تمكين إذن الكاميرا لاستخدام الماسح الضوئي</value>
</data> </data>
<data name="Language" xml:space="preserve"> <data name="Language" xml:space="preserve">
<value>اللّغة</value> <value>Language</value>
</data> </data>
<data name="LanguageChangeXDescription" xml:space="preserve"> <data name="LanguageChangeXDescription" xml:space="preserve">
<value>تم تغيير اللغة إلى {0}. الرجاء إعادة تشغيل التطبيق لرؤية التغيير</value> <value>The language has been changed to {0}. Please restart the app to see the change</value>
</data> </data>
<data name="LanguageChangeRequiresAppRestart" xml:space="preserve"> <data name="LanguageChangeRequiresAppRestart" xml:space="preserve">
<value>تغيير اللّغة يتطلب إعادة تشغيل التطبيق</value> <value>Language change requires app restart</value>
</data> </data>
<data name="DefaultSystem" xml:space="preserve"> <data name="DefaultSystem" xml:space="preserve">
<value>الافتراضي (النظام)</value> <value>Default (System)</value>
</data> </data>
<data name="Important" xml:space="preserve"> <data name="Important" xml:space="preserve">
<value>مهم</value> <value>مهم</value>
@@ -2596,51 +2590,18 @@
<value>كلمة مرور ضعيفة محددة وموجودة في خرق البيانات. استخدم كلمة مرور قوية وفريدة لحماية حسابك. هل أنت متأكد من أنك تريد استخدام كلمة المرور هذه؟</value> <value>كلمة مرور ضعيفة محددة وموجودة في خرق البيانات. استخدم كلمة مرور قوية وفريدة لحماية حسابك. هل أنت متأكد من أنك تريد استخدام كلمة المرور هذه؟</value>
</data> </data>
<data name="OrganizationSsoIdentifierRequired" xml:space="preserve"> <data name="OrganizationSsoIdentifierRequired" xml:space="preserve">
<value>معرف الـSSO للمنظمة مطلوب.</value> <value>Organization SSO identifier required.</value>
</data> </data>
<data name="AddTheKeyToAnExistingOrNewItem" xml:space="preserve"> <data name="AddTheKeyToAnExistingOrNewItem" xml:space="preserve">
<value>إضافة المفتاح إلى عنصر موجود أو جديد</value> <value>Add the key to an existing or new item</value>
</data> </data>
<data name="ThereAreNoItemsInYourVaultThatMatchX" xml:space="preserve"> <data name="ThereAreNoItemsInYourVaultThatMatchX" xml:space="preserve">
<value>لا توجد عناصر في خزانتك تتطابق مع "{0}"</value> <value>There are no items in your vault that match "{0}"</value>
</data> </data>
<data name="SearchForAnItemOrAddANewItem" xml:space="preserve"> <data name="SearchForAnItemOrAddANewItem" xml:space="preserve">
<value>البحث عن عنصر أو إضافة عنصر جديد</value> <value>Search for an item or add a new item</value>
</data> </data>
<data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve"> <data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve">
<value>لا توجد عناصر تطابق البحث</value> <value>There are no items that match the search</value>
</data>
<data name="US" xml:space="preserve">
<value>الولايات المتحدة</value>
</data>
<data name="EU" xml:space="preserve">
<value>الاتحاد الأوروبي</value>
</data>
<data name="SelfHosted" xml:space="preserve">
<value>استضافة ذاتية</value>
</data>
<data name="DataRegion" xml:space="preserve">
<value>منطقة البيانات</value>
</data>
<data name="Region" xml:space="preserve">
<value>المنطقة</value>
</data>
<data name="UpdateWeakMasterPasswordWarning" xml:space="preserve">
<value>كلمة المرور الرئيسية الخاصة بك لا تفي بواحدة أو أكثر من سياسات مؤسستك. من أجل الوصول إلى الخزنة، يجب عليك تحديث كلمة المرور الرئيسية الآن. سيتم تسجيل خروجك من الجلسة الحالية، مما يتطلب منك تسجيل الدخول مرة أخرى. وقد تظل الجلسات النشطة على أجهزة أخرى نشطة لمدة تصل إلى ساعة واحدة.</value>
</data>
<data name="CurrentMasterPassword" xml:space="preserve">
<value>كلمة المرور الرئيسية الحالية</value>
</data>
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
<value>Master password re-prompt help</value>
</data>
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
<value>Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve.</value>
</data>
<data name="InvalidAPIKey" xml:space="preserve">
<value>Invalid API key</value>
</data>
<data name="InvalidAPIToken" xml:space="preserve">
<value>Invalid API token</value>
</data> </data>
</root> </root>

View File

@@ -1752,11 +1752,11 @@ Skan prosesi avtomatik baş tutacaq.</value>
<value>Həqiqətən tullantı qutusuna göndərmək istəyirsiniz?</value> <value>Həqiqətən tullantı qutusuna göndərmək istəyirsiniz?</value>
<comment>Confirmation alert message when soft-deleting a cipher.</comment> <comment>Confirmation alert message when soft-deleting a cipher.</comment>
</data> </data>
<data name="AccountBiometricInvalidated" xml:space="preserve"> <data name="BiometricInvalidated" xml:space="preserve">
<value>Ana parolun təsdiqlənməsi gözlənildiyi üçün bu hesab üzrə biometrik kilid açma sıradan çıxarıldı.</value> <value>Ana parolun təsdiqlənməsi gözlənildiyi üçün biometrik kilid açma sıradan çıxarıldı.</value>
</data> </data>
<data name="AccountBiometricInvalidatedExtension" xml:space="preserve"> <data name="BiometricInvalidatedExtension" xml:space="preserve">
<value>Ana parolun təsdiqlənməsi gözlənildiyi üçün bu hesab üzrə avto-doldurma biometrik kilid açma sıradan çıxarıldı.</value> <value>Ana parolun təsdiqlənməsi gözlənildiyi üçün avto-doldurma üzrə biometrik kilid açma sıradan çıxarıldı.</value>
</data> </data>
<data name="EnableSyncOnRefresh" xml:space="preserve"> <data name="EnableSyncOnRefresh" xml:space="preserve">
<value>Təzələmə əsnasında eyniləşdirməni fəallaşdır</value> <value>Təzələmə əsnasında eyniləşdirməni fəallaşdır</value>
@@ -2142,12 +2142,6 @@ Skan prosesi avtomatik baş tutacaq.</value>
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve"> <data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
<value>Təşkilatınızın siyasətləri, anbarınızın vaxt bitişinə təsir edir. Anbar vaxt bitişi üçün icazə verilən maksimum vaxt {0} saat {1} dəqiqədir</value> <value>Təşkilatınızın siyasətləri, anbarınızın vaxt bitişinə təsir edir. Anbar vaxt bitişi üçün icazə verilən maksimum vaxt {0} saat {1} dəqiqədir</value>
</data> </data>
<data name="VaultTimeoutPolicyWithActionInEffect" xml:space="preserve">
<value>Təşkilatınızın siyasətləri, anbarınızın vaxt bitişinə təsir edir. Anbar vaxt bitişi üçün icazə verilən maksimum vaxt {0} saat {1} dəqiqədir. Anbar vaxt bitişi əməliyyatı {2} olaraq tənzimləndi.</value>
</data>
<data name="VaultTimeoutActionPolicyInEffect" xml:space="preserve">
<value>Təşkilatınızın siyasətləri, anbar vaxt bitişi əməliyyatınızı {0} olaraq tənzimlədi.</value>
</data>
<data name="VaultTimeoutToLarge" xml:space="preserve"> <data name="VaultTimeoutToLarge" xml:space="preserve">
<value>Anbar vaxt bitişi, təşkilatınız tərəfindən tənzimlənən məhdudiyyətləri aşır.</value> <value>Anbar vaxt bitişi, təşkilatınız tərəfindən tənzimlənən məhdudiyyətləri aşır.</value>
</data> </data>
@@ -2494,8 +2488,8 @@ Bu hesaba keçmək istəyirsiniz?</value>
<data name="GetMasterPasswordwordHint" xml:space="preserve"> <data name="GetMasterPasswordwordHint" xml:space="preserve">
<value>Ana parol üçün məsləhət alın</value> <value>Ana parol üçün məsləhət alın</value>
</data> </data>
<data name="LoggingInAsXOnY" xml:space="preserve"> <data name="LoggingInAsX" xml:space="preserve">
<value>{1} üzərində {0} olaraq giriş edildi</value> <value>{0} olaraq giriş edilir</value>
</data> </data>
<data name="NotYou" xml:space="preserve"> <data name="NotYou" xml:space="preserve">
<value>Siz deyilsiniz?</value> <value>Siz deyilsiniz?</value>
@@ -2608,37 +2602,4 @@ Bu hesaba keçmək istəyirsiniz?</value>
<data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve"> <data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve">
<value>Axtarışa uyğun gələn heç bir element yoxdur</value> <value>Axtarışa uyğun gələn heç bir element yoxdur</value>
</data> </data>
<data name="US" xml:space="preserve">
<value>ABŞ</value>
</data>
<data name="EU" xml:space="preserve">
<value>AB</value>
</data>
<data name="SelfHosted" xml:space="preserve">
<value>Öz-özünə sahiblik edən</value>
</data>
<data name="DataRegion" xml:space="preserve">
<value>Data bölgəsi</value>
</data>
<data name="Region" xml:space="preserve">
<value>Bölgə</value>
</data>
<data name="UpdateWeakMasterPasswordWarning" xml:space="preserve">
<value>Ana parolunuz təşkilatınızdakı siyasətlərdən birinə və ya bir neçəsinə uyğun gəlmir. Anbara müraciət üçün ana parolunuzu indi güncəlləməlisiniz. Davam etsəniz, hazırkı seansdan çıxış etmiş və təkrar giriş etməli olacaqsınız. Digər cihazlardakı aktiv seanslar bir saata qədər aktiv qalmağa davam edə bilər.</value>
</data>
<data name="CurrentMasterPassword" xml:space="preserve">
<value>Hazırkı ana parol</value>
</data>
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
<value>Ana parolu təkrar soruş köməyi</value>
</data>
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
<value>Yetərsiz yaddaşa görə kilid açma uğursuz ola bilər. Həll etmək üçün KDF yaddaş tənzimləmələrinizi azaldın</value>
</data>
<data name="InvalidAPIKey" xml:space="preserve">
<value>Yararsız API açarı</value>
</data>
<data name="InvalidAPIToken" xml:space="preserve">
<value>Yararsız API tokeni</value>
</data>
</root> </root>

View File

@@ -1751,11 +1751,11 @@
<value>Вы сапраўды хочаце адправіць элемент у сметніцу?</value> <value>Вы сапраўды хочаце адправіць элемент у сметніцу?</value>
<comment>Confirmation alert message when soft-deleting a cipher.</comment> <comment>Confirmation alert message when soft-deleting a cipher.</comment>
</data> </data>
<data name="AccountBiometricInvalidated" xml:space="preserve"> <data name="BiometricInvalidated" xml:space="preserve">
<value>Разблакіроўка па біяметрыі для гэтага ўліковага запісу адключана. Чакаецца праверка асноўным паролем.</value> <value>Разблакіраванне па біяметрыі адключана. Чакаецца праверка асноўным паролем.</value>
</data> </data>
<data name="AccountBiometricInvalidatedExtension" xml:space="preserve"> <data name="BiometricInvalidatedExtension" xml:space="preserve">
<value>Аўтазапаўненне з дапамогай разблакіроўкі па біяметрыі для гэтага ўліковага запісу адключана. Чакаецца праверка асноўным паролем.</value> <value>Разблакіраванне па біяметрыі для аўтазапаўнення адключана. Чакаецца праверка асноўным паролем.</value>
</data> </data>
<data name="EnableSyncOnRefresh" xml:space="preserve"> <data name="EnableSyncOnRefresh" xml:space="preserve">
<value>Уключыць сінхранізацыю пры абнаўленні</value> <value>Уключыць сінхранізацыю пры абнаўленні</value>
@@ -2142,12 +2142,6 @@
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve"> <data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
<value>Палітыка вашай арганізацыі ўплывае на час чакання сховішча. Максімальны дазволены час чакання сховішча складае {0} гадз. і {1} хв.</value> <value>Палітыка вашай арганізацыі ўплывае на час чакання сховішча. Максімальны дазволены час чакання сховішча складае {0} гадз. і {1} хв.</value>
</data> </data>
<data name="VaultTimeoutPolicyWithActionInEffect" xml:space="preserve">
<value>Палітыка вашай арганізацыі ўплывае на час чакання сховішча. Максімальны дазволены час чакання сховішча складае гадзін: {0}; хвілін: {1}. Для часу чакання вашага сховішча прызначана дзеянне {2}.</value>
</data>
<data name="VaultTimeoutActionPolicyInEffect" xml:space="preserve">
<value>Палітыкай вашай арганізацыі прызначана дзеянне {0} для часу чакання вашага сховішча.</value>
</data>
<data name="VaultTimeoutToLarge" xml:space="preserve"> <data name="VaultTimeoutToLarge" xml:space="preserve">
<value>Час чакання вашага сховішча перавышае дазволеныя абмежаванні, якія прызначыла ваша арганізацыя.</value> <value>Час чакання вашага сховішча перавышае дазволеныя абмежаванні, якія прызначыла ваша арганізацыя.</value>
</data> </data>
@@ -2495,8 +2489,8 @@
<data name="GetMasterPasswordwordHint" xml:space="preserve"> <data name="GetMasterPasswordwordHint" xml:space="preserve">
<value>Атрымаць падказку да асноўнага пароля</value> <value>Атрымаць падказку да асноўнага пароля</value>
</data> </data>
<data name="LoggingInAsXOnY" xml:space="preserve"> <data name="LoggingInAsX" xml:space="preserve">
<value>Вы ўваходзіце як {0} у {1}</value> <value>Увайсці як {0}</value>
</data> </data>
<data name="NotYou" xml:space="preserve"> <data name="NotYou" xml:space="preserve">
<value>Не вы?</value> <value>Не вы?</value>
@@ -2598,48 +2592,15 @@
<value>Неабходны ідэнтыфікатар SSO арганізацыі.</value> <value>Неабходны ідэнтыфікатар SSO арганізацыі.</value>
</data> </data>
<data name="AddTheKeyToAnExistingOrNewItem" xml:space="preserve"> <data name="AddTheKeyToAnExistingOrNewItem" xml:space="preserve">
<value>Дадаць ключ да існуючага або новага элемента</value> <value>Add the key to an existing or new item</value>
</data> </data>
<data name="ThereAreNoItemsInYourVaultThatMatchX" xml:space="preserve"> <data name="ThereAreNoItemsInYourVaultThatMatchX" xml:space="preserve">
<value>У вашым сховішчы адсутнічаюць элементы, якія адпавядаюць "{0}"</value> <value>There are no items in your vault that match "{0}"</value>
</data> </data>
<data name="SearchForAnItemOrAddANewItem" xml:space="preserve"> <data name="SearchForAnItemOrAddANewItem" xml:space="preserve">
<value>Пошук або дабаўленне новага элемента</value> <value>Search for an item or add a new item</value>
</data> </data>
<data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve"> <data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve">
<value>Адсутнічаюць элементы, якія адпавядаюць пошуку</value> <value>There are no items that match the search</value>
</data>
<data name="US" xml:space="preserve">
<value>ЗША</value>
</data>
<data name="EU" xml:space="preserve">
<value>ЕС</value>
</data>
<data name="SelfHosted" xml:space="preserve">
<value>Уласнае размяшчэнне</value>
</data>
<data name="DataRegion" xml:space="preserve">
<value>Рэгіён даных</value>
</data>
<data name="Region" xml:space="preserve">
<value>Рэгіён</value>
</data>
<data name="UpdateWeakMasterPasswordWarning" xml:space="preserve">
<value>Ваш асноўны пароль не адпавядае адной або некалькім палітыкам арганізацыі. Для атрымання доступу да сховішча, вы павінны абнавіць яго. Працягваючы, вы выйдзіце з бягучага сеанса і вам неабходна будзе ўвайсці паўторна. Актыўныя сеансы на іншых прыладах могуць заставацца актыўнымі на працягу адной гадзіны.</value>
</data>
<data name="CurrentMasterPassword" xml:space="preserve">
<value>Бягучы асноўны пароль</value>
</data>
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
<value>Дапамога з паўторным запытам асноўнага пароля</value>
</data>
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
<value>Па прычыне недахопу памяці можа адбыцца збой разблакіроўкі. Паменшыце налады памяці KDF, каб вырашыць гэту праблему</value>
</data>
<data name="InvalidAPIKey" xml:space="preserve">
<value>Памылковы ключ API</value>
</data>
<data name="InvalidAPIToken" xml:space="preserve">
<value>Памылковы токен API</value>
</data> </data>
</root> </root>

View File

@@ -1752,11 +1752,11 @@
<value>Сигурни ли сте, че искате да преместите записа в кошчето?</value> <value>Сигурни ли сте, че искате да преместите записа в кошчето?</value>
<comment>Confirmation alert message when soft-deleting a cipher.</comment> <comment>Confirmation alert message when soft-deleting a cipher.</comment>
</data> </data>
<data name="AccountBiometricInvalidated" xml:space="preserve"> <data name="BiometricInvalidated" xml:space="preserve">
<value>Потвърждаването с биометрични данни е изключено за тази регистрация. Въведете главната парола, за да го включите.</value> <value>Засечена е промяна в биометричните данни. Впишете се с главната парола, за да я включите отново биометричната идентификация.</value>
</data> </data>
<data name="AccountBiometricInvalidatedExtension" xml:space="preserve"> <data name="BiometricInvalidatedExtension" xml:space="preserve">
<value>Автоматичното попълване за потвърждаване с биометрични данни е изключено за тази регистрация. Въведете главната парола, за да го включите.</value> <value>Потвърждаването с биометрични данни е изключено. Въведете главната парола, за да го включите.</value>
</data> </data>
<data name="EnableSyncOnRefresh" xml:space="preserve"> <data name="EnableSyncOnRefresh" xml:space="preserve">
<value>Синхронизация при опресняване</value> <value>Синхронизация при опресняване</value>
@@ -2142,12 +2142,6 @@
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve"> <data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
<value>Настройките на организацията Ви влияят върху времето за достъп до трезора Ви. Максималното разрешено време за достъп е {0} час(а) и {1} минути</value> <value>Настройките на организацията Ви влияят върху времето за достъп до трезора Ви. Максималното разрешено време за достъп е {0} час(а) и {1} минути</value>
</data> </data>
<data name="VaultTimeoutPolicyWithActionInEffect" xml:space="preserve">
<value>Настройките на организацията Ви влияят върху времето за достъп до трезора Ви. Максималното разрешено време за достъп е {0} час(а) и {1} минута/и. Зададеното действие при изтичане на това време е {2}.</value>
</data>
<data name="VaultTimeoutActionPolicyInEffect" xml:space="preserve">
<value>Настройките на организацията Ви задават следното действие при изтичане на времето за достъп до трезора: {0}.</value>
</data>
<data name="VaultTimeoutToLarge" xml:space="preserve"> <data name="VaultTimeoutToLarge" xml:space="preserve">
<value>Времето за достъп до трезора Ви превишава ограничението, определено от организацията Ви.</value> <value>Времето за достъп до трезора Ви превишава ограничението, определено от организацията Ви.</value>
</data> </data>
@@ -2495,8 +2489,8 @@ select Add TOTP to store the key safely</value>
<data name="GetMasterPasswordwordHint" xml:space="preserve"> <data name="GetMasterPasswordwordHint" xml:space="preserve">
<value>Получете подсказване за главната парола</value> <value>Получете подсказване за главната парола</value>
</data> </data>
<data name="LoggingInAsXOnY" xml:space="preserve"> <data name="LoggingInAsX" xml:space="preserve">
<value>Влизате като {0} в {1}</value> <value>Вписване като {0}</value>
</data> </data>
<data name="NotYou" xml:space="preserve"> <data name="NotYou" xml:space="preserve">
<value>Това не сте Вие?</value> <value>Това не сте Вие?</value>
@@ -2609,37 +2603,4 @@ select Add TOTP to store the key safely</value>
<data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve"> <data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve">
<value>Няма елементи, които отговарят на търсенето</value> <value>Няма елементи, които отговарят на търсенето</value>
</data> </data>
<data name="US" xml:space="preserve">
<value>САЩ</value>
</data>
<data name="EU" xml:space="preserve">
<value>ЕС</value>
</data>
<data name="SelfHosted" xml:space="preserve">
<value>Собствен хостинг</value>
</data>
<data name="DataRegion" xml:space="preserve">
<value>Регион на данните</value>
</data>
<data name="Region" xml:space="preserve">
<value>Регион</value>
</data>
<data name="UpdateWeakMasterPasswordWarning" xml:space="preserve">
<value>Вашата главна парола не отговаря на една или повече политики на организацията Ви. За да получите достъп до трезора, трябва да промените главната си парола сега. Това означава, че ще бъдете отписан(а) от текущата си сесия и ще трябва да се впишете отново. Активните сесии на други устройства може да продължат да бъдат активни още един час.</value>
</data>
<data name="CurrentMasterPassword" xml:space="preserve">
<value>Текуща главна парола</value>
</data>
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
<value>Помощ за повторното запитване за главната парола</value>
</data>
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
<value>Отключването може да бъде неуспешно заради недостатъчно памет. Намалете настройките на паметта за KDF, за да разрешите проблема.</value>
</data>
<data name="InvalidAPIKey" xml:space="preserve">
<value>Неправилен ключ за ППИ</value>
</data>
<data name="InvalidAPIToken" xml:space="preserve">
<value>Неправилен идентификатор за ППИ</value>
</data>
</root> </root>

View File

@@ -160,7 +160,7 @@
<comment>The button text that allows a user to copy the login's username to their clipboard.</comment> <comment>The button text that allows a user to copy the login's username to their clipboard.</comment>
</data> </data>
<data name="Credits" xml:space="preserve"> <data name="Credits" xml:space="preserve">
<value>কৃতিত্ব</value> <value>Credits</value>
<comment>Title for page that we use to give credit to resources that we use.</comment> <comment>Title for page that we use to give credit to resources that we use.</comment>
</data> </data>
<data name="Delete" xml:space="preserve"> <data name="Delete" xml:space="preserve">
@@ -168,11 +168,11 @@
<comment>Delete an entity (verb).</comment> <comment>Delete an entity (verb).</comment>
</data> </data>
<data name="Deleting" xml:space="preserve"> <data name="Deleting" xml:space="preserve">
<value>মোছা হচ্ছে...</value> <value>Deleting...</value>
<comment>Message shown when interacting with the server</comment> <comment>Message shown when interacting with the server</comment>
</data> </data>
<data name="DoYouReallyWantToDelete" xml:space="preserve"> <data name="DoYouReallyWantToDelete" xml:space="preserve">
<value>আপনি কি সত্যিই মুছে ফেলতে চান? এটিকে পূর্বের মত করা যাবে না।.</value> <value>Do you really want to delete? This cannot be undone.</value>
<comment>Confirmation alert message when deleteing something.</comment> <comment>Confirmation alert message when deleteing something.</comment>
</data> </data>
<data name="Edit" xml:space="preserve"> <data name="Edit" xml:space="preserve">
@@ -190,16 +190,16 @@
<comment>Full label for a email address.</comment> <comment>Full label for a email address.</comment>
</data> </data>
<data name="EmailUs" xml:space="preserve"> <data name="EmailUs" xml:space="preserve">
<value>আমাদের ইমেইল করুন</value> <value>Email us</value>
</data> </data>
<data name="EmailUsDescription" xml:space="preserve"> <data name="EmailUsDescription" xml:space="preserve">
<value>সাহায্য পেতে বা মতামত দিতে সরাসরি আমাদের ইমেল করুন।</value> <value>Email us directly to get help or leave feedback.</value>
</data> </data>
<data name="EnterPIN" xml:space="preserve"> <data name="EnterPIN" xml:space="preserve">
<value>আপনার পিন কোডটি প্রবেশ করান</value> <value>Enter your PIN code.</value>
</data> </data>
<data name="Favorites" xml:space="preserve"> <data name="Favorites" xml:space="preserve">
<value>পছন্দসমূহ</value> <value>Favorites</value>
<comment>Title for your favorite items in the vault.</comment> <comment>Title for your favorite items in the vault.</comment>
</data> </data>
<data name="FileBugReport" xml:space="preserve"> <data name="FileBugReport" xml:space="preserve">
@@ -216,7 +216,7 @@
<comment>Label for a folder.</comment> <comment>Label for a folder.</comment>
</data> </data>
<data name="FolderCreated" xml:space="preserve"> <data name="FolderCreated" xml:space="preserve">
<value>নতুন ফোল্ডার তৈরি হয়েছে।</value> <value>New folder created.</value>
</data> </data>
<data name="FolderDeleted" xml:space="preserve"> <data name="FolderDeleted" xml:space="preserve">
<value>Folder deleted.</value> <value>Folder deleted.</value>
@@ -276,7 +276,7 @@
<value>আপনি লগ আউট করতে চান?</value> <value>আপনি লগ আউট করতে চান?</value>
</data> </data>
<data name="RemoveAccount" xml:space="preserve"> <data name="RemoveAccount" xml:space="preserve">
<value>অ্যাকাউন্ট অপসারণ করুন</value> <value>Remove account</value>
</data> </data>
<data name="RemoveAccountConfirmation" xml:space="preserve"> <data name="RemoveAccountConfirmation" xml:space="preserve">
<value>Are you sure you want to remove this account?</value> <value>Are you sure you want to remove this account?</value>
@@ -315,30 +315,30 @@
<comment>Label for notes.</comment> <comment>Label for notes.</comment>
</data> </data>
<data name="Ok" xml:space="preserve"> <data name="Ok" xml:space="preserve">
<value>ঠিক আছে</value> <value>Ok</value>
<comment>Acknowledgement.</comment> <comment>Acknowledgement.</comment>
</data> </data>
<data name="Password" xml:space="preserve"> <data name="Password" xml:space="preserve">
<value>পাসওয়ার্ড</value> <value>Password</value>
<comment>Label for a password.</comment> <comment>Label for a password.</comment>
</data> </data>
<data name="Save" xml:space="preserve"> <data name="Save" xml:space="preserve">
<value>সংরক্ষণ করুন</value> <value>Save</value>
<comment>Button text for a save operation (verb).</comment> <comment>Button text for a save operation (verb).</comment>
</data> </data>
<data name="Move" xml:space="preserve"> <data name="Move" xml:space="preserve">
<value>স্থানান্তর করুন</value> <value>Move</value>
</data> </data>
<data name="Saving" xml:space="preserve"> <data name="Saving" xml:space="preserve">
<value>Saving...</value> <value>Saving...</value>
<comment>Message shown when interacting with the server</comment> <comment>Message shown when interacting with the server</comment>
</data> </data>
<data name="Settings" xml:space="preserve"> <data name="Settings" xml:space="preserve">
<value>সেটিংস</value> <value>Settings</value>
<comment>The title for the settings page.</comment> <comment>The title for the settings page.</comment>
</data> </data>
<data name="Show" xml:space="preserve"> <data name="Show" xml:space="preserve">
<value>দেখান</value> <value>Show</value>
<comment>Reveal a hidden value (password).</comment> <comment>Reveal a hidden value (password).</comment>
</data> </data>
<data name="ItemDeleted" xml:space="preserve"> <data name="ItemDeleted" xml:space="preserve">
@@ -346,17 +346,17 @@
<comment>Confirmation message after successfully deleting a login.</comment> <comment>Confirmation message after successfully deleting a login.</comment>
</data> </data>
<data name="Submit" xml:space="preserve"> <data name="Submit" xml:space="preserve">
<value>জমা দিন</value> <value>Submit</value>
</data> </data>
<data name="Sync" xml:space="preserve"> <data name="Sync" xml:space="preserve">
<value>Sync</value> <value>Sync</value>
<comment>The title for the sync page.</comment> <comment>The title for the sync page.</comment>
</data> </data>
<data name="ThankYou" xml:space="preserve"> <data name="ThankYou" xml:space="preserve">
<value>ধন্যবাদ</value> <value>Thank you</value>
</data> </data>
<data name="Tools" xml:space="preserve"> <data name="Tools" xml:space="preserve">
<value>টুলস</value> <value>Tools</value>
<comment>The title for the tools page.</comment> <comment>The title for the tools page.</comment>
</data> </data>
<data name="URI" xml:space="preserve"> <data name="URI" xml:space="preserve">
@@ -379,7 +379,7 @@
<comment>Confirmation message after successfully copying a value to the clipboard.</comment> <comment>Confirmation message after successfully copying a value to the clipboard.</comment>
</data> </data>
<data name="VerifyFingerprint" xml:space="preserve"> <data name="VerifyFingerprint" xml:space="preserve">
<value>আঙ্গুলের ছাপ যাচাই করুন</value> <value>Verify fingerprint</value>
</data> </data>
<data name="VerifyMasterPassword" xml:space="preserve"> <data name="VerifyMasterPassword" xml:space="preserve">
<value>প্রধান পাসওয়ার্ড যাচাইকরণ</value> <value>প্রধান পাসওয়ার্ড যাচাইকরণ</value>
@@ -388,32 +388,32 @@
<value>Verify PIN</value> <value>Verify PIN</value>
</data> </data>
<data name="Version" xml:space="preserve"> <data name="Version" xml:space="preserve">
<value>সংস্করণ</value> <value>Version</value>
</data> </data>
<data name="View" xml:space="preserve"> <data name="View" xml:space="preserve">
<value>View</value> <value>View</value>
</data> </data>
<data name="VisitOurWebsite" xml:space="preserve"> <data name="VisitOurWebsite" xml:space="preserve">
<value>আমাদের ওয়েবসাইটে ঢু মেরে আসুন</value> <value>Visit our website</value>
</data> </data>
<data name="VisitOurWebsiteDescription" xml:space="preserve"> <data name="VisitOurWebsiteDescription" xml:space="preserve">
<value>সাহায্য পেতে, খবর পেতে, আমাদের ইমেল করতে এবং/অথবা বিটওয়ার্ডেন কীভাবে ব্যবহার করবেন সে সম্পর্কে আরও জানতে আমাদের ওয়েবসাইট দেখুন।</value> <value>Visit our website to get help, news, email us, and/or learn more about how to use Bitwarden.</value>
</data> </data>
<data name="Website" xml:space="preserve"> <data name="Website" xml:space="preserve">
<value>ওয়েবসাইট</value> <value>Website</value>
<comment>Label for a website.</comment> <comment>Label for a website.</comment>
</data> </data>
<data name="Yes" xml:space="preserve"> <data name="Yes" xml:space="preserve">
<value>হ্যাঁ</value> <value>Yes</value>
</data> </data>
<data name="Account" xml:space="preserve"> <data name="Account" xml:space="preserve">
<value>অ্যাকাউন্ট</value> <value>Account</value>
</data> </data>
<data name="AccountCreated" xml:space="preserve"> <data name="AccountCreated" xml:space="preserve">
<value>আপনার নতুন অ্যাকাউন্ট তৈরি করা হয়েছে! আপনি এখন লগইন করতে পারবেন।</value> <value>Your new account has been created! You may now log in.</value>
</data> </data>
<data name="AddAnItem" xml:space="preserve"> <data name="AddAnItem" xml:space="preserve">
<value>একটি আইটেম যোগ করুন</value> <value>Add an Item</value>
</data> </data>
<data name="AppExtension" xml:space="preserve"> <data name="AppExtension" xml:space="preserve">
<value>App extension</value> <value>App extension</value>
@@ -425,7 +425,7 @@
<value>Auto-fill service</value> <value>Auto-fill service</value>
</data> </data>
<data name="AvoidAmbiguousCharacters" xml:space="preserve"> <data name="AvoidAmbiguousCharacters" xml:space="preserve">
<value>অস্পষ্ট বর্ণগুলি বাদ দিয়ে যান</value> <value>Avoid ambiguous characters</value>
</data> </data>
<data name="BitwardenAppExtension" xml:space="preserve"> <data name="BitwardenAppExtension" xml:space="preserve">
<value>Bitwarden app extension</value> <value>Bitwarden app extension</value>
@@ -440,13 +440,13 @@
<value>Bitwarden Auto-fill Service</value> <value>Bitwarden Auto-fill Service</value>
</data> </data>
<data name="BitwardenAutofillAccessibilityServiceDescription" xml:space="preserve"> <data name="BitwardenAutofillAccessibilityServiceDescription" xml:space="preserve">
<value>আপনার লগইনগুলি স্বয়ংক্রিয়ভাবে পূরণ করতে বিটওয়ার্ডেন অ্যাক্সেসিবিলিটি পরিষেবা ব্যবহার করুন৷</value> <value>Use the Bitwarden accessibility service to auto-fill your logins.</value>
</data> </data>
<data name="ChangeEmail" xml:space="preserve"> <data name="ChangeEmail" xml:space="preserve">
<value>ইমেইল পরিবর্তন করুন</value> <value>Change email</value>
</data> </data>
<data name="ChangeEmailConfirmation" xml:space="preserve"> <data name="ChangeEmailConfirmation" xml:space="preserve">
<value>আপনি bitwarden.com ওয়েব ভল্টে আপনার ইমেল ঠিকানা পরিবর্তন করতে পারেন। আপনি কি এখনই ওয়েবসাইট ভিজিট করতে চান?</value> <value>You can change your email address on the bitwarden.com web vault. Do you want to visit the website now?</value>
</data> </data>
<data name="ChangeMasterPassword" xml:space="preserve"> <data name="ChangeMasterPassword" xml:space="preserve">
<value>প্রধান পাসওয়ার্ড পরিবর্তন</value> <value>প্রধান পাসওয়ার্ড পরিবর্তন</value>
@@ -455,7 +455,7 @@
<value>আপনি bitwarden.com ওয়েব ভল্ট থেকে প্রধান পাসওয়ার্ডটি পরিবর্তন করতে পারেন। আপনি কি এখনই ওয়েবসাইটটি দেখতে চান?</value> <value>আপনি bitwarden.com ওয়েব ভল্ট থেকে প্রধান পাসওয়ার্ডটি পরিবর্তন করতে পারেন। আপনি কি এখনই ওয়েবসাইটটি দেখতে চান?</value>
</data> </data>
<data name="Close" xml:space="preserve"> <data name="Close" xml:space="preserve">
<value>বন্ধ করুন</value> <value>Close</value>
</data> </data>
<data name="Continue" xml:space="preserve"> <data name="Continue" xml:space="preserve">
<value>অবিরত</value> <value>অবিরত</value>
@@ -480,7 +480,7 @@
<value>Reactivate app extension</value> <value>Reactivate app extension</value>
</data> </data>
<data name="ExtensionAlmostDone" xml:space="preserve"> <data name="ExtensionAlmostDone" xml:space="preserve">
<value>প্রায় হয়ে গেছে!</value> <value>Almost done!</value>
</data> </data>
<data name="ExtensionEnable" xml:space="preserve"> <data name="ExtensionEnable" xml:space="preserve">
<value>Activate app extension</value> <value>Activate app extension</value>
@@ -493,7 +493,7 @@
<value>Get instant access to your passwords!</value> <value>Get instant access to your passwords!</value>
</data> </data>
<data name="ExtensionReady" xml:space="preserve"> <data name="ExtensionReady" xml:space="preserve">
<value>আপনি লগইন করার জন্য একদম তৈরি!</value> <value>You're ready to log in!</value>
</data> </data>
<data name="ExtensionSetup" xml:space="preserve"> <data name="ExtensionSetup" xml:space="preserve">
<value>Your logins are now easily accessible from Safari, Chrome, and other supported apps.</value> <value>Your logins are now easily accessible from Safari, Chrome, and other supported apps.</value>
@@ -511,7 +511,7 @@
<value>প্রিয়</value> <value>প্রিয়</value>
</data> </data>
<data name="Fingerprint" xml:space="preserve"> <data name="Fingerprint" xml:space="preserve">
<value>আঙুলের ছাপ</value> <value>Fingerprint</value>
</data> </data>
<data name="GeneratePassword" xml:space="preserve"> <data name="GeneratePassword" xml:space="preserve">
<value>পাসওয়ার্ড তৈরি করুন</value> <value>পাসওয়ার্ড তৈরি করুন</value>
@@ -584,7 +584,7 @@
<value>যদি আপনি আপনার পাসওয়ার্ড ভুলে যান তাহলে একটি প্রধান পাসওয়ার্ডের ইঙ্গিতটি আপনাকে মনে করাতে সাহায্য করতে পারে।</value> <value>যদি আপনি আপনার পাসওয়ার্ড ভুলে যান তাহলে একটি প্রধান পাসওয়ার্ডের ইঙ্গিতটি আপনাকে মনে করাতে সাহায্য করতে পারে।</value>
</data> </data>
<data name="MasterPasswordLengthValMessageX" xml:space="preserve"> <data name="MasterPasswordLengthValMessageX" xml:space="preserve">
<value>মাস্টার পাসওয়ার্ড কমপক্ষে {0} অক্ষরের হতে হবে।</value> <value>Master password must be at least {0} characters long.</value>
</data> </data>
<data name="MinNumbers" xml:space="preserve"> <data name="MinNumbers" xml:space="preserve">
<value>সর্বনিম্ন সংখ্যা</value> <value>সর্বনিম্ন সংখ্যা</value>
@@ -595,7 +595,7 @@
<comment>Minimum special characters for password generator settings</comment> <comment>Minimum special characters for password generator settings</comment>
</data> </data>
<data name="MoreSettings" xml:space="preserve"> <data name="MoreSettings" xml:space="preserve">
<value>আরও সেটিংস</value> <value>More settings</value>
</data> </data>
<data name="MustLogInMainApp" xml:space="preserve"> <data name="MustLogInMainApp" xml:space="preserve">
<value>You must log into the main Bitwarden app before you can use the extension.</value> <value>You must log into the main Bitwarden app before you can use the extension.</value>
@@ -651,7 +651,7 @@
<comment>Push notifications for apple products</comment> <comment>Push notifications for apple products</comment>
</data> </data>
<data name="RateTheApp" xml:space="preserve"> <data name="RateTheApp" xml:space="preserve">
<value>অ্যাপ্লিকেশনটকে রেট দিন</value> <value>Rate the app</value>
</data> </data>
<data name="RateTheAppDescription" xml:space="preserve"> <data name="RateTheAppDescription" xml:space="preserve">
<value>দয়া করে একটি ভাল পর্যালোচনার মাধ্যমে সাহায্য করতে আমাদের বিবেচনা করুন!</value> <value>দয়া করে একটি ভাল পর্যালোচনার মাধ্যমে সাহায্য করতে আমাদের বিবেচনা করুন!</value>
@@ -684,7 +684,7 @@
<value>Item saved</value> <value>Item saved</value>
</data> </data>
<data name="Submitting" xml:space="preserve"> <data name="Submitting" xml:space="preserve">
<value>জমা দেওয়া হচ্ছে...</value> <value>Submitting...</value>
<comment>Message shown when interacting with the server</comment> <comment>Message shown when interacting with the server</comment>
</data> </data>
<data name="Syncing" xml:space="preserve"> <data name="Syncing" xml:space="preserve">
@@ -701,7 +701,7 @@
<value>Sync vault now</value> <value>Sync vault now</value>
</data> </data>
<data name="TouchID" xml:space="preserve"> <data name="TouchID" xml:space="preserve">
<value>টাচ আইডি</value> <value>Touch ID</value>
<comment>What Apple calls their fingerprint reader.</comment> <comment>What Apple calls their fingerprint reader.</comment>
</data> </data>
<data name="TwoStepLogin" xml:space="preserve"> <data name="TwoStepLogin" xml:space="preserve">
@@ -717,17 +717,17 @@
<value>Unlock with PIN code</value> <value>Unlock with PIN code</value>
</data> </data>
<data name="Validating" xml:space="preserve"> <data name="Validating" xml:space="preserve">
<value>কার্যকর করা হচ্ছে</value> <value>Validating</value>
<comment>Message shown when interacting with the server</comment> <comment>Message shown when interacting with the server</comment>
</data> </data>
<data name="VerificationCode" xml:space="preserve"> <data name="VerificationCode" xml:space="preserve">
<value>যাচাইকরণ কোড</value> <value>Verification code</value>
</data> </data>
<data name="ViewItem" xml:space="preserve"> <data name="ViewItem" xml:space="preserve">
<value>View item</value> <value>View item</value>
</data> </data>
<data name="WebVault" xml:space="preserve"> <data name="WebVault" xml:space="preserve">
<value>বিটওয়ার্ডেন ওয়েব ভল্ট</value> <value>Bitwarden web vault</value>
</data> </data>
<data name="Lost2FAApp" xml:space="preserve"> <data name="Lost2FAApp" xml:space="preserve">
<value>Lost authenticator app?</value> <value>Lost authenticator app?</value>
@@ -740,10 +740,10 @@
<value>Extension activated!</value> <value>Extension activated!</value>
</data> </data>
<data name="Icons" xml:space="preserve"> <data name="Icons" xml:space="preserve">
<value>আইকন</value> <value>Icons</value>
</data> </data>
<data name="Translations" xml:space="preserve"> <data name="Translations" xml:space="preserve">
<value>অনুবাদসমূহ</value> <value>Translations</value>
</data> </data>
<data name="ItemsForUri" xml:space="preserve"> <data name="ItemsForUri" xml:space="preserve">
<value>Items for {0}</value> <value>Items for {0}</value>
@@ -769,19 +769,19 @@
<value>2. Switch on the toggle and press OK to accept.</value> <value>2. Switch on the toggle and press OK to accept.</value>
</data> </data>
<data name="Disabled" xml:space="preserve"> <data name="Disabled" xml:space="preserve">
<value>নিষ্ক্রিয়</value> <value>Disabled</value>
</data> </data>
<data name="Enabled" xml:space="preserve"> <data name="Enabled" xml:space="preserve">
<value>সক্রিয়</value> <value>Enabled</value>
</data> </data>
<data name="Off" xml:space="preserve"> <data name="Off" xml:space="preserve">
<value>বন্ধ</value> <value>Off</value>
</data> </data>
<data name="On" xml:space="preserve"> <data name="On" xml:space="preserve">
<value>চালু</value> <value>On</value>
</data> </data>
<data name="Status" xml:space="preserve"> <data name="Status" xml:space="preserve">
<value>অবস্থা</value> <value>Status</value>
</data> </data>
<data name="BitwardenAutofillServiceAlert2" xml:space="preserve"> <data name="BitwardenAutofillServiceAlert2" xml:space="preserve">
<value>The easiest way to add new logins to your vault is from the Bitwarden Auto-fill Service. Learn more about using the Bitwarden Auto-fill Service by navigating to the "Settings" screen.</value> <value>The easiest way to add new logins to your vault is from the Bitwarden Auto-fill Service. Learn more about using the Bitwarden Auto-fill Service by navigating to the "Settings" screen.</value>
@@ -876,7 +876,7 @@
<value>Unable to download file.</value> <value>Unable to download file.</value>
</data> </data>
<data name="UnableToOpenFile" xml:space="preserve"> <data name="UnableToOpenFile" xml:space="preserve">
<value>আপনার ডিভাইস এই ধরনের ফাইল খুলতে পারে না।</value> <value>Your device cannot open this type of file.</value>
</data> </data>
<data name="Downloading" xml:space="preserve"> <data name="Downloading" xml:space="preserve">
<value>ডাউনলোড হচ্ছে...</value> <value>ডাউনলোড হচ্ছে...</value>
@@ -904,13 +904,13 @@
Scanning will happen automatically.</value> Scanning will happen automatically.</value>
</data> </data>
<data name="ScanQrTitle" xml:space="preserve"> <data name="ScanQrTitle" xml:space="preserve">
<value>QR কোড স্ক্যান করুন</value> <value>Scan QR Code</value>
</data> </data>
<data name="Camera" xml:space="preserve"> <data name="Camera" xml:space="preserve">
<value>ক্যামেরা</value> <value>Camera</value>
</data> </data>
<data name="Photos" xml:space="preserve"> <data name="Photos" xml:space="preserve">
<value>ছবিসমূহ</value> <value>Photos</value>
</data> </data>
<data name="CopyTotp" xml:space="preserve"> <data name="CopyTotp" xml:space="preserve">
<value>Copy TOTP</value> <value>Copy TOTP</value>
@@ -934,7 +934,7 @@ Scanning will happen automatically.</value>
<value>Choose file</value> <value>Choose file</value>
</data> </data>
<data name="File" xml:space="preserve"> <data name="File" xml:space="preserve">
<value>ফাইল</value> <value>File</value>
</data> </data>
<data name="NoFileChosen" xml:space="preserve"> <data name="NoFileChosen" xml:space="preserve">
<value>No file chosen</value> <value>No file chosen</value>
@@ -1080,7 +1080,7 @@ Scanning will happen automatically.</value>
<value>নামের শেষাংশ</value> <value>নামের শেষাংশ</value>
</data> </data>
<data name="FullName" xml:space="preserve"> <data name="FullName" xml:space="preserve">
<value>পুরো নাম</value> <value>Full name</value>
</data> </data>
<data name="LicenseNumber" xml:space="preserve"> <data name="LicenseNumber" xml:space="preserve">
<value>লাইসেন্স নম্বর</value> <value>লাইসেন্স নম্বর</value>
@@ -1107,34 +1107,34 @@ Scanning will happen automatically.</value>
<value>Mx</value> <value>Mx</value>
</data> </data>
<data name="November" xml:space="preserve"> <data name="November" xml:space="preserve">
<value>নভেম্বর</value> <value>November</value>
</data> </data>
<data name="October" xml:space="preserve"> <data name="October" xml:space="preserve">
<value>অক্টোবর</value> <value>October</value>
</data> </data>
<data name="PassportNumber" xml:space="preserve"> <data name="PassportNumber" xml:space="preserve">
<value>পাসপোর্ট নম্বর</value> <value>Passport number</value>
</data> </data>
<data name="Phone" xml:space="preserve"> <data name="Phone" xml:space="preserve">
<value>ফোন</value> <value>Phone</value>
</data> </data>
<data name="September" xml:space="preserve"> <data name="September" xml:space="preserve">
<value>সেপ্টেম্বর</value> <value>September</value>
</data> </data>
<data name="SSN" xml:space="preserve"> <data name="SSN" xml:space="preserve">
<value>সামাজিক সুরক্ষা নম্বর</value> <value>Social Security number</value>
</data> </data>
<data name="StateProvince" xml:space="preserve"> <data name="StateProvince" xml:space="preserve">
<value>রাজ্য / প্রদেশ</value> <value>State / Province</value>
</data> </data>
<data name="Title" xml:space="preserve"> <data name="Title" xml:space="preserve">
<value>শিরোনাম</value> <value>Title</value>
</data> </data>
<data name="ZipPostalCode" xml:space="preserve"> <data name="ZipPostalCode" xml:space="preserve">
<value>জিপ / পোস্টকোড</value> <value>Zip / Postal code</value>
</data> </data>
<data name="Address" xml:space="preserve"> <data name="Address" xml:space="preserve">
<value>ঠিকানা</value> <value>Address</value>
</data> </data>
<data name="Expiration" xml:space="preserve"> <data name="Expiration" xml:space="preserve">
<value>Expiration</value> <value>Expiration</value>
@@ -1204,7 +1204,7 @@ Scanning will happen automatically.</value>
<value>Custom field name</value> <value>Custom field name</value>
</data> </data>
<data name="FieldTypeBoolean" xml:space="preserve"> <data name="FieldTypeBoolean" xml:space="preserve">
<value>বুলিয়ান</value> <value>Boolean</value>
</data> </data>
<data name="FieldTypeHidden" xml:space="preserve"> <data name="FieldTypeHidden" xml:space="preserve">
<value>Hidden</value> <value>Hidden</value>
@@ -1225,7 +1225,7 @@ Scanning will happen automatically.</value>
<value>Remove</value> <value>Remove</value>
</data> </data>
<data name="NewUri" xml:space="preserve"> <data name="NewUri" xml:space="preserve">
<value>নতুন URI</value> <value>New URI</value>
</data> </data>
<data name="URIPosition" xml:space="preserve"> <data name="URIPosition" xml:space="preserve">
<value>URI {0}</value> <value>URI {0}</value>
@@ -1272,7 +1272,7 @@ Scanning will happen automatically.</value>
<value>Hold your Yubikey near the top of the device.</value> <value>Hold your Yubikey near the top of the device.</value>
</data> </data>
<data name="TryAgain" xml:space="preserve"> <data name="TryAgain" xml:space="preserve">
<value>আবার চেষ্টা করুন</value> <value>Try again</value>
</data> </data>
<data name="YubiKeyInstructionIos" xml:space="preserve"> <data name="YubiKeyInstructionIos" xml:space="preserve">
<value>To continue, hold your YubiKey NEO against the back of the device.</value> <value>To continue, hold your YubiKey NEO against the back of the device.</value>
@@ -1440,16 +1440,16 @@ Scanning will happen automatically.</value>
<value>Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved.</value> <value>Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved.</value>
</data> </data>
<data name="NumberOfWords" xml:space="preserve"> <data name="NumberOfWords" xml:space="preserve">
<value>শব্দসংখ্যা</value> <value>Number of words</value>
</data> </data>
<data name="Passphrase" xml:space="preserve"> <data name="Passphrase" xml:space="preserve">
<value>গুপ্ত-বাক্যাংশ</value> <value>Passphrase</value>
</data> </data>
<data name="WordSeparator" xml:space="preserve"> <data name="WordSeparator" xml:space="preserve">
<value>শব্দ বিভাজক</value> <value>Word separator</value>
</data> </data>
<data name="Clear" xml:space="preserve"> <data name="Clear" xml:space="preserve">
<value>পরিষ্কার করুন</value> <value>Clear</value>
<comment>To clear something out. example: To clear browser history.</comment> <comment>To clear something out. example: To clear browser history.</comment>
</data> </data>
<data name="Generator" xml:space="preserve"> <data name="Generator" xml:space="preserve">
@@ -1474,42 +1474,42 @@ Scanning will happen automatically.</value>
<value>Export vault</value> <value>Export vault</value>
</data> </data>
<data name="LockNow" xml:space="preserve"> <data name="LockNow" xml:space="preserve">
<value>এখনই লক করুন</value> <value>Lock now</value>
</data> </data>
<data name="PIN" xml:space="preserve"> <data name="PIN" xml:space="preserve">
<value>পিন</value> <value>PIN</value>
</data> </data>
<data name="Unlock" xml:space="preserve"> <data name="Unlock" xml:space="preserve">
<value>আনলক করুন</value> <value>Unlock</value>
</data> </data>
<data name="UnlockVault" xml:space="preserve"> <data name="UnlockVault" xml:space="preserve">
<value>Unlock vault</value> <value>Unlock vault</value>
</data> </data>
<data name="ThirtyMinutes" xml:space="preserve"> <data name="ThirtyMinutes" xml:space="preserve">
<value>৩০ মিনিট</value> <value>30 minutes</value>
</data> </data>
<data name="SetPINDescription" xml:space="preserve"> <data name="SetPINDescription" xml:space="preserve">
<value>Bitwarden আনলক করার জন্য আপনার পিন কোডটি সেট করুন। আপনি যদি অ্যাপ্লিকেশনটি থেকে পুরোপুরি লগ আউট করেন তবে আপনার পিন সেটিংস রিসেট করা হবে।</value> <value>Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application.</value>
</data> </data>
<data name="LoggedInAsOn" xml:space="preserve"> <data name="LoggedInAsOn" xml:space="preserve">
<value>{1}-এ {0} হিসেবে লগইন করা হয়েছে।</value> <value>Logged in as {0} on {1}.</value>
<comment>ex: Logged in as user@example.com on bitwarden.com.</comment> <comment>ex: Logged in as user@example.com on bitwarden.com.</comment>
</data> </data>
<data name="VaultLockedMasterPassword" xml:space="preserve"> <data name="VaultLockedMasterPassword" xml:space="preserve">
<value>আপনার ভল্ট লক করা আছে। চালিয়ে যেতে আপনার প্রধান পাসওয়ার্ডটি যাচাই করান।</value> <value>আপনার ভল্ট লক করা আছে। চালিয়ে যেতে আপনার প্রধান পাসওয়ার্ডটি যাচাই করান।</value>
</data> </data>
<data name="VaultLockedPIN" xml:space="preserve"> <data name="VaultLockedPIN" xml:space="preserve">
<value>আপনার ভল্ট লক করা আছে। চালিয়ে যেতে আপনার পিন কোড যাচাই করান।</value> <value>Your vault is locked. Verify your PIN code to continue.</value>
</data> </data>
<data name="VaultLockedIdentity" xml:space="preserve"> <data name="VaultLockedIdentity" xml:space="preserve">
<value>আপনার ভল্ট লক করা আছে। চালিয়ে যেতে আপনার পরিচয় যাচাই করান।</value> <value>Your vault is locked. Verify your identity to continue.</value>
</data> </data>
<data name="Dark" xml:space="preserve"> <data name="Dark" xml:space="preserve">
<value>গাঢ়</value> <value>Dark</value>
<comment>A dark color</comment> <comment>A dark color</comment>
</data> </data>
<data name="Light" xml:space="preserve"> <data name="Light" xml:space="preserve">
<value>উজ্জ্বল</value> <value>Light</value>
<comment>A light color</comment> <comment>A light color</comment>
</data> </data>
<data name="FiveMinutes" xml:space="preserve"> <data name="FiveMinutes" xml:space="preserve">
@@ -1752,11 +1752,11 @@ Scanning will happen automatically.</value>
<value>Do you really want to send to the trash?</value> <value>Do you really want to send to the trash?</value>
<comment>Confirmation alert message when soft-deleting a cipher.</comment> <comment>Confirmation alert message when soft-deleting a cipher.</comment>
</data> </data>
<data name="AccountBiometricInvalidated" xml:space="preserve"> <data name="BiometricInvalidated" xml:space="preserve">
<value>Biometric unlock for this account is disabled pending verification of master password.</value> <value>Biometric unlock disabled pending verification of master password.</value>
</data> </data>
<data name="AccountBiometricInvalidatedExtension" xml:space="preserve"> <data name="BiometricInvalidatedExtension" xml:space="preserve">
<value>Autofill biometric unlock for this account is disabled pending verification of master password.</value> <value>Biometric unlock for autofill disabled pending verification of master password.</value>
</data> </data>
<data name="EnableSyncOnRefresh" xml:space="preserve"> <data name="EnableSyncOnRefresh" xml:space="preserve">
<value>Allow sync on refresh</value> <value>Allow sync on refresh</value>
@@ -1887,10 +1887,10 @@ Scanning will happen automatically.</value>
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment> <comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
</data> </data>
<data name="Text" xml:space="preserve"> <data name="Text" xml:space="preserve">
<value>লেখা</value> <value>Text</value>
</data> </data>
<data name="TypeText" xml:space="preserve"> <data name="TypeText" xml:space="preserve">
<value>লেখা</value> <value>Text</value>
</data> </data>
<data name="TypeTextInfo" xml:space="preserve"> <data name="TypeTextInfo" xml:space="preserve">
<value>The text you want to send.</value> <value>The text you want to send.</value>
@@ -1900,7 +1900,7 @@ Scanning will happen automatically.</value>
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment> <comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
</data> </data>
<data name="TypeFile" xml:space="preserve"> <data name="TypeFile" xml:space="preserve">
<value>ফাইল</value> <value>File</value>
</data> </data>
<data name="TypeFileInfo" xml:space="preserve"> <data name="TypeFileInfo" xml:space="preserve">
<value>The file you want to send.</value> <value>The file you want to send.</value>
@@ -1918,7 +1918,7 @@ Scanning will happen automatically.</value>
<value>Text type is not selected, tap to select.</value> <value>Text type is not selected, tap to select.</value>
</data> </data>
<data name="DeletionDate" xml:space="preserve"> <data name="DeletionDate" xml:space="preserve">
<value>মুছে ফেলার তারিখ</value> <value>Deletion date</value>
</data> </data>
<data name="DeletionTime" xml:space="preserve"> <data name="DeletionTime" xml:space="preserve">
<value>Deletion time</value> <value>Deletion time</value>
@@ -1957,7 +1957,7 @@ Scanning will happen automatically.</value>
<value>Current access count</value> <value>Current access count</value>
</data> </data>
<data name="NewPassword" xml:space="preserve"> <data name="NewPassword" xml:space="preserve">
<value>নতুন পাসওয়ার্ড</value> <value>New password</value>
</data> </data>
<data name="PasswordInfo" xml:space="preserve"> <data name="PasswordInfo" xml:space="preserve">
<value>Optionally require a password for users to access this Send.</value> <value>Optionally require a password for users to access this Send.</value>
@@ -2030,19 +2030,19 @@ Scanning will happen automatically.</value>
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment> <comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
</data> </data>
<data name="OneDay" xml:space="preserve"> <data name="OneDay" xml:space="preserve">
<value>১ দিন</value> <value>1 day</value>
</data> </data>
<data name="TwoDays" xml:space="preserve"> <data name="TwoDays" xml:space="preserve">
<value>২ দিন</value> <value>2 days</value>
</data> </data>
<data name="ThreeDays" xml:space="preserve"> <data name="ThreeDays" xml:space="preserve">
<value>3 days</value> <value>3 days</value>
</data> </data>
<data name="SevenDays" xml:space="preserve"> <data name="SevenDays" xml:space="preserve">
<value> দিন</value> <value>7 days</value>
</data> </data>
<data name="ThirtyDays" xml:space="preserve"> <data name="ThirtyDays" xml:space="preserve">
<value>৩০ দিন</value> <value>30 days</value>
</data> </data>
<data name="Custom" xml:space="preserve"> <data name="Custom" xml:space="preserve">
<value>Custom</value> <value>Custom</value>
@@ -2141,13 +2141,7 @@ Scanning will happen automatically.</value>
<value>This organization has an enterprise policy that will automatically enroll you in password reset. Enrollment will allow organization administrators to change your master password.</value> <value>This organization has an enterprise policy that will automatically enroll you in password reset. Enrollment will allow organization administrators to change your master password.</value>
</data> </data>
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve"> <data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
<value>Your organization policies have set your maximum allowed vault timeout to {0} hour(s) and {1} minute(s).</value> <value>Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is {0} hour(s) and {1} minute(s)</value>
</data>
<data name="VaultTimeoutPolicyWithActionInEffect" xml:space="preserve">
<value>Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is {0} hour(s) and {1} minute(s). Your vault timeout action is set to {2}.</value>
</data>
<data name="VaultTimeoutActionPolicyInEffect" xml:space="preserve">
<value>Your organization policies have set your vault timeout action to {0}.</value>
</data> </data>
<data name="VaultTimeoutToLarge" xml:space="preserve"> <data name="VaultTimeoutToLarge" xml:space="preserve">
<value>Your vault timeout exceeds the restrictions set by your organization.</value> <value>Your vault timeout exceeds the restrictions set by your organization.</value>
@@ -2240,16 +2234,16 @@ Scanning will happen automatically.</value>
<value>Options are collapsed, tap to expand.</value> <value>Options are collapsed, tap to expand.</value>
</data> </data>
<data name="UppercaseAtoZ" xml:space="preserve"> <data name="UppercaseAtoZ" xml:space="preserve">
<value>বড় হাতের অক্ষর (A-Z)</value> <value>Uppercase (A to Z)</value>
</data> </data>
<data name="LowercaseAtoZ" xml:space="preserve"> <data name="LowercaseAtoZ" xml:space="preserve">
<value>Lowercase (A to Z)</value> <value>Lowercase (A to Z)</value>
</data> </data>
<data name="NumbersZeroToNine" xml:space="preserve"> <data name="NumbersZeroToNine" xml:space="preserve">
<value>সংখ্যা (0-9)</value> <value>Numbers (0 to 9)</value>
</data> </data>
<data name="SpecialCharacters" xml:space="preserve"> <data name="SpecialCharacters" xml:space="preserve">
<value>বিশেষ অক্ষর (!@#$%^&amp;*)</value> <value>Special characters (!@#$%^&amp;*)</value>
</data> </data>
<data name="TapToGoBack" xml:space="preserve"> <data name="TapToGoBack" xml:space="preserve">
<value>Tap to go back</value> <value>Tap to go back</value>
@@ -2352,10 +2346,10 @@ select Add TOTP to store the key safely</value>
<value>Deny login</value> <value>Deny login</value>
</data> </data>
<data name="JustNow" xml:space="preserve"> <data name="JustNow" xml:space="preserve">
<value>এইমাত্র</value> <value>Just now</value>
</data> </data>
<data name="XMinutesAgo" xml:space="preserve"> <data name="XMinutesAgo" xml:space="preserve">
<value>{0} মিনিট আগে</value> <value>{0} minutes ago</value>
</data> </data>
<data name="LogInAccepted" xml:space="preserve"> <data name="LogInAccepted" xml:space="preserve">
<value>Login confirmed</value> <value>Login confirmed</value>
@@ -2496,8 +2490,8 @@ Do you want to switch to this account?</value>
<data name="GetMasterPasswordwordHint" xml:space="preserve"> <data name="GetMasterPasswordwordHint" xml:space="preserve">
<value>Get master password hint</value> <value>Get master password hint</value>
</data> </data>
<data name="LoggingInAsXOnY" xml:space="preserve"> <data name="LoggingInAsX" xml:space="preserve">
<value>Logging in as {0} on {1}</value> <value>Logging in as {0}</value>
</data> </data>
<data name="NotYou" xml:space="preserve"> <data name="NotYou" xml:space="preserve">
<value>Not you?</value> <value>Not you?</value>
@@ -2548,7 +2542,7 @@ Do you want to switch to this account?</value>
<value>Enable camera permission to use the scanner</value> <value>Enable camera permission to use the scanner</value>
</data> </data>
<data name="Language" xml:space="preserve"> <data name="Language" xml:space="preserve">
<value>ভাষা</value> <value>Language</value>
</data> </data>
<data name="LanguageChangeXDescription" xml:space="preserve"> <data name="LanguageChangeXDescription" xml:space="preserve">
<value>The language has been changed to {0}. Please restart the app to see the change</value> <value>The language has been changed to {0}. Please restart the app to see the change</value>
@@ -2575,10 +2569,10 @@ Do you want to switch to this account?</value>
<value>Weak</value> <value>Weak</value>
</data> </data>
<data name="Good" xml:space="preserve"> <data name="Good" xml:space="preserve">
<value>ভালো</value> <value>Good</value>
</data> </data>
<data name="Strong" xml:space="preserve"> <data name="Strong" xml:space="preserve">
<value>শক্তিশালী</value> <value>Strong</value>
</data> </data>
<data name="CheckKnownDataBreachesForThisPassword" xml:space="preserve"> <data name="CheckKnownDataBreachesForThisPassword" xml:space="preserve">
<value>Check known data breaches for this password</value> <value>Check known data breaches for this password</value>
@@ -2610,37 +2604,4 @@ Do you want to switch to this account?</value>
<data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve"> <data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve">
<value>There are no items that match the search</value> <value>There are no items that match the search</value>
</data> </data>
<data name="US" xml:space="preserve">
<value>US</value>
</data>
<data name="EU" xml:space="preserve">
<value>EU</value>
</data>
<data name="SelfHosted" xml:space="preserve">
<value>Self-hosted</value>
</data>
<data name="DataRegion" xml:space="preserve">
<value>Data region</value>
</data>
<data name="Region" xml:space="preserve">
<value>Region</value>
</data>
<data name="UpdateWeakMasterPasswordWarning" xml:space="preserve">
<value>Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour.</value>
</data>
<data name="CurrentMasterPassword" xml:space="preserve">
<value>Current master password</value>
</data>
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
<value>Master password re-prompt help</value>
</data>
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
<value>Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve.</value>
</data>
<data name="InvalidAPIKey" xml:space="preserve">
<value>Invalid API key</value>
</data>
<data name="InvalidAPIToken" xml:space="preserve">
<value>Invalid API token</value>
</data>
</root> </root>

View File

@@ -1752,11 +1752,11 @@ Skeniranje će biti izvršeno automatski.</value>
<value>Želite li zaista poslati u smeće?</value> <value>Želite li zaista poslati u smeće?</value>
<comment>Confirmation alert message when soft-deleting a cipher.</comment> <comment>Confirmation alert message when soft-deleting a cipher.</comment>
</data> </data>
<data name="AccountBiometricInvalidated" xml:space="preserve"> <data name="BiometricInvalidated" xml:space="preserve">
<value>Biometric unlock for this account is disabled pending verification of master password.</value> <value>Biometrijsko otključavanje onemogućeno do potvrde glavne lozinke.</value>
</data> </data>
<data name="AccountBiometricInvalidatedExtension" xml:space="preserve"> <data name="BiometricInvalidatedExtension" xml:space="preserve">
<value>Autofill biometric unlock for this account is disabled pending verification of master password.</value> <value>Biometrijsko otključavanje za auto-ispunu onemogućeno do potvrde glavne lozinke.</value>
</data> </data>
<data name="EnableSyncOnRefresh" xml:space="preserve"> <data name="EnableSyncOnRefresh" xml:space="preserve">
<value>Uključi sinkronizaciju pri osvježavanju</value> <value>Uključi sinkronizaciju pri osvježavanju</value>
@@ -2142,12 +2142,6 @@ Skeniranje će biti izvršeno automatski.</value>
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve"> <data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
<value>Pravilo tvoje organizacije utječe na vrijeme isteka trezora. Najveće dozvoljeno vrijeme isteka je {0} sata(i) i {1} minuta.</value> <value>Pravilo tvoje organizacije utječe na vrijeme isteka trezora. Najveće dozvoljeno vrijeme isteka je {0} sata(i) i {1} minuta.</value>
</data> </data>
<data name="VaultTimeoutPolicyWithActionInEffect" xml:space="preserve">
<value>Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is {0} hour(s) and {1} minute(s). Your vault timeout action is set to {2}.</value>
</data>
<data name="VaultTimeoutActionPolicyInEffect" xml:space="preserve">
<value>Your organization policies have set your vault timeout action to {0}.</value>
</data>
<data name="VaultTimeoutToLarge" xml:space="preserve"> <data name="VaultTimeoutToLarge" xml:space="preserve">
<value>Vrijeme isteka premašuje ograničenje koju je postavila tvoja organizacija.</value> <value>Vrijeme isteka premašuje ograničenje koju je postavila tvoja organizacija.</value>
</data> </data>
@@ -2494,8 +2488,8 @@ Skeniranje će biti izvršeno automatski.</value>
<data name="GetMasterPasswordwordHint" xml:space="preserve"> <data name="GetMasterPasswordwordHint" xml:space="preserve">
<value>Dobijte podsjetnik glavne lozinke</value> <value>Dobijte podsjetnik glavne lozinke</value>
</data> </data>
<data name="LoggingInAsXOnY" xml:space="preserve"> <data name="LoggingInAsX" xml:space="preserve">
<value>Logging in as {0} on {1}</value> <value>Prijava kao {0}</value>
</data> </data>
<data name="NotYou" xml:space="preserve"> <data name="NotYou" xml:space="preserve">
<value>Nisi ti?</value> <value>Nisi ti?</value>
@@ -2608,37 +2602,4 @@ Skeniranje će biti izvršeno automatski.</value>
<data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve"> <data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve">
<value>There are no items that match the search</value> <value>There are no items that match the search</value>
</data> </data>
<data name="US" xml:space="preserve">
<value>US</value>
</data>
<data name="EU" xml:space="preserve">
<value>EU</value>
</data>
<data name="SelfHosted" xml:space="preserve">
<value>Self-hosted</value>
</data>
<data name="DataRegion" xml:space="preserve">
<value>Data region</value>
</data>
<data name="Region" xml:space="preserve">
<value>Region</value>
</data>
<data name="UpdateWeakMasterPasswordWarning" xml:space="preserve">
<value>Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour.</value>
</data>
<data name="CurrentMasterPassword" xml:space="preserve">
<value>Current master password</value>
</data>
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
<value>Master password re-prompt help</value>
</data>
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
<value>Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve.</value>
</data>
<data name="InvalidAPIKey" xml:space="preserve">
<value>Invalid API key</value>
</data>
<data name="InvalidAPIToken" xml:space="preserve">
<value>Invalid API token</value>
</data>
</root> </root>

View File

@@ -1752,11 +1752,11 @@ L'escaneig es farà automàticament.</value>
<value>Esteu segur que voleu enviar-ho a la paperera?</value> <value>Esteu segur que voleu enviar-ho a la paperera?</value>
<comment>Confirmation alert message when soft-deleting a cipher.</comment> <comment>Confirmation alert message when soft-deleting a cipher.</comment>
</data> </data>
<data name="AccountBiometricInvalidated" xml:space="preserve"> <data name="BiometricInvalidated" xml:space="preserve">
<value>El desbloqueig biomètric d'aquest compte està deshabilitat mentre es verifica la contrasenya mestra.</value> <value>S'ha detectat un canvi biomètric. Inicieu la sessió mitjançant la contrasenya principal per tornar a activar-la.</value>
</data> </data>
<data name="AccountBiometricInvalidatedExtension" xml:space="preserve"> <data name="BiometricInvalidatedExtension" xml:space="preserve">
<value>El desbloqueig biomètric d'emplenament automàtic d'aquest compte està deshabilitat mentre es verifica la contrasenya mestra.</value> <value>Desbloqueig biomètric d'emplenament automàtic deshabilitat. Està pendent de verificació de la contrasenya mestra.</value>
</data> </data>
<data name="EnableSyncOnRefresh" xml:space="preserve"> <data name="EnableSyncOnRefresh" xml:space="preserve">
<value>Activa la sincronització en actualitzar-se</value> <value>Activa la sincronització en actualitzar-se</value>
@@ -2140,13 +2140,7 @@ L'escaneig es farà automàticament.</value>
<value>Aquesta organització té una política empresarial que us inscriurà automàticament al restabliment de la contrasenya. La inscripció permetrà als administradors de lorganització canviar la vostra contrasenya mestra.</value> <value>Aquesta organització té una política empresarial que us inscriurà automàticament al restabliment de la contrasenya. La inscripció permetrà als administradors de lorganització canviar la vostra contrasenya mestra.</value>
</data> </data>
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve"> <data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
<value>Les polítiques de l'organització afecten el temps d'espera de la caixa forta. El temps d'espera màxim permès de la caixa forta és de {0} hores i {1} minuts</value> <value>Les polítiques de la vostra organització afecten el temps d'espera de la vostra caixa forta. El temps d'espera màxim permès de la caixa forta és de {0} hores i {1} minuts</value>
</data>
<data name="VaultTimeoutPolicyWithActionInEffect" xml:space="preserve">
<value>Les polítiques de l'organització afecten el temps d'espera de la vostra caixa forta. El temps d'espera màxim permès és de {0} hores i {1} minuts. L'acció de temps d'espera de la caixa forta està definida en {2}.</value>
</data>
<data name="VaultTimeoutActionPolicyInEffect" xml:space="preserve">
<value>Les polítiques de l'organització han establert l'acció de temps d'espera de la caixa forta a {0}.</value>
</data> </data>
<data name="VaultTimeoutToLarge" xml:space="preserve"> <data name="VaultTimeoutToLarge" xml:space="preserve">
<value>El temps d'espera de la caixa forta supera les restriccions establertes per la vostra organització.</value> <value>El temps d'espera de la caixa forta supera les restriccions establertes per la vostra organització.</value>
@@ -2495,8 +2489,8 @@ Voleu canviar a aquest compte?</value>
<data name="GetMasterPasswordwordHint" xml:space="preserve"> <data name="GetMasterPasswordwordHint" xml:space="preserve">
<value>Obteniu la pista de la contrasenya mestra</value> <value>Obteniu la pista de la contrasenya mestra</value>
</data> </data>
<data name="LoggingInAsXOnY" xml:space="preserve"> <data name="LoggingInAsX" xml:space="preserve">
<value>Inici de sessió com a {0} a {1}</value> <value>Connectat com {0}</value>
</data> </data>
<data name="NotYou" xml:space="preserve"> <data name="NotYou" xml:space="preserve">
<value>No sou vosaltres?</value> <value>No sou vosaltres?</value>
@@ -2609,37 +2603,4 @@ Voleu canviar a aquest compte?</value>
<data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve"> <data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve">
<value>No hi ha elements que coincidisquen amb la cerca</value> <value>No hi ha elements que coincidisquen amb la cerca</value>
</data> </data>
<data name="US" xml:space="preserve">
<value>EUA</value>
</data>
<data name="EU" xml:space="preserve">
<value>UE</value>
</data>
<data name="SelfHosted" xml:space="preserve">
<value>Autoallotjat</value>
</data>
<data name="DataRegion" xml:space="preserve">
<value>Regió de dades</value>
</data>
<data name="Region" xml:space="preserve">
<value>Regió</value>
</data>
<data name="UpdateWeakMasterPasswordWarning" xml:space="preserve">
<value>La vostra contrasenya mestra no compleix una o més de les polítiques de l'organització. Per accedir a la caixa forta, heu d'actualitzar-la ara. Si continueu, es tancarà la sessió actual i us demanarà que torneu a iniciar-la. Les sessions en altres dispositius poden continuar romanent actives fins a una hora.</value>
</data>
<data name="CurrentMasterPassword" xml:space="preserve">
<value>Contrasenya mestra actual</value>
</data>
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
<value>Ajuda per tornar a demanar la contrasenya mestra</value>
</data>
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
<value>El desbloqueig pot fallar a causa de memòria insuficient. Disminueix la configuració de memòria KDF per resoldre-ho</value>
</data>
<data name="InvalidAPIKey" xml:space="preserve">
<value>Clau API no vàlida</value>
</data>
<data name="InvalidAPIToken" xml:space="preserve">
<value>Token API no vàlid</value>
</data>
</root> </root>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1752,11 +1752,11 @@ Skanning vil ske automatisk.</value>
<value>Er du sikker på, at du sende til papirkurven?</value> <value>Er du sikker på, at du sende til papirkurven?</value>
<comment>Confirmation alert message when soft-deleting a cipher.</comment> <comment>Confirmation alert message when soft-deleting a cipher.</comment>
</data> </data>
<data name="AccountBiometricInvalidated" xml:space="preserve"> <data name="BiometricInvalidated" xml:space="preserve">
<value>Biometrisk oplåsning af denne konto deaktiveret afventende bekræftelse af hovedadgangskode.</value> <value>Biometrisk oplåsning deaktiveret - afventer verifikation af hovedadgangskoden.</value>
</data> </data>
<data name="AccountBiometricInvalidatedExtension" xml:space="preserve"> <data name="BiometricInvalidatedExtension" xml:space="preserve">
<value>Autoudfyldelse for biometrisk oplåsning af denne konto deaktiveret afventende bekræftelse af hovedadgangskode.</value> <value>Biometrisk oplåsning til autoudfyldning deaktiveret - afventer verifikation af hovedadgangskoden.</value>
</data> </data>
<data name="EnableSyncOnRefresh" xml:space="preserve"> <data name="EnableSyncOnRefresh" xml:space="preserve">
<value>Tillad synk ved opfriskning</value> <value>Tillad synk ved opfriskning</value>
@@ -2142,12 +2142,6 @@ Skanning vil ske automatisk.</value>
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve"> <data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
<value>Dine organisationspolitikker påvirker din boks-timeout. Maksimum tilladte boks-timeout er {0} time(r) og {1} minut(ter)</value> <value>Dine organisationspolitikker påvirker din boks-timeout. Maksimum tilladte boks-timeout er {0} time(r) og {1} minut(ter)</value>
</data> </data>
<data name="VaultTimeoutPolicyWithActionInEffect" xml:space="preserve">
<value>Organisationspolitikkerne påvirker boks-timeout. Maks. tilladt boks-timeout er {0} time(r) og {1} minut(ter). Boks-timeout er pt. sat til {2}.</value>
</data>
<data name="VaultTimeoutActionPolicyInEffect" xml:space="preserve">
<value>Organisationspolitikker har sat boks-timeouthandlingen til {0}.</value>
</data>
<data name="VaultTimeoutToLarge" xml:space="preserve"> <data name="VaultTimeoutToLarge" xml:space="preserve">
<value>Timeout for din boks overskrider de begrænsninger, der er angivet af din organisation.</value> <value>Timeout for din boks overskrider de begrænsninger, der er angivet af din organisation.</value>
</data> </data>
@@ -2495,8 +2489,8 @@ Vil du skifte til denne konto?</value>
<data name="GetMasterPasswordwordHint" xml:space="preserve"> <data name="GetMasterPasswordwordHint" xml:space="preserve">
<value>Få hovedadgangskodetip</value> <value>Få hovedadgangskodetip</value>
</data> </data>
<data name="LoggingInAsXOnY" xml:space="preserve"> <data name="LoggingInAsX" xml:space="preserve">
<value>Logger ind som {0} på {1}</value> <value>Logger ind som {0}</value>
</data> </data>
<data name="NotYou" xml:space="preserve"> <data name="NotYou" xml:space="preserve">
<value>Ikke dig?</value> <value>Ikke dig?</value>
@@ -2609,37 +2603,4 @@ Vil du skifte til denne konto?</value>
<data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve"> <data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve">
<value>Ingen emner matcher søgningen</value> <value>Ingen emner matcher søgningen</value>
</data> </data>
<data name="US" xml:space="preserve">
<value>USA</value>
</data>
<data name="EU" xml:space="preserve">
<value>EU</value>
</data>
<data name="SelfHosted" xml:space="preserve">
<value>Selv-hostet</value>
</data>
<data name="DataRegion" xml:space="preserve">
<value>Dataregion</value>
</data>
<data name="Region" xml:space="preserve">
<value>Region</value>
</data>
<data name="UpdateWeakMasterPasswordWarning" xml:space="preserve">
<value>Din hovedadgangskode overholder ikke én eller flere organisationspolitikker. For at få adgang til boksen skal hovedadgangskode opdateres nu. Fortsættes, logges du ud af den nuværende session og vil skulle logger ind igen. Aktive sessioner på andre enheder kan forblive aktive i op til én time.</value>
</data>
<data name="CurrentMasterPassword" xml:space="preserve">
<value>Aktuel hovedadgangskode</value>
</data>
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
<value>Hjælp til genanmodning om hovedadgangskode</value>
</data>
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
<value>Oplåsning kan fejle grundet utilstrækkelig hukommelse. Reducér KDF-hukommelsesindstillinger for at afhjælpe</value>
</data>
<data name="InvalidAPIKey" xml:space="preserve">
<value>Ugyldig API-nøgle</value>
</data>
<data name="InvalidAPIToken" xml:space="preserve">
<value>Ugyldigt API-token</value>
</data>
</root> </root>

View File

@@ -1252,10 +1252,10 @@ Das Scannen erfolgt automatisch.</value>
<value>Beginnt mit</value> <value>Beginnt mit</value>
</data> </data>
<data name="URIMatchDetection" xml:space="preserve"> <data name="URIMatchDetection" xml:space="preserve">
<value>URI-Übereinstimmungserkennung</value> <value>URI Match-Erkennung</value>
</data> </data>
<data name="MatchDetection" xml:space="preserve"> <data name="MatchDetection" xml:space="preserve">
<value>Übereinstimmungserkennung</value> <value>Match-Erkennung</value>
<comment>URI match detection for auto-fill.</comment> <comment>URI match detection for auto-fill.</comment>
</data> </data>
<data name="YesAndSave" xml:space="preserve"> <data name="YesAndSave" xml:space="preserve">
@@ -1536,7 +1536,7 @@ Das Scannen erfolgt automatisch.</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment> <comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data> </data>
<data name="DefaultUriMatchDetection" xml:space="preserve"> <data name="DefaultUriMatchDetection" xml:space="preserve">
<value>Standard URI-Übereinstimmungserkennung</value> <value>Standard-URI-Match-Erkennung</value>
<comment>Default URI match detection for auto-fill.</comment> <comment>Default URI match detection for auto-fill.</comment>
</data> </data>
<data name="DefaultUriMatchDetectionDescription" xml:space="preserve"> <data name="DefaultUriMatchDetectionDescription" xml:space="preserve">
@@ -1556,7 +1556,8 @@ Das Scannen erfolgt automatisch.</value>
<value>Dunkles Standard-Design</value> <value>Dunkles Standard-Design</value>
</data> </data>
<data name="DefaultDarkThemeDescription" xml:space="preserve"> <data name="DefaultDarkThemeDescription" xml:space="preserve">
<value>Wähle das zu verwendende dunkle Design aus, das bei der Auswahl vom Standard-(System)-Design verwendet werden soll, während der Dark Mode deines Geräts aktiviert ist.</value> <value>Wähle das zu verwendende dunkle Design aus, das bei der Auswahl vom Standard-(System)-Design verwendet werden soll, während der Dark
Mode deines Geräts aktiviert ist.</value>
</data> </data>
<data name="CopyNotes" xml:space="preserve"> <data name="CopyNotes" xml:space="preserve">
<value>Notiz kopieren</value> <value>Notiz kopieren</value>
@@ -1752,11 +1753,11 @@ Das Scannen erfolgt automatisch.</value>
<value>Soll dieser Eintrag wirklich in den Papierkorb verschoben werden?</value> <value>Soll dieser Eintrag wirklich in den Papierkorb verschoben werden?</value>
<comment>Confirmation alert message when soft-deleting a cipher.</comment> <comment>Confirmation alert message when soft-deleting a cipher.</comment>
</data> </data>
<data name="AccountBiometricInvalidated" xml:space="preserve"> <data name="BiometricInvalidated" xml:space="preserve">
<value>Das Entsperren über Biometrie ist für dieses Konto deaktiviert, bis das Master-Passwort verifiziert wurde.</value> <value>Biometrisches Entsperren ist deaktiviert, bis das Master-Passwort eingegeben wurde.</value>
</data> </data>
<data name="AccountBiometricInvalidatedExtension" xml:space="preserve"> <data name="BiometricInvalidatedExtension" xml:space="preserve">
<value>Das Entsperren von Auto-Ausfüllen über Biometrie ist für dieses Konto deaktiviert, bis das Master-Passwort verifiziert wurde.</value> <value>Biometrisches Entsperren für automatisches Ausfüllen ist deaktiviert, bis das Master-Passwort eingegeben wurde.</value>
</data> </data>
<data name="EnableSyncOnRefresh" xml:space="preserve"> <data name="EnableSyncOnRefresh" xml:space="preserve">
<value>Synchronisation beim Aktualisieren aktivieren</value> <value>Synchronisation beim Aktualisieren aktivieren</value>
@@ -1768,7 +1769,7 @@ Das Scannen erfolgt automatisch.</value>
<value>Enterprise Single-Sign-On</value> <value>Enterprise Single-Sign-On</value>
</data> </data>
<data name="LogInSsoSummary" xml:space="preserve"> <data name="LogInSsoSummary" xml:space="preserve">
<value>Melde dich schnell über das Single Sign-on-Portal deiner Organisation an. Bitte gib die Organisationskennung ein, um zu beginnen.</value> <value>Schnell über den Single Sign-on deiner Organisation anmelden. Bitte gib deine Organisations-ID an, um zu beginnen.</value>
</data> </data>
<data name="OrgIdentifier" xml:space="preserve"> <data name="OrgIdentifier" xml:space="preserve">
<value>Organisationskennung</value> <value>Organisationskennung</value>
@@ -2140,13 +2141,7 @@ Das Scannen erfolgt automatisch.</value>
<value>Diese Organisation hat eine Unternehmensrichtlinie, die dich automatisch für die Passwortzurücksetzung registriert. Die Registrierung wird es Administratoren der Organisation erlauben, dein Master-Passwort zu ändern.</value> <value>Diese Organisation hat eine Unternehmensrichtlinie, die dich automatisch für die Passwortzurücksetzung registriert. Die Registrierung wird es Administratoren der Organisation erlauben, dein Master-Passwort zu ändern.</value>
</data> </data>
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve"> <data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
<value>Deine Unternehmensrichtlinien haben das maximal zulässige Tresor-Timeout auf {0} Stunde(n) und {1} Minute(n) festgelegt.</value> <value>Deine Organisationsrichtlinien haben Auswirkungen auf dein Tresor-Timeout. Das maximal zulässige Tresor-Timeout beträgt {0} Stunde(n) und {1} Minute(n)</value>
</data>
<data name="VaultTimeoutPolicyWithActionInEffect" xml:space="preserve">
<value>Deine Organisationsrichtlinien beeinflussen dein Tresor-Timeout. Das maximal zulässige Tresor-Timeout beträgt {0} Stunde(n) und {1} Minute(n). Deine Tresor-Timeout-Aktion ist auf {2} gesetzt.</value>
</data>
<data name="VaultTimeoutActionPolicyInEffect" xml:space="preserve">
<value>Deine Organisationsrichtlinien haben deine Tresor-Timeout-Aktion auf {0} gesetzt.</value>
</data> </data>
<data name="VaultTimeoutToLarge" xml:space="preserve"> <data name="VaultTimeoutToLarge" xml:space="preserve">
<value>Dein Tresor-Timeout überschreitet die von deinem Unternehmen festgelegten Beschränkungen.</value> <value>Dein Tresor-Timeout überschreitet die von deinem Unternehmen festgelegten Beschränkungen.</value>
@@ -2392,7 +2387,7 @@ Das Scannen erfolgt automatisch.</value>
<value>Benutzernamenstyp</value> <value>Benutzernamenstyp</value>
</data> </data>
<data name="PlusAddressedEmail" xml:space="preserve"> <data name="PlusAddressedEmail" xml:space="preserve">
<value>Plus-adressierte E-Mail-Adresse</value> <value>Mit Plus adressierte E-Mail-Adresse</value>
</data> </data>
<data name="CatchAllEmail" xml:space="preserve"> <data name="CatchAllEmail" xml:space="preserve">
<value>Catch-All E-Mail-Adresse</value> <value>Catch-All E-Mail-Adresse</value>
@@ -2494,8 +2489,8 @@ Möchtest du zu diesem Konto wechseln?</value>
<data name="GetMasterPasswordwordHint" xml:space="preserve"> <data name="GetMasterPasswordwordHint" xml:space="preserve">
<value>Hinweis zum Master-Passwort erhalten</value> <value>Hinweis zum Master-Passwort erhalten</value>
</data> </data>
<data name="LoggingInAsXOnY" xml:space="preserve"> <data name="LoggingInAsX" xml:space="preserve">
<value>Anmelden als {0} auf {1}</value> <value>Anmelden als {0}</value>
</data> </data>
<data name="NotYou" xml:space="preserve"> <data name="NotYou" xml:space="preserve">
<value>Nicht du?</value> <value>Nicht du?</value>
@@ -2594,10 +2589,10 @@ Möchtest du zu diesem Konto wechseln?</value>
<value>Schwaches Passwort erkannt und in einem Datendiebstahl gefunden. Verwende ein starkes und einzigartiges Passwort, um dein Konto zu schützen. Bist du sicher, dass du dieses Passwort verwenden möchtest?</value> <value>Schwaches Passwort erkannt und in einem Datendiebstahl gefunden. Verwende ein starkes und einzigartiges Passwort, um dein Konto zu schützen. Bist du sicher, dass du dieses Passwort verwenden möchtest?</value>
</data> </data>
<data name="OrganizationSsoIdentifierRequired" xml:space="preserve"> <data name="OrganizationSsoIdentifierRequired" xml:space="preserve">
<value>SSO-Kennung der Organisation erforderlich.</value> <value>SSO-ID der Organisation erforderlich.</value>
</data> </data>
<data name="AddTheKeyToAnExistingOrNewItem" xml:space="preserve"> <data name="AddTheKeyToAnExistingOrNewItem" xml:space="preserve">
<value>Den Schlüssel zu einem bestehenden oder neuen Eintrag hinzufügen</value> <value>Schlüssel zu einem bestehenden oder neuen Eintrag hinzufügen</value>
</data> </data>
<data name="ThereAreNoItemsInYourVaultThatMatchX" xml:space="preserve"> <data name="ThereAreNoItemsInYourVaultThatMatchX" xml:space="preserve">
<value>Es gibt keine Einträge in deinem Tresor, die mit "{0}" übereinstimmen</value> <value>Es gibt keine Einträge in deinem Tresor, die mit "{0}" übereinstimmen</value>
@@ -2608,37 +2603,4 @@ Möchtest du zu diesem Konto wechseln?</value>
<data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve"> <data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve">
<value>Es gibt keine Einträge, die mit der Suche übereinstimmen</value> <value>Es gibt keine Einträge, die mit der Suche übereinstimmen</value>
</data> </data>
<data name="US" xml:space="preserve">
<value>US</value>
</data>
<data name="EU" xml:space="preserve">
<value>EU</value>
</data>
<data name="SelfHosted" xml:space="preserve">
<value>Selbst gehostet</value>
</data>
<data name="DataRegion" xml:space="preserve">
<value>Datenregion</value>
</data>
<data name="Region" xml:space="preserve">
<value>Region</value>
</data>
<data name="UpdateWeakMasterPasswordWarning" xml:space="preserve">
<value>Dein Master-Passwort entspricht nicht einer oder mehreren Richtlinien deiner Organisation. Um auf den Tresor zugreifen zu können, musst du dein Master-Passwort jetzt aktualisieren. Wenn du fortfährst, wirst du von deiner aktuellen Sitzung abgemeldet und musst dich erneut anmelden. Aktive Sitzungen auf anderen Geräten können noch bis zu einer Stunde lang aktiv bleiben.</value>
</data>
<data name="CurrentMasterPassword" xml:space="preserve">
<value>Aktuelles Master-Passwort</value>
</data>
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
<value>Hilfe zum erneuten Abfragen des Master-Passworts</value>
</data>
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
<value>Das Entsperren kann aufgrund von unzureichendem Arbeitsspeicher fehlschlagen. Verringere deine KDF-Speichereinstellungen, um das Problem zu beheben.</value>
</data>
<data name="InvalidAPIKey" xml:space="preserve">
<value>Ungültiger API-Schlüssel</value>
</data>
<data name="InvalidAPIToken" xml:space="preserve">
<value>Ungültiger API-Token</value>
</data>
</root> </root>

Some files were not shown because too many files have changed in this diff Show More