mirror of
https://github.com/bitwarden/mobile
synced 2025-12-12 14:23:26 +00:00
Compare commits
66 Commits
feature/io
...
fix-avatar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4e599f680 | ||
|
|
d41e1a0bc3 | ||
|
|
e717992b2b | ||
|
|
d52a1bf668 | ||
|
|
6f3999016f | ||
|
|
796cf6dc25 | ||
|
|
59e7969856 | ||
|
|
afecc1f569 | ||
|
|
a3922f7fbb | ||
|
|
7e288c62f9 | ||
|
|
ed2bb9b94e | ||
|
|
1c6a8964fe | ||
|
|
6aa96b8964 | ||
|
|
f6d4b0a443 | ||
|
|
2791d4b8ec | ||
|
|
e20ae26808 | ||
|
|
e3a8d3bb55 | ||
|
|
65739489a7 | ||
|
|
42403210a0 | ||
|
|
120fb70039 | ||
|
|
d1ece79d2e | ||
|
|
ebae2585f6 | ||
|
|
5e15a2f30e | ||
|
|
c6547771a5 | ||
|
|
9fdcba386e | ||
|
|
86397a6f1e | ||
|
|
4c2e7331e3 | ||
|
|
b55a450f44 | ||
|
|
b28e265ed4 | ||
|
|
6164c764b4 | ||
|
|
ad3b401ed3 | ||
|
|
adb8bb4f1b | ||
|
|
04c7409418 | ||
|
|
705b8ac12b | ||
|
|
23a164b245 | ||
|
|
6f936343ae | ||
|
|
5eeec7d9ed | ||
|
|
b95efae7fb | ||
|
|
4a1f28caf8 | ||
|
|
dddc38ef64 | ||
|
|
05bcc10277 | ||
|
|
ea1ee2c3d3 | ||
|
|
2a373dd3fc | ||
|
|
14d2b833d8 | ||
|
|
9fdf2ada6f | ||
|
|
833103b2a0 | ||
|
|
6bae85b22d | ||
|
|
34dfb0b57e | ||
|
|
ff35e3c022 | ||
|
|
316cb4d21c | ||
|
|
7d42d19ae3 | ||
|
|
75ed72f91b | ||
|
|
e9b0bbb3a9 | ||
|
|
94994af4a9 | ||
|
|
68c484b67f | ||
|
|
1b60ac3699 | ||
|
|
1c006d6218 | ||
|
|
3e0e620bb7 | ||
|
|
14177efdda | ||
|
|
3ee80beda8 | ||
|
|
13869b5a1b | ||
|
|
90b62d61ae | ||
|
|
3cb8adeeff | ||
|
|
5b972eec24 | ||
|
|
e97ac1dd9b | ||
|
|
df496e39ff |
76
.github/workflows/build.yml
vendored
76
.github/workflows/build.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
|||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
outputs:
|
outputs:
|
||||||
rc_branch_exists: ${{ steps.branch-check.outputs.rc_branch_exists }}
|
rc_branch_exists: ${{ steps.branch-check.outputs.rc_branch_exists }}
|
||||||
release_branch_exists: ${{ steps.branch-check.outputs.release_branch_exists }}
|
hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
@@ -43,10 +43,10 @@ jobs:
|
|||||||
echo "::set-output name=rc_branch_exists::0"
|
echo "::set-output name=rc_branch_exists::0"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $(git ls-remote --heads origin release) ]]; then
|
if [[ $(git ls-remote --heads origin hotfix) ]]; then
|
||||||
echo "::set-output name=release_branch_exists::1"
|
echo "::set-output name=hotfix_branch_exists::1"
|
||||||
else
|
else
|
||||||
echo "::set-output name=release_branch_exists::0"
|
echo "::set-output name=hotfix_branch_exists::0"
|
||||||
fi
|
fi
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@@ -180,9 +180,9 @@ jobs:
|
|||||||
if: |
|
if: |
|
||||||
(github.ref == 'refs/heads/master'
|
(github.ref == 'refs/heads/master'
|
||||||
&& needs.setup.outputs.rc_branch_exists == 0
|
&& needs.setup.outputs.rc_branch_exists == 0
|
||||||
&& needs.setup.outputs.release_branch_exists == 0)
|
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.release_branch_exists == 0)
|
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| github.ref == 'refs/heads/release'
|
|| github.ref == 'refs/heads/hotfix'
|
||||||
run: |
|
run: |
|
||||||
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/netcoreapp2.0/Publisher.dll"
|
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/netcoreapp2.0/Publisher.dll"
|
||||||
CREDS_PATH="$HOME/secrets/play_creds.json"
|
CREDS_PATH="$HOME/secrets/play_creds.json"
|
||||||
@@ -397,6 +397,15 @@ jobs:
|
|||||||
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
|
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
- name: Update Entitlements
|
||||||
|
run: |
|
||||||
|
echo "########################################"
|
||||||
|
echo "##### Updating Entitlements"
|
||||||
|
echo "########################################"
|
||||||
|
|
||||||
|
perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>production<\/string>/' ./src/iOS/Entitlements.plist
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Set up Keychain
|
- name: Set up Keychain
|
||||||
env:
|
env:
|
||||||
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
|
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
|
||||||
@@ -474,9 +483,9 @@ jobs:
|
|||||||
if: |
|
if: |
|
||||||
(github.ref == 'refs/heads/master'
|
(github.ref == 'refs/heads/master'
|
||||||
&& needs.setup.outputs.rc_branch_exists == 0
|
&& needs.setup.outputs.rc_branch_exists == 0
|
||||||
&& needs.setup.outputs.release_branch_exists == 0)
|
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.release_branch_exists == 0)
|
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| github.ref == 'refs/heads/release'
|
|| github.ref == 'refs/heads/hotfix'
|
||||||
env:
|
env:
|
||||||
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
|
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
|
||||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||||
@@ -486,6 +495,44 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
|
||||||
|
crowdin-push:
|
||||||
|
name: Crowdin Push
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
needs:
|
||||||
|
- android
|
||||||
|
- f-droid
|
||||||
|
- ios
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
env:
|
||||||
|
_CROWDIN_PROJECT_ID: "269690"
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
|
- name: Login to Azure
|
||||||
|
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||||
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Retrieve secrets
|
||||||
|
id: retrieve-secrets
|
||||||
|
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
||||||
|
with:
|
||||||
|
keyvault: "bitwarden-prod-kv"
|
||||||
|
secrets: "crowdin-api-token"
|
||||||
|
|
||||||
|
- name: Upload Sources
|
||||||
|
uses: crowdin/github-action@e39093fd75daae7859c68eded4b43d42ec78d8ea # v1.3.2
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
||||||
|
with:
|
||||||
|
config: crowdin.yml
|
||||||
|
crowdin_branch_name: master
|
||||||
|
upload_sources: true
|
||||||
|
upload_translations: false
|
||||||
|
|
||||||
|
|
||||||
check-failures:
|
check-failures:
|
||||||
name: Check for failures
|
name: Check for failures
|
||||||
if: always()
|
if: always()
|
||||||
@@ -495,14 +542,19 @@ jobs:
|
|||||||
- android
|
- android
|
||||||
- f-droid
|
- f-droid
|
||||||
- ios
|
- ios
|
||||||
|
- crowdin-push
|
||||||
steps:
|
steps:
|
||||||
- name: Check if any job failed
|
- name: Check if any job failed
|
||||||
if: ${{ (github.ref == 'refs/heads/master') || (github.ref == 'refs/heads/rc') }}
|
if: |
|
||||||
|
(github.ref == 'refs/heads/master')
|
||||||
|
|| (github.ref == 'refs/heads/rc')
|
||||||
|
|| (github.ref == 'refs/heads/hotfix')
|
||||||
env:
|
env:
|
||||||
CLOC_STATUS: ${{ needs.cloc.result }}
|
CLOC_STATUS: ${{ needs.cloc.result }}
|
||||||
ANDROID_STATUS: ${{ needs.android.result }}
|
ANDROID_STATUS: ${{ needs.android.result }}
|
||||||
F_DROID_STATUS: ${{ needs.f-droid.result }}
|
F_DROID_STATUS: ${{ needs.f-droid.result }}
|
||||||
IOS_STATUS: ${{ needs.ios.result }}
|
IOS_STATUS: ${{ needs.ios.result }}
|
||||||
|
CROWDIN_PUSH_STATUS: ${{ needs.crowdin-push.result }}
|
||||||
run: |
|
run: |
|
||||||
if [ "$CLOC_STATUS" = "failure" ]; then
|
if [ "$CLOC_STATUS" = "failure" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
@@ -512,6 +564,8 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
elif [ "$IOS_STATUS" = "failure" ]; then
|
elif [ "$IOS_STATUS" = "failure" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
|
elif [ "$CROWDIN_PUSH_STATUS" = "failure" ]; then
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Login to Azure - Prod Subscription
|
- name: Login to Azure - Prod Subscription
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ name: Crowdin Sync
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs: {}
|
inputs: {}
|
||||||
# schedule:
|
schedule:
|
||||||
# - cron: '0 0 * * *'
|
- cron: '0 0 * * 5'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
crowdin-sync:
|
crowdin-sync:
|
||||||
30
.github/workflows/release.yml
vendored
30
.github/workflows/release.yml
vendored
@@ -3,25 +3,34 @@ name: Release
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
release_type:
|
||||||
|
description: 'Release Options'
|
||||||
|
required: true
|
||||||
|
default: 'Initial Release'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- Initial Release
|
||||||
|
- Redeploy
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Create Release
|
name: Create Release
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
outputs:
|
||||||
|
branch-name: ${{ steps.branch.outputs.branch-name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Branch check
|
- name: Branch check
|
||||||
run: |
|
run: |
|
||||||
if [[ "$GITHUB_REF" != "refs/heads/release" ]]; then
|
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix" ]]; then
|
||||||
echo "==================================="
|
echo "==================================="
|
||||||
echo "[!] Can only release from the 'release' branch"
|
echo "[!] Can only release from the 'rc' or 'hotfix' branches"
|
||||||
echo "==================================="
|
echo "==================================="
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
with:
|
|
||||||
ref: release
|
|
||||||
|
|
||||||
- name: Retrieve Mobile release version
|
- name: Retrieve Mobile release version
|
||||||
id: retrieve-mobile-version
|
id: retrieve-mobile-version
|
||||||
@@ -31,6 +40,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Check to make sure Mobile release version has been bumped
|
- name: Check to make sure Mobile release version has been bumped
|
||||||
|
if: ${{ github.event.inputs.release_type == 'Initial Release' }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
@@ -45,12 +55,18 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
- name: Get branch name
|
||||||
|
id: branch
|
||||||
|
run: |
|
||||||
|
BRANCH_NAME=$(basename ${{ github.ref }})
|
||||||
|
echo "::set-output name=branch-name::$BRANCH_NAME"
|
||||||
|
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74 # v2.14.0
|
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74 # v2.14.0
|
||||||
with:
|
with:
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
branch: release
|
branch: ${{ steps.branch.outputs.branch-name }}
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 # v2.8.5
|
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 # v2.8.5
|
||||||
@@ -74,15 +90,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
with:
|
|
||||||
ref: release
|
|
||||||
|
|
||||||
- name: Download F-Droid .apk artifact
|
- name: Download F-Droid .apk artifact
|
||||||
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74 # v2.14.0
|
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74 # v2.14.0
|
||||||
with:
|
with:
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
branch: release
|
branch: ${{ needs.release.outputs.branch-name }}
|
||||||
name: com.x8bit.bitwarden-fdroid.apk
|
name: com.x8bit.bitwarden-fdroid.apk
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
|
|||||||
83
.github/workflows/version-bump.yml
vendored
Normal file
83
.github/workflows/version-bump.yml
vendored
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
name: Version Bump
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version_number:
|
||||||
|
description: "New Version"
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
bump_version:
|
||||||
|
name: "Create version_bump_${{ github.event.inputs.version_number }} branch"
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout Branch
|
||||||
|
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
|
||||||
|
|
||||||
|
- name: Create Version Branch
|
||||||
|
run: |
|
||||||
|
git switch -c version_bump_${{ github.event.inputs.version_number }}
|
||||||
|
git push -u origin version_bump_${{ github.event.inputs.version_number }}
|
||||||
|
|
||||||
|
- name: Checkout Version Branch
|
||||||
|
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
|
||||||
|
with:
|
||||||
|
ref: version_bump_${{ github.event.inputs.version_number }}
|
||||||
|
|
||||||
|
- name: Bump Version - Android XML
|
||||||
|
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||||
|
with:
|
||||||
|
version: ${{ github.event.inputs.version_number }}
|
||||||
|
file_path: "./src/Android/Properties/AndroidManifest.xml"
|
||||||
|
|
||||||
|
- name: Bump Version - iOS.Autofill
|
||||||
|
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||||
|
with:
|
||||||
|
version: ${{ github.event.inputs.version_number }}
|
||||||
|
file_path: "./src/iOS.Autofill/Info.plist"
|
||||||
|
|
||||||
|
- name: Bump Version - iOS.Extension
|
||||||
|
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||||
|
with:
|
||||||
|
version: ${{ github.event.inputs.version_number }}
|
||||||
|
file_path: "./src/iOS.Extension/Info.plist"
|
||||||
|
|
||||||
|
- name: Bump Version - iOS
|
||||||
|
uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945
|
||||||
|
with:
|
||||||
|
version: ${{ github.event.inputs.version_number }}
|
||||||
|
file_path: "./src/iOS/Info.plist"
|
||||||
|
|
||||||
|
- name: Commit files
|
||||||
|
run: |
|
||||||
|
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config --local user.name "github-actions[bot]"
|
||||||
|
git commit -m "Bumped version to ${{ github.event.inputs.version_number }}" -a
|
||||||
|
|
||||||
|
- name: Push changes
|
||||||
|
run: git push -u origin version_bump_${{ github.event.inputs.version_number }}
|
||||||
|
|
||||||
|
- name: Create Version PR
|
||||||
|
env:
|
||||||
|
PR_BRANCH: "version_bump_${{ github.event.inputs.version_number }}"
|
||||||
|
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
BASE_BRANCH: master
|
||||||
|
TITLE: "Bump version to ${{ github.event.inputs.version_number }}"
|
||||||
|
run: |
|
||||||
|
gh pr create --title "$TITLE" \
|
||||||
|
--base "$BASE" \
|
||||||
|
--head "$PR_BRANCH" \
|
||||||
|
--label "version update" \
|
||||||
|
--label "automated pr" \
|
||||||
|
--body "
|
||||||
|
## Type of change
|
||||||
|
- [ ] Bug fix
|
||||||
|
- [ ] New feature development
|
||||||
|
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
|
||||||
|
- [ ] Build/deploy pipeline (DevOps)
|
||||||
|
- [X] Other
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Automated version bump to ${{ github.event.inputs.version_number }}"
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
project_id_env: _CROWDIN_PROJECT_ID
|
project_id_env: _CROWDIN_PROJECT_ID
|
||||||
api_token_env: CROWDIN_API_TOKEN
|
api_token_env: CROWDIN_API_TOKEN
|
||||||
|
preserve_hierarchy: true
|
||||||
files:
|
files:
|
||||||
- source: /src/App/Resources/AppResources.resx
|
- source: /src/App/Resources/AppResources.resx
|
||||||
|
dest: /src/App/Resources/%original_file_name%
|
||||||
translation: /src/App/Resources/AppResources.%two_letters_code%.resx
|
translation: /src/App/Resources/AppResources.%two_letters_code%.resx
|
||||||
update_option: update_as_unapproved
|
update_option: update_as_unapproved
|
||||||
languages_mapping:
|
languages_mapping:
|
||||||
@@ -13,6 +15,7 @@ files:
|
|||||||
en-GB: en-GB
|
en-GB: en-GB
|
||||||
en-IN: en-IN
|
en-IN: en-IN
|
||||||
- source: /store/apple/en/copy.resx
|
- source: /store/apple/en/copy.resx
|
||||||
|
dest: /store/apple/en/%original_file_name%
|
||||||
translation: /store/apple/%two_letters_code%/copy.resx
|
translation: /store/apple/%two_letters_code%/copy.resx
|
||||||
update_option: update_as_unapproved
|
update_option: update_as_unapproved
|
||||||
languages_mapping:
|
languages_mapping:
|
||||||
@@ -24,6 +27,7 @@ files:
|
|||||||
en-GB: en-GB
|
en-GB: en-GB
|
||||||
en-IN: en-IN
|
en-IN: en-IN
|
||||||
- source: /store/google/en/copy.resx
|
- source: /store/google/en/copy.resx
|
||||||
|
dest: /store/google/en/%original_file_name%
|
||||||
translation: /store/google/%two_letters_code%/copy.resx
|
translation: /store/google/%two_letters_code%/copy.resx
|
||||||
update_option: update_as_unapproved
|
update_option: update_as_unapproved
|
||||||
languages_mapping:
|
languages_mapping:
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Android.Views;
|
|||||||
using System;
|
using System;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
|
|
||||||
namespace Bit.Droid.Accessibility
|
namespace Bit.Droid.Accessibility
|
||||||
{
|
{
|
||||||
@@ -17,6 +18,7 @@ namespace Bit.Droid.Accessibility
|
|||||||
|
|
||||||
protected override void OnCreate(Bundle bundle)
|
protected override void OnCreate(Bundle bundle)
|
||||||
{
|
{
|
||||||
|
Intent?.Validate();
|
||||||
base.OnCreate(bundle);
|
base.OnCreate(bundle);
|
||||||
HandleIntent(Intent, 932473);
|
HandleIntent(Intent, 932473);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ namespace Bit.Droid.Accessibility
|
|||||||
// Rem. for "com.google.android.captiveportallogin": URL displayed in ActionBar subtitle without viewId.
|
// Rem. for "com.google.android.captiveportallogin": URL displayed in ActionBar subtitle without viewId.
|
||||||
new Browser("com.jamal2367.styx", "search"),
|
new Browser("com.jamal2367.styx", "search"),
|
||||||
new Browser("com.kiwibrowser.browser", "url_bar"),
|
new Browser("com.kiwibrowser.browser", "url_bar"),
|
||||||
|
new Browser("com.kiwibrowser.browser.dev", "url_bar"),
|
||||||
new Browser("com.microsoft.emmx", "url_bar"),
|
new Browser("com.microsoft.emmx", "url_bar"),
|
||||||
new Browser("com.microsoft.emmx.beta", "url_bar"),
|
new Browser("com.microsoft.emmx.beta", "url_bar"),
|
||||||
new Browser("com.microsoft.emmx.canary", "url_bar"),
|
new Browser("com.microsoft.emmx.canary", "url_bar"),
|
||||||
@@ -67,6 +68,7 @@ namespace Bit.Droid.Accessibility
|
|||||||
new Browser("com.opera.mini.native", "url_field"),
|
new Browser("com.opera.mini.native", "url_field"),
|
||||||
new Browser("com.opera.mini.native.beta", "url_field"),
|
new Browser("com.opera.mini.native.beta", "url_field"),
|
||||||
new Browser("com.opera.touch", "addressbarEdit"),
|
new Browser("com.opera.touch", "addressbarEdit"),
|
||||||
|
new Browser("com.qflair.browserq", "url"),
|
||||||
new Browser("com.qwant.liberty", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v4)
|
new Browser("com.qwant.liberty", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v4)
|
||||||
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
|
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
|
||||||
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
|
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
|
||||||
@@ -101,8 +103,10 @@ namespace Bit.Droid.Accessibility
|
|||||||
new Browser("org.mozilla.fennec_fdroid", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
new Browser("org.mozilla.fennec_fdroid", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||||
new Browser("org.mozilla.firefox", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
new Browser("org.mozilla.firefox", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||||
new Browser("org.mozilla.firefox_beta", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
new Browser("org.mozilla.firefox_beta", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||||
new Browser("org.mozilla.focus", "display_url"),
|
new Browser("org.mozilla.focus", "mozac_browser_toolbar_url_view,display_url"), // 2nd = Legacy
|
||||||
new Browser("org.mozilla.klar", "display_url"),
|
new Browser("org.mozilla.focus.beta", "mozac_browser_toolbar_url_view,display_url"), // 2nd = Legacy
|
||||||
|
new Browser("org.mozilla.focus.nightly", "mozac_browser_toolbar_url_view,display_url"), // 2nd = Legacy
|
||||||
|
new Browser("org.mozilla.klar", "mozac_browser_toolbar_url_view,display_url"), // 2nd = Legacy
|
||||||
new Browser("org.mozilla.reference.browser", "mozac_browser_toolbar_url_view"),
|
new Browser("org.mozilla.reference.browser", "mozac_browser_toolbar_url_view"),
|
||||||
new Browser("org.mozilla.rocket", "display_url"),
|
new Browser("org.mozilla.rocket", "display_url"),
|
||||||
new Browser("org.torproject.torbrowser", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v10.0.3)
|
new Browser("org.torproject.torbrowser", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v10.0.3)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using Android.Views;
|
|||||||
using Android.Views.Accessibility;
|
using Android.Views.Accessibility;
|
||||||
using Android.Widget;
|
using Android.Widget;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ namespace Bit.Droid.Accessibility
|
|||||||
private const string BitwardenPackage = "com.x8bit.bitwarden";
|
private const string BitwardenPackage = "com.x8bit.bitwarden";
|
||||||
private const string BitwardenWebsite = "vault.bitwarden.com";
|
private const string BitwardenWebsite = "vault.bitwarden.com";
|
||||||
|
|
||||||
private IStorageService _storageService;
|
private IStateService _stateService;
|
||||||
private IBroadcasterService _broadcasterService;
|
private IBroadcasterService _broadcasterService;
|
||||||
private DateTime? _lastSettingsReload = null;
|
private DateTime? _lastSettingsReload = null;
|
||||||
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
|
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
|
||||||
@@ -444,9 +443,9 @@ namespace Bit.Droid.Accessibility
|
|||||||
|
|
||||||
private void LoadServices()
|
private void LoadServices()
|
||||||
{
|
{
|
||||||
if (_storageService == null)
|
if (_stateService == null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
}
|
}
|
||||||
if (_broadcasterService == null)
|
if (_broadcasterService == null)
|
||||||
{
|
{
|
||||||
@@ -460,12 +459,12 @@ namespace Bit.Droid.Accessibility
|
|||||||
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
|
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
|
||||||
{
|
{
|
||||||
_lastSettingsReload = now;
|
_lastSettingsReload = now;
|
||||||
var uris = await _storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
var uris = await _stateService.GetAutofillBlacklistedUrisAsync();
|
||||||
if (uris != null)
|
if (uris != null)
|
||||||
{
|
{
|
||||||
_blacklistedUris = new HashSet<string>(uris);
|
_blacklistedUris = new HashSet<string>(uris);
|
||||||
}
|
}
|
||||||
var isAutoFillTileAdded = await _storageService.GetAsync<bool?>(Constants.AutofillTileAdded);
|
var isAutoFillTileAdded = await _stateService.GetAutofillTileAddedAsync();
|
||||||
AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault();
|
AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,7 @@
|
|||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Net.Http" Condition="'$(Configuration)'=='FDroid'" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Plugin.CurrentActivity">
|
<PackageReference Include="Plugin.CurrentActivity">
|
||||||
@@ -148,6 +149,8 @@
|
|||||||
<Compile Include="Utilities\ThemeHelpers.cs" />
|
<Compile Include="Utilities\ThemeHelpers.cs" />
|
||||||
<Compile Include="WebAuthCallbackActivity.cs" />
|
<Compile Include="WebAuthCallbackActivity.cs" />
|
||||||
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
||||||
|
<Compile Include="Services\ClipboardService.cs" />
|
||||||
|
<Compile Include="Utilities\IntentExtensions.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidAsset Include="Assets\FontAwesome.ttf" />
|
<AndroidAsset Include="Assets\FontAwesome.ttf" />
|
||||||
@@ -168,9 +171,11 @@
|
|||||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo_legacy.png" />
|
<AndroidResource Include="Resources\drawable-xxhdpi\logo_legacy.png" />
|
||||||
<AndroidResource Include="Resources\drawable-xxhdpi\logo_white_legacy.png" />
|
<AndroidResource Include="Resources\drawable-xxhdpi\logo_white_legacy.png" />
|
||||||
<AndroidResource Include="Resources\drawable\card.xml" />
|
<AndroidResource Include="Resources\drawable\card.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\cog.xml" />
|
<AndroidResource Include="Resources\drawable\cog_environment.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\cog_settings.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\icon.xml" />
|
<AndroidResource Include="Resources\drawable\icon.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
|
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\ic_warning.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\id.xml" />
|
<AndroidResource Include="Resources\drawable\id.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\info.xml" />
|
<AndroidResource Include="Resources\drawable\info.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\list_item_bg.xml" />
|
<AndroidResource Include="Resources\drawable\list_item_bg.xml" />
|
||||||
@@ -204,6 +209,8 @@
|
|||||||
<AndroidResource Include="Resources\values-night\styles.xml" />
|
<AndroidResource Include="Resources\values-night\styles.xml" />
|
||||||
<AndroidResource Include="Resources\values\styles.xml" />
|
<AndroidResource Include="Resources\values\styles.xml" />
|
||||||
<AndroidResource Include="Resources\values\colors.xml" />
|
<AndroidResource Include="Resources\values\colors.xml" />
|
||||||
|
<AndroidResource Include="Resources\values\manifest.xml" />
|
||||||
|
<AndroidResource Include="Resources\values-v30\manifest.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
|
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
|
||||||
@@ -269,5 +276,8 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</AndroidResource>
|
</AndroidResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Resources\values-v30\" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
@@ -37,6 +37,8 @@ namespace Bit.Droid.Autofill
|
|||||||
"com.duckduckgo.mobile.android",
|
"com.duckduckgo.mobile.android",
|
||||||
"com.google.android.googlequicksearchbox",
|
"com.google.android.googlequicksearchbox",
|
||||||
"org.mozilla.focus",
|
"org.mozilla.focus",
|
||||||
|
"org.mozilla.focus.beta",
|
||||||
|
"org.mozilla.focus.nightly",
|
||||||
"org.mozilla.klar",
|
"org.mozilla.klar",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,6 +73,7 @@ namespace Bit.Droid.Autofill
|
|||||||
"com.google.android.captiveportallogin",
|
"com.google.android.captiveportallogin",
|
||||||
"com.jamal2367.styx",
|
"com.jamal2367.styx",
|
||||||
"com.kiwibrowser.browser",
|
"com.kiwibrowser.browser",
|
||||||
|
"com.kiwibrowser.browser.dev",
|
||||||
"com.microsoft.emmx",
|
"com.microsoft.emmx",
|
||||||
"com.microsoft.emmx.beta",
|
"com.microsoft.emmx.beta",
|
||||||
"com.microsoft.emmx.canary",
|
"com.microsoft.emmx.canary",
|
||||||
@@ -84,6 +87,7 @@ namespace Bit.Droid.Autofill
|
|||||||
"com.opera.mini.native",
|
"com.opera.mini.native",
|
||||||
"com.opera.mini.native.beta",
|
"com.opera.mini.native.beta",
|
||||||
"com.opera.touch",
|
"com.opera.touch",
|
||||||
|
"com.qflair.browserq",
|
||||||
"com.qwant.liberty",
|
"com.qwant.liberty",
|
||||||
"com.sec.android.app.sbrowser",
|
"com.sec.android.app.sbrowser",
|
||||||
"com.sec.android.app.sbrowser.beta",
|
"com.sec.android.app.sbrowser.beta",
|
||||||
|
|||||||
@@ -22,9 +22,8 @@ namespace Bit.Droid.Autofill
|
|||||||
{
|
{
|
||||||
private ICipherService _cipherService;
|
private ICipherService _cipherService;
|
||||||
private IVaultTimeoutService _vaultTimeoutService;
|
private IVaultTimeoutService _vaultTimeoutService;
|
||||||
private IStorageService _storageService;
|
|
||||||
private IPolicyService _policyService;
|
private IPolicyService _policyService;
|
||||||
private IUserService _userService;
|
private IStateService _stateService;
|
||||||
|
|
||||||
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
|
||||||
FillCallback callback)
|
FillCallback callback)
|
||||||
@@ -38,18 +37,18 @@ namespace Bit.Droid.Autofill
|
|||||||
var parser = new Parser(structure, ApplicationContext);
|
var parser = new Parser(structure, ApplicationContext);
|
||||||
parser.Parse();
|
parser.Parse();
|
||||||
|
|
||||||
if (_storageService == null)
|
if (_stateService == null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
}
|
}
|
||||||
|
|
||||||
var shouldAutofill = await parser.ShouldAutofillAsync(_storageService);
|
var shouldAutofill = await parser.ShouldAutofillAsync(_stateService);
|
||||||
if (!shouldAutofill)
|
if (!shouldAutofill)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var inlineAutofillEnabled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
|
var inlineAutofillEnabled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
||||||
|
|
||||||
if (_vaultTimeoutService == null)
|
if (_vaultTimeoutService == null)
|
||||||
{
|
{
|
||||||
@@ -81,12 +80,12 @@ namespace Bit.Droid.Autofill
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_storageService == null)
|
if (_stateService == null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
}
|
}
|
||||||
|
|
||||||
var disableSavePrompt = await _storageService.GetAsync<bool?>(Constants.AutofillDisableSavePromptKey);
|
var disableSavePrompt = await _stateService.GetAutofillDisableSavePromptAsync();
|
||||||
if (disableSavePrompt.GetValueOrDefault())
|
if (disableSavePrompt.GetValueOrDefault())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -80,13 +80,13 @@ namespace Bit.Droid.Autofill
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ShouldAutofillAsync(IStorageService storageService)
|
public async Task<bool> ShouldAutofillAsync(IStateService stateService)
|
||||||
{
|
{
|
||||||
var fillable = !string.IsNullOrWhiteSpace(Uri) && !AutofillHelpers.BlacklistedUris.Contains(Uri) &&
|
var fillable = !string.IsNullOrWhiteSpace(Uri) && !AutofillHelpers.BlacklistedUris.Contains(Uri) &&
|
||||||
FieldCollection != null && FieldCollection.Fillable;
|
FieldCollection != null && FieldCollection.Fillable;
|
||||||
if (fillable)
|
if (fillable)
|
||||||
{
|
{
|
||||||
var blacklistedUris = await storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
|
var blacklistedUris = await stateService.GetAutofillBlacklistedUrisAsync();
|
||||||
if (blacklistedUris != null && blacklistedUris.Count > 0)
|
if (blacklistedUris != null && blacklistedUris.Count > 0)
|
||||||
{
|
{
|
||||||
fillable = !new HashSet<string>(blacklistedUris).Contains(Uri);
|
fillable = !new HashSet<string>(blacklistedUris).Contains(Uri);
|
||||||
|
|||||||
@@ -19,39 +19,23 @@ using System.Threading.Tasks;
|
|||||||
using AndroidX.Core.Content;
|
using AndroidX.Core.Content;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using ZXing.Net.Mobile.Android;
|
using ZXing.Net.Mobile.Android;
|
||||||
|
using Android.Util;
|
||||||
|
|
||||||
namespace Bit.Droid
|
namespace Bit.Droid
|
||||||
{
|
{
|
||||||
[Activity(
|
// Activity and IntentFilter declarations have been moved to Properties/AndroidManifest.xml
|
||||||
Label = "Bitwarden",
|
// They have been hardcoded so we can use the default LaunchMode on Android 11+
|
||||||
Icon = "@mipmap/ic_launcher",
|
// LaunchMode defined in values/manifest.xml for Android 10- and values-v30/manifest.xml for Android 11+
|
||||||
Theme = "@style/LaunchTheme",
|
// See https://github.com/bitwarden/mobile/pull/1673 for details
|
||||||
MainLauncher = true,
|
|
||||||
LaunchMode = LaunchMode.SingleTask,
|
|
||||||
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation |
|
|
||||||
ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden |
|
|
||||||
ConfigChanges.Navigation | ConfigChanges.UiMode)]
|
|
||||||
[IntentFilter(
|
|
||||||
new[] { Intent.ActionSend },
|
|
||||||
Categories = new[] { Intent.CategoryDefault },
|
|
||||||
DataMimeTypes = new[]
|
|
||||||
{
|
|
||||||
@"application/*",
|
|
||||||
@"image/*",
|
|
||||||
@"video/*",
|
|
||||||
@"text/*"
|
|
||||||
})]
|
|
||||||
[Register("com.x8bit.bitwarden.MainActivity")]
|
[Register("com.x8bit.bitwarden.MainActivity")]
|
||||||
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
||||||
{
|
{
|
||||||
private IDeviceActionService _deviceActionService;
|
private IDeviceActionService _deviceActionService;
|
||||||
private IMessagingService _messagingService;
|
private IMessagingService _messagingService;
|
||||||
private IBroadcasterService _broadcasterService;
|
private IBroadcasterService _broadcasterService;
|
||||||
private IUserService _userService;
|
private IStateService _stateService;
|
||||||
private IAppIdService _appIdService;
|
private IAppIdService _appIdService;
|
||||||
private IStorageService _storageService;
|
|
||||||
private IEventService _eventService;
|
private IEventService _eventService;
|
||||||
private PendingIntent _clearClipboardPendingIntent;
|
|
||||||
private PendingIntent _eventUploadPendingIntent;
|
private PendingIntent _eventUploadPendingIntent;
|
||||||
private AppOptions _appOptions;
|
private AppOptions _appOptions;
|
||||||
private string _activityKey = $"{nameof(MainActivity)}_{Java.Lang.JavaSystem.CurrentTimeMillis().ToString()}";
|
private string _activityKey = $"{nameof(MainActivity)}_{Java.Lang.JavaSystem.CurrentTimeMillis().ToString()}";
|
||||||
@@ -63,9 +47,6 @@ namespace Bit.Droid
|
|||||||
var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver));
|
var eventUploadIntent = new Intent(this, typeof(EventUploadReceiver));
|
||||||
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
|
_eventUploadPendingIntent = PendingIntent.GetBroadcast(this, 0, eventUploadIntent,
|
||||||
PendingIntentFlags.UpdateCurrent);
|
PendingIntentFlags.UpdateCurrent);
|
||||||
var clearClipboardIntent = new Intent(this, typeof(ClearClipboardAlarmReceiver));
|
|
||||||
_clearClipboardPendingIntent = PendingIntent.GetBroadcast(this, 0, clearClipboardIntent,
|
|
||||||
PendingIntentFlags.UpdateCurrent);
|
|
||||||
|
|
||||||
var policy = new StrictMode.ThreadPolicy.Builder().PermitAll().Build();
|
var policy = new StrictMode.ThreadPolicy.Builder().PermitAll().Build();
|
||||||
StrictMode.SetThreadPolicy(policy);
|
StrictMode.SetThreadPolicy(policy);
|
||||||
@@ -73,14 +54,16 @@ namespace Bit.Droid
|
|||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||||
|
|
||||||
TabLayoutResource = Resource.Layout.Tabbar;
|
TabLayoutResource = Resource.Layout.Tabbar;
|
||||||
ToolbarResource = Resource.Layout.Toolbar;
|
ToolbarResource = Resource.Layout.Toolbar;
|
||||||
|
|
||||||
|
// this needs to be called here before base.OnCreate(...)
|
||||||
|
Intent?.Validate();
|
||||||
|
|
||||||
base.OnCreate(savedInstanceState);
|
base.OnCreate(savedInstanceState);
|
||||||
if (!CoreHelpers.InDebugMode())
|
if (!CoreHelpers.InDebugMode())
|
||||||
{
|
{
|
||||||
@@ -88,7 +71,7 @@ namespace Bit.Droid
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if !FDROID
|
#if !FDROID
|
||||||
var appCenterHelper = new AppCenterHelper(_appIdService, _userService);
|
var appCenterHelper = new AppCenterHelper(_appIdService, _stateService);
|
||||||
var appCenterTask = appCenterHelper.InitAsync();
|
var appCenterTask = appCenterHelper.InitAsync();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -123,10 +106,6 @@ namespace Bit.Droid
|
|||||||
{
|
{
|
||||||
ExitApp();
|
ExitApp();
|
||||||
}
|
}
|
||||||
else if (message.Command == "copiedToClipboard")
|
|
||||||
{
|
|
||||||
var task = ClearClipboardAlarmAsync(message.Data as Tuple<string, int?, bool>);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +138,15 @@ namespace Bit.Droid
|
|||||||
base.OnNewIntent(intent);
|
base.OnNewIntent(intent);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (intent.GetBooleanExtra("generatorTile", false))
|
if (intent?.GetStringExtra("uri") is string uri)
|
||||||
|
{
|
||||||
|
_messagingService.Send("popAllAndGoToAutofillCiphers");
|
||||||
|
if (_appOptions != null)
|
||||||
|
{
|
||||||
|
_appOptions.Uri = uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (intent.GetBooleanExtra("generatorTile", false))
|
||||||
{
|
{
|
||||||
_messagingService.Send("popAllAndGoToTabGenerator");
|
_messagingService.Send("popAllAndGoToTabGenerator");
|
||||||
if (_appOptions != null)
|
if (_appOptions != null)
|
||||||
@@ -386,7 +373,7 @@ namespace Bit.Droid
|
|||||||
{
|
{
|
||||||
Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor);
|
Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor);
|
||||||
Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor);
|
Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor);
|
||||||
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(true), ThemeManager.OsDarkModeEnabled());
|
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(), ThemeManager.OsDarkModeEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExitApp()
|
private void ExitApp()
|
||||||
@@ -395,30 +382,6 @@ namespace Bit.Droid
|
|||||||
Java.Lang.JavaSystem.Exit(0);
|
Java.Lang.JavaSystem.Exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ClearClipboardAlarmAsync(Tuple<string, int?, bool> data)
|
|
||||||
{
|
|
||||||
if (data.Item3)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var clearMs = data.Item2;
|
|
||||||
if (clearMs == null)
|
|
||||||
{
|
|
||||||
var clearSeconds = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey);
|
|
||||||
if (clearSeconds != null)
|
|
||||||
{
|
|
||||||
clearMs = clearSeconds.Value * 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (clearMs == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs.Value;
|
|
||||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
|
||||||
alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartEventAlarm()
|
private void StartEventAlarm()
|
||||||
{
|
{
|
||||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ using Bit.Droid.Utilities;
|
|||||||
using Plugin.CurrentActivity;
|
using Plugin.CurrentActivity;
|
||||||
using Plugin.Fingerprint;
|
using Plugin.Fingerprint;
|
||||||
using Xamarin.Android.Net;
|
using Xamarin.Android.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net;
|
||||||
#if !FDROID
|
#if !FDROID
|
||||||
using Android.Gms.Security;
|
using Android.Gms.Security;
|
||||||
#endif
|
#endif
|
||||||
@@ -78,7 +80,8 @@ namespace Bit.Droid
|
|||||||
FFImageLoading.ImageService.Instance.Initialize(new FFImageLoading.Config.Configuration
|
FFImageLoading.ImageService.Instance.Initialize(new FFImageLoading.Config.Configuration
|
||||||
{
|
{
|
||||||
FadeAnimationEnabled = false,
|
FadeAnimationEnabled = false,
|
||||||
FadeAnimationForCachedImages = false
|
FadeAnimationForCachedImages = false,
|
||||||
|
HttpClient = new HttpClient(new AndroidClientHandler() { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate })
|
||||||
});
|
});
|
||||||
ZXing.Net.Mobile.Forms.Android.Platform.Init();
|
ZXing.Net.Mobile.Forms.Android.Platform.Init();
|
||||||
});
|
});
|
||||||
@@ -94,13 +97,14 @@ namespace Bit.Droid
|
|||||||
var secureStorageService = new SecureStorageService();
|
var secureStorageService = new SecureStorageService();
|
||||||
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
var cryptoPrimitiveService = new CryptoPrimitiveService();
|
||||||
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
|
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
|
||||||
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService,
|
var stateService = new StateService(mobileStorageService, secureStorageService);
|
||||||
|
var deviceActionService = new DeviceActionService(stateService, messagingService,
|
||||||
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
||||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
||||||
broadcasterService);
|
broadcasterService);
|
||||||
var biometricService = new BiometricService();
|
var biometricService = new BiometricService();
|
||||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||||
var cryptoService = new CryptoService(mobileStorageService, secureStorageService, cryptoFunctionService);
|
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||||
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
|
||||||
|
|
||||||
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
|
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
|
||||||
@@ -110,6 +114,8 @@ namespace Bit.Droid
|
|||||||
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
|
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
|
||||||
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
|
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
|
||||||
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
||||||
|
ServiceContainer.Register<IStateService>("stateService", stateService);
|
||||||
|
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(stateService));
|
||||||
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
|
||||||
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
|
||||||
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
|
||||||
@@ -128,7 +134,7 @@ namespace Bit.Droid
|
|||||||
ServiceContainer.Register<IPushNotificationListenerService>(
|
ServiceContainer.Register<IPushNotificationListenerService>(
|
||||||
"pushNotificationListenerService", notificationListenerService);
|
"pushNotificationListenerService", notificationListenerService);
|
||||||
var androidPushNotificationService = new AndroidPushNotificationService(
|
var androidPushNotificationService = new AndroidPushNotificationService(
|
||||||
mobileStorageService, notificationListenerService);
|
stateService, notificationListenerService);
|
||||||
ServiceContainer.Register<IPushNotificationService>(
|
ServiceContainer.Register<IPushNotificationService>(
|
||||||
"pushNotificationService", androidPushNotificationService);
|
"pushNotificationService", androidPushNotificationService);
|
||||||
#endif
|
#endif
|
||||||
@@ -144,10 +150,6 @@ namespace Bit.Droid
|
|||||||
|
|
||||||
private async Task BootstrapAsync()
|
private async Task BootstrapAsync()
|
||||||
{
|
{
|
||||||
var disableFavicon = await ServiceContainer.Resolve<IStorageService>("storageService")
|
|
||||||
.GetAsync<bool?>(Constants.DisableFaviconKey);
|
|
||||||
await ServiceContainer.Resolve<IStateService>("stateService").SaveAsync(
|
|
||||||
Constants.DisableFaviconKey, disableFavicon);
|
|
||||||
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
|
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<manifest
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2.15.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:versionCode="1"
|
|
||||||
android:versionName="2.14.3"
|
|
||||||
android:installLocation="internalOnly"
|
|
||||||
package="com.x8bit.bitwarden">
|
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
|
||||||
|
|
||||||
@@ -22,22 +16,9 @@
|
|||||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
||||||
|
|
||||||
<application
|
<application android:label="Bitwarden" android:theme="@style/LaunchTheme" android:allowBackup="false" tools:replace="android:allowBackup" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:networkSecurityConfig="@xml/network_security_config">
|
||||||
android:label="Bitwarden"
|
<provider android:name="androidx.core.content.FileProvider" android:authorities="com.x8bit.bitwarden.fileprovider" android:exported="false" android:grantUriPermissions="true">
|
||||||
android:theme="@style/LaunchTheme"
|
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths"/>
|
||||||
android:allowBackup="false"
|
|
||||||
tools:replace="android:allowBackup"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
|
||||||
android:networkSecurityConfig="@xml/network_security_config">
|
|
||||||
<provider
|
|
||||||
android:name="androidx.core.content.FileProvider"
|
|
||||||
android:authorities="com.x8bit.bitwarden.fileprovider"
|
|
||||||
android:exported="false"
|
|
||||||
android:grantUriPermissions="true">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
||||||
android:resource="@xml/filepaths" />
|
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<meta-data android:name="android.max_aspect" android:value="2.1"/>
|
<meta-data android:name="android.max_aspect" android:value="2.1"/>
|
||||||
@@ -49,6 +30,21 @@
|
|||||||
|
|
||||||
<!-- Support for LG "Dual Window" mode (for Android < 7.0 users) -->
|
<!-- Support for LG "Dual Window" mode (for Android < 7.0 users) -->
|
||||||
<meta-data android:name="com.lge.support.SPLIT_WINDOW" android:value="true"/>
|
<meta-data android:name="com.lge.support.SPLIT_WINDOW" android:value="true"/>
|
||||||
|
<!-- Declare MainActivity manually so we can set LaunchMode using API dependant resource -->
|
||||||
|
<activity android:name="com.x8bit.bitwarden.MainActivity" android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize|uiMode" android:exported="true" android:icon="@mipmap/ic_launcher" android:label="Bitwarden" android:launchMode="@integer/launchModeAPIlevel" android:theme="@style/LaunchTheme">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<data android:mimeType="application/*"/>
|
||||||
|
<data android:mimeType="image/*"/>
|
||||||
|
<data android:mimeType="video/*"/>
|
||||||
|
<data android:mimeType="text/*"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<!-- Package visibility (for Android 11+) -->
|
<!-- Package visibility (for Android 11+) -->
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ namespace Bit.Droid.Push
|
|||||||
{
|
{
|
||||||
public async override void OnNewToken(string token)
|
public async override void OnNewToken(string token)
|
||||||
{
|
{
|
||||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
var pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
var pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
||||||
|
|
||||||
await storageService.SaveAsync(Core.Constants.PushRegisteredTokenKey, token);
|
await stateService.SetPushRegisteredTokenAsync(token);
|
||||||
await pushNotificationService.RegisterAsync();
|
await pushNotificationService.RegisterAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using Android.App;
|
||||||
using Android.App;
|
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
@@ -14,9 +13,10 @@ namespace Bit.Droid.Receivers
|
|||||||
{
|
{
|
||||||
public override async void OnReceive(Context context, Intent intent)
|
public override async void OnReceive(Context context, Intent intent)
|
||||||
{
|
{
|
||||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
await AppHelpers.PerformUpdateTasksAsync(
|
||||||
await AppHelpers.PerformUpdateTasksAsync(ServiceContainer.Resolve<ISyncService>("syncService"),
|
ServiceContainer.Resolve<ISyncService>("syncService"),
|
||||||
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"), storageService);
|
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"),
|
||||||
|
ServiceContainer.Resolve<IStateService>("stateService"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,15 @@ namespace Bit.Droid.Renderers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for bug preventing long-press -> copy/paste on Android 11
|
||||||
|
// See https://issuetracker.google.com/issues/37095917
|
||||||
|
protected override void OnAttachedToWindow()
|
||||||
|
{
|
||||||
|
base.OnAttachedToWindow();
|
||||||
|
Control.Enabled = false;
|
||||||
|
Control.Enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Workaround for failure to disable text prediction on non-password fields
|
// Workaround for failure to disable text prediction on non-password fields
|
||||||
// see https://github.com/xamarin/Xamarin.Forms/issues/10857
|
// see https://github.com/xamarin/Xamarin.Forms/issues/10857
|
||||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
|||||||
9
src/Android/Resources/drawable/cog_environment.xml
Normal file
9
src/Android/Resources/drawable/cog_environment.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="1792"
|
||||||
|
android:viewportHeight="1792">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M1152,896q0,-106 -75,-181t-181,-75 -181,75 -75,181 75,181 181,75 181,-75 75,-181zM1664,787v222q0,12 -8,23t-20,13l-185,28q-19,54 -39,91 35,50 107,138 10,12 10,25t-9,23q-27,37 -99,108t-94,71q-12,0 -26,-9l-138,-108q-44,23 -91,38 -16,136 -29,186 -7,28 -36,28h-222q-14,0 -24.5,-8.5t-11.5,-21.5l-28,-184q-49,-16 -90,-37l-141,107q-10,9 -25,9 -14,0 -25,-11 -126,-114 -165,-168 -7,-10 -7,-23 0,-12 8,-23 15,-21 51,-66.5t54,-70.5q-27,-50 -41,-99l-183,-27q-13,-2 -21,-12.5t-8,-23.5v-222q0,-12 8,-23t19,-13l186,-28q14,-46 39,-92 -40,-57 -107,-138 -10,-12 -10,-24 0,-10 9,-23 26,-36 98.5,-107.5t94.5,-71.5q13,0 26,10l138,107q44,-23 91,-38 16,-136 29,-186 7,-28 36,-28h222q14,0 24.5,8.5t11.5,21.5l28,184q49,16 90,37l142,-107q9,-9 24,-9 13,0 25,10 129,119 165,170 7,8 7,22 0,12 -8,23 -15,21 -51,66.5t-54,70.5q26,50 41,98l183,28q13,2 21,12.5t8,23.5z" />
|
||||||
|
</vector>
|
||||||
9
src/Android/Resources/drawable/ic_warning.xml
Normal file
9
src/Android/Resources/drawable/ic_warning.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="22dp"
|
||||||
|
android:height="19dp"
|
||||||
|
android:viewportWidth="22"
|
||||||
|
android:viewportHeight="19">
|
||||||
|
<path
|
||||||
|
android:fillColor="#dd4b39"
|
||||||
|
android:pathData="M19.16 18.71H2.64c-0.36 0-0.72-0.09-1.03-0.27c-0.31-0.2-0.57-0.46-0.74-0.78c-0.18-0.32-0.27-0.67-0.27-1.04c0-0.36 0.1-0.72 0.28-1.03L9.14 1.1C9.32 0.76 9.58 0.5 9.89 0.32c0.3-0.18 0.65-0.28 1-0.28c0.36 0 0.7 0.1 1.02 0.28c0.3 0.18 0.56 0.44 0.74 0.75l8.26 14.51c0.18 0.31 0.28 0.67 0.28 1.03c0 0.37-0.09 0.72-0.26 1.04c-0.18 0.32-0.44 0.59-0.75 0.78c-0.31 0.18-0.66 0.28-1.02 0.27zM10.9 1.38c-0.13 0-0.26 0.04-0.38 0.1c-0.11 0.07-0.2 0.16-0.27 0.28L1.99 16.27c-0.07 0.11-0.1 0.24-0.1 0.36C1.9 16.76 1.92 16.9 2 17c0.06 0.12 0.16 0.22 0.27 0.3c0.12 0.06 0.25 0.1 0.38 0.1h16.52c0.13 0 0.26-0.04 0.37-0.1c0.12-0.08 0.21-0.18 0.28-0.3c0.06-0.1 0.1-0.23 0.1-0.36c0-0.12-0.04-0.25-0.1-0.36l-8.26-14.5c-0.07-0.13-0.17-0.22-0.28-0.29c-0.11-0.06-0.24-0.1-0.37-0.1zm0 11.42c-0.17 0-0.34-0.07-0.46-0.2c-0.12-0.12-0.19-0.29-0.19-0.46v-6.1c0-0.18 0.07-0.35 0.2-0.47c0.11-0.13 0.28-0.2 0.45-0.2c0.17 0 0.33 0.07 0.45 0.2c0.12 0.12 0.19 0.3 0.19 0.47v6.1c0 0.17-0.07 0.34-0.19 0.47c-0.12 0.12-0.28 0.2-0.45 0.2zm0 3.3c0.42 0 0.76-0.36 0.76-0.8c0-0.43-0.34-0.78-0.76-0.78c-0.43 0-0.77 0.35-0.77 0.79c0 0.43 0.34 0.79 0.77 0.79z"/>
|
||||||
|
</vector>
|
||||||
4
src/Android/Resources/values-v30/manifest.xml
Normal file
4
src/Android/Resources/values-v30/manifest.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<resources>
|
||||||
|
<integer name="launchModeAPIlevel">0</integer>
|
||||||
|
</resources>
|
||||||
4
src/Android/Resources/values/manifest.xml
Normal file
4
src/Android/Resources/values/manifest.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<resources>
|
||||||
|
<integer name="launchModeAPIlevel">2</integer>
|
||||||
|
</resources>
|
||||||
@@ -77,6 +77,9 @@
|
|||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.kiwibrowser.browser"
|
android:name="com.kiwibrowser.browser"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.kiwibrowser.browser.dev"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.microsoft.emmx"
|
android:name="com.microsoft.emmx"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
@@ -116,6 +119,9 @@
|
|||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.opera.touch"
|
android:name="com.opera.touch"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.qflair.browserq"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.qwant.liberty"
|
android:name="com.qwant.liberty"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#if !FDROID
|
#if !FDROID
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AndroidX.Core.App;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -10,25 +10,27 @@ namespace Bit.Droid.Services
|
|||||||
{
|
{
|
||||||
public class AndroidPushNotificationService : IPushNotificationService
|
public class AndroidPushNotificationService : IPushNotificationService
|
||||||
{
|
{
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IPushNotificationListenerService _pushNotificationListenerService;
|
private readonly IPushNotificationListenerService _pushNotificationListenerService;
|
||||||
|
|
||||||
public AndroidPushNotificationService(
|
public AndroidPushNotificationService(
|
||||||
IStorageService storageService,
|
IStateService stateService,
|
||||||
IPushNotificationListenerService pushNotificationListenerService)
|
IPushNotificationListenerService pushNotificationListenerService)
|
||||||
{
|
{
|
||||||
_storageService = storageService;
|
_stateService = stateService;
|
||||||
_pushNotificationListenerService = pushNotificationListenerService;
|
_pushNotificationListenerService = pushNotificationListenerService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsRegisteredForPush => NotificationManagerCompat.From(Android.App.Application.Context)?.AreNotificationsEnabled() ?? false;
|
||||||
|
|
||||||
public async Task<string> GetTokenAsync()
|
public async Task<string> GetTokenAsync()
|
||||||
{
|
{
|
||||||
return await _storageService.GetAsync<string>(Constants.PushCurrentTokenKey);
|
return await _stateService.GetPushCurrentTokenAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RegisterAsync()
|
public async Task RegisterAsync()
|
||||||
{
|
{
|
||||||
var registeredToken = await _storageService.GetAsync<string>(Constants.PushRegisteredTokenKey);
|
var registeredToken = await _stateService.GetPushRegisteredTokenAsync();
|
||||||
var currentToken = await GetTokenAsync();
|
var currentToken = await GetTokenAsync();
|
||||||
if (!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != currentToken)
|
if (!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != currentToken)
|
||||||
{
|
{
|
||||||
@@ -36,7 +38,7 @@ namespace Bit.Droid.Services
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.PushLastRegistrationDateKey, DateTime.UtcNow);
|
await _stateService.SetPushLastRegistrationDateAsync(DateTime.UtcNow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
57
src/Android/Services/ClipboardService.cs
Normal file
57
src/Android/Services/ClipboardService.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Bit.Core;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Droid.Receivers;
|
||||||
|
using Plugin.CurrentActivity;
|
||||||
|
using Xamarin.Essentials;
|
||||||
|
|
||||||
|
namespace Bit.Droid.Services
|
||||||
|
{
|
||||||
|
public class ClipboardService : IClipboardService
|
||||||
|
{
|
||||||
|
private readonly IStateService _stateService;
|
||||||
|
private readonly Lazy<PendingIntent> _clearClipboardPendingIntent;
|
||||||
|
|
||||||
|
public ClipboardService(IStateService stateService)
|
||||||
|
{
|
||||||
|
_stateService = stateService;
|
||||||
|
|
||||||
|
_clearClipboardPendingIntent = new Lazy<PendingIntent>(() =>
|
||||||
|
PendingIntent.GetBroadcast(CrossCurrentActivity.Current.Activity,
|
||||||
|
0,
|
||||||
|
new Intent(CrossCurrentActivity.Current.Activity, typeof(ClearClipboardAlarmReceiver)),
|
||||||
|
PendingIntentFlags.UpdateCurrent));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CopyTextAsync(string text, int expiresInMs = -1)
|
||||||
|
{
|
||||||
|
await Clipboard.SetTextAsync(text);
|
||||||
|
|
||||||
|
await ClearClipboardAlarmAsync(expiresInMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ClearClipboardAlarmAsync(int expiresInMs = -1)
|
||||||
|
{
|
||||||
|
var clearMs = expiresInMs;
|
||||||
|
if (clearMs < 0)
|
||||||
|
{
|
||||||
|
// if not set then we need to check if the user set this config
|
||||||
|
var clearSeconds = await _stateService.GetClearClipboardAsync();
|
||||||
|
if (clearSeconds != null)
|
||||||
|
{
|
||||||
|
clearMs = clearSeconds.Value * 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (clearMs < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + clearMs;
|
||||||
|
var alarmManager = CrossCurrentActivity.Current.Activity.GetSystemService(Context.AlarmService) as AlarmManager;
|
||||||
|
alarmManager.Set(AlarmType.Rtc, triggerMs, _clearClipboardPendingIntent.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ namespace Bit.Droid.Services
|
|||||||
{
|
{
|
||||||
public class DeviceActionService : IDeviceActionService
|
public class DeviceActionService : IDeviceActionService
|
||||||
{
|
{
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly Func<IEventService> _eventServiceFunc;
|
private readonly Func<IEventService> _eventServiceFunc;
|
||||||
@@ -43,12 +43,12 @@ namespace Bit.Droid.Services
|
|||||||
private string _userAgent;
|
private string _userAgent;
|
||||||
|
|
||||||
public DeviceActionService(
|
public DeviceActionService(
|
||||||
IStorageService storageService,
|
IStateService stateService,
|
||||||
IMessagingService messagingService,
|
IMessagingService messagingService,
|
||||||
IBroadcasterService broadcasterService,
|
IBroadcasterService broadcasterService,
|
||||||
Func<IEventService> eventServiceFunc)
|
Func<IEventService> eventServiceFunc)
|
||||||
{
|
{
|
||||||
_storageService = storageService;
|
_stateService = stateService;
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
_broadcasterService = broadcasterService;
|
_broadcasterService = broadcasterService;
|
||||||
_eventServiceFunc = eventServiceFunc;
|
_eventServiceFunc = eventServiceFunc;
|
||||||
@@ -250,7 +250,7 @@ namespace Bit.Droid.Services
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
|
DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
|
||||||
await _storageService.SaveAsync(Constants.LastFileCacheClearKey, DateTime.UtcNow);
|
await _stateService.SetLastFileCacheClearAsync(DateTime.UtcNow);
|
||||||
}
|
}
|
||||||
catch (Exception) { }
|
catch (Exception) { }
|
||||||
}
|
}
|
||||||
@@ -833,9 +833,8 @@ namespace Bit.Droid.Services
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(cipher?.Login?.Totp))
|
if (!string.IsNullOrWhiteSpace(cipher?.Login?.Totp))
|
||||||
{
|
{
|
||||||
var userService = ServiceContainer.Resolve<IUserService>("userService");
|
var autoCopyDisabled = await _stateService.GetDisableAutoTotpCopyAsync();
|
||||||
var autoCopyDisabled = await _storageService.GetAsync<bool?>(Constants.DisableAutoTotpCopyKey);
|
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||||
var canAccessPremium = await userService.CanAccessPremiumAsync();
|
|
||||||
if ((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault())
|
if ((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault())
|
||||||
{
|
{
|
||||||
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using Android.Content;
|
|||||||
using Android.Runtime;
|
using Android.Runtime;
|
||||||
using Android.Service.QuickSettings;
|
using Android.Service.QuickSettings;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Droid.Accessibility;
|
using Bit.Droid.Accessibility;
|
||||||
@@ -18,7 +17,7 @@ namespace Bit.Droid.Tile
|
|||||||
[Register("com.x8bit.bitwarden.AutofillTileService")]
|
[Register("com.x8bit.bitwarden.AutofillTileService")]
|
||||||
public class AutofillTileService : TileService
|
public class AutofillTileService : TileService
|
||||||
{
|
{
|
||||||
private IStorageService _storageService;
|
private IStateService _stateService;
|
||||||
|
|
||||||
public override void OnTileAdded()
|
public override void OnTileAdded()
|
||||||
{
|
{
|
||||||
@@ -59,11 +58,11 @@ namespace Bit.Droid.Tile
|
|||||||
private void SetTileAdded(bool isAdded)
|
private void SetTileAdded(bool isAdded)
|
||||||
{
|
{
|
||||||
AccessibilityHelpers.IsAutofillTileAdded = isAdded;
|
AccessibilityHelpers.IsAutofillTileAdded = isAdded;
|
||||||
if (_storageService == null)
|
if (_stateService == null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
}
|
}
|
||||||
_storageService.SaveAsync(Constants.AutofillTileAdded, isAdded);
|
_stateService.SetAutofillTileAddedAsync(isAdded);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ScanAndFill()
|
private void ScanAndFill()
|
||||||
|
|||||||
@@ -12,22 +12,22 @@ namespace Bit.Droid.Utilities
|
|||||||
private const string AppSecret = "d3834185-b4a6-4347-9047-b86c65293d42";
|
private const string AppSecret = "d3834185-b4a6-4347-9047-b86c65293d42";
|
||||||
|
|
||||||
private readonly IAppIdService _appIdService;
|
private readonly IAppIdService _appIdService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
|
|
||||||
private string _userId;
|
private string _userId;
|
||||||
private string _appId;
|
private string _appId;
|
||||||
|
|
||||||
public AppCenterHelper(
|
public AppCenterHelper(
|
||||||
IAppIdService appIdService,
|
IAppIdService appIdService,
|
||||||
IUserService userService)
|
IStateService stateService)
|
||||||
{
|
{
|
||||||
_appIdService = appIdService;
|
_appIdService = appIdService;
|
||||||
_userService = userService;
|
_stateService = stateService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
_userId = await _userService.GetUserIdAsync();
|
_userId = await _stateService.GetActiveUserIdAsync();
|
||||||
_appId = await _appIdService.GetAppIdAsync();
|
_appId = await _appIdService.GetAppIdAsync();
|
||||||
|
|
||||||
AppCenter.Start(AppSecret, typeof(Crashes));
|
AppCenter.Start(AppSecret, typeof(Crashes));
|
||||||
|
|||||||
22
src/Android/Utilities/IntentExtensions.cs
Normal file
22
src/Android/Utilities/IntentExtensions.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using Android.Content;
|
||||||
|
using Android.OS;
|
||||||
|
|
||||||
|
namespace Bit.Droid.Utilities
|
||||||
|
{
|
||||||
|
public static class IntentExtensions
|
||||||
|
{
|
||||||
|
public static void Validate(this Intent intent)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check if getting the bundle of the extras causes any exception when unparcelling
|
||||||
|
// Note: getting the bundle like this will cause to call unparcel() internally
|
||||||
|
var b = intent?.Extras?.GetBundle("trashstringwhichhasnousebuttocheckunparcel");
|
||||||
|
}
|
||||||
|
catch (BadParcelableException)
|
||||||
|
{
|
||||||
|
intent.ReplaceExtras((Bundle)null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content.PM;
|
using Android.Content.PM;
|
||||||
|
using Android.OS;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
|
|
||||||
namespace Bit.Droid
|
namespace Bit.Droid
|
||||||
{
|
{
|
||||||
@@ -9,5 +11,12 @@ namespace Bit.Droid
|
|||||||
[IntentFilter(new[] { Android.Content.Intent.ActionView },
|
[IntentFilter(new[] { Android.Content.Intent.ActionView },
|
||||||
Categories = new[] { Android.Content.Intent.CategoryDefault, Android.Content.Intent.CategoryBrowsable },
|
Categories = new[] { Android.Content.Intent.CategoryDefault, Android.Content.Intent.CategoryBrowsable },
|
||||||
DataScheme = "bitwarden")]
|
DataScheme = "bitwarden")]
|
||||||
public class WebAuthCallbackActivity : Xamarin.Essentials.WebAuthenticatorCallbackActivity { }
|
public class WebAuthCallbackActivity : Xamarin.Essentials.WebAuthenticatorCallbackActivity
|
||||||
|
{
|
||||||
|
protected override void OnCreate(Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
Intent?.Validate();
|
||||||
|
base.OnCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,5 +7,9 @@ namespace Bit.App.Abstractions
|
|||||||
string[] ProtectedFields { get; }
|
string[] ProtectedFields { get; }
|
||||||
|
|
||||||
Task<bool> ShowPasswordPromptAsync();
|
Task<bool> ShowPasswordPromptAsync();
|
||||||
|
|
||||||
|
Task<(string password, bool valid)> ShowPasswordPromptAndGetItAsync();
|
||||||
|
|
||||||
|
Task<bool> Enabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace Bit.App.Abstractions
|
|||||||
{
|
{
|
||||||
public interface IPushNotificationService
|
public interface IPushNotificationService
|
||||||
{
|
{
|
||||||
|
bool IsRegisteredForPush { get; }
|
||||||
Task<string> GetTokenAsync();
|
Task<string> GetTokenAsync();
|
||||||
Task RegisterAsync();
|
Task RegisterAsync();
|
||||||
Task UnregisterAsync();
|
Task UnregisterAsync();
|
||||||
|
|||||||
@@ -15,9 +15,11 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
|
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
|
||||||
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
||||||
|
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.80.3" />
|
||||||
|
<PackageReference Include="Xamarin.CommunityToolkit" Version="1.3.2" />
|
||||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
||||||
<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.2125" />
|
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2291" />
|
||||||
<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" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -120,6 +122,7 @@
|
|||||||
<DependentUpon>SendGroupingsPage.xaml</DependentUpon>
|
<DependentUpon>SendGroupingsPage.xaml</DependentUpon>
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Remove="Pages\Accounts\AccountsPopupPage.xaml.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -131,6 +134,7 @@
|
|||||||
<EmbeddedResource Update="Controls\CipherViewCell\CipherViewCell.xaml">
|
<EmbeddedResource Update="Controls\CipherViewCell\CipherViewCell.xaml">
|
||||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Remove="Pages\Accounts\AccountsPopupPage.xaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ using Bit.App.Pages;
|
|||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.App.Services;
|
using Bit.App.Services;
|
||||||
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.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -17,7 +17,6 @@ namespace Bit.App
|
|||||||
{
|
{
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
private readonly IUserService _userService;
|
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
@@ -25,7 +24,6 @@ namespace Bit.App
|
|||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IStorageService _secureStorageService;
|
private readonly IStorageService _secureStorageService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
|
|
||||||
@@ -39,7 +37,6 @@ namespace Bit.App
|
|||||||
Current = this;
|
Current = this;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
@@ -47,7 +44,6 @@ namespace Bit.App
|
|||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
|
|
||||||
@@ -84,8 +80,12 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
else if (message.Command == "logout")
|
else if (message.Command == "logout")
|
||||||
{
|
{
|
||||||
|
var extras = message.Data as Tuple<string, bool, bool>;
|
||||||
|
var userId = extras?.Item1;
|
||||||
|
var userInitiated = extras?.Item2;
|
||||||
|
var expired = extras?.Item3;
|
||||||
Device.BeginInvokeOnMainThread(async () =>
|
Device.BeginInvokeOnMainThread(async () =>
|
||||||
await LogOutAsync((message.Data as bool?).GetValueOrDefault()));
|
await LogOutAsync(userId, userInitiated, expired));
|
||||||
}
|
}
|
||||||
else if (message.Command == "loggedOut")
|
else if (message.Command == "loggedOut")
|
||||||
{
|
{
|
||||||
@@ -106,6 +106,18 @@ namespace Bit.App
|
|||||||
await SleptAsync();
|
await SleptAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (message.Command == "addAccount")
|
||||||
|
{
|
||||||
|
await AddAccount();
|
||||||
|
}
|
||||||
|
else if (message.Command == "accountAdded")
|
||||||
|
{
|
||||||
|
UpdateTheme();
|
||||||
|
}
|
||||||
|
else if (message.Command == "switchedAccount")
|
||||||
|
{
|
||||||
|
await SwitchedAccountAsync();
|
||||||
|
}
|
||||||
else if (message.Command == "migrated")
|
else if (message.Command == "migrated")
|
||||||
{
|
{
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
@@ -113,7 +125,8 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
else if (message.Command == "popAllAndGoToTabGenerator" ||
|
else if (message.Command == "popAllAndGoToTabGenerator" ||
|
||||||
message.Command == "popAllAndGoToTabMyVault" ||
|
message.Command == "popAllAndGoToTabMyVault" ||
|
||||||
message.Command == "popAllAndGoToTabSend")
|
message.Command == "popAllAndGoToTabSend" ||
|
||||||
|
message.Command == "popAllAndGoToAutofillCiphers")
|
||||||
{
|
{
|
||||||
Device.BeginInvokeOnMainThread(async () =>
|
Device.BeginInvokeOnMainThread(async () =>
|
||||||
{
|
{
|
||||||
@@ -123,7 +136,11 @@ namespace Bit.App
|
|||||||
{
|
{
|
||||||
await tabsPage.Navigation.PopModalAsync(false);
|
await tabsPage.Navigation.PopModalAsync(false);
|
||||||
}
|
}
|
||||||
if (message.Command == "popAllAndGoToTabMyVault")
|
if (message.Command == "popAllAndGoToAutofillCiphers")
|
||||||
|
{
|
||||||
|
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
|
||||||
|
}
|
||||||
|
else if (message.Command == "popAllAndGoToTabMyVault")
|
||||||
{
|
{
|
||||||
Options.MyVaultTile = false;
|
Options.MyVaultTile = false;
|
||||||
tabsPage.ResetToVaultPage();
|
tabsPage.ResetToVaultPage();
|
||||||
@@ -140,6 +157,15 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else if (message.Command == "convertAccountToKeyConnector")
|
||||||
|
{
|
||||||
|
Device.BeginInvokeOnMainThread(async () =>
|
||||||
|
{
|
||||||
|
await Application.Current.MainPage.Navigation.PushModalAsync(
|
||||||
|
new NavigationPage(new RemoveMasterPasswordPage()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +179,7 @@ namespace Bit.App
|
|||||||
if (string.IsNullOrWhiteSpace(Options.Uri))
|
if (string.IsNullOrWhiteSpace(Options.Uri))
|
||||||
{
|
{
|
||||||
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
|
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
|
||||||
_storageService);
|
_stateService);
|
||||||
if (!updated)
|
if (!updated)
|
||||||
{
|
{
|
||||||
SyncIfNeeded();
|
SyncIfNeeded();
|
||||||
@@ -162,6 +188,8 @@ namespace Bit.App
|
|||||||
if (Device.RuntimePlatform == Device.Android)
|
if (Device.RuntimePlatform == Device.Android)
|
||||||
{
|
{
|
||||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
|
// Reset delay on every start
|
||||||
|
_vaultTimeoutService.DelayLockAndLogoutMs = null;
|
||||||
}
|
}
|
||||||
_messagingService.Send("startEventTimer");
|
_messagingService.Send("startEventTimer");
|
||||||
}
|
}
|
||||||
@@ -175,7 +203,7 @@ namespace Bit.App
|
|||||||
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
||||||
if (!isLocked)
|
if (!isLocked)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
|
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||||
}
|
}
|
||||||
SetTabsPageFromAutofill(isLocked);
|
SetTabsPageFromAutofill(isLocked);
|
||||||
await SleptAsync();
|
await SleptAsync();
|
||||||
@@ -194,7 +222,7 @@ namespace Bit.App
|
|||||||
|
|
||||||
private async Task SleptAsync()
|
private async Task SleptAsync()
|
||||||
{
|
{
|
||||||
await HandleVaultTimeoutAsync();
|
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
_messagingService.Send("stopEventTimer");
|
_messagingService.Send("stopEventTimer");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,28 +248,66 @@ namespace Bit.App
|
|||||||
new System.Globalization.UmAlQuraCalendar();
|
new System.Globalization.UmAlQuraCalendar();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LogOutAsync(bool expired)
|
private async Task LogOutAsync(string userId, bool? userInitiated, bool? expired)
|
||||||
{
|
{
|
||||||
await AppHelpers.LogOutAsync();
|
await AppHelpers.LogOutAsync(userId, userInitiated.GetValueOrDefault(true));
|
||||||
|
await SetMainPageAsync();
|
||||||
_authService.LogOut(() =>
|
_authService.LogOut(() =>
|
||||||
{
|
{
|
||||||
Current.MainPage = new HomePage();
|
if (expired.GetValueOrDefault())
|
||||||
if (expired)
|
|
||||||
{
|
{
|
||||||
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
|
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task AddAccount()
|
||||||
|
{
|
||||||
|
Device.BeginInvokeOnMainThread(async () =>
|
||||||
|
{
|
||||||
|
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SwitchedAccountAsync()
|
||||||
|
{
|
||||||
|
await AppHelpers.OnAccountSwitchAsync();
|
||||||
|
var shouldTimeout = await _vaultTimeoutService.ShouldTimeoutAsync();
|
||||||
|
Device.BeginInvokeOnMainThread(async () =>
|
||||||
|
{
|
||||||
|
if (shouldTimeout)
|
||||||
|
{
|
||||||
|
await _vaultTimeoutService.ExecuteTimeoutActionAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await SetMainPageAsync();
|
||||||
|
}
|
||||||
|
await Task.Delay(50);
|
||||||
|
UpdateTheme();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SetMainPageAsync()
|
private async Task SetMainPageAsync()
|
||||||
{
|
{
|
||||||
var authed = await _userService.IsAuthenticatedAsync();
|
var authed = await _stateService.IsAuthenticatedAsync();
|
||||||
if (authed)
|
if (authed)
|
||||||
{
|
{
|
||||||
if (await _vaultTimeoutService.IsLockedAsync())
|
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
||||||
|
var shouldTimeout = await _vaultTimeoutService.ShouldTimeoutAsync();
|
||||||
|
var vaultTimeoutAction = await _stateService.GetVaultTimeoutActionAsync();
|
||||||
|
if (isLocked || shouldTimeout)
|
||||||
|
{
|
||||||
|
if (vaultTimeoutAction == "logOut")
|
||||||
|
{
|
||||||
|
var email = await _stateService.GetEmailAsync();
|
||||||
|
Current.MainPage = new NavigationPage(new LoginPage(email, Options));
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Current.MainPage = new NavigationPage(new LockPage(Options));
|
Current.MainPage = new NavigationPage(new LockPage(Options));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (Options.FromAutofillFramework && Options.SaveType.HasValue)
|
else if (Options.FromAutofillFramework && Options.SaveType.HasValue)
|
||||||
{
|
{
|
||||||
Current.MainPage = new NavigationPage(new AddEditPage(appOptions: Options));
|
Current.MainPage = new NavigationPage(new AddEditPage(appOptions: Options));
|
||||||
@@ -261,40 +327,13 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Current.MainPage = new HomePage(Options);
|
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleVaultTimeoutAsync()
|
|
||||||
{
|
|
||||||
if (await _vaultTimeoutService.IsLockedAsync())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var authed = await _userService.IsAuthenticatedAsync();
|
|
||||||
if (!authed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
|
||||||
vaultTimeout = vaultTimeout.GetValueOrDefault(-1);
|
|
||||||
if (vaultTimeout == 0)
|
|
||||||
{
|
|
||||||
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey);
|
|
||||||
if (action == "logOut")
|
|
||||||
{
|
|
||||||
await _vaultTimeoutService.LogOutAsync();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _vaultTimeoutService.LockAsync(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ClearCacheIfNeededAsync()
|
private async Task ClearCacheIfNeededAsync()
|
||||||
{
|
{
|
||||||
var lastClear = await _storageService.GetAsync<DateTime?>(Constants.LastFileCacheClearKey);
|
var lastClear = await _stateService.GetLastFileCacheClearAsync();
|
||||||
if ((DateTime.UtcNow - lastClear.GetValueOrDefault(DateTime.MinValue)).TotalDays >= 1)
|
if ((DateTime.UtcNow - lastClear.GetValueOrDefault(DateTime.MinValue)).TotalDays >= 1)
|
||||||
{
|
{
|
||||||
var task = Task.Run(() => _deviceActionService.ClearCacheAsync());
|
var task = Task.Run(() => _deviceActionService.ClearCacheAsync());
|
||||||
@@ -337,12 +376,12 @@ namespace Bit.App
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
SetCulture();
|
SetCulture();
|
||||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
ThemeManager.SetTheme(Current.Resources);
|
||||||
Current.RequestedThemeChanged += (s, a) =>
|
Current.RequestedThemeChanged += (s, a) =>
|
||||||
{
|
{
|
||||||
UpdateTheme();
|
UpdateTheme();
|
||||||
};
|
};
|
||||||
Current.MainPage = new HomePage();
|
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||||
var mainPageTask = SetMainPageAsync();
|
var mainPageTask = SetMainPageAsync();
|
||||||
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
||||||
}
|
}
|
||||||
@@ -368,17 +407,16 @@ namespace Bit.App
|
|||||||
{
|
{
|
||||||
Device.BeginInvokeOnMainThread(() =>
|
Device.BeginInvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
ThemeManager.SetTheme(Current.Resources);
|
||||||
_messagingService.Send("updatedTheme");
|
_messagingService.Send("updatedTheme");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LockedAsync(bool autoPromptBiometric)
|
private async Task LockedAsync(bool autoPromptBiometric)
|
||||||
{
|
{
|
||||||
await _stateService.PurgeAsync();
|
|
||||||
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
|
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
var vaultTimeout = await _stateService.GetVaultTimeoutAsync();
|
||||||
if (vaultTimeout == 0)
|
if (vaultTimeout == 0)
|
||||||
{
|
{
|
||||||
autoPromptBiometric = false;
|
autoPromptBiometric = false;
|
||||||
@@ -408,7 +446,7 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await _storageService.SaveAsync(Constants.PreviousPageKey, lastPageBeforeLock);
|
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
|
||||||
var lockPage = new LockPage(Options, autoPromptBiometric);
|
var lockPage = new LockPage(Options, autoPromptBiometric);
|
||||||
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
|
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
|
||||||
}
|
}
|
||||||
|
|||||||
101
src/App/Controls/AccountViewCell/AccountViewCell.xaml
Normal file
101
src/App/Controls/AccountViewCell/AccountViewCell.xaml
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||||
|
x:Class="Bit.App.Controls.AccountViewCell"
|
||||||
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
|
x:DataType="controls:AccountViewCellViewModel">
|
||||||
|
<Grid RowSpacing="0"
|
||||||
|
ColumnSpacing="0">
|
||||||
|
|
||||||
|
<Grid.Resources>
|
||||||
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
|
</Grid.Resources>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
IsVisible="{Binding IsAccount}"
|
||||||
|
VerticalOptions="CenterAndExpand">
|
||||||
|
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
RowSpacing="3"
|
||||||
|
Margin="12,0,0,0"
|
||||||
|
VerticalOptions="Center">
|
||||||
|
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Label
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="0"
|
||||||
|
Text="{Binding AccountView.Email}"
|
||||||
|
StyleClass="list-title"
|
||||||
|
LineBreakMode="TailTruncation" />
|
||||||
|
<Label
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.Row="1"
|
||||||
|
Text="{Binding AccountView.Hostname}"
|
||||||
|
StyleClass="list-sub"
|
||||||
|
LineBreakMode="TailTruncation" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Label
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.Row="0"
|
||||||
|
Text="{Binding AuthStatusText}"
|
||||||
|
FontSize="Small"
|
||||||
|
FontAttributes="Italic"
|
||||||
|
LineBreakMode="NoWrap"
|
||||||
|
HorizontalOptions="End"
|
||||||
|
Margin="10,0,20,0"
|
||||||
|
VerticalOptions="Center" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
IsVisible="{Binding IsAccount, Converter={StaticResource inverseBool}}"
|
||||||
|
VerticalOptions="CenterAndExpand">
|
||||||
|
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="36" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Image
|
||||||
|
Grid.Column="0"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
Margin="12,0,0,0"
|
||||||
|
WidthRequest="{OnPlatform 14, iOS=14, Android=26}"
|
||||||
|
HeightRequest="{OnPlatform 14, iOS=14, Android=26}"
|
||||||
|
Source="plus.png"
|
||||||
|
xct:IconTintColorEffect.TintColor="{DynamicResource TextColor}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
|
<Label
|
||||||
|
Text="Add Account"
|
||||||
|
StyleClass="list-title"
|
||||||
|
LineBreakMode="TailTruncation"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
Grid.Column="1" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</ViewCell>
|
||||||
35
src/App/Controls/AccountViewCell/AccountViewCell.xaml.cs
Normal file
35
src/App/Controls/AccountViewCell/AccountViewCell.xaml.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Bit.Core.Models.View;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public partial class AccountViewCell : ViewCell
|
||||||
|
{
|
||||||
|
public static readonly BindableProperty AccountProperty = BindableProperty.Create(
|
||||||
|
nameof(Account), typeof(AccountView), typeof(AccountViewCell), default(AccountView), BindingMode.OneWay);
|
||||||
|
|
||||||
|
public AccountViewCell()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountView Account
|
||||||
|
{
|
||||||
|
get => GetValue(AccountProperty) as AccountView;
|
||||||
|
set => SetValue(AccountProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPropertyChanged(string propertyName = null)
|
||||||
|
{
|
||||||
|
base.OnPropertyChanged(propertyName);
|
||||||
|
if (propertyName == AccountProperty.PropertyName)
|
||||||
|
{
|
||||||
|
if (Account == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BindingContext = new AccountViewCellViewModel(Account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/App/Controls/AccountViewCell/AccountViewCellViewModel.cs
Normal file
31
src/App/Controls/AccountViewCell/AccountViewCellViewModel.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using Bit.Core.Models.View;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class AccountViewCellViewModel : ExtendedViewModel
|
||||||
|
{
|
||||||
|
private AccountView _accountView;
|
||||||
|
|
||||||
|
public AccountViewCellViewModel(AccountView accountView)
|
||||||
|
{
|
||||||
|
AccountView = accountView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountView AccountView
|
||||||
|
{
|
||||||
|
get => _accountView;
|
||||||
|
set => SetProperty(ref _accountView, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsAccount
|
||||||
|
{
|
||||||
|
get => AccountView.IsAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string AuthStatusText
|
||||||
|
{
|
||||||
|
get => AccountView.AuthStatus.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
114
src/App/Controls/AvatarImageSource.cs
Normal file
114
src/App/Controls/AvatarImageSource.cs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using SkiaSharp;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class AvatarImageSource : StreamImageSource
|
||||||
|
{
|
||||||
|
private string _data;
|
||||||
|
|
||||||
|
public AvatarImageSource(string data = null)
|
||||||
|
{
|
||||||
|
_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Func<CancellationToken, Task<Stream>> Stream => GetStreamAsync;
|
||||||
|
|
||||||
|
private Task<Stream> GetStreamAsync(CancellationToken userToken = new CancellationToken())
|
||||||
|
{
|
||||||
|
OnLoadingStarted();
|
||||||
|
userToken.Register(CancellationTokenSource.Cancel);
|
||||||
|
var result = Draw();
|
||||||
|
OnLoadingCompleted(CancellationTokenSource.IsCancellationRequested);
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream Draw()
|
||||||
|
{
|
||||||
|
string chars = null;
|
||||||
|
string upperData = null;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(_data))
|
||||||
|
{
|
||||||
|
chars = "..";
|
||||||
|
}
|
||||||
|
else if (_data?.Length > 2)
|
||||||
|
{
|
||||||
|
upperData = _data.ToUpper();
|
||||||
|
chars = upperData.Substring(0, 2).ToUpper();
|
||||||
|
}
|
||||||
|
|
||||||
|
var bgColor = StringToColor(upperData);
|
||||||
|
var textColor = Color.White;
|
||||||
|
var size = 50;
|
||||||
|
|
||||||
|
var bitmap = new SKBitmap(
|
||||||
|
size * 2,
|
||||||
|
size * 2,
|
||||||
|
SKImageInfo.PlatformColorType,
|
||||||
|
SKAlphaType.Premul);
|
||||||
|
var canvas = new SKCanvas(bitmap);
|
||||||
|
canvas.Clear(SKColors.Transparent);
|
||||||
|
|
||||||
|
var midX = canvas.LocalClipBounds.Size.ToSizeI().Width / 2;
|
||||||
|
var midY = canvas.LocalClipBounds.Size.ToSizeI().Height / 2;
|
||||||
|
var radius = midX - midX / 5;
|
||||||
|
|
||||||
|
var circlePaint = new SKPaint
|
||||||
|
{
|
||||||
|
IsAntialias = true,
|
||||||
|
Style = SKPaintStyle.Fill,
|
||||||
|
StrokeJoin = SKStrokeJoin.Miter,
|
||||||
|
Color = SKColor.Parse(bgColor.ToHex())
|
||||||
|
};
|
||||||
|
canvas.DrawCircle(midX, midY, radius, circlePaint);
|
||||||
|
|
||||||
|
var typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal);
|
||||||
|
var textSize = midX / 1.3f;
|
||||||
|
var textPaint = new SKPaint
|
||||||
|
{
|
||||||
|
IsAntialias = true,
|
||||||
|
Style = SKPaintStyle.Fill,
|
||||||
|
Color = SKColor.Parse(textColor.ToHex()),
|
||||||
|
TextSize = textSize,
|
||||||
|
TextAlign = SKTextAlign.Center,
|
||||||
|
Typeface = typeface
|
||||||
|
};
|
||||||
|
var rect = new SKRect();
|
||||||
|
textPaint.MeasureText(chars, ref rect);
|
||||||
|
canvas.DrawText(chars, midX, midY + rect.Height / 2, textPaint);
|
||||||
|
|
||||||
|
return SKImage.FromBitmap(bitmap).Encode(SKEncodedImageFormat.Png, 100).AsStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color StringToColor(string str)
|
||||||
|
{
|
||||||
|
if (str == null)
|
||||||
|
{
|
||||||
|
return Color.FromHex("#33ffffff");
|
||||||
|
}
|
||||||
|
var hash = 0;
|
||||||
|
for (var i = 0; i < str.Length; i++)
|
||||||
|
{
|
||||||
|
hash = str[i] + ((hash << 5) - hash);
|
||||||
|
}
|
||||||
|
var color = "#FF";
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
var value = (hash >> (i * 8)) & 0xff;
|
||||||
|
var base16 = "00" + Convert.ToString(value, 16);
|
||||||
|
color += base16.Substring(base16.Length - 2);
|
||||||
|
}
|
||||||
|
//if (Device.RuntimePlatform == Device.iOS)
|
||||||
|
//{
|
||||||
|
// // TODO remove this once iOS ToolbarItem tint issue is solved
|
||||||
|
// return Color.FromHex("#33ffffff");
|
||||||
|
//}
|
||||||
|
return Color.FromHex(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/App/Controls/ExtendedToolbarItem.cs
Normal file
9
src/App/Controls/ExtendedToolbarItem.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class ExtendedToolbarItem : ToolbarItem
|
||||||
|
{
|
||||||
|
public bool UseOriginalImage { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ namespace Bit.App.Pages
|
|||||||
public class BaseChangePasswordViewModel : BaseViewModel
|
public class BaseChangePasswordViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
protected readonly IPlatformUtilsService _platformUtilsService;
|
protected readonly IPlatformUtilsService _platformUtilsService;
|
||||||
protected readonly IUserService _userService;
|
protected readonly IStateService _stateService;
|
||||||
protected readonly IPolicyService _policyService;
|
protected readonly IPolicyService _policyService;
|
||||||
protected readonly IPasswordGenerationService _passwordGenerationService;
|
protected readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
protected readonly II18nService _i18nService;
|
protected readonly II18nService _i18nService;
|
||||||
@@ -31,7 +31,7 @@ namespace Bit.App.Pages
|
|||||||
protected BaseChangePasswordViewModel()
|
protected BaseChangePasswordViewModel()
|
||||||
{
|
{
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
_passwordGenerationService =
|
_passwordGenerationService =
|
||||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||||
@@ -172,7 +172,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task<List<string>> GetPasswordStrengthUserInput()
|
private async Task<List<string>> GetPasswordStrengthUserInput()
|
||||||
{
|
{
|
||||||
var email = await _userService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
List<string> userInput = null;
|
List<string> userInput = null;
|
||||||
var atPosition = email.IndexOf('@');
|
var atPosition = email.IndexOf('@');
|
||||||
if (atPosition > -1)
|
if (atPosition > -1)
|
||||||
|
|||||||
81
src/App/Pages/Accounts/DeleteAccountPage.xaml
Normal file
81
src/App/Pages/Accounts/DeleteAccountPage.xaml
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<pages:BaseContentPage
|
||||||
|
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
x:Class="Bit.App.Pages.Accounts.DeleteAccountPage"
|
||||||
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
|
x:DataType="pages:DeleteAccountViewModel"
|
||||||
|
Title="{Binding PageTitle}">
|
||||||
|
<ContentPage.BindingContext>
|
||||||
|
<pages:DeleteAccountViewModel />
|
||||||
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
|
<ContentPage.ToolbarItems>
|
||||||
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
|
<ContentPage.Resources>
|
||||||
|
<Style TargetType="Label" x:Key="lblDescription">
|
||||||
|
<Setter Property="FontSize" Value="{OnPlatform Android=Large, iOS=Small}" />
|
||||||
|
</Style>
|
||||||
|
</ContentPage.Resources>
|
||||||
|
|
||||||
|
<ContentPage.Content>
|
||||||
|
<Grid Padding="20, 30" RowSpacing="0">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Image
|
||||||
|
Source="ic_warning"
|
||||||
|
WidthRequest="28"
|
||||||
|
HeightRequest="25"
|
||||||
|
HorizontalOptions="Start" />
|
||||||
|
<Label
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Text="{u:I18n DeletingYourAccountIsPermanent}"
|
||||||
|
HorizontalOptions="Start"
|
||||||
|
StyleClass="title-danger"
|
||||||
|
Margin="0,15,0,0"/>
|
||||||
|
<Label
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Text="{u:I18n DeleteAccountExplanation}"
|
||||||
|
Style="{StaticResource lblDescription}"
|
||||||
|
HorizontalOptions="Start"
|
||||||
|
Margin="0,6,50,0"
|
||||||
|
Opacity="0.6" />
|
||||||
|
<Button
|
||||||
|
Grid.Row="3"
|
||||||
|
Text="{u:I18n DeleteAccount}"
|
||||||
|
StyleClass="btn-danger"
|
||||||
|
HorizontalOptions="Start"
|
||||||
|
VerticalOptions="Start"
|
||||||
|
Margin="0,20,0,0"
|
||||||
|
Padding="16,0"
|
||||||
|
CornerRadius="2"
|
||||||
|
TextTransform="Uppercase"
|
||||||
|
Clicked="DeleteAccount_Clicked"/>
|
||||||
|
<Button
|
||||||
|
Grid.Row="3"
|
||||||
|
Grid.Column="1"
|
||||||
|
Text="{u:I18n Cancel}"
|
||||||
|
StyleClass="btn-secondary"
|
||||||
|
HorizontalOptions="Start"
|
||||||
|
VerticalOptions="Start"
|
||||||
|
Margin="0,20,0,0"
|
||||||
|
Padding="16,0"
|
||||||
|
CornerRadius="2"
|
||||||
|
TextTransform="Uppercase"
|
||||||
|
Clicked="Close_Clicked" />
|
||||||
|
</Grid>
|
||||||
|
</ContentPage.Content>
|
||||||
|
</pages:BaseContentPage>
|
||||||
33
src/App/Pages/Accounts/DeleteAccountPage.xaml.cs
Normal file
33
src/App/Pages/Accounts/DeleteAccountPage.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages.Accounts
|
||||||
|
{
|
||||||
|
public partial class DeleteAccountPage : BaseContentPage
|
||||||
|
{
|
||||||
|
DeleteAccountViewModel _vm;
|
||||||
|
|
||||||
|
public DeleteAccountPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_vm = BindingContext as DeleteAccountViewModel;
|
||||||
|
_vm.Page = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Close_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
await Navigation.PopModalAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void DeleteAccount_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
await _vm.DeleteAccountAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/App/Pages/Accounts/DeleteAccountViewModel.cs
Normal file
84
src/App/Pages/Accounts/DeleteAccountViewModel.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
#if !FDROID
|
||||||
|
using Microsoft.AppCenter.Crashes;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class DeleteAccountViewModel : BaseViewModel
|
||||||
|
{
|
||||||
|
readonly IApiService _apiService;
|
||||||
|
readonly IPasswordRepromptService _passwordRepromptService;
|
||||||
|
readonly IMessagingService _messagingService;
|
||||||
|
readonly ICryptoService _cryptoService;
|
||||||
|
readonly IPlatformUtilsService _platformUtilsService;
|
||||||
|
readonly IDeviceActionService _deviceActionService;
|
||||||
|
|
||||||
|
public DeleteAccountViewModel()
|
||||||
|
{
|
||||||
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
|
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
|
|
||||||
|
PageTitle = AppResources.DeleteAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAccountAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
||||||
|
AppResources.InternetConnectionRequiredTitle, AppResources.Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (password, valid) = await _passwordRepromptService.ShowPasswordPromptAndGetItAsync();
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _deviceActionService.ShowLoadingAsync(AppResources.DeletingYourAccount);
|
||||||
|
|
||||||
|
var masterPasswordHashKey = await _cryptoService.HashPasswordAsync(password, null);
|
||||||
|
await _apiService.DeleteAccountAsync(new Core.Models.Request.DeleteAccountRequest
|
||||||
|
{
|
||||||
|
MasterPasswordHash = masterPasswordHashKey
|
||||||
|
});
|
||||||
|
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
|
||||||
|
_messagingService.Send("logout");
|
||||||
|
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.YourAccountHasBeenPermanentlyDeleted);
|
||||||
|
}
|
||||||
|
catch (ApiException apiEx)
|
||||||
|
{
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
|
||||||
|
if (apiEx?.Error != null)
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(apiEx.Error.GetSingleMessage(), AppResources.AnErrorHasOccurred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
#if !FDROID
|
||||||
|
Crashes.TrackError(ex);
|
||||||
|
#endif
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,14 +10,11 @@ namespace Bit.App.Pages
|
|||||||
public partial class EnvironmentPage : BaseContentPage
|
public partial class EnvironmentPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IMessagingService _messagingService;
|
|
||||||
private readonly EnvironmentPageViewModel _vm;
|
private readonly EnvironmentPageViewModel _vm;
|
||||||
|
|
||||||
public EnvironmentPage()
|
public EnvironmentPage()
|
||||||
{
|
{
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_messagingService.Send("showStatusBar", true);
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as EnvironmentPageViewModel;
|
_vm = BindingContext as EnvironmentPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
@@ -35,7 +32,6 @@ namespace Bit.App.Pages
|
|||||||
_vm.SubmitSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SubmitSuccessAsync());
|
_vm.SubmitSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SubmitSuccessAsync());
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("showStatusBar", false);
|
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<pages:BaseContentPage
|
<pages:BaseContentPage
|
||||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
x:Class="Bit.App.Pages.HomePage"
|
x:Class="Bit.App.Pages.HomePage"
|
||||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
|
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
x:DataType="pages:HomeViewModel"
|
x:DataType="pages:HomeViewModel"
|
||||||
Title="{Binding PageTitle}">
|
Title="{Binding PageTitle}">
|
||||||
@@ -13,23 +15,27 @@
|
|||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<controls:ExtendedToolbarItem
|
||||||
|
x:Name="_accountAvatar"
|
||||||
|
x:Key="accountAvatar"
|
||||||
|
IconImageSource="{Binding AvatarImageSource}"
|
||||||
|
Clicked="AccountSwitch_Clicked"
|
||||||
|
Order="Primary"
|
||||||
|
Priority="-1"
|
||||||
|
UseOriginalImage="True"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Account}" />
|
||||||
|
<ToolbarItem x:Name="_closeItem" x:Key="closeItem" Text="{u:I18n Close}"
|
||||||
|
Clicked="Close_Clicked" 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>
|
||||||
|
|
||||||
<StackLayout Spacing="0" Padding="10, 5">
|
<ContentPage.Resources>
|
||||||
<controls:FaButton Text=""
|
<ResourceDictionary>
|
||||||
StyleClass="btn-muted, btn-icon, btn-icon-platform"
|
<StackLayout x:Name="_mainLayout" x:Key="mainLayout" Spacing="0" Padding="10, 5">
|
||||||
HorizontalOptions="Start"
|
|
||||||
Clicked="Environment_Clicked"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
|
||||||
AutomationProperties.Name="{u:I18n Options}">
|
|
||||||
<controls:FaButton.Margin>
|
|
||||||
<OnPlatform x:TypeArguments="Thickness">
|
|
||||||
<On Platform="iOS" Value="0, 10, 0, 0" />
|
|
||||||
<On Platform="Android" Value="0" />
|
|
||||||
</OnPlatform>
|
|
||||||
</controls:FaButton.Margin>
|
|
||||||
</controls:FaButton>
|
|
||||||
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
|
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
|
||||||
<Image
|
<Image
|
||||||
x:Name="_logo"
|
x:Name="_logo"
|
||||||
@@ -37,7 +43,8 @@
|
|||||||
VerticalOptions="Center" />
|
VerticalOptions="Center" />
|
||||||
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
||||||
StyleClass="text-lg"
|
StyleClass="text-lg"
|
||||||
HorizontalTextAlignment="Center"></Label>
|
HorizontalTextAlignment="Center">
|
||||||
|
</Label>
|
||||||
<StackLayout Spacing="5">
|
<StackLayout Spacing="5">
|
||||||
<Button Text="{u:I18n LogIn}"
|
<Button Text="{u:I18n LogIn}"
|
||||||
StyleClass="btn-primary"
|
StyleClass="btn-primary"
|
||||||
@@ -49,5 +56,52 @@
|
|||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</ContentPage.Resources>
|
||||||
|
|
||||||
|
<AbsoluteLayout
|
||||||
|
x:Name="_absLayout"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
HorizontalOptions="FillAndExpand">
|
||||||
|
<ContentView
|
||||||
|
x:Name="_mainContent"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||||
|
</ContentView>
|
||||||
|
|
||||||
|
<!-- Account Switching Overlay -->
|
||||||
|
<ContentView
|
||||||
|
x:Name="_accountListOverlay"
|
||||||
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
IsVisible="False"
|
||||||
|
BackgroundColor="#22000000"
|
||||||
|
Padding="0">
|
||||||
|
|
||||||
|
<StackLayout
|
||||||
|
x:Name="_accountListContainer"
|
||||||
|
VerticalOptions="StartAndExpand"
|
||||||
|
HorizontalOptions="FillAndExpand"
|
||||||
|
BackgroundColor="{DynamicResource BackgroundColor}"
|
||||||
|
xct:ShadowEffect.Color="Black"
|
||||||
|
xct:ShadowEffect.Radius="10"
|
||||||
|
xct:ShadowEffect.OffsetY="3">
|
||||||
|
<ListView
|
||||||
|
x:Name="_accountListView"
|
||||||
|
ItemsSource="{Binding AccountViews}"
|
||||||
|
ItemSelected="AccountRow_Selected"
|
||||||
|
BackgroundColor="Transparent"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
RowHeight="60">
|
||||||
|
<ListView.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="view:AccountView">
|
||||||
|
<controls:AccountViewCell
|
||||||
|
Account="{Binding .}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ListView.ItemTemplate>
|
||||||
|
</ListView>
|
||||||
|
</StackLayout>
|
||||||
|
</ContentView>
|
||||||
|
</AbsoluteLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -12,13 +12,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly HomeViewModel _vm;
|
private readonly HomeViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
private IMessagingService _messagingService;
|
|
||||||
private IBroadcasterService _broadcasterService;
|
private IBroadcasterService _broadcasterService;
|
||||||
|
|
||||||
public HomePage(AppOptions appOptions = null)
|
public HomePage(AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_messagingService.Send("showStatusBar", false);
|
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -29,6 +26,11 @@ namespace Bit.App.Pages
|
|||||||
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
|
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
|
||||||
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
|
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
|
||||||
UpdateLogo();
|
UpdateLogo();
|
||||||
|
|
||||||
|
if (!_appOptions?.IosExtension ?? false)
|
||||||
|
{
|
||||||
|
ToolbarItems.Remove(_closeItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DismissRegisterPageAndLogInAsync(string email)
|
public async Task DismissRegisterPageAndLogInAsync(string email)
|
||||||
@@ -37,10 +39,18 @@ namespace Bit.App.Pages
|
|||||||
await Navigation.PushModalAsync(new NavigationPage(new LoginPage(email, _appOptions)));
|
await Navigation.PushModalAsync(new NavigationPage(new LoginPage(email, _appOptions)));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAppearing()
|
protected override async void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
_messagingService.Send("showStatusBar", false);
|
_mainContent.Content = _mainLayout;
|
||||||
|
if (await ShowAccountSwitcherAsync())
|
||||||
|
{
|
||||||
|
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ToolbarItems.Remove(_accountAvatar);
|
||||||
|
}
|
||||||
_broadcasterService.Subscribe(nameof(HomePage), async (message) =>
|
_broadcasterService.Subscribe(nameof(HomePage), async (message) =>
|
||||||
{
|
{
|
||||||
if (message.Command == "updatedTheme")
|
if (message.Command == "updatedTheme")
|
||||||
@@ -127,5 +137,23 @@ namespace Bit.App.Pages
|
|||||||
var page = new EnvironmentPage();
|
var page = new EnvironmentPage();
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void AccountSwitch_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (_accountListOverlay.IsVisible)
|
||||||
|
{
|
||||||
|
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await RefreshAccountViewsAsync(_accountListView, false);
|
||||||
|
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
|
||||||
|
{
|
||||||
|
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, null, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,27 @@
|
|||||||
using System;
|
using System;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Models.View;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class HomeViewModel : BaseViewModel
|
public class HomeViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
|
private readonly IStateService _stateService;
|
||||||
|
|
||||||
public HomeViewModel()
|
public HomeViewModel()
|
||||||
{
|
{
|
||||||
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
|
|
||||||
PageTitle = AppResources.Bitwarden;
|
PageTitle = AppResources.Bitwarden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExtendedObservableCollection<AccountView> AccountViews
|
||||||
|
{
|
||||||
|
get => _stateService.AccountViews;
|
||||||
|
}
|
||||||
|
|
||||||
public Action StartLoginAction { get; set; }
|
public Action StartLoginAction { get; set; }
|
||||||
public Action StartRegisterAction { get; set; }
|
public Action StartRegisterAction { get; set; }
|
||||||
public Action StartSsoLoginAction { get; set; }
|
public Action StartSsoLoginAction { get; set; }
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<pages:BaseContentPage
|
<pages:BaseContentPage
|
||||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
x:Class="Bit.App.Pages.LockPage"
|
x:Class="Bit.App.Pages.LockPage"
|
||||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
|
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
x:DataType="pages:LockPageViewModel"
|
x:DataType="pages:LockPageViewModel"
|
||||||
Title="{Binding PageTitle}">
|
Title="{Binding PageTitle}">
|
||||||
@@ -13,6 +15,19 @@
|
|||||||
<pages:LockPageViewModel />
|
<pages:LockPageViewModel />
|
||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
|
<ContentPage.ToolbarItems>
|
||||||
|
<controls:ExtendedToolbarItem
|
||||||
|
x:Name="_accountAvatar"
|
||||||
|
x:Key="accountAvatar"
|
||||||
|
IconImageSource="{Binding AvatarImageSource}"
|
||||||
|
Clicked="AccountSwitch_Clicked"
|
||||||
|
Order="Primary"
|
||||||
|
Priority="-1"
|
||||||
|
UseOriginalImage="True"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Account}" />
|
||||||
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
@@ -25,16 +40,14 @@
|
|||||||
x:Name="_logOut"
|
x:Name="_logOut"
|
||||||
Clicked="LogOut_Clicked"
|
Clicked="LogOut_Clicked"
|
||||||
Order="Secondary"/>
|
Order="Secondary"/>
|
||||||
</ResourceDictionary>
|
|
||||||
</ContentPage.Resources>
|
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ScrollView x:Name="_mainLayout" x:Key="mainLayout">
|
||||||
</ContentPage.ToolbarItems>
|
|
||||||
|
|
||||||
<ScrollView>
|
|
||||||
<StackLayout Spacing="20">
|
<StackLayout Spacing="20">
|
||||||
<StackLayout StyleClass="box">
|
<StackLayout StyleClass="box">
|
||||||
<Grid StyleClass="box-row" IsVisible="{Binding PinLock}">
|
<Grid
|
||||||
|
StyleClass="box-row"
|
||||||
|
IsVisible="{Binding PinLock}"
|
||||||
|
Padding="0, 10, 0, 0">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
@@ -70,7 +83,11 @@
|
|||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid StyleClass="box-row" IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}">
|
<Grid
|
||||||
|
x:Name="_passwordGrid"
|
||||||
|
StyleClass="box-row"
|
||||||
|
IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}"
|
||||||
|
Padding="0, 10, 0, 0">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
@@ -105,6 +122,9 @@
|
|||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<StackLayout
|
||||||
|
StyleClass="box-row"
|
||||||
|
Padding="0, 10, 0, 0">
|
||||||
<Label
|
<Label
|
||||||
Text="{Binding LockedVerifyText}"
|
Text="{Binding LockedVerifyText}"
|
||||||
StyleClass="box-footer-label" />
|
StyleClass="box-footer-label" />
|
||||||
@@ -113,18 +133,69 @@
|
|||||||
StyleClass="box-footer-label"
|
StyleClass="box-footer-label"
|
||||||
Margin="0, 10, 0, 0" />
|
Margin="0, 10, 0, 0" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
</StackLayout>
|
||||||
<StackLayout Padding="10, 0">
|
<StackLayout Padding="10, 0">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n BiometricInvalidated}"
|
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"
|
||||||
IsVisible="{Binding BiometricButtonVisible}"></Button>
|
IsVisible="{Binding BiometricButtonVisible}">
|
||||||
<Button Text="{u:I18n Unlock}"
|
</Button>
|
||||||
|
<Button
|
||||||
|
x:Name="_unlockButton"
|
||||||
|
Text="{u:I18n Unlock}"
|
||||||
StyleClass="btn-primary"
|
StyleClass="btn-primary"
|
||||||
Clicked="Unlock_Clicked" />
|
Clicked="Unlock_Clicked" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</ContentPage.Resources>
|
||||||
|
|
||||||
|
<AbsoluteLayout
|
||||||
|
x:Name="_absLayout"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
HorizontalOptions="FillAndExpand">
|
||||||
|
<ContentView
|
||||||
|
x:Name="_mainContent"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||||
|
</ContentView>
|
||||||
|
|
||||||
|
<!-- Account Switching Overlay -->
|
||||||
|
<ContentView
|
||||||
|
x:Name="_accountListOverlay"
|
||||||
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
IsVisible="False"
|
||||||
|
BackgroundColor="#22000000"
|
||||||
|
Padding="0">
|
||||||
|
|
||||||
|
<StackLayout
|
||||||
|
x:Name="_accountListContainer"
|
||||||
|
VerticalOptions="StartAndExpand"
|
||||||
|
HorizontalOptions="FillAndExpand"
|
||||||
|
BackgroundColor="{DynamicResource BackgroundColor}"
|
||||||
|
xct:ShadowEffect.Color="Black"
|
||||||
|
xct:ShadowEffect.Radius="10"
|
||||||
|
xct:ShadowEffect.OffsetY="3">
|
||||||
|
<ListView
|
||||||
|
x:Name="_accountListView"
|
||||||
|
ItemsSource="{Binding AccountViews}"
|
||||||
|
ItemSelected="AccountRow_Selected"
|
||||||
|
BackgroundColor="Transparent"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
RowHeight="60">
|
||||||
|
<ListView.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="view:AccountView">
|
||||||
|
<controls:AccountViewCell
|
||||||
|
Account="{Binding .}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ListView.ItemTemplate>
|
||||||
|
</ListView>
|
||||||
|
</StackLayout>
|
||||||
|
</ContentView>
|
||||||
|
</AbsoluteLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
@@ -11,7 +9,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public partial class LockPage : BaseContentPage
|
public partial class LockPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
private readonly bool _autoPromptBiometric;
|
private readonly bool _autoPromptBiometric;
|
||||||
private readonly LockPageViewModel _vm;
|
private readonly LockPageViewModel _vm;
|
||||||
@@ -21,7 +18,6 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public LockPage(AppOptions appOptions = null, bool autoPromptBiometric = true)
|
public LockPage(AppOptions appOptions = null, bool autoPromptBiometric = true)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
_autoPromptBiometric = autoPromptBiometric;
|
_autoPromptBiometric = autoPromptBiometric;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -65,7 +61,16 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_appeared = true;
|
_appeared = true;
|
||||||
await _vm.InitAsync(_autoPromptBiometric);
|
_mainContent.Content = _mainLayout;
|
||||||
|
if (await ShowAccountSwitcherAsync())
|
||||||
|
{
|
||||||
|
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ToolbarItems.Remove(_accountAvatar);
|
||||||
|
}
|
||||||
|
await _vm.InitAsync();
|
||||||
if (!_vm.BiometricLock)
|
if (!_vm.BiometricLock)
|
||||||
{
|
{
|
||||||
if (_vm.PinLock)
|
if (_vm.PinLock)
|
||||||
@@ -77,6 +82,22 @@ namespace Bit.App.Pages
|
|||||||
RequestFocus(MasterPasswordEntry);
|
RequestFocus(MasterPasswordEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_vm.UsingKeyConnector && !_vm.PinLock)
|
||||||
|
{
|
||||||
|
_passwordGrid.IsVisible = false;
|
||||||
|
_unlockButton.IsVisible = false;
|
||||||
|
}
|
||||||
|
if (_autoPromptBiometric)
|
||||||
|
{
|
||||||
|
var tasks = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(500);
|
||||||
|
Device.BeginInvokeOnMainThread(async () => await _vm.PromptBiometricAsync());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Unlock_Clicked(object sender, EventArgs e)
|
private void Unlock_Clicked(object sender, EventArgs e)
|
||||||
@@ -130,7 +151,26 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||||
|
|
||||||
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void AccountSwitch_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (_accountListOverlay.IsVisible)
|
||||||
|
{
|
||||||
|
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await RefreshAccountViewsAsync(_accountListView, false);
|
||||||
|
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
|
||||||
|
{
|
||||||
|
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, null, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
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.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
@@ -10,6 +8,7 @@ using System;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
|
using Bit.Core.Models.View;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -21,13 +20,11 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IUserService _userService;
|
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStorageService _secureStorageService;
|
|
||||||
private readonly IEnvironmentService _environmentService;
|
private readonly IEnvironmentService _environmentService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IBiometricService _biometricService;
|
private readonly IBiometricService _biometricService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
|
||||||
private string _email;
|
private string _email;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
@@ -35,6 +32,7 @@ namespace Bit.App.Pages
|
|||||||
private bool _biometricLock;
|
private bool _biometricLock;
|
||||||
private bool _biometricIntegrityValid = true;
|
private bool _biometricIntegrityValid = true;
|
||||||
private bool _biometricButtonVisible;
|
private bool _biometricButtonVisible;
|
||||||
|
private bool _usingKeyConnector;
|
||||||
private string _biometricButtonText;
|
private string _biometricButtonText;
|
||||||
private string _loggedInAsText;
|
private string _loggedInAsText;
|
||||||
private string _lockedVerifyText;
|
private string _lockedVerifyText;
|
||||||
@@ -47,13 +45,11 @@ namespace Bit.App.Pages
|
|||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
||||||
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
|
||||||
PageTitle = AppResources.VerifyMasterPassword;
|
PageTitle = AppResources.VerifyMasterPassword;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
@@ -76,6 +72,11 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _pinLock, value);
|
set => SetProperty(ref _pinLock, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool UsingKeyConnector
|
||||||
|
{
|
||||||
|
get => _usingKeyConnector;
|
||||||
|
}
|
||||||
|
|
||||||
public bool BiometricLock
|
public bool BiometricLock
|
||||||
{
|
{
|
||||||
get => _biometricLock;
|
get => _biometricLock;
|
||||||
@@ -112,6 +113,11 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _lockedVerifyText, value);
|
set => SetProperty(ref _lockedVerifyText, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExtendedObservableCollection<AccountView> AccountViews
|
||||||
|
{
|
||||||
|
get => _stateService.AccountViews;
|
||||||
|
}
|
||||||
|
|
||||||
public Command SubmitCommand { get; }
|
public Command SubmitCommand { get; }
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||||
@@ -119,12 +125,19 @@ namespace Bit.App.Pages
|
|||||||
public string Pin { get; set; }
|
public string Pin { get; set; }
|
||||||
public Action UnlockedAction { get; set; }
|
public Action UnlockedAction { get; set; }
|
||||||
|
|
||||||
public async Task InitAsync(bool autoPromptBiometric)
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
_pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
_pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
|
||||||
PinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
|
PinLock = (_pinSet.Item1 && _stateService.GetPinProtectedAsync() != null) || _pinSet.Item2;
|
||||||
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
|
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
|
||||||
_email = await _userService.GetEmailAsync();
|
|
||||||
|
// Users with key connector and without biometric or pin has no MP to unlock with
|
||||||
|
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
|
||||||
|
if (_usingKeyConnector && !(BiometricLock || PinLock))
|
||||||
|
{
|
||||||
|
await _vaultTimeoutService.LogOutAsync();
|
||||||
|
}
|
||||||
|
_email = await _stateService.GetEmailAsync();
|
||||||
var webVault = _environmentService.GetWebVaultUrl();
|
var webVault = _environmentService.GetWebVaultUrl();
|
||||||
if (string.IsNullOrWhiteSpace(webVault))
|
if (string.IsNullOrWhiteSpace(webVault))
|
||||||
{
|
{
|
||||||
@@ -138,10 +151,18 @@ namespace Bit.App.Pages
|
|||||||
LockedVerifyText = AppResources.VaultLockedPIN;
|
LockedVerifyText = AppResources.VaultLockedPIN;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (_usingKeyConnector)
|
||||||
|
{
|
||||||
|
PageTitle = AppResources.UnlockVault;
|
||||||
|
LockedVerifyText = AppResources.VaultLockedIdentity;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
PageTitle = AppResources.VerifyMasterPassword;
|
PageTitle = AppResources.VerifyMasterPassword;
|
||||||
LockedVerifyText = AppResources.VaultLockedMasterPassword;
|
LockedVerifyText = AppResources.VaultLockedMasterPassword;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (BiometricLock)
|
if (BiometricLock)
|
||||||
{
|
{
|
||||||
@@ -159,14 +180,7 @@ namespace Bit.App.Pages
|
|||||||
BiometricButtonText = supportsFace ? AppResources.UseFaceIDToUnlock :
|
BiometricButtonText = supportsFace ? AppResources.UseFaceIDToUnlock :
|
||||||
AppResources.UseFingerprintToUnlock;
|
AppResources.UseFingerprintToUnlock;
|
||||||
}
|
}
|
||||||
if (autoPromptBiometric)
|
|
||||||
{
|
|
||||||
var tasks = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await Task.Delay(500);
|
|
||||||
Device.BeginInvokeOnMainThread(async () => await PromptBiometricAsync());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,8 +202,8 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShowPassword = false;
|
ShowPassword = false;
|
||||||
var kdf = await _userService.GetKdfAsync();
|
var kdf = await _stateService.GetKdfTypeAsync();
|
||||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||||
|
|
||||||
if (PinLock)
|
if (PinLock)
|
||||||
{
|
{
|
||||||
@@ -200,9 +214,9 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
|
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
|
||||||
_vaultTimeoutService.PinProtectedKey);
|
await _stateService.GetPinProtectedCachedAsync());
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||||
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||||
failed = decPin != Pin;
|
failed = decPin != Pin;
|
||||||
if (!failed)
|
if (!failed)
|
||||||
@@ -271,12 +285,12 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_pinSet.Item1)
|
if (_pinSet.Item1)
|
||||||
{
|
{
|
||||||
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
|
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
|
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
|
||||||
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
|
||||||
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
await _stateService.SetPinProtectedCachedAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
|
||||||
}
|
}
|
||||||
MasterPassword = string.Empty;
|
MasterPassword = string.Empty;
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
@@ -342,7 +356,7 @@ namespace Bit.App.Pages
|
|||||||
page.MasterPasswordEntry.Focus();
|
page.MasterPasswordEntry.Focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
_vaultTimeoutService.BiometricLocked = !success;
|
_stateService.BiometricLocked = !success;
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
await DoContinueAsync();
|
await DoContinueAsync();
|
||||||
@@ -361,9 +375,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task DoContinueAsync()
|
private async Task DoContinueAsync()
|
||||||
{
|
{
|
||||||
_vaultTimeoutService.BiometricLocked = false;
|
_stateService.BiometricLocked = false;
|
||||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
|
||||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
|
||||||
_messagingService.Send("unlocked");
|
_messagingService.Send("unlocked");
|
||||||
UnlockedAction?.Invoke();
|
UnlockedAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<pages:BaseContentPage
|
<pages:BaseContentPage
|
||||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
x:Class="Bit.App.Pages.LoginPage"
|
x:Class="Bit.App.Pages.LoginPage"
|
||||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
|
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
x:DataType="pages:LoginPageViewModel"
|
x:DataType="pages:LoginPageViewModel"
|
||||||
Title="{Binding PageTitle}">
|
Title="{Binding PageTitle}">
|
||||||
@@ -13,6 +15,21 @@
|
|||||||
<pages:LoginPageViewModel />
|
<pages:LoginPageViewModel />
|
||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
|
<ContentPage.ToolbarItems>
|
||||||
|
<controls:ExtendedToolbarItem
|
||||||
|
x:Name="_accountAvatar"
|
||||||
|
x:Key="accountAvatar"
|
||||||
|
IconImageSource="{Binding AvatarImageSource}"
|
||||||
|
Clicked="AccountSwitch_Clicked"
|
||||||
|
Order="Primary"
|
||||||
|
Priority="-1"
|
||||||
|
UseOriginalImage="True"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Account}" />
|
||||||
|
<ToolbarItem x:Name="_closeItem" x:Key="closeItem" Text="{u:I18n Close}"
|
||||||
|
Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
@@ -25,14 +42,8 @@
|
|||||||
x:Name="_getPasswordHint"
|
x:Name="_getPasswordHint"
|
||||||
Clicked="Hint_Clicked"
|
Clicked="Hint_Clicked"
|
||||||
Order="Secondary"/>
|
Order="Secondary"/>
|
||||||
</ResourceDictionary>
|
|
||||||
</ContentPage.Resources>
|
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ScrollView x:Name="_mainLayout" x:Key="mainLayout">
|
||||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
|
||||||
</ContentPage.ToolbarItems>
|
|
||||||
|
|
||||||
<ScrollView>
|
|
||||||
<StackLayout Spacing="20">
|
<StackLayout Spacing="20">
|
||||||
<StackLayout StyleClass="box">
|
<StackLayout StyleClass="box">
|
||||||
<StackLayout StyleClass="box-row">
|
<StackLayout StyleClass="box-row">
|
||||||
@@ -88,5 +99,52 @@
|
|||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</ContentPage.Resources>
|
||||||
|
|
||||||
|
<AbsoluteLayout
|
||||||
|
x:Name="_absLayout"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
HorizontalOptions="FillAndExpand">
|
||||||
|
<ContentView
|
||||||
|
x:Name="_mainContent"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||||
|
</ContentView>
|
||||||
|
|
||||||
|
<!-- Account Switching Overlay -->
|
||||||
|
<ContentView
|
||||||
|
x:Name="_accountListOverlay"
|
||||||
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
IsVisible="False"
|
||||||
|
BackgroundColor="#22000000"
|
||||||
|
Padding="0">
|
||||||
|
|
||||||
|
<StackLayout
|
||||||
|
x:Name="_accountListContainer"
|
||||||
|
VerticalOptions="StartAndExpand"
|
||||||
|
HorizontalOptions="FillAndExpand"
|
||||||
|
BackgroundColor="{DynamicResource BackgroundColor}"
|
||||||
|
xct:ShadowEffect.Color="Black"
|
||||||
|
xct:ShadowEffect.Radius="10"
|
||||||
|
xct:ShadowEffect.OffsetY="3">
|
||||||
|
<ListView
|
||||||
|
x:Name="_accountListView"
|
||||||
|
ItemsSource="{Binding AccountViews}"
|
||||||
|
ItemSelected="AccountRow_Selected"
|
||||||
|
BackgroundColor="Transparent"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
RowHeight="60">
|
||||||
|
<ListView.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="view:AccountView">
|
||||||
|
<controls:AccountViewCell
|
||||||
|
Account="{Binding .}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ListView.ItemTemplate>
|
||||||
|
</ListView>
|
||||||
|
</StackLayout>
|
||||||
|
</ContentView>
|
||||||
|
</AbsoluteLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
@@ -11,8 +9,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public partial class LoginPage : BaseContentPage
|
public partial class LoginPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IMessagingService _messagingService;
|
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly LoginPageViewModel _vm;
|
private readonly LoginPageViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
|
|
||||||
@@ -20,9 +16,6 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public LoginPage(string email = null, AppOptions appOptions = null)
|
public LoginPage(string email = null, AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_messagingService.Send("showStatusBar", true);
|
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as LoginPageViewModel;
|
_vm = BindingContext as LoginPageViewModel;
|
||||||
@@ -33,7 +26,6 @@ namespace Bit.App.Pages
|
|||||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("showStatusBar", false);
|
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
};
|
};
|
||||||
_vm.Email = email;
|
_vm.Email = email;
|
||||||
@@ -54,6 +46,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
ToolbarItems.Add(_getPasswordHint);
|
ToolbarItems.Add(_getPasswordHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_appOptions?.IosExtension ?? false)
|
||||||
|
{
|
||||||
|
ToolbarItems.Remove(_closeItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry MasterPasswordEntry { get; set; }
|
public Entry MasterPasswordEntry { get; set; }
|
||||||
@@ -61,6 +58,15 @@ namespace Bit.App.Pages
|
|||||||
protected override async void OnAppearing()
|
protected override async void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
|
_mainContent.Content = _mainLayout;
|
||||||
|
if (await ShowAccountSwitcherAsync())
|
||||||
|
{
|
||||||
|
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ToolbarItems.Remove(_accountAvatar);
|
||||||
|
}
|
||||||
await _vm.InitAsync();
|
await _vm.InitAsync();
|
||||||
if (!_inputFocused)
|
if (!_inputFocused)
|
||||||
{
|
{
|
||||||
@@ -130,5 +136,23 @@ namespace Bit.App.Pages
|
|||||||
var page = new UpdateTempPasswordPage();
|
var page = new UpdateTempPasswordPage();
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void AccountSwitch_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (_accountListOverlay.IsVisible)
|
||||||
|
{
|
||||||
|
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await RefreshAccountViewsAsync(_accountListView, false);
|
||||||
|
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
|
||||||
|
{
|
||||||
|
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, null, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,21 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Models.View;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class LoginPageViewModel : CaptchaProtectedViewModel
|
public class LoginPageViewModel : CaptchaProtectedViewModel
|
||||||
{
|
{
|
||||||
private const string Keys_RememberedEmail = "rememberedEmail";
|
|
||||||
private const string Keys_RememberEmail = "rememberEmail";
|
|
||||||
|
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IEnvironmentService _environmentService;
|
private readonly IEnvironmentService _environmentService;
|
||||||
@@ -34,7 +30,6 @@ namespace Bit.App.Pages
|
|||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
@@ -67,6 +62,11 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _masterPassword, value);
|
set => SetProperty(ref _masterPassword, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExtendedObservableCollection<AccountView> AccountViews
|
||||||
|
{
|
||||||
|
get => _stateService.AccountViews;
|
||||||
|
}
|
||||||
|
|
||||||
public Command LogInCommand { get; }
|
public Command LogInCommand { get; }
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||||
@@ -85,9 +85,9 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Email))
|
if (string.IsNullOrWhiteSpace(Email))
|
||||||
{
|
{
|
||||||
Email = await _storageService.GetAsync<string>(Keys_RememberedEmail);
|
Email = await _stateService.GetRememberedEmailAsync();
|
||||||
}
|
}
|
||||||
var rememberEmail = await _storageService.GetAsync<bool?>(Keys_RememberEmail);
|
var rememberEmail = await _stateService.GetRememberEmailAsync();
|
||||||
RememberEmail = rememberEmail.GetValueOrDefault(true);
|
RememberEmail = rememberEmail.GetValueOrDefault(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,11 +131,11 @@ namespace Bit.App.Pages
|
|||||||
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
|
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
|
||||||
if (RememberEmail)
|
if (RememberEmail)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Keys_RememberedEmail, Email);
|
await _stateService.SetRememberedEmailAsync(Email);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _storageService.RemoveAsync(Keys_RememberedEmail);
|
await _stateService.SetRememberedEmailAsync(null);
|
||||||
}
|
}
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
|
|
||||||
@@ -163,8 +163,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
|
||||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
|
||||||
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
||||||
LogInSuccessAction?.Invoke();
|
LogInSuccessAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public partial class LoginSsoPage : BaseContentPage
|
public partial class LoginSsoPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IMessagingService _messagingService;
|
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly LoginSsoPageViewModel _vm;
|
private readonly LoginSsoPageViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
@@ -20,10 +18,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public LoginSsoPage(AppOptions appOptions = null)
|
public LoginSsoPage(AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_messagingService.Send("showStatusBar", true);
|
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as LoginSsoPageViewModel;
|
_vm = BindingContext as LoginSsoPageViewModel;
|
||||||
@@ -36,7 +31,6 @@ namespace Bit.App.Pages
|
|||||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("showStatusBar", false);
|
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
};
|
};
|
||||||
if (Device.RuntimePlatform == Device.Android)
|
if (Device.RuntimePlatform == Device.Android)
|
||||||
@@ -116,7 +110,14 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
RestoreAppOptionsFromCopy();
|
RestoreAppOptionsFromCopy();
|
||||||
await AppHelpers.ClearPreviousPage();
|
await AppHelpers.ClearPreviousPage();
|
||||||
|
if (await _vaultTimeoutService.IsLockedAsync())
|
||||||
|
{
|
||||||
Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions));
|
Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Application.Current.MainPage = new TabsPage(_appOptions, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
@@ -16,16 +15,12 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public class LoginSsoPageViewModel : BaseViewModel
|
public class LoginSsoPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private const string Keys_RememberedOrgIdentifier = "rememberedOrgIdentifier";
|
|
||||||
private const string Keys_RememberOrgIdentifier = "rememberOrgIdentifier";
|
|
||||||
|
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
private readonly ICryptoFunctionService _cryptoFunctionService;
|
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
|
|
||||||
@@ -40,7 +35,6 @@ namespace Bit.App.Pages
|
|||||||
_passwordGenerationService =
|
_passwordGenerationService =
|
||||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||||
_cryptoFunctionService = ServiceContainer.Resolve<ICryptoFunctionService>("cryptoFunctionService");
|
_cryptoFunctionService = ServiceContainer.Resolve<ICryptoFunctionService>("cryptoFunctionService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
|
|
||||||
@@ -66,9 +60,9 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(OrgIdentifier))
|
if (string.IsNullOrWhiteSpace(OrgIdentifier))
|
||||||
{
|
{
|
||||||
OrgIdentifier = await _storageService.GetAsync<string>(Keys_RememberedOrgIdentifier);
|
OrgIdentifier = await _stateService.GetRememberedOrgIdentifierAsync();
|
||||||
}
|
}
|
||||||
var rememberOrgIdentifier = await _storageService.GetAsync<bool?>(Keys_RememberOrgIdentifier);
|
var rememberOrgIdentifier = await _stateService.GetRememberOrgIdentifierAsync();
|
||||||
RememberOrgIdentifier = rememberOrgIdentifier.GetValueOrDefault(true);
|
RememberOrgIdentifier = rememberOrgIdentifier.GetValueOrDefault(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +133,7 @@ namespace Bit.App.Pages
|
|||||||
var code = GetResultCode(authResult, state);
|
var code = GetResultCode(authResult, state);
|
||||||
if (!string.IsNullOrEmpty(code))
|
if (!string.IsNullOrEmpty(code))
|
||||||
{
|
{
|
||||||
await LogIn(code, codeVerifier, redirectUri);
|
await LogIn(code, codeVerifier, redirectUri, OrgIdentifier);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -164,19 +158,19 @@ namespace Bit.App.Pages
|
|||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LogIn(string code, string codeVerifier, string redirectUri)
|
private async Task LogIn(string code, string codeVerifier, string redirectUri, string orgId)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await _authService.LogInSsoAsync(code, codeVerifier, redirectUri);
|
var response = await _authService.LogInSsoAsync(code, codeVerifier, redirectUri, orgId);
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
if (RememberOrgIdentifier)
|
if (RememberOrgIdentifier)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Keys_RememberedOrgIdentifier, OrgIdentifier);
|
await _stateService.SetRememberedOrgIdentifierAsync(OrgIdentifier);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _storageService.RemoveAsync(Keys_RememberedOrgIdentifier);
|
await _stateService.SetRememberedOrgIdentifierAsync(null);
|
||||||
}
|
}
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
if (response.TwoFactor)
|
if (response.TwoFactor)
|
||||||
@@ -193,8 +187,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
|
||||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
|
||||||
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
||||||
SsoAuthSuccessAction?.Invoke();
|
SsoAuthSuccessAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using Bit.Core.Abstractions;
|
using System;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -8,22 +6,18 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public partial class RegisterPage : BaseContentPage
|
public partial class RegisterPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IMessagingService _messagingService;
|
|
||||||
private readonly RegisterPageViewModel _vm;
|
private readonly RegisterPageViewModel _vm;
|
||||||
|
|
||||||
private bool _inputFocused;
|
private bool _inputFocused;
|
||||||
|
|
||||||
public RegisterPage(HomePage homePage)
|
public RegisterPage(HomePage homePage)
|
||||||
{
|
{
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_messagingService.Send("showStatusBar", true);
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as RegisterPageViewModel;
|
_vm = BindingContext as RegisterPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_vm.RegistrationSuccess = () => Device.BeginInvokeOnMainThread(async () => await RegistrationSuccessAsync(homePage));
|
_vm.RegistrationSuccess = () => Device.BeginInvokeOnMainThread(async () => await RegistrationSuccessAsync(homePage));
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("showStatusBar", false);
|
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
};
|
};
|
||||||
MasterPasswordEntry = _masterPassword;
|
MasterPasswordEntry = _masterPassword;
|
||||||
|
|||||||
33
src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml
Normal file
33
src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<pages:BaseContentPage
|
||||||
|
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
x:Class="Bit.App.Pages.RemoveMasterPasswordPage"
|
||||||
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
|
x:DataType="pages:RemoveMasterPasswordPageViewModel"
|
||||||
|
Title="{Binding PageTitle}">
|
||||||
|
|
||||||
|
<ContentPage.BindingContext>
|
||||||
|
<pages:RemoveMasterPasswordPageViewModel />
|
||||||
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
|
<StackLayout Spacing="20"
|
||||||
|
Padding="10, 5">
|
||||||
|
<StackLayout Spacing="18"
|
||||||
|
Padding="30">
|
||||||
|
<Label x:Name="_warningLabel"
|
||||||
|
HorizontalTextAlignment="Center"/>
|
||||||
|
<Label x:Name="_warningLabel2"
|
||||||
|
HorizontalTextAlignment="Center"/>
|
||||||
|
</StackLayout>
|
||||||
|
<StackLayout Spacing="5">
|
||||||
|
<Button Text="{u:I18n Continue}"
|
||||||
|
StyleClass="btn-primary"
|
||||||
|
Clicked="Continue_Clicked" />
|
||||||
|
<Button Text="{u:I18n LeaveOrganization}"
|
||||||
|
Clicked="LeaveOrg_Clicked" />
|
||||||
|
</StackLayout>
|
||||||
|
</StackLayout>
|
||||||
|
|
||||||
|
</pages:BaseContentPage>
|
||||||
58
src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml.cs
Normal file
58
src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public partial class RemoveMasterPasswordPage : BaseContentPage
|
||||||
|
{
|
||||||
|
private readonly RemoveMasterPasswordPageViewModel _vm;
|
||||||
|
|
||||||
|
public Action NavigateAction { get; set; }
|
||||||
|
|
||||||
|
public RemoveMasterPasswordPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_vm = BindingContext as RemoveMasterPasswordPageViewModel;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async void OnAppearing()
|
||||||
|
{
|
||||||
|
await _vm.Init();
|
||||||
|
_warningLabel.Text = string.Format(AppResources.RemoveMasterPasswordWarning,
|
||||||
|
_vm.Organization.Name);
|
||||||
|
_warningLabel2.Text = AppResources.RemoveMasterPasswordWarning2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Continue_Clicked(object sender, System.EventArgs e)
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
await _vm.MigrateAccount();
|
||||||
|
await Navigation.PopModalAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void LeaveOrg_Clicked(object sender, System.EventArgs e)
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
var confirm = await DisplayAlert(AppResources.LeaveOrganization,
|
||||||
|
string.Format(AppResources.LeaveOrganizationName, _vm.Organization.Name),
|
||||||
|
AppResources.Yes, AppResources.No);
|
||||||
|
if (confirm)
|
||||||
|
{
|
||||||
|
await _vm.LeaveOrganization();
|
||||||
|
await Navigation.PopModalAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async void OnDisappearing()
|
||||||
|
{
|
||||||
|
NavigateAction?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/App/Pages/Accounts/RemoveMasterPasswordPageViewModel.cs
Normal file
56
src/App/Pages/Accounts/RemoveMasterPasswordPageViewModel.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class RemoveMasterPasswordPageViewModel : BaseViewModel
|
||||||
|
{
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
|
private readonly IApiService _apiService;
|
||||||
|
private readonly ISyncService _syncService;
|
||||||
|
|
||||||
|
public Organization Organization;
|
||||||
|
|
||||||
|
public RemoveMasterPasswordPageViewModel()
|
||||||
|
{
|
||||||
|
PageTitle = AppResources.RemoveMasterPassword;
|
||||||
|
|
||||||
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Init()
|
||||||
|
{
|
||||||
|
Organization = await _keyConnectorService.GetManagingOrganization();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task MigrateAccount()
|
||||||
|
{
|
||||||
|
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||||
|
|
||||||
|
await _keyConnectorService.MigrateUser();
|
||||||
|
await _syncService.FullSyncAsync(true);
|
||||||
|
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LeaveOrganization()
|
||||||
|
{
|
||||||
|
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||||
|
|
||||||
|
await _apiService.PostLeaveOrganization(Organization.Id);
|
||||||
|
await _syncService.FullSyncAsync(true);
|
||||||
|
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
using Bit.Core.Abstractions;
|
using System;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
@@ -10,14 +8,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public partial class SetPasswordPage : BaseContentPage
|
public partial class SetPasswordPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly IMessagingService _messagingService;
|
|
||||||
private readonly SetPasswordPageViewModel _vm;
|
private readonly SetPasswordPageViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
|
|
||||||
public SetPasswordPage(AppOptions appOptions = null, string orgIdentifier = null)
|
public SetPasswordPage(AppOptions appOptions = null, string orgIdentifier = null)
|
||||||
{
|
{
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_messagingService.Send("showStatusBar", true);
|
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as SetPasswordPageViewModel;
|
_vm = BindingContext as SetPasswordPageViewModel;
|
||||||
@@ -26,7 +21,6 @@ namespace Bit.App.Pages
|
|||||||
() => Device.BeginInvokeOnMainThread(async () => await SetPasswordSuccessAsync());
|
() => Device.BeginInvokeOnMainThread(async () => await SetPasswordSuccessAsync());
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("showStatusBar", false);
|
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
};
|
};
|
||||||
_vm.OrgIdentifier = orgIdentifier;
|
_vm.OrgIdentifier = orgIdentifier;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using Bit.Core.Models.Request;
|
|||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -23,7 +22,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
private readonly II18nService _i18nService;
|
private readonly II18nService _i18nService;
|
||||||
@@ -40,7 +39,7 @@ namespace Bit.App.Pages
|
|||||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
_passwordGenerationService =
|
_passwordGenerationService =
|
||||||
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||||
@@ -159,7 +158,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
var kdf = KdfType.PBKDF2_SHA256;
|
var kdf = KdfType.PBKDF2_SHA256;
|
||||||
var kdfIterations = 100000;
|
var kdfIterations = 100000;
|
||||||
var email = await _userService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
|
||||||
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
|
||||||
@@ -196,8 +195,8 @@ namespace Bit.App.Pages
|
|||||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||||
// Set Password and relevant information
|
// Set Password and relevant information
|
||||||
await _apiService.SetPasswordAsync(request);
|
await _apiService.SetPasswordAsync(request);
|
||||||
await _userService.SetInformationAsync(await _userService.GetUserIdAsync(),
|
await _stateService.SetKdfTypeAsync(kdf);
|
||||||
await _userService.GetEmailAsync(), kdf, kdfIterations);
|
await _stateService.SetKdfIterationsAsync(kdfIterations);
|
||||||
await _cryptoService.SetKeyAsync(key);
|
await _cryptoService.SetKeyAsync(key);
|
||||||
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
||||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||||
@@ -216,7 +215,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
ResetPasswordKey = encryptedKey.EncryptedString
|
ResetPasswordKey = encryptedKey.EncryptedString
|
||||||
};
|
};
|
||||||
var userId = await _userService.GetUserIdAsync();
|
var userId = await _stateService.GetActiveUserIdAsync();
|
||||||
// Enroll user
|
// Enroll user
|
||||||
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
||||||
}
|
}
|
||||||
@@ -289,7 +288,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task<List<string>> GetPasswordStrengthUserInput()
|
private async Task<List<string>> GetPasswordStrengthUserInput()
|
||||||
{
|
{
|
||||||
var email = await _userService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
List<string> userInput = null;
|
List<string> userInput = null;
|
||||||
var atPosition = email.IndexOf('@');
|
var atPosition = email.IndexOf('@');
|
||||||
if (atPosition > -1)
|
if (atPosition > -1)
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
|
|
||||||
private TwoFactorPageViewModel _vm;
|
private TwoFactorPageViewModel _vm;
|
||||||
@@ -30,10 +28,8 @@ namespace Bit.App.Pages
|
|||||||
_authingWithSso = authingWithSso ?? false;
|
_authingWithSso = authingWithSso ?? false;
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
_orgIdentifier = orgIdentifier;
|
_orgIdentifier = orgIdentifier;
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
|
||||||
_vm = BindingContext as TwoFactorPageViewModel;
|
_vm = BindingContext as TwoFactorPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_vm.StartSetPasswordAction = () =>
|
_vm.StartSetPasswordAction = () =>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
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.Exceptions;
|
||||||
@@ -23,7 +22,6 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IEnvironmentService _environmentService;
|
private readonly IEnvironmentService _environmentService;
|
||||||
@@ -43,7 +41,6 @@ namespace Bit.App.Pages
|
|||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
@@ -303,8 +300,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
|
||||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
|
|
||||||
TwoFactorAuthSuccessAction?.Invoke();
|
TwoFactorAuthSuccessAction?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,6 @@ namespace Bit.App.Pages
|
|||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
|
|
||||||
// Service Use
|
|
||||||
_messagingService.Send("showStatusBar", true);
|
|
||||||
|
|
||||||
// Binding
|
// Binding
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks);
|
_pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks);
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve details for key generation
|
// Retrieve details for key generation
|
||||||
var kdf = await _userService.GetKdfAsync();
|
var kdf = await _stateService.GetKdfTypeAsync();
|
||||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||||
var email = await _userService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
|
|
||||||
// Create new key and hash new password
|
// Create new key and hash new password
|
||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
using Bit.Core;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.PlatformConfiguration;
|
using Xamarin.Forms.PlatformConfiguration;
|
||||||
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
||||||
@@ -13,8 +14,9 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public class BaseContentPage : ContentPage
|
public class BaseContentPage : ContentPage
|
||||||
{
|
{
|
||||||
private IStorageService _storageService;
|
private IStateService _stateService;
|
||||||
private IDeviceActionService _deviceActionService;
|
private IDeviceActionService _deviceActionService;
|
||||||
|
private IMessagingService _messagingService;
|
||||||
|
|
||||||
protected int ShowModalAnimationDelay = 400;
|
protected int ShowModalAnimationDelay = 400;
|
||||||
protected int ShowPageAnimationDelay = 100;
|
protected int ShowPageAnimationDelay = 100;
|
||||||
@@ -30,10 +32,10 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public DateTime? LastPageAction { get; set; }
|
public DateTime? LastPageAction { get; set; }
|
||||||
|
|
||||||
protected override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
SaveActivity();
|
await SaveActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DoOnce(Action action = null, int milliseconds = 1000)
|
public bool DoOnce(Action action = null, int milliseconds = 1000)
|
||||||
@@ -106,22 +108,121 @@ namespace Bit.App.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async Task<bool> ShowAccountSwitcherAsync()
|
||||||
|
{
|
||||||
|
return await _stateService.HasMultipleAccountsAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task RefreshAccountViewsAsync(Xamarin.Forms.ListView accountListView, bool allowAddAccountRow)
|
||||||
|
{
|
||||||
|
await _stateService.RefreshAccountViewsAsync(allowAddAccountRow);
|
||||||
|
// Property change trigger on account listview is yielding inconsistent results, using a hammer instead
|
||||||
|
accountListView.ItemsSource = null;
|
||||||
|
accountListView.ItemsSource = _stateService.AccountViews;
|
||||||
|
}
|
||||||
|
protected async Task<AvatarImageSource> GetAvatarImageSourceAsync(bool useCurrentActiveAccount = true)
|
||||||
|
{
|
||||||
|
return new AvatarImageSource(useCurrentActiveAccount ? await _stateService.GetEmailAsync() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task ShowAccountListAsync(bool isVisible, View listContainer, View overlay, View fab = null)
|
||||||
|
{
|
||||||
|
Device.BeginInvokeOnMainThread(async () =>
|
||||||
|
{
|
||||||
|
// Not all animations are awaited. This is intentional to allow multiple simultaneous animations.
|
||||||
|
if (isVisible)
|
||||||
|
{
|
||||||
|
// start listView in default (off-screen) position
|
||||||
|
await listContainer.TranslateTo(0, listContainer.Height * -1, 0);
|
||||||
|
|
||||||
|
// set overlay opacity to zero before making visible and start fade-in
|
||||||
|
overlay.Opacity = 0;
|
||||||
|
overlay.IsVisible = true;
|
||||||
|
overlay.FadeTo(1, 100);
|
||||||
|
|
||||||
|
if (Device.RuntimePlatform == Device.Android && fab != null)
|
||||||
|
{
|
||||||
|
// start fab fade-out
|
||||||
|
fab.FadeTo(0, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// slide account list into view
|
||||||
|
await listContainer.TranslateTo(0, 0, 200, Easing.SinOut);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// start overlay fade-out
|
||||||
|
overlay.FadeTo(0, 200);
|
||||||
|
|
||||||
|
if (Device.RuntimePlatform == Device.Android && fab != null)
|
||||||
|
{
|
||||||
|
// start fab fade-in
|
||||||
|
fab.FadeTo(1, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// slide account list out of view
|
||||||
|
await listContainer.TranslateTo(0, listContainer.Height * -1, 200, Easing.SinIn);
|
||||||
|
|
||||||
|
// remove overlay
|
||||||
|
overlay.IsVisible = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task AccountRowSelectedAsync(object sender, SelectedItemChangedEventArgs e, View listContainer,
|
||||||
|
View overlay, View fab = null, bool? allowActiveAccountSelection = false)
|
||||||
|
{
|
||||||
|
if (!DoOnce())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(e.SelectedItem is AccountViewCellViewModel item))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
((Xamarin.Forms.ListView)sender).SelectedItem = null;
|
||||||
|
await Task.Delay(100);
|
||||||
|
await ShowAccountListAsync(false, listContainer, overlay, fab);
|
||||||
|
|
||||||
|
if (item.AccountView.IsAccount)
|
||||||
|
{
|
||||||
|
if (item.AccountView.AuthStatus != AuthenticationStatus.Active)
|
||||||
|
{
|
||||||
|
await _stateService.SetActiveUserAsync(item.AccountView.UserId);
|
||||||
|
_messagingService.Send("switchedAccount");
|
||||||
|
}
|
||||||
|
else if (allowActiveAccountSelection ?? false)
|
||||||
|
{
|
||||||
|
_messagingService.Send("switchedAccount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_messagingService.Send("addAccount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SetServices()
|
private void SetServices()
|
||||||
{
|
{
|
||||||
if (_storageService == null)
|
if (_stateService == null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
}
|
}
|
||||||
if (_deviceActionService == null)
|
if (_deviceActionService == null)
|
||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
}
|
}
|
||||||
|
if (_messagingService == null)
|
||||||
|
{
|
||||||
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveActivity()
|
private async Task SaveActivity()
|
||||||
{
|
{
|
||||||
SetServices();
|
SetServices();
|
||||||
_storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
|
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Utilities;
|
using Bit.App.Controls;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -6,6 +7,7 @@ namespace Bit.App.Pages
|
|||||||
public abstract class BaseViewModel : ExtendedViewModel
|
public abstract class BaseViewModel : ExtendedViewModel
|
||||||
{
|
{
|
||||||
private string _pageTitle = string.Empty;
|
private string _pageTitle = string.Empty;
|
||||||
|
private AvatarImageSource _avatar;
|
||||||
|
|
||||||
public string PageTitle
|
public string PageTitle
|
||||||
{
|
{
|
||||||
@@ -13,6 +15,12 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _pageTitle, value);
|
set => SetProperty(ref _pageTitle, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AvatarImageSource AvatarImageSource
|
||||||
|
{
|
||||||
|
get => _avatar ?? new AvatarImageSource();
|
||||||
|
set => SetProperty(ref _avatar, value);
|
||||||
|
}
|
||||||
|
|
||||||
public ContentPage Page { get; set; }
|
public ContentPage Page { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
|
private readonly IClipboardService _clipboardService;
|
||||||
|
|
||||||
private bool _showNoData;
|
private bool _showNoData;
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ namespace Bit.App.Pages
|
|||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
||||||
"passwordGenerationService");
|
"passwordGenerationService");
|
||||||
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
|
||||||
PageTitle = AppResources.PasswordHistory;
|
PageTitle = AppResources.PasswordHistory;
|
||||||
History = new ExtendedObservableCollection<GeneratedPasswordHistory>();
|
History = new ExtendedObservableCollection<GeneratedPasswordHistory>();
|
||||||
@@ -51,7 +53,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async void CopyAsync(GeneratedPasswordHistory ph)
|
private async void CopyAsync(GeneratedPasswordHistory ph)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.CopyToClipboardAsync(ph.Password);
|
await _clipboardService.CopyTextAsync(ph.Password);
|
||||||
_platformUtilsService.ShowToast("info", null,
|
_platformUtilsService.ShowToast("info", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
|
private readonly IClipboardService _clipboardService;
|
||||||
|
|
||||||
private PasswordGenerationOptions _options;
|
private PasswordGenerationOptions _options;
|
||||||
private PasswordGeneratorPolicyOptions _enforcedPolicyOptions;
|
private PasswordGeneratorPolicyOptions _enforcedPolicyOptions;
|
||||||
@@ -38,6 +39,8 @@ namespace Bit.App.Pages
|
|||||||
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
||||||
"passwordGenerationService");
|
"passwordGenerationService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
|
||||||
PageTitle = AppResources.PasswordGenerator;
|
PageTitle = AppResources.PasswordGenerator;
|
||||||
TypeOptions = new List<string> { AppResources.Password, AppResources.Passphrase };
|
TypeOptions = new List<string> { AppResources.Password, AppResources.Passphrase };
|
||||||
}
|
}
|
||||||
@@ -305,7 +308,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task CopyAsync()
|
public async Task CopyAsync()
|
||||||
{
|
{
|
||||||
await _platformUtilsService.CopyToClipboardAsync(Password);
|
await _clipboardService.CopyTextAsync(Password);
|
||||||
_platformUtilsService.ShowToast("success", null,
|
_platformUtilsService.ShowToast("success", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
private readonly ISendService _sendService;
|
private readonly ISendService _sendService;
|
||||||
private bool _sendEnabled;
|
private bool _sendEnabled;
|
||||||
private bool _canAccessPremium;
|
private bool _canAccessPremium;
|
||||||
@@ -51,7 +51,7 @@ namespace Bit.App.Pages
|
|||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
|
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
|
|
||||||
@@ -220,8 +220,8 @@ namespace Bit.App.Pages
|
|||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
|
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
|
||||||
_canAccessPremium = await _userService.CanAccessPremiumAsync();
|
_canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||||
_emailVerified = await _userService.GetEmailVerifiedAsync();
|
_emailVerified = await _stateService.GetEmailVerifiedAsync();
|
||||||
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||||
DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync();
|
DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync();
|
||||||
SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail;
|
SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail;
|
||||||
|
|||||||
@@ -32,21 +32,19 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private readonly ISendService _sendService;
|
private readonly ISendService _sendService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
|
|
||||||
public SendGroupingsPageViewModel()
|
public SendGroupingsPageViewModel()
|
||||||
{
|
{
|
||||||
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
|
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
|
|
||||||
Loading = true;
|
Loading = true;
|
||||||
PageTitle = AppResources.Send;
|
PageTitle = AppResources.Send;
|
||||||
@@ -116,7 +114,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var authed = await _userService.IsAuthenticatedAsync();
|
var authed = await _stateService.IsAuthenticatedAsync();
|
||||||
if (!authed)
|
if (!authed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -125,7 +123,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing)
|
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
|
||||||
{
|
{
|
||||||
SyncRefreshing = true;
|
SyncRefreshing = true;
|
||||||
await _syncService.FullSyncAsync(false);
|
await _syncService.FullSyncAsync(false);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.App.Services;
|
using Bit.App.Services;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
@@ -11,7 +10,7 @@ namespace Bit.App.Pages
|
|||||||
public class AutofillServicesPageViewModel : BaseViewModel
|
public class AutofillServicesPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly MobileI18nService _i18nService;
|
private readonly MobileI18nService _i18nService;
|
||||||
|
|
||||||
private bool _autofillServiceToggled;
|
private bool _autofillServiceToggled;
|
||||||
@@ -23,7 +22,7 @@ namespace Bit.App.Pages
|
|||||||
public AutofillServicesPageViewModel()
|
public AutofillServicesPageViewModel()
|
||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
|
||||||
PageTitle = AppResources.AutofillServices;
|
PageTitle = AppResources.AutofillServices;
|
||||||
}
|
}
|
||||||
@@ -152,7 +151,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
InlineAutofillToggled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
|
InlineAutofillToggled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
||||||
_inited = true;
|
_inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +201,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited)
|
if (_inited)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.InlineAutofillEnabledKey, InlineAutofillToggled);
|
await _stateService.SetInlineAutofillEnabledAsync(InlineAutofillToggled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,9 @@
|
|||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<StackLayout Spacing="20">
|
<StackLayout
|
||||||
<StackLayout StyleClass="box">
|
StyleClass="box"
|
||||||
|
Spacing="20">
|
||||||
<Frame
|
<Frame
|
||||||
IsVisible="{Binding DisablePrivateVaultPolicyEnabled}"
|
IsVisible="{Binding DisablePrivateVaultPolicyEnabled}"
|
||||||
Padding="10"
|
Padding="10"
|
||||||
@@ -39,7 +40,12 @@
|
|||||||
StyleClass="text-muted, text-sm, text-bold"
|
StyleClass="text-muted, text-sm, text-bold"
|
||||||
HorizontalTextAlignment="Center" />
|
HorizontalTextAlignment="Center" />
|
||||||
</Frame>
|
</Frame>
|
||||||
<StackLayout StyleClass="box-row, box-row-input, box-row-input-options-platform">
|
<Grid
|
||||||
|
RowSpacing="10"
|
||||||
|
RowDefinitions="70, Auto, Auto">
|
||||||
|
<StackLayout
|
||||||
|
StyleClass="box-row"
|
||||||
|
Grid.Row="0">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n FileFormat}"
|
Text="{u:I18n FileFormat}"
|
||||||
StyleClass="box-label" />
|
StyleClass="box-label" />
|
||||||
@@ -51,23 +57,37 @@
|
|||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
IsEnabled="{Binding DisablePrivateVaultPolicyEnabled, Converter={StaticResource inverseBool}}" />
|
IsEnabled="{Binding DisablePrivateVaultPolicyEnabled, Converter={StaticResource inverseBool}}" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Grid StyleClass="box-row">
|
<StackLayout
|
||||||
<Grid.RowDefinitions>
|
StyleClass="box-row"
|
||||||
<RowDefinition Height="Auto" />
|
Grid.Row="1"
|
||||||
<RowDefinition Height="*" />
|
IsVisible="{Binding UseOTPVerification}">
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n MasterPassword}"
|
Text="{u:I18n SendVerificationCodeToEmail}"
|
||||||
|
StyleClass="box-label"
|
||||||
|
LineBreakMode="WordWrap"
|
||||||
|
Margin="0,0,0,10" />
|
||||||
|
<Button x:Name="_requestOTP"
|
||||||
|
Text="{u:I18n SendCode}"
|
||||||
|
Clicked="RequestOTP_Clicked"
|
||||||
|
HorizontalOptions="Fill"
|
||||||
|
VerticalOptions="End"
|
||||||
|
IsEnabled="{Binding DisablePrivateVaultPolicyEnabled, Converter={StaticResource inverseBool}}"
|
||||||
|
Margin="0,0,0,10"/>
|
||||||
|
</StackLayout>
|
||||||
|
<Grid
|
||||||
|
StyleClass="box-row"
|
||||||
|
Grid.Row="2"
|
||||||
|
RowDefinitions="Auto,Auto,Auto"
|
||||||
|
ColumnDefinitions="*,Auto"
|
||||||
|
Padding="0">
|
||||||
|
<Label
|
||||||
|
Text="{Binding SecretName}"
|
||||||
StyleClass="box-label"
|
StyleClass="box-label"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="0" />
|
Grid.ColumnSpan="2" />
|
||||||
<controls:MonoEntry
|
<controls:MonoEntry
|
||||||
x:Name="_masterPassword"
|
x:Name="_secret"
|
||||||
Text="{Binding MasterPassword}"
|
Text="{Binding Secret}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
IsSpellCheckEnabled="False"
|
IsSpellCheckEnabled="False"
|
||||||
IsTextPredictionEnabled="False"
|
IsTextPredictionEnabled="False"
|
||||||
@@ -81,24 +101,35 @@
|
|||||||
StyleClass="box-row-button, box-row-button-platform"
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
Text="{Binding ShowPasswordIcon}"
|
Text="{Binding ShowPasswordIcon}"
|
||||||
Command="{Binding TogglePasswordCommand}"
|
Command="{Binding TogglePasswordCommand}"
|
||||||
Grid.Row="0"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||||
|
IsVisible="{Binding UseOTPVerification, Converter={StaticResource inverseBool}}"/>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n ConfirmYourIdentity}"
|
||||||
|
StyleClass="box-footer-label"
|
||||||
|
LineBreakMode="WordWrap"
|
||||||
|
Grid.Row="2"
|
||||||
|
Margin="0,10,0,0"
|
||||||
|
IsVisible="{Binding UseOTPVerification}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
<StackLayout
|
||||||
|
StyleClass="box-row">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n ExportVaultMasterPasswordDescription}"
|
Text="{u:I18n ExportVaultMasterPasswordDescription}"
|
||||||
StyleClass="box-footer-label, box-footer-label-switch" />
|
StyleClass="box-footer-label, box-footer-label-switch"
|
||||||
<StackLayout Spacing="20">
|
IsVisible="{Binding UseOTPVerification, Converter={StaticResource inverseBool}}"/>
|
||||||
<Button Text="{u:I18n ExportVault}"
|
<Button
|
||||||
|
Text="{u:I18n ExportVault}"
|
||||||
Clicked="ExportVault_Clicked"
|
Clicked="ExportVault_Clicked"
|
||||||
HorizontalOptions="Fill"
|
HorizontalOptions="Fill"
|
||||||
VerticalOptions="End"
|
VerticalOptions="End"
|
||||||
IsEnabled="{Binding DisablePrivateVaultPolicyEnabled, Converter={StaticResource inverseBool}}"/>
|
IsEnabled="{Binding DisablePrivateVaultPolicyEnabled, Converter={StaticResource inverseBool}}"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
@@ -17,7 +17,7 @@ namespace Bit.App.Pages
|
|||||||
_vm = BindingContext as ExportVaultPageViewModel;
|
_vm = BindingContext as ExportVaultPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_fileFormatPicker.ItemDisplayBinding = new Binding("Value");
|
_fileFormatPicker.ItemDisplayBinding = new Binding("Value");
|
||||||
MasterPasswordEntry = _masterPassword;
|
SecretEntry = _secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
@@ -39,7 +39,7 @@ namespace Bit.App.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
RequestFocus(_masterPassword);
|
RequestFocus(_secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async override void OnDisappearing()
|
protected async override void OnDisappearing()
|
||||||
@@ -48,7 +48,7 @@ namespace Bit.App.Pages
|
|||||||
_broadcasterService.Unsubscribe(nameof(ExportVaultPage));
|
_broadcasterService.Unsubscribe(nameof(ExportVaultPage));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry MasterPasswordEntry { get; set; }
|
public Entry SecretEntry { get; set; }
|
||||||
|
|
||||||
private async void Close_Clicked(object sender, System.EventArgs e)
|
private async void Close_Clicked(object sender, System.EventArgs e)
|
||||||
{
|
{
|
||||||
@@ -66,6 +66,15 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void RequestOTP_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
await _vm.RequestOTP();
|
||||||
|
_requestOTP.IsEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FileFormat_Changed(object sender, EventArgs e)
|
void FileFormat_Changed(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
_vm?.UpdateWarning();
|
_vm?.UpdateWarning();
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ using Bit.App.Resources;
|
|||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
#if !FDROID
|
#if !FDROID
|
||||||
using Microsoft.AppCenter.Crashes;
|
using Microsoft.AppCenter.Crashes;
|
||||||
#endif
|
#endif
|
||||||
@@ -21,26 +20,33 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly II18nService _i18nService;
|
private readonly II18nService _i18nService;
|
||||||
private readonly ICryptoService _cryptoService;
|
|
||||||
private readonly IExportService _exportService;
|
private readonly IExportService _exportService;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
private readonly IUserVerificationService _userVerificationService;
|
||||||
|
private readonly IApiService _apiService;
|
||||||
|
|
||||||
private int _fileFormatSelectedIndex;
|
private int _fileFormatSelectedIndex;
|
||||||
private string _exportWarningMessage;
|
private string _exportWarningMessage;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
private string _masterPassword;
|
private string _secret;
|
||||||
private byte[] _exportResult;
|
private byte[] _exportResult;
|
||||||
private string _defaultFilename;
|
private string _defaultFilename;
|
||||||
private bool _initialized = false;
|
private bool _initialized = false;
|
||||||
|
private bool _useOTPVerification = false;
|
||||||
|
private string _secretName;
|
||||||
|
private string _instructionText;
|
||||||
|
|
||||||
public ExportVaultPageViewModel()
|
public ExportVaultPageViewModel()
|
||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
|
||||||
_exportService = ServiceContainer.Resolve<IExportService>("exportService");
|
_exportService = ServiceContainer.Resolve<IExportService>("exportService");
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
_userVerificationService = ServiceContainer.Resolve<IUserVerificationService>("userVerificationService");
|
||||||
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
|
|
||||||
PageTitle = AppResources.ExportVault;
|
PageTitle = AppResources.ExportVault;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
@@ -59,6 +65,18 @@ namespace Bit.App.Pages
|
|||||||
_initialized = true;
|
_initialized = true;
|
||||||
FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json");
|
FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json");
|
||||||
DisablePrivateVaultPolicyEnabled = await _policyService.PolicyAppliesToUser(PolicyType.DisablePersonalVaultExport);
|
DisablePrivateVaultPolicyEnabled = await _policyService.PolicyAppliesToUser(PolicyType.DisablePersonalVaultExport);
|
||||||
|
UseOTPVerification = await _keyConnectorService.GetUsesKeyConnector();
|
||||||
|
|
||||||
|
if (UseOTPVerification)
|
||||||
|
{
|
||||||
|
InstructionText = _i18nService.T("ExportVaultOTPDescription");
|
||||||
|
SecretName = _i18nService.T("VerificationCode");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InstructionText = _i18nService.T("ExportVaultMasterPasswordDescription");
|
||||||
|
SecretName = _i18nService.T("MasterPassword");
|
||||||
|
}
|
||||||
|
|
||||||
UpdateWarning();
|
UpdateWarning();
|
||||||
}
|
}
|
||||||
@@ -94,10 +112,28 @@ namespace Bit.App.Pages
|
|||||||
additionalPropertyNames: new string[] {nameof(ShowPasswordIcon)});
|
additionalPropertyNames: new string[] {nameof(ShowPasswordIcon)});
|
||||||
}
|
}
|
||||||
|
|
||||||
public string MasterPassword
|
public bool UseOTPVerification
|
||||||
{
|
{
|
||||||
get => _masterPassword;
|
get => _useOTPVerification;
|
||||||
set => SetProperty(ref _masterPassword, value);
|
set => SetProperty(ref _useOTPVerification, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Secret
|
||||||
|
{
|
||||||
|
get => _secret;
|
||||||
|
set => SetProperty(ref _secret, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SecretName
|
||||||
|
{
|
||||||
|
get => _secretName;
|
||||||
|
set => SetProperty(ref _secretName, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string InstructionText
|
||||||
|
{
|
||||||
|
get => _instructionText;
|
||||||
|
set => SetProperty(ref _instructionText, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
@@ -107,27 +143,13 @@ namespace Bit.App.Pages
|
|||||||
public void TogglePassword()
|
public void TogglePassword()
|
||||||
{
|
{
|
||||||
ShowPassword = !ShowPassword;
|
ShowPassword = !ShowPassword;
|
||||||
(Page as ExportVaultPage).MasterPasswordEntry.Focus();
|
(Page as ExportVaultPage).SecretEntry.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Command ExportVaultCommand { get; }
|
public Command ExportVaultCommand { get; }
|
||||||
|
|
||||||
public async Task ExportVaultAsync()
|
public async Task ExportVaultAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(_masterPassword))
|
|
||||||
{
|
|
||||||
await _platformUtilsService.ShowDialogAsync(_i18nService.T("InvalidMasterPassword"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(_masterPassword, null);
|
|
||||||
MasterPassword = string.Empty;
|
|
||||||
if (!passwordValid)
|
|
||||||
{
|
|
||||||
await _platformUtilsService.ShowDialogAsync(_i18nService.T("InvalidMasterPassword"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool userConfirmedExport = await _platformUtilsService.ShowDialogAsync(ExportWarningMessage,
|
bool userConfirmedExport = await _platformUtilsService.ShowDialogAsync(ExportWarningMessage,
|
||||||
_i18nService.T("ExportVaultConfirmationTitle"), _i18nService.T("ExportVault"), _i18nService.T("Cancel"));
|
_i18nService.T("ExportVaultConfirmationTitle"), _i18nService.T("ExportVault"), _i18nService.T("Cancel"));
|
||||||
|
|
||||||
@@ -136,6 +158,16 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var verificationType = await _keyConnectorService.GetUsesKeyConnector()
|
||||||
|
? VerificationType.OTP
|
||||||
|
: VerificationType.MasterPassword;
|
||||||
|
if (!await _userVerificationService.VerifyUser(Secret, verificationType))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Secret = string.Empty;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var data = await _exportService.GetExport(FileFormatOptions[FileFormatSelectedIndex].Key);
|
var data = await _exportService.GetExport(FileFormatOptions[FileFormatSelectedIndex].Key);
|
||||||
@@ -162,6 +194,27 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task RequestOTP()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _deviceActionService.ShowLoadingAsync(AppResources.Sending);
|
||||||
|
await _apiService.PostAccountRequestOTP();
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
_platformUtilsService.ShowToast("success", null, AppResources.CodeSent);
|
||||||
|
}
|
||||||
|
catch (ApiException e)
|
||||||
|
{
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
if (e?.Error != null)
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
||||||
|
AppResources.AnErrorHasOccurred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public async void SaveFileSelected(string contentUri, string filename)
|
public async void SaveFileSelected(string contentUri, string filename)
|
||||||
{
|
{
|
||||||
if (_deviceActionService.SaveFile(_exportResult, null, filename ?? _defaultFilename, contentUri))
|
if (_deviceActionService.SaveFile(_exportResult, null, filename ?? _defaultFilename, contentUri))
|
||||||
|
|||||||
@@ -7,12 +7,8 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public class ExtensionPageViewModel : BaseViewModel
|
public class ExtensionPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private const string StartedKey = "appExtensionStarted";
|
|
||||||
private const string ActivatedKey = "appExtensionActivated";
|
|
||||||
|
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
|
||||||
|
|
||||||
private bool _started;
|
private bool _started;
|
||||||
private bool _activated;
|
private bool _activated;
|
||||||
@@ -20,8 +16,7 @@ namespace Bit.App.Pages
|
|||||||
public ExtensionPageViewModel()
|
public ExtensionPageViewModel()
|
||||||
{
|
{
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
|
||||||
PageTitle = AppResources.AppExtension;
|
PageTitle = AppResources.AppExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,8 +47,8 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
var started = await _storageService.GetAsync<bool?>(StartedKey);
|
var started = await _stateService.GetAppExtensionStartedAsync();
|
||||||
var activated = await _storageService.GetAsync<bool?>(ActivatedKey);
|
var activated = await _stateService.GetAppExtensionActivatedAsync();
|
||||||
Started = started.GetValueOrDefault();
|
Started = started.GetValueOrDefault();
|
||||||
Activated = activated.GetValueOrDefault();
|
Activated = activated.GetValueOrDefault();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Resources;
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
@@ -13,9 +12,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public class OptionsPageViewModel : BaseViewModel
|
public class OptionsPageViewModel : BaseViewModel
|
||||||
{
|
{
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly ITotpService _totpService;
|
private readonly ITotpService _totpService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
@@ -34,9 +30,6 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public OptionsPageViewModel()
|
public OptionsPageViewModel()
|
||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
@@ -166,19 +159,17 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
AutofillDisableSavePrompt = (await _storageService.GetAsync<bool?>(
|
AutofillDisableSavePrompt = (await _stateService.GetAutofillDisableSavePromptAsync()).GetValueOrDefault();
|
||||||
Constants.AutofillDisableSavePromptKey)).GetValueOrDefault();
|
var blacklistedUrisList = await _stateService.GetAutofillBlacklistedUrisAsync();
|
||||||
var blacklistedUrisList = await _storageService.GetAsync<List<string>>(
|
|
||||||
Constants.AutofillBlacklistedUrisKey);
|
|
||||||
AutofillBlacklistedUris = blacklistedUrisList != null ? string.Join(", ", blacklistedUrisList) : null;
|
AutofillBlacklistedUris = blacklistedUrisList != null ? string.Join(", ", blacklistedUrisList) : null;
|
||||||
DisableAutoTotpCopy = !(await _totpService.IsAutoCopyEnabledAsync());
|
DisableAutoTotpCopy = !(await _totpService.IsAutoCopyEnabledAsync());
|
||||||
DisableFavicon = (await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey)).GetValueOrDefault();
|
DisableFavicon = (await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||||
var theme = await _storageService.GetAsync<string>(Constants.ThemeKey);
|
var theme = await _stateService.GetThemeAsync();
|
||||||
ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme);
|
ThemeSelectedIndex = ThemeOptions.FindIndex(k => k.Key == theme);
|
||||||
var defaultUriMatch = await _storageService.GetAsync<int?>(Constants.DefaultUriMatch);
|
var defaultUriMatch = await _stateService.GetDefaultUriMatchAsync();
|
||||||
UriMatchSelectedIndex = defaultUriMatch == null ? 0 :
|
UriMatchSelectedIndex = defaultUriMatch == null ? 0 :
|
||||||
UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch);
|
UriMatchOptions.FindIndex(k => (int?)k.Key == defaultUriMatch);
|
||||||
var clearClipboard = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey);
|
var clearClipboard = await _stateService.GetClearClipboardAsync();
|
||||||
ClearClipboardSelectedIndex = ClearClipboardOptions.FindIndex(k => k.Key == clearClipboard);
|
ClearClipboardSelectedIndex = ClearClipboardOptions.FindIndex(k => k.Key == clearClipboard);
|
||||||
_inited = true;
|
_inited = true;
|
||||||
}
|
}
|
||||||
@@ -187,7 +178,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited)
|
if (_inited)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.DisableAutoTotpCopyKey, DisableAutoTotpCopy);
|
await _stateService.SetDisableAutoTotpCopyAsync(DisableAutoTotpCopy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,8 +186,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited)
|
if (_inited)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon);
|
await _stateService.SetDisableFaviconAsync(DisableFavicon);
|
||||||
await _stateService.SaveAsync(Constants.DisableFaviconKey, DisableFavicon);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,8 +194,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited && ClearClipboardSelectedIndex > -1)
|
if (_inited && ClearClipboardSelectedIndex > -1)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.ClearClipboardKey,
|
await _stateService.SetClearClipboardAsync(ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
|
||||||
ClearClipboardOptions[ClearClipboardSelectedIndex].Key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,8 +203,8 @@ namespace Bit.App.Pages
|
|||||||
if (_inited && ThemeSelectedIndex > -1)
|
if (_inited && ThemeSelectedIndex > -1)
|
||||||
{
|
{
|
||||||
var theme = ThemeOptions[ThemeSelectedIndex].Key;
|
var theme = ThemeOptions[ThemeSelectedIndex].Key;
|
||||||
await _storageService.SaveAsync(Constants.ThemeKey, theme);
|
await _stateService.SetThemeAsync(theme);
|
||||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Application.Current.Resources);
|
ThemeManager.SetTheme(Application.Current.Resources);
|
||||||
_messagingService.Send("updatedTheme");
|
_messagingService.Send("updatedTheme");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,8 +213,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited && UriMatchSelectedIndex > -1)
|
if (_inited && UriMatchSelectedIndex > -1)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.DefaultUriMatch,
|
await _stateService.SetDefaultUriMatchAsync((int?)UriMatchOptions[UriMatchSelectedIndex].Key);
|
||||||
(int?)UriMatchOptions[UriMatchSelectedIndex].Key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +221,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited)
|
if (_inited)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.AutofillDisableSavePromptKey, AutofillDisableSavePrompt);
|
await _stateService.SetAutofillDisableSavePromptAsync(AutofillDisableSavePrompt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +231,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(AutofillBlacklistedUris))
|
if (string.IsNullOrWhiteSpace(AutofillBlacklistedUris))
|
||||||
{
|
{
|
||||||
await _storageService.RemoveAsync(Constants.AutofillBlacklistedUrisKey);
|
await _stateService.SetAutofillBlacklistedUrisAsync(null);
|
||||||
AutofillBlacklistedUris = null;
|
AutofillBlacklistedUris = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -265,7 +253,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
urisList.Add(cleanedUri);
|
urisList.Add(cleanedUri);
|
||||||
}
|
}
|
||||||
await _storageService.SaveAsync(Constants.AutofillBlacklistedUrisKey, urisList);
|
await _stateService.SetAutofillBlacklistedUrisAsync(urisList);
|
||||||
AutofillBlacklistedUris = string.Join(", ", urisList);
|
AutofillBlacklistedUris = string.Join(", ", urisList);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Bit.App.Abstractions;
|
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
|
using Bit.App.Pages.Accounts;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -134,6 +135,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _vm.LogOutAsync();
|
await _vm.LogOutAsync();
|
||||||
}
|
}
|
||||||
|
else if (item.Name == AppResources.DeleteAccount)
|
||||||
|
{
|
||||||
|
await Navigation.PushModalAsync(new NavigationPage(new DeleteAccountPage()));
|
||||||
|
}
|
||||||
else if (item.Name == AppResources.LockNow)
|
else if (item.Name == AppResources.LockNow)
|
||||||
{
|
{
|
||||||
await _vm.LockAsync();
|
await _vm.LockAsync();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
@@ -17,16 +16,17 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IEnvironmentService _environmentService;
|
private readonly IEnvironmentService _environmentService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IBiometricService _biometricService;
|
private readonly IBiometricService _biometricService;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
private readonly ILocalizeService _localizeService;
|
private readonly ILocalizeService _localizeService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
private readonly IClipboardService _clipboardService;
|
||||||
|
|
||||||
private const int CustomVaultTimeoutValue = -100;
|
private const int CustomVaultTimeoutValue = -100;
|
||||||
|
|
||||||
@@ -36,6 +36,8 @@ namespace Bit.App.Pages
|
|||||||
private string _lastSyncDate;
|
private string _lastSyncDate;
|
||||||
private string _vaultTimeoutDisplayValue;
|
private string _vaultTimeoutDisplayValue;
|
||||||
private string _vaultTimeoutActionDisplayValue;
|
private string _vaultTimeoutActionDisplayValue;
|
||||||
|
private bool _showChangeMasterPassword;
|
||||||
|
|
||||||
private List<KeyValuePair<string, int?>> _vaultTimeouts =
|
private List<KeyValuePair<string, int?>> _vaultTimeouts =
|
||||||
new List<KeyValuePair<string, int?>>
|
new List<KeyValuePair<string, int?>>
|
||||||
{
|
{
|
||||||
@@ -64,16 +66,17 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||||
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
|
||||||
GroupedItems = new ExtendedObservableCollection<SettingsPageListGroup>();
|
GroupedItems = new ExtendedObservableCollection<SettingsPageListGroup>();
|
||||||
PageTitle = AppResources.Settings;
|
PageTitle = AppResources.Settings;
|
||||||
@@ -105,7 +108,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
_vaultTimeout = await _vaultTimeoutService.GetVaultTimeout();
|
_vaultTimeout = await _vaultTimeoutService.GetVaultTimeout();
|
||||||
_vaultTimeoutDisplayValue = _vaultTimeouts.FirstOrDefault(o => o.Value == _vaultTimeout).Key;
|
_vaultTimeoutDisplayValue = _vaultTimeouts.FirstOrDefault(o => o.Value == _vaultTimeout).Key;
|
||||||
var action = await _storageService.GetAsync<string>(Constants.VaultTimeoutActionKey) ?? "lock";
|
var action = await _stateService.GetVaultTimeoutActionAsync() ?? "lock";
|
||||||
_vaultTimeoutActionDisplayValue = _vaultTimeoutActions.FirstOrDefault(o => o.Value == action).Key;
|
_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;
|
||||||
@@ -116,6 +119,9 @@ namespace Bit.App.Pages
|
|||||||
_vaultTimeoutDisplayValue = AppResources.Custom;
|
_vaultTimeoutDisplayValue = AppResources.Custom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_showChangeMasterPassword = IncludeLinksWithSubscriptionInfo() &&
|
||||||
|
!await _keyConnectorService.GetUsesKeyConnector();
|
||||||
|
|
||||||
BuildList();
|
BuildList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +134,7 @@ namespace Bit.App.Pages
|
|||||||
AppResources.Close);
|
AppResources.Close);
|
||||||
if (copy)
|
if (copy)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.CopyToClipboardAsync(debugText);
|
await _clipboardService.CopyTextAsync(debugText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +148,7 @@ namespace Bit.App.Pages
|
|||||||
List<string> fingerprint;
|
List<string> fingerprint;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
fingerprint = await _cryptoService.GetFingerprintAsync(await _userService.GetUserIdAsync());
|
fingerprint = await _cryptoService.GetFingerprintAsync(await _stateService.GetActiveUserIdAsync());
|
||||||
}
|
}
|
||||||
catch (Exception e) when (e.Message == "No public key available.")
|
catch (Exception e) when (e.Message == "No public key available.")
|
||||||
{
|
{
|
||||||
@@ -312,13 +318,17 @@ namespace Bit.App.Pages
|
|||||||
AppResources.SetPINDescription, null, AppResources.Submit, AppResources.Cancel, true);
|
AppResources.SetPINDescription, null, AppResources.Submit, AppResources.Cancel, true);
|
||||||
if (!string.IsNullOrWhiteSpace(pin))
|
if (!string.IsNullOrWhiteSpace(pin))
|
||||||
{
|
{
|
||||||
var masterPassOnRestart = await _platformUtilsService.ShowDialogAsync(
|
var masterPassOnRestart = false;
|
||||||
|
if (!await _keyConnectorService.GetUsesKeyConnector())
|
||||||
|
{
|
||||||
|
masterPassOnRestart = await _platformUtilsService.ShowDialogAsync(
|
||||||
AppResources.PINRequireMasterPasswordRestart, AppResources.UnlockWithPIN,
|
AppResources.PINRequireMasterPasswordRestart, AppResources.UnlockWithPIN,
|
||||||
AppResources.Yes, AppResources.No);
|
AppResources.Yes, AppResources.No);
|
||||||
|
}
|
||||||
|
|
||||||
var kdf = await _userService.GetKdfAsync();
|
var kdf = await _stateService.GetKdfTypeAsync();
|
||||||
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||||
var email = await _userService.GetEmailAsync();
|
var email = await _stateService.GetEmailAsync();
|
||||||
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email,
|
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email,
|
||||||
kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256),
|
kdf.GetValueOrDefault(Core.Enums.KdfType.PBKDF2_SHA256),
|
||||||
kdfIterations.GetValueOrDefault(5000));
|
kdfIterations.GetValueOrDefault(5000));
|
||||||
@@ -328,12 +338,12 @@ namespace Bit.App.Pages
|
|||||||
if (masterPassOnRestart)
|
if (masterPassOnRestart)
|
||||||
{
|
{
|
||||||
var encPin = await _cryptoService.EncryptAsync(pin);
|
var encPin = await _cryptoService.EncryptAsync(pin);
|
||||||
await _storageService.SaveAsync(Constants.ProtectedPin, encPin.EncryptedString);
|
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
||||||
_vaultTimeoutService.PinProtectedKey = pinProtectedKey;
|
await _stateService.SetPinProtectedCachedAsync(pinProtectedKey);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.PinProtectedKey, pinProtectedKey.EncryptedString);
|
await _stateService.SetPinProtectedAsync(pinProtectedKey.EncryptedString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -368,13 +378,13 @@ namespace Bit.App.Pages
|
|||||||
if (_biometric)
|
if (_biometric)
|
||||||
{
|
{
|
||||||
await _biometricService.SetupBiometricAsync();
|
await _biometricService.SetupBiometricAsync();
|
||||||
await _storageService.SaveAsync(Constants.BiometricUnlockKey, true);
|
await _stateService.SetBiometricUnlockAsync(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _storageService.RemoveAsync(Constants.BiometricUnlockKey);
|
await _stateService.SetBiometricUnlockAsync(null);
|
||||||
}
|
}
|
||||||
_vaultTimeoutService.BiometricLocked = false;
|
_stateService.BiometricLocked = false;
|
||||||
await _cryptoService.ToggleKeyAsync();
|
await _cryptoService.ToggleKeyAsync();
|
||||||
BuildList();
|
BuildList();
|
||||||
}
|
}
|
||||||
@@ -460,7 +470,7 @@ namespace Bit.App.Pages
|
|||||||
new SettingsPageListItem { Name = AppResources.FingerprintPhrase },
|
new SettingsPageListItem { Name = AppResources.FingerprintPhrase },
|
||||||
new SettingsPageListItem { Name = AppResources.LogOut }
|
new SettingsPageListItem { Name = AppResources.LogOut }
|
||||||
};
|
};
|
||||||
if (IncludeLinksWithSubscriptionInfo())
|
if (_showChangeMasterPassword)
|
||||||
{
|
{
|
||||||
accountItems.Insert(0, new SettingsPageListItem { Name = AppResources.ChangeMasterPassword });
|
accountItems.Insert(0, new SettingsPageListItem { Name = AppResources.ChangeMasterPassword });
|
||||||
}
|
}
|
||||||
@@ -479,7 +489,8 @@ namespace Bit.App.Pages
|
|||||||
new SettingsPageListItem { Name = AppResources.Options },
|
new SettingsPageListItem { Name = AppResources.Options },
|
||||||
new SettingsPageListItem { Name = AppResources.About },
|
new SettingsPageListItem { Name = AppResources.About },
|
||||||
new SettingsPageListItem { Name = AppResources.HelpAndFeedback },
|
new SettingsPageListItem { Name = AppResources.HelpAndFeedback },
|
||||||
new SettingsPageListItem { Name = AppResources.RateTheApp }
|
new SettingsPageListItem { Name = AppResources.RateTheApp },
|
||||||
|
new SettingsPageListItem { Name = AppResources.DeleteAccount }
|
||||||
};
|
};
|
||||||
GroupedItems.ResetWithRange(new List<SettingsPageListGroup>
|
GroupedItems.ResetWithRange(new List<SettingsPageListGroup>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@@ -12,7 +11,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly ILocalizeService _localizeService;
|
private readonly ILocalizeService _localizeService;
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||||
|
|
||||||
@@ -52,7 +51,7 @@ namespace Bit.App.Pages
|
|||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
await SetLastSyncAsync();
|
await SetLastSyncAsync();
|
||||||
EnableSyncOnRefresh = await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey);
|
EnableSyncOnRefresh = await _stateService.GetSyncOnRefreshAsync();
|
||||||
_inited = true;
|
_inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +59,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (_inited)
|
if (_inited)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.SyncOnRefreshKey, _syncOnRefresh);
|
await _stateService.SetSyncOnRefreshAsync(_syncOnRefresh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
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.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ namespace Bit.App.Pages
|
|||||||
public class TabsPage : TabbedPage
|
public class TabsPage : TabbedPage
|
||||||
{
|
{
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
|
||||||
private NavigationPage _groupingsPage;
|
private NavigationPage _groupingsPage;
|
||||||
private NavigationPage _sendGroupingsPage;
|
private NavigationPage _sendGroupingsPage;
|
||||||
@@ -18,6 +20,7 @@ namespace Bit.App.Pages
|
|||||||
public TabsPage(AppOptions appOptions = null, PreviousPageInfo previousPage = null)
|
public TabsPage(AppOptions appOptions = null, PreviousPageInfo previousPage = null)
|
||||||
{
|
{
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
|
||||||
_groupingsPage = new NavigationPage(new GroupingsPage(true, previousPage: previousPage))
|
_groupingsPage = new NavigationPage(new GroupingsPage(true, previousPage: previousPage))
|
||||||
{
|
{
|
||||||
@@ -43,7 +46,7 @@ namespace Bit.App.Pages
|
|||||||
var settingsPage = new NavigationPage(new SettingsPage(this))
|
var settingsPage = new NavigationPage(new SettingsPage(this))
|
||||||
{
|
{
|
||||||
Title = AppResources.Settings,
|
Title = AppResources.Settings,
|
||||||
IconImageSource = "cog.png"
|
IconImageSource = "cog_settings.png"
|
||||||
};
|
};
|
||||||
Children.Add(settingsPage);
|
Children.Add(settingsPage);
|
||||||
|
|
||||||
@@ -72,6 +75,15 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override async void OnAppearing()
|
||||||
|
{
|
||||||
|
base.OnAppearing();
|
||||||
|
if (await _keyConnectorService.UserNeedsMigration())
|
||||||
|
{
|
||||||
|
_messagingService.Send("convertAccountToKeyConnector");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ResetToVaultPage()
|
public void ResetToVaultPage()
|
||||||
{
|
{
|
||||||
CurrentPage = _groupingsPage;
|
CurrentPage = _groupingsPage;
|
||||||
|
|||||||
@@ -556,7 +556,7 @@
|
|||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End" />
|
HorizontalOptions="End" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout x:Name="_passwordPrompt" StyleClass="box-row, box-row-switch">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n PasswordPrompt}"
|
Text="{u:I18n PasswordPrompt}"
|
||||||
StyleClass="box-label-regular" />
|
StyleClass="box-label-regular" />
|
||||||
@@ -656,6 +656,16 @@
|
|||||||
</Keyboard>
|
</Keyboard>
|
||||||
</Entry.Keyboard>
|
</Entry.Keyboard>
|
||||||
</controls:MonoEntry>
|
</controls:MonoEntry>
|
||||||
|
<StackLayout
|
||||||
|
StyleClass="box-row, box-row-input"
|
||||||
|
IsVisible="{Binding IsLinkedType}">
|
||||||
|
<Picker
|
||||||
|
x:Name="_linkedFieldOptionPicker"
|
||||||
|
ItemsSource="{Binding LinkedFieldOptions, Mode=OneTime}"
|
||||||
|
SelectedIndex="{Binding LinkedFieldOptionSelectedIndex}"
|
||||||
|
ItemDisplayBinding="{Binding Key}"
|
||||||
|
StyleClass="box-value" />
|
||||||
|
</StackLayout>
|
||||||
<Switch
|
<Switch
|
||||||
IsToggled="{Binding BooleanValue}"
|
IsToggled="{Binding BooleanValue}"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
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.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@@ -18,9 +17,10 @@ namespace Bit.App.Pages
|
|||||||
public partial class AddEditPage : BaseContentPage
|
public partial class AddEditPage : BaseContentPage
|
||||||
{
|
{
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
|
|
||||||
private AddEditPageViewModel _vm;
|
private AddEditPageViewModel _vm;
|
||||||
private bool _fromAutofill;
|
private bool _fromAutofill;
|
||||||
@@ -37,9 +37,11 @@ namespace Bit.App.Pages
|
|||||||
bool cloneMode = false,
|
bool cloneMode = false,
|
||||||
ViewPage viewPage = null)
|
ViewPage viewPage = null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
_fromAutofill = fromAutofill;
|
_fromAutofill = fromAutofill;
|
||||||
FromAutofillFramework = _appOptions?.FromAutofillFramework ?? false;
|
FromAutofillFramework = _appOptions?.FromAutofillFramework ?? false;
|
||||||
@@ -171,6 +173,8 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
_scrollView.Scrolled += (sender, args) => _vm.HandleScroll();
|
_scrollView.Scrolled += (sender, args) => _vm.HandleScroll();
|
||||||
});
|
});
|
||||||
|
// Hide password reprompt option if using key connector
|
||||||
|
_passwordPrompt.IsVisible = !await _keyConnectorService.GetUsesKeyConnector();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDisappearing()
|
protected override void OnDisappearing()
|
||||||
@@ -328,10 +332,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var addLoginShown = await _storageService.GetAsync<bool?>(Constants.AddSitePromptShownKey);
|
var addLoginShown = await _stateService.GetAddSitePromptShownAsync();
|
||||||
if (_vm.Cipher.Type == CipherType.Login && !_fromAutofill && !addLoginShown.GetValueOrDefault())
|
if (_vm.Cipher.Type == CipherType.Login && !_fromAutofill && !addLoginShown.GetValueOrDefault())
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.AddSitePromptShownKey, true);
|
await _stateService.SetAddSitePromptShownAsync(true);
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
if (_deviceActionService.SystemMajorVersion() < 12)
|
if (_deviceActionService.SystemMajorVersion() < 12)
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ namespace Bit.App.Pages
|
|||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly IFolderService _folderService;
|
private readonly IFolderService _folderService;
|
||||||
private readonly ICollectionService _collectionService;
|
private readonly ICollectionService _collectionService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
|
private readonly IOrganizationService _organizationService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IAuditService _auditService;
|
private readonly IAuditService _auditService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
@@ -64,20 +65,14 @@ namespace Bit.App.Pages
|
|||||||
new KeyValuePair<UriMatchType?, string>(UriMatchType.Exact, AppResources.Exact),
|
new KeyValuePair<UriMatchType?, string>(UriMatchType.Exact, AppResources.Exact),
|
||||||
new KeyValuePair<UriMatchType?, string>(UriMatchType.Never, AppResources.Never)
|
new KeyValuePair<UriMatchType?, string>(UriMatchType.Never, AppResources.Never)
|
||||||
};
|
};
|
||||||
private List<KeyValuePair<FieldType, string>> _fieldTypeOptions =
|
|
||||||
new List<KeyValuePair<FieldType, string>>
|
|
||||||
{
|
|
||||||
new KeyValuePair<FieldType, string>(FieldType.Text, AppResources.FieldTypeText),
|
|
||||||
new KeyValuePair<FieldType, string>(FieldType.Hidden, AppResources.FieldTypeHidden),
|
|
||||||
new KeyValuePair<FieldType, string>(FieldType.Boolean, AppResources.FieldTypeBoolean)
|
|
||||||
};
|
|
||||||
|
|
||||||
public AddEditPageViewModel()
|
public AddEditPageViewModel()
|
||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
|
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
@@ -309,9 +304,9 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task<bool> LoadAsync(AppOptions appOptions = null)
|
public async Task<bool> LoadAsync(AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
var myEmail = await _userService.GetEmailAsync();
|
var myEmail = await _stateService.GetEmailAsync();
|
||||||
OwnershipOptions.Add(new KeyValuePair<string, string>(myEmail, null));
|
OwnershipOptions.Add(new KeyValuePair<string, string>(myEmail, null));
|
||||||
var orgs = await _userService.GetAllOrganizationAsync();
|
var orgs = await _organizationService.GetAllAsync();
|
||||||
foreach (var org in orgs.OrderBy(o => o.Name))
|
foreach (var org in orgs.OrderBy(o => o.Name))
|
||||||
{
|
{
|
||||||
if (org.Enabled && org.Status == OrganizationUserStatusType.Confirmed)
|
if (org.Enabled && org.Status == OrganizationUserStatusType.Confirmed)
|
||||||
@@ -667,8 +662,20 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async void AddField()
|
public async void AddField()
|
||||||
{
|
{
|
||||||
|
var fieldTypeOptions = new List<KeyValuePair<FieldType, string>>
|
||||||
|
{
|
||||||
|
new KeyValuePair<FieldType, string>(FieldType.Text, AppResources.FieldTypeText),
|
||||||
|
new KeyValuePair<FieldType, string>(FieldType.Hidden, AppResources.FieldTypeHidden),
|
||||||
|
new KeyValuePair<FieldType, string>(FieldType.Boolean, AppResources.FieldTypeBoolean),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Cipher.LinkedFieldOptions != null)
|
||||||
|
{
|
||||||
|
fieldTypeOptions.Add(new KeyValuePair<FieldType, string>(FieldType.Linked, AppResources.FieldTypeLinked));
|
||||||
|
}
|
||||||
|
|
||||||
var typeSelection = await Page.DisplayActionSheet(AppResources.SelectTypeField, AppResources.Cancel, null,
|
var typeSelection = await Page.DisplayActionSheet(AppResources.SelectTypeField, AppResources.Cancel, null,
|
||||||
_fieldTypeOptions.Select(f => f.Value).ToArray());
|
fieldTypeOptions.Select(f => f.Value).ToArray());
|
||||||
if (typeSelection != null && typeSelection != AppResources.Cancel)
|
if (typeSelection != null && typeSelection != AppResources.Cancel)
|
||||||
{
|
{
|
||||||
var name = await _deviceActionService.DisplayPromptAync(AppResources.CustomFieldName);
|
var name = await _deviceActionService.DisplayPromptAync(AppResources.CustomFieldName);
|
||||||
@@ -680,7 +687,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
Fields = new ExtendedObservableCollection<AddEditPageFieldViewModel>();
|
Fields = new ExtendedObservableCollection<AddEditPageFieldViewModel>();
|
||||||
}
|
}
|
||||||
var type = _fieldTypeOptions.FirstOrDefault(f => f.Value == typeSelection).Key;
|
var type = fieldTypeOptions.FirstOrDefault(f => f.Value == typeSelection).Key;
|
||||||
Fields.Add(new AddEditPageFieldViewModel(Cipher, new FieldView
|
Fields.Add(new AddEditPageFieldViewModel(Cipher, new FieldView
|
||||||
{
|
{
|
||||||
Type = type,
|
Type = type,
|
||||||
@@ -746,6 +753,12 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
Cipher.Type = TypeOptions[TypeSelectedIndex].Value;
|
Cipher.Type = TypeOptions[TypeSelectedIndex].Value;
|
||||||
TriggerCipherChanged();
|
TriggerCipherChanged();
|
||||||
|
|
||||||
|
// Linked Custom Fields only apply to a specific item type
|
||||||
|
foreach (var field in Fields.Where(f => f.IsLinkedType).ToList())
|
||||||
|
{
|
||||||
|
Fields.Remove(field);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -850,23 +863,29 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public class AddEditPageFieldViewModel : ExtendedViewModel
|
public class AddEditPageFieldViewModel : ExtendedViewModel
|
||||||
{
|
{
|
||||||
|
private II18nService _i18nService;
|
||||||
private FieldView _field;
|
private FieldView _field;
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private bool _showHiddenValue;
|
private bool _showHiddenValue;
|
||||||
private bool _booleanValue;
|
private bool _booleanValue;
|
||||||
|
private int _linkedFieldOptionSelectedIndex;
|
||||||
private string[] _additionalFieldProperties = new string[]
|
private string[] _additionalFieldProperties = new string[]
|
||||||
{
|
{
|
||||||
nameof(IsBooleanType),
|
nameof(IsBooleanType),
|
||||||
nameof(IsHiddenType),
|
nameof(IsHiddenType),
|
||||||
nameof(IsTextType),
|
nameof(IsTextType),
|
||||||
|
nameof(IsLinkedType),
|
||||||
};
|
};
|
||||||
|
|
||||||
public AddEditPageFieldViewModel(CipherView cipher, FieldView field)
|
public AddEditPageFieldViewModel(CipherView cipher, FieldView field)
|
||||||
{
|
{
|
||||||
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||||
_cipher = cipher;
|
_cipher = cipher;
|
||||||
Field = field;
|
Field = field;
|
||||||
ToggleHiddenValueCommand = new Command(ToggleHiddenValue);
|
ToggleHiddenValueCommand = new Command(ToggleHiddenValue);
|
||||||
BooleanValue = IsBooleanType && field.Value == "true";
|
BooleanValue = IsBooleanType && field.Value == "true";
|
||||||
|
LinkedFieldOptionSelectedIndex = !Field.LinkedId.HasValue ? 0 :
|
||||||
|
LinkedFieldOptions.FindIndex(lfo => lfo.Value == Field.LinkedId.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FieldView Field
|
public FieldView Field
|
||||||
@@ -898,12 +917,32 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int LinkedFieldOptionSelectedIndex
|
||||||
|
{
|
||||||
|
get => _linkedFieldOptionSelectedIndex;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _linkedFieldOptionSelectedIndex, value))
|
||||||
|
{
|
||||||
|
LinkedFieldValueChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KeyValuePair<string, LinkedIdType>> LinkedFieldOptions
|
||||||
|
{
|
||||||
|
get => _cipher.LinkedFieldOptions?
|
||||||
|
.Select(kvp => new KeyValuePair<string, LinkedIdType>(_i18nService.T(kvp.Key), kvp.Value))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public Command ToggleHiddenValueCommand { get; set; }
|
public Command ToggleHiddenValueCommand { get; set; }
|
||||||
|
|
||||||
public string ShowHiddenValueIcon => _showHiddenValue ? "" : "";
|
public string ShowHiddenValueIcon => _showHiddenValue ? "" : "";
|
||||||
public bool IsTextType => _field.Type == FieldType.Text;
|
public bool IsTextType => _field.Type == FieldType.Text;
|
||||||
public bool IsBooleanType => _field.Type == FieldType.Boolean;
|
public bool IsBooleanType => _field.Type == FieldType.Boolean;
|
||||||
public bool IsHiddenType => _field.Type == FieldType.Hidden;
|
public bool IsHiddenType => _field.Type == FieldType.Hidden;
|
||||||
|
public bool IsLinkedType => _field.Type == FieldType.Linked;
|
||||||
public bool ShowViewHidden => IsHiddenType && (_cipher.ViewPassword || _field.NewField);
|
public bool ShowViewHidden => IsHiddenType && (_cipher.ViewPassword || _field.NewField);
|
||||||
|
|
||||||
public void ToggleHiddenValue()
|
public void ToggleHiddenValue()
|
||||||
@@ -920,5 +959,14 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
TriggerPropertyChanged(nameof(Field), _additionalFieldProperties);
|
TriggerPropertyChanged(nameof(Field), _additionalFieldProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LinkedFieldValueChanged()
|
||||||
|
{
|
||||||
|
if (Field != null && LinkedFieldOptionSelectedIndex > -1)
|
||||||
|
{
|
||||||
|
Field.LinkedId = LinkedFieldOptions.Find(lfo =>
|
||||||
|
lfo.Value == LinkedFieldOptions[LinkedFieldOptionSelectedIndex].Value).Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private Cipher _cipherDomain;
|
private Cipher _cipherDomain;
|
||||||
@@ -32,7 +33,8 @@ namespace Bit.App.Pages
|
|||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
Attachments = new ExtendedObservableCollection<AttachmentView>();
|
Attachments = new ExtendedObservableCollection<AttachmentView>();
|
||||||
DeleteAttachmentCommand = new Command<AttachmentView>(DeleteAsync);
|
DeleteAttachmentCommand = new Command<AttachmentView>(DeleteAsync);
|
||||||
PageTitle = AppResources.Attachments;
|
PageTitle = AppResources.Attachments;
|
||||||
@@ -64,7 +66,7 @@ namespace Bit.App.Pages
|
|||||||
Cipher = await _cipherDomain.DecryptAsync();
|
Cipher = await _cipherDomain.DecryptAsync();
|
||||||
LoadAttachments();
|
LoadAttachments();
|
||||||
_hasUpdatedKey = await _cryptoService.HasEncKeyAsync();
|
_hasUpdatedKey = await _cryptoService.HasEncKeyAsync();
|
||||||
var canAccessPremium = await _userService.CanAccessPremiumAsync();
|
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||||
_canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null;
|
_canAccessAttachments = canAccessPremium || Cipher.OrganizationId != null;
|
||||||
if (!_canAccessAttachments)
|
if (!_canAccessAttachments)
|
||||||
{
|
{
|
||||||
@@ -135,6 +137,11 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task ChooseFileAsync()
|
public async Task ChooseFileAsync()
|
||||||
{
|
{
|
||||||
|
// Prevent Android from locking if vault timeout set to "immediate"
|
||||||
|
if (Device.RuntimePlatform == Device.Android)
|
||||||
|
{
|
||||||
|
_vaultTimeoutService.DelayLockAndLogoutMs = 60000;
|
||||||
|
}
|
||||||
await _deviceActionService.SelectFileAsync();
|
await _deviceActionService.SelectFileAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,8 @@ using Bit.Core.Enums;
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -88,8 +86,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task LoadAsync()
|
public async Task LoadAsync()
|
||||||
{
|
{
|
||||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||||
.GetValueOrDefault();
|
|
||||||
ShowList = false;
|
ShowList = false;
|
||||||
var groupedItems = new List<GroupingsPageListGroup>();
|
var groupedItems = new List<GroupingsPageListGroup>();
|
||||||
var ciphers = await _cipherService.GetAllDecryptedByUrlAsync(Uri, null);
|
var ciphers = await _cipherService.GetAllDecryptedByUrlAsync(Uri, null);
|
||||||
|
|||||||
@@ -76,8 +76,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||||
.GetValueOrDefault();
|
|
||||||
if (!string.IsNullOrWhiteSpace((Page as CiphersPage).SearchBar.Text))
|
if (!string.IsNullOrWhiteSpace((Page as CiphersPage).SearchBar.Text))
|
||||||
{
|
{
|
||||||
Search((Page as CiphersPage).SearchBar.Text, 200);
|
Search((Page as CiphersPage).SearchBar.Text, 200);
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<pages:BaseContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
<pages:BaseContentPage 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"
|
||||||
x:Class="Bit.App.Pages.GroupingsPage"
|
x:Class="Bit.App.Pages.GroupingsPage"
|
||||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
xmlns:effects="clr-namespace:Bit.App.Effects"
|
xmlns:effects="clr-namespace:Bit.App.Effects"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
|
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
|
||||||
x:DataType="pages:GroupingsPageViewModel"
|
x:DataType="pages:GroupingsPageViewModel"
|
||||||
Title="{Binding PageTitle}"
|
Title="{Binding PageTitle}"
|
||||||
x:Name="_page">
|
x:Name="_page">
|
||||||
@@ -15,6 +17,15 @@
|
|||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
|
<controls:ExtendedToolbarItem
|
||||||
|
x:Name="_accountAvatar"
|
||||||
|
IconImageSource="{Binding AvatarImageSource}"
|
||||||
|
Clicked="AccountSwitch_Clicked"
|
||||||
|
Order="Primary"
|
||||||
|
Priority="-1"
|
||||||
|
UseOriginalImage="True"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Account}" />
|
||||||
<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}" />
|
||||||
@@ -135,6 +146,8 @@
|
|||||||
AbsoluteLayout.LayoutFlags="All"
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
|
||||||
</ContentView>
|
</ContentView>
|
||||||
|
|
||||||
|
<!-- Android FAB -->
|
||||||
<Button
|
<Button
|
||||||
x:Name="_fab"
|
x:Name="_fab"
|
||||||
Image="plus.png"
|
Image="plus.png"
|
||||||
@@ -148,6 +161,40 @@
|
|||||||
<effects:FabShadowEffect />
|
<effects:FabShadowEffect />
|
||||||
</Button.Effects>
|
</Button.Effects>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<!-- Account Switching Overlay -->
|
||||||
|
<ContentView
|
||||||
|
x:Name="_accountListOverlay"
|
||||||
|
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
|
||||||
|
AbsoluteLayout.LayoutFlags="All"
|
||||||
|
IsVisible="False"
|
||||||
|
BackgroundColor="#22000000"
|
||||||
|
Padding="0">
|
||||||
|
|
||||||
|
<StackLayout
|
||||||
|
x:Name="_accountListContainer"
|
||||||
|
VerticalOptions="StartAndExpand"
|
||||||
|
HorizontalOptions="FillAndExpand"
|
||||||
|
BackgroundColor="{DynamicResource BackgroundColor}"
|
||||||
|
xct:ShadowEffect.Color="Black"
|
||||||
|
xct:ShadowEffect.Radius="10"
|
||||||
|
xct:ShadowEffect.OffsetY="3">
|
||||||
|
<ListView
|
||||||
|
x:Name="_accountListView"
|
||||||
|
ItemsSource="{Binding AccountViews}"
|
||||||
|
ItemSelected="AccountRow_Selected"
|
||||||
|
BackgroundColor="Transparent"
|
||||||
|
VerticalOptions="FillAndExpand"
|
||||||
|
RowHeight="60">
|
||||||
|
<ListView.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="view:AccountView">
|
||||||
|
<controls:AccountViewCell
|
||||||
|
Account="{Binding .}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ListView.ItemTemplate>
|
||||||
|
</ListView>
|
||||||
|
</StackLayout>
|
||||||
|
</ContentView>
|
||||||
</AbsoluteLayout>
|
</AbsoluteLayout>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
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.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@@ -9,6 +7,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -18,7 +17,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IPushNotificationService _pushNotificationService;
|
private readonly IPushNotificationService _pushNotificationService;
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
@@ -37,7 +36,7 @@ namespace Bit.App.Pages
|
|||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
@@ -70,6 +69,10 @@ namespace Bit.App.Pages
|
|||||||
_absLayout.Children.Remove(_fab);
|
_absLayout.Children.Remove(_fab);
|
||||||
ToolbarItems.Remove(_addItem);
|
ToolbarItems.Remove(_addItem);
|
||||||
}
|
}
|
||||||
|
if (!mainPage)
|
||||||
|
{
|
||||||
|
ToolbarItems.Remove(_accountAvatar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
@@ -80,6 +83,11 @@ namespace Bit.App.Pages
|
|||||||
IsBusy = true;
|
IsBusy = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_vm.MainPage)
|
||||||
|
{
|
||||||
|
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||||
|
}
|
||||||
|
|
||||||
_broadcasterService.Subscribe(_pageName, async (message) =>
|
_broadcasterService.Subscribe(_pageName, async (message) =>
|
||||||
{
|
{
|
||||||
if (message.Command == "syncStarted")
|
if (message.Command == "syncStarted")
|
||||||
@@ -100,7 +108,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var migratedFromV1 = await _storageService.GetAsync<bool?>(Constants.MigratedFromV1);
|
var migratedFromV1 = await _stateService.GetMigratedFromV1Async();
|
||||||
await LoadOnAppearedAsync(_mainLayout, false, async () =>
|
await LoadOnAppearedAsync(_mainLayout, false, async () =>
|
||||||
{
|
{
|
||||||
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
|
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
|
||||||
@@ -128,10 +136,10 @@ namespace Bit.App.Pages
|
|||||||
!_vm.HasCiphers &&
|
!_vm.HasCiphers &&
|
||||||
Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.None)
|
Xamarin.Essentials.Connectivity.NetworkAccess != Xamarin.Essentials.NetworkAccess.None)
|
||||||
{
|
{
|
||||||
var triedV1ReSync = await _storageService.GetAsync<bool?>(Constants.TriedV1Resync);
|
var triedV1ReSync = await _stateService.GetTriedV1ResyncAsync();
|
||||||
if (!triedV1ReSync.GetValueOrDefault())
|
if (!triedV1ReSync.GetValueOrDefault())
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.TriedV1Resync, true);
|
await _stateService.SetTriedV1ResyncAsync(true);
|
||||||
await _syncService.FullSyncAsync(true);
|
await _syncService.FullSyncAsync(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,14 +153,14 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push registration
|
// Push registration
|
||||||
var lastPushRegistration = await _storageService.GetAsync<DateTime?>(Constants.PushLastRegistrationDateKey);
|
var lastPushRegistration = await _stateService.GetPushLastRegistrationDateAsync();
|
||||||
lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue);
|
lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue);
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
var pushPromptShow = await _storageService.GetAsync<bool?>(Constants.PushInitialPromptShownKey);
|
var pushPromptShow = await _stateService.GetPushInitialPromptShownAsync();
|
||||||
if (!pushPromptShow.GetValueOrDefault(false))
|
if (!pushPromptShow.GetValueOrDefault(false))
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Constants.PushInitialPromptShownKey, true);
|
await _stateService.SetPushInitialPromptShownAsync(true);
|
||||||
await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert,
|
await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert,
|
||||||
AppResources.OkGotIt);
|
AppResources.OkGotIt);
|
||||||
}
|
}
|
||||||
@@ -173,8 +181,8 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (migratedFromV1.GetValueOrDefault())
|
if (migratedFromV1.GetValueOrDefault())
|
||||||
{
|
{
|
||||||
var migratedFromV1AutofillPromptShown = await _storageService.GetAsync<bool?>(
|
var migratedFromV1AutofillPromptShown =
|
||||||
Constants.MigratedFromV1AutofillPromptShown);
|
await _stateService.GetMigratedFromV1AutofillPromptShownAsync();
|
||||||
if (!migratedFromV1AutofillPromptShown.GetValueOrDefault())
|
if (!migratedFromV1AutofillPromptShown.GetValueOrDefault())
|
||||||
{
|
{
|
||||||
await DisplayAlert(AppResources.Autofill,
|
await DisplayAlert(AppResources.Autofill,
|
||||||
@@ -182,7 +190,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await _storageService.SaveAsync(Constants.MigratedFromV1AutofillPromptShown, true);
|
await _stateService.SetMigratedFromV1AutofillPromptShownAsync(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,5 +292,24 @@ namespace Bit.App.Pages
|
|||||||
_addItem.IsEnabled = !_vm.Deleted;
|
_addItem.IsEnabled = !_vm.Deleted;
|
||||||
_addItem.IconImageSource = _vm.Deleted ? null : "plus.png";
|
_addItem.IconImageSource = _vm.Deleted ? null : "plus.png";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void AccountSwitch_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (_accountListOverlay.IsVisible)
|
||||||
|
{
|
||||||
|
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay, _fab);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await RefreshAccountViewsAsync(_accountListView, true);
|
||||||
|
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay, _fab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
|
||||||
|
{
|
||||||
|
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, _fab);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
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.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
@@ -11,6 +10,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -39,13 +39,11 @@ namespace Bit.App.Pages
|
|||||||
private readonly IFolderService _folderService;
|
private readonly IFolderService _folderService;
|
||||||
private readonly ICollectionService _collectionService;
|
private readonly ICollectionService _collectionService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly IUserService _userService;
|
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IStorageService _storageService;
|
|
||||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||||
|
|
||||||
public GroupingsPageViewModel()
|
public GroupingsPageViewModel()
|
||||||
@@ -54,13 +52,11 @@ namespace Bit.App.Pages
|
|||||||
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
||||||
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
|
|
||||||
Loading = true;
|
Loading = true;
|
||||||
@@ -80,7 +76,6 @@ namespace Bit.App.Pages
|
|||||||
public string CollectionId { get; set; }
|
public string CollectionId { get; set; }
|
||||||
public Func<CipherView, bool> Filter { get; set; }
|
public Func<CipherView, bool> Filter { get; set; }
|
||||||
public bool Deleted { get; set; }
|
public bool Deleted { get; set; }
|
||||||
|
|
||||||
public bool HasCiphers { get; set; }
|
public bool HasCiphers { get; set; }
|
||||||
public bool HasFolders { get; set; }
|
public bool HasFolders { get; set; }
|
||||||
public bool HasCollections { get; set; }
|
public bool HasCollections { get; set; }
|
||||||
@@ -139,6 +134,11 @@ namespace Bit.App.Pages
|
|||||||
get => _websiteIconsEnabled;
|
get => _websiteIconsEnabled;
|
||||||
set => SetProperty(ref _websiteIconsEnabled, value);
|
set => SetProperty(ref _websiteIconsEnabled, value);
|
||||||
}
|
}
|
||||||
|
public ExtendedObservableCollection<AccountView> AccountViews
|
||||||
|
{
|
||||||
|
get => _stateService.AccountViews;
|
||||||
|
}
|
||||||
|
|
||||||
public ExtendedObservableCollection<GroupingsPageListGroup> GroupedItems { get; set; }
|
public ExtendedObservableCollection<GroupingsPageListGroup> GroupedItems { get; set; }
|
||||||
public Command RefreshCommand { get; set; }
|
public Command RefreshCommand { get; set; }
|
||||||
public Command<CipherView> CipherOptionsCommand { get; set; }
|
public Command<CipherView> CipherOptionsCommand { get; set; }
|
||||||
@@ -150,7 +150,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var authed = await _userService.IsAuthenticatedAsync();
|
var authed = await _stateService.IsAuthenticatedAsync();
|
||||||
if (!authed)
|
if (!authed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -159,7 +159,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing)
|
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
|
||||||
{
|
{
|
||||||
SyncRefreshing = true;
|
SyncRefreshing = true;
|
||||||
await _syncService.FullSyncAsync(false);
|
await _syncService.FullSyncAsync(false);
|
||||||
@@ -175,8 +175,7 @@ namespace Bit.App.Pages
|
|||||||
var groupedItems = new List<GroupingsPageListGroup>();
|
var groupedItems = new List<GroupingsPageListGroup>();
|
||||||
var page = Page as GroupingsPage;
|
var page = Page as GroupingsPage;
|
||||||
|
|
||||||
WebsiteIconsEnabled = !(await _stateService.GetAsync<bool?>(Constants.DisableFaviconKey))
|
WebsiteIconsEnabled = !(await _stateService.GetDisableFaviconAsync()).GetValueOrDefault();
|
||||||
.GetValueOrDefault();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await LoadDataAsync();
|
await LoadDataAsync();
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
|
private readonly IClipboardService _clipboardService;
|
||||||
|
|
||||||
private bool _showNoData;
|
private bool _showNoData;
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
|
||||||
PageTitle = AppResources.PasswordHistory;
|
PageTitle = AppResources.PasswordHistory;
|
||||||
History = new ExtendedObservableCollection<PasswordHistoryView>();
|
History = new ExtendedObservableCollection<PasswordHistoryView>();
|
||||||
@@ -45,7 +47,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async void CopyAsync(PasswordHistoryView ph)
|
private async void CopyAsync(PasswordHistoryView ph)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.CopyToClipboardAsync(ph.Password);
|
await _clipboardService.CopyTextAsync(ph.Password);
|
||||||
_platformUtilsService.ShowToast("info", null,
|
_platformUtilsService.ShowToast("info", null,
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly ICollectionService _collectionService;
|
private readonly ICollectionService _collectionService;
|
||||||
private readonly IUserService _userService;
|
private readonly IOrganizationService _organizationService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private int _organizationSelectedIndex;
|
private int _organizationSelectedIndex;
|
||||||
@@ -28,7 +28,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
||||||
Collections = new ExtendedObservableCollection<CollectionViewModel>();
|
Collections = new ExtendedObservableCollection<CollectionViewModel>();
|
||||||
@@ -67,7 +67,7 @@ namespace Bit.App.Pages
|
|||||||
var allCollections = await _collectionService.GetAllDecryptedAsync();
|
var allCollections = await _collectionService.GetAllDecryptedAsync();
|
||||||
_writeableCollections = allCollections.Where(c => !c.ReadOnly).ToList();
|
_writeableCollections = allCollections.Where(c => !c.ReadOnly).ToList();
|
||||||
|
|
||||||
var orgs = await _userService.GetAllOrganizationAsync();
|
var orgs = await _organizationService.GetAllAsync();
|
||||||
OrganizationOptions = orgs.OrderBy(o => o.Name)
|
OrganizationOptions = orgs.OrderBy(o => o.Name)
|
||||||
.Where(o => o.Enabled && o.Status == OrganizationUserStatusType.Confirmed)
|
.Where(o => o.Enabled && o.Status == OrganizationUserStatusType.Confirmed)
|
||||||
.Select(o => new KeyValuePair<string, string>(o.Name, o.Id)).ToList();
|
.Select(o => new KeyValuePair<string, string>(o.Name, o.Id)).ToList();
|
||||||
@@ -110,7 +110,7 @@ namespace Bit.App.Pages
|
|||||||
await _cipherService.ShareWithServerAsync(cipherView, OrganizationId, checkedCollectionIds);
|
await _cipherService.ShareWithServerAsync(cipherView, OrganizationId, checkedCollectionIds);
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
var movedItemToOrgText = string.Format(AppResources.MovedItemToOrg, cipherView.Name,
|
var movedItemToOrgText = string.Format(AppResources.MovedItemToOrg, cipherView.Name,
|
||||||
(await _userService.GetOrganizationAsync(OrganizationId)).Name);
|
(await _organizationService.GetAsync(OrganizationId)).Name);
|
||||||
_platformUtilsService.ShowToast("success", null, movedItemToOrgText);
|
_platformUtilsService.ShowToast("success", null, movedItemToOrgText);
|
||||||
await Page.Navigation.PopModalAsync();
|
await Page.Navigation.PopModalAsync();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -561,6 +561,12 @@
|
|||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
IsVisible="{Binding IsTextType}" />
|
IsVisible="{Binding IsTextType}" />
|
||||||
|
<controls:FaLabel
|
||||||
|
Text="{Binding ValueText, Mode=OneWay}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
IsVisible="{Binding IsLinkedType}" />
|
||||||
<controls:FaLabel
|
<controls:FaLabel
|
||||||
Text="{Binding ValueText, Mode=OneWay}"
|
Text="{Binding ValueText, Mode=OneWay}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
using Bit.App.Abstractions;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
@@ -6,10 +10,6 @@ using Bit.Core.Enums;
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -18,7 +18,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly IUserService _userService;
|
private readonly IStateService _stateService;
|
||||||
private readonly ITotpService _totpService;
|
private readonly ITotpService _totpService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IAuditService _auditService;
|
private readonly IAuditService _auditService;
|
||||||
@@ -26,6 +26,8 @@ namespace Bit.App.Pages
|
|||||||
private readonly IEventService _eventService;
|
private readonly IEventService _eventService;
|
||||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||||
private readonly ILocalizeService _localizeService;
|
private readonly ILocalizeService _localizeService;
|
||||||
|
private readonly IClipboardService _clipboardService;
|
||||||
|
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private List<ViewPageFieldViewModel> _fields;
|
private List<ViewPageFieldViewModel> _fields;
|
||||||
private bool _canAccessPremium;
|
private bool _canAccessPremium;
|
||||||
@@ -46,7 +48,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
||||||
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
||||||
@@ -54,6 +56,8 @@ namespace Bit.App.Pages
|
|||||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||||
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
||||||
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
||||||
|
|
||||||
CopyCommand = new Command<string>((id) => CopyAsync(id, null));
|
CopyCommand = new Command<string>((id) => CopyAsync(id, null));
|
||||||
CopyUriCommand = new Command<LoginUriView>(CopyUri);
|
CopyUriCommand = new Command<LoginUriView>(CopyUri);
|
||||||
CopyFieldCommand = new Command<FieldView>(CopyField);
|
CopyFieldCommand = new Command<FieldView>(CopyField);
|
||||||
@@ -244,7 +248,7 @@ namespace Bit.App.Pages
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Cipher = await cipher.DecryptAsync();
|
Cipher = await cipher.DecryptAsync();
|
||||||
CanAccessPremium = await _userService.CanAccessPremiumAsync();
|
CanAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||||
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(this, Cipher, f)).ToList();
|
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(this, Cipher, f)).ToList();
|
||||||
|
|
||||||
if (Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
|
if (Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
|
||||||
@@ -653,7 +657,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
if (text != null)
|
if (text != null)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.CopyToClipboardAsync(text);
|
await _clipboardService.CopyTextAsync(text);
|
||||||
if (!string.IsNullOrWhiteSpace(name))
|
if (!string.IsNullOrWhiteSpace(name))
|
||||||
{
|
{
|
||||||
_platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, name));
|
_platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, name));
|
||||||
@@ -704,6 +708,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public class ViewPageFieldViewModel : ExtendedViewModel
|
public class ViewPageFieldViewModel : ExtendedViewModel
|
||||||
{
|
{
|
||||||
|
private II18nService _i18nService;
|
||||||
private ViewPageViewModel _vm;
|
private ViewPageViewModel _vm;
|
||||||
private FieldView _field;
|
private FieldView _field;
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
@@ -711,6 +716,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public ViewPageFieldViewModel(ViewPageViewModel vm, CipherView cipher, FieldView field)
|
public ViewPageFieldViewModel(ViewPageViewModel vm, CipherView cipher, FieldView field)
|
||||||
{
|
{
|
||||||
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||||
_vm = vm;
|
_vm = vm;
|
||||||
_cipher = cipher;
|
_cipher = cipher;
|
||||||
Field = field;
|
Field = field;
|
||||||
@@ -741,16 +747,38 @@ namespace Bit.App.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string ValueText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsBooleanType)
|
||||||
|
{
|
||||||
|
return _field.Value == "true" ? "" : "";
|
||||||
|
}
|
||||||
|
else if (IsLinkedType)
|
||||||
|
{
|
||||||
|
var i18nKey = _cipher.LinkedFieldI18nKey(Field.LinkedId.GetValueOrDefault());
|
||||||
|
return " " + _i18nService.T(i18nKey);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return _field.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Command ToggleHiddenValueCommand { get; set; }
|
public Command ToggleHiddenValueCommand { get; set; }
|
||||||
|
|
||||||
public string ValueText => IsBooleanType ? (_field.Value == "true" ? "" : "") : _field.Value;
|
|
||||||
public string ShowHiddenValueIcon => _showHiddenValue ? "" : "";
|
public string ShowHiddenValueIcon => _showHiddenValue ? "" : "";
|
||||||
public bool IsTextType => _field.Type == Core.Enums.FieldType.Text;
|
public bool IsTextType => _field.Type == Core.Enums.FieldType.Text;
|
||||||
public bool IsBooleanType => _field.Type == Core.Enums.FieldType.Boolean;
|
public bool IsBooleanType => _field.Type == Core.Enums.FieldType.Boolean;
|
||||||
public bool IsHiddenType => _field.Type == Core.Enums.FieldType.Hidden;
|
public bool IsHiddenType => _field.Type == Core.Enums.FieldType.Hidden;
|
||||||
|
public bool IsLinkedType => _field.Type == Core.Enums.FieldType.Linked;
|
||||||
public bool ShowViewHidden => IsHiddenType && _cipher.ViewPassword;
|
public bool ShowViewHidden => IsHiddenType && _cipher.ViewPassword;
|
||||||
public bool ShowCopyButton => _field.Type != Core.Enums.FieldType.Boolean &&
|
public bool ShowCopyButton => _field.Type != Core.Enums.FieldType.Boolean &&
|
||||||
!string.IsNullOrWhiteSpace(_field.Value) && !(IsHiddenType && !_cipher.ViewPassword);
|
!string.IsNullOrWhiteSpace(_field.Value) &&
|
||||||
|
!(IsHiddenType && !_cipher.ViewPassword) &&
|
||||||
|
_field.Type != FieldType.Linked;
|
||||||
|
|
||||||
public async void ToggleHiddenValue()
|
public async void ToggleHiddenValue()
|
||||||
{
|
{
|
||||||
|
|||||||
124
src/App/Resources/AppResources.Designer.cs
generated
124
src/App/Resources/AppResources.Designer.cs
generated
@@ -1,6 +1,7 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// <auto-generated>
|
// <auto-generated>
|
||||||
// This code was generated by a tool.
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
//
|
//
|
||||||
// Changes to this file may cause incorrect behavior and will be lost if
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
// the code is regenerated.
|
// the code is regenerated.
|
||||||
@@ -9,9 +10,10 @@
|
|||||||
|
|
||||||
namespace Bit.App.Resources {
|
namespace Bit.App.Resources {
|
||||||
using System;
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
|
||||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
public class AppResources {
|
public class AppResources {
|
||||||
@@ -1779,6 +1781,12 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string FullName {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("FullName", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string LicenseNumber {
|
public static string LicenseNumber {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("LicenseNumber", resourceCulture);
|
return ResourceManager.GetString("LicenseNumber", resourceCulture);
|
||||||
@@ -2025,6 +2033,12 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string FieldTypeLinked {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("FieldTypeLinked", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string FieldTypeText {
|
public static string FieldTypeText {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("FieldTypeText", resourceCulture);
|
return ResourceManager.GetString("FieldTypeText", resourceCulture);
|
||||||
@@ -2529,6 +2543,12 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string UnlockVault {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("UnlockVault", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string ThirtyMinutes {
|
public static string ThirtyMinutes {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ThirtyMinutes", resourceCulture);
|
return ResourceManager.GetString("ThirtyMinutes", resourceCulture);
|
||||||
@@ -2559,6 +2579,12 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string VaultLockedIdentity {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("VaultLockedIdentity", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string Dark {
|
public static string Dark {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("Dark", resourceCulture);
|
return ResourceManager.GetString("Dark", resourceCulture);
|
||||||
@@ -2823,6 +2849,24 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string SendVerificationCodeToEmail {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SendVerificationCodeToEmail", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CodeSent {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CodeSent", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ConfirmYourIdentity {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ConfirmYourIdentity", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string ExportVaultWarning {
|
public static string ExportVaultWarning {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ExportVaultWarning", resourceCulture);
|
return ResourceManager.GetString("ExportVaultWarning", resourceCulture);
|
||||||
@@ -3585,6 +3629,36 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string RemoveMasterPassword {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RemoveMasterPassword", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string RemoveMasterPasswordWarning {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RemoveMasterPasswordWarning", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string RemoveMasterPasswordWarning2 {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RemoveMasterPasswordWarning2", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string LeaveOrganization {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("LeaveOrganization", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string LeaveOrganizationName {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("LeaveOrganizationName", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string Fido2Title {
|
public static string Fido2Title {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("Fido2Title", resourceCulture);
|
return ResourceManager.GetString("Fido2Title", resourceCulture);
|
||||||
@@ -3644,5 +3718,53 @@ namespace Bit.App.Resources {
|
|||||||
return ResourceManager.GetString("DisablePersonalVaultExportPolicyInEffect", resourceCulture);
|
return ResourceManager.GetString("DisablePersonalVaultExportPolicyInEffect", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string DeleteAccount {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("DeleteAccount", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string DeletingYourAccountIsPermanent {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("DeletingYourAccountIsPermanent", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string DeleteAccountExplanation {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("DeleteAccountExplanation", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string DeletingYourAccount {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("DeletingYourAccount", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string YourAccountHasBeenPermanentlyDeleted {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("YourAccountHasBeenPermanentlyDeleted", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string InvalidVerificationCode {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("InvalidVerificationCode", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SendCode {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SendCode", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Sending {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Sending", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1059,6 +1059,9 @@
|
|||||||
<data name="LastName" xml:space="preserve">
|
<data name="LastName" xml:space="preserve">
|
||||||
<value>Van</value>
|
<value>Van</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="FullName" xml:space="preserve">
|
||||||
|
<value>Volle naam</value>
|
||||||
|
</data>
|
||||||
<data name="LicenseNumber" xml:space="preserve">
|
<data name="LicenseNumber" xml:space="preserve">
|
||||||
<value>Lisensienommer</value>
|
<value>Lisensienommer</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1183,6 +1186,9 @@
|
|||||||
<data name="FieldTypeHidden" xml:space="preserve">
|
<data name="FieldTypeHidden" xml:space="preserve">
|
||||||
<value>Versteek</value>
|
<value>Versteek</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="FieldTypeLinked" xml:space="preserve">
|
||||||
|
<value>Gekoppel</value>
|
||||||
|
</data>
|
||||||
<data name="FieldTypeText" xml:space="preserve">
|
<data name="FieldTypeText" xml:space="preserve">
|
||||||
<value>Teks</value>
|
<value>Teks</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1449,6 +1455,9 @@
|
|||||||
<data name="Unlock" xml:space="preserve">
|
<data name="Unlock" xml:space="preserve">
|
||||||
<value>Ontgrendel</value>
|
<value>Ontgrendel</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="UnlockVault" xml:space="preserve">
|
||||||
|
<value>Ontgrendel kluis</value>
|
||||||
|
</data>
|
||||||
<data name="ThirtyMinutes" xml:space="preserve">
|
<data name="ThirtyMinutes" xml:space="preserve">
|
||||||
<value>30 minute</value>
|
<value>30 minute</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1465,6 +1474,9 @@
|
|||||||
<data name="VaultLockedPIN" xml:space="preserve">
|
<data name="VaultLockedPIN" xml:space="preserve">
|
||||||
<value>U kluis is vergrendel. Verifieer u PIN-kode om voort te gaan.</value>
|
<value>U kluis is vergrendel. Verifieer u PIN-kode om voort te gaan.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||||
|
<value>U kluis is vergrendel. Bevestig u identiteit om voort te gaan.</value>
|
||||||
|
</data>
|
||||||
<data name="Dark" xml:space="preserve">
|
<data name="Dark" xml:space="preserve">
|
||||||
<value>Donker</value>
|
<value>Donker</value>
|
||||||
<comment>A dark color</comment>
|
<comment>A dark color</comment>
|
||||||
@@ -1605,6 +1617,15 @@
|
|||||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||||
<value>Voer u hoofwagwoord in om u kluisdata uit te stuur.</value>
|
<value>Voer u hoofwagwoord in om u kluisdata uit te stuur.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||||
|
<value>Stuur ’n bevestigingskode na u e-pos</value>
|
||||||
|
</data>
|
||||||
|
<data name="CodeSent" xml:space="preserve">
|
||||||
|
<value>Kode verstuur!</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||||
|
<value>Bevestig u identiteit om voort te gaan.</value>
|
||||||
|
</data>
|
||||||
<data name="ExportVaultWarning" xml:space="preserve">
|
<data name="ExportVaultWarning" xml:space="preserve">
|
||||||
<value>Hierdie uitstuur bevat u kluisdata in ’n ongeënkripteerde formaat. U behoort dit nie oor onbeveiligde kanale (soos e-pos) te bewaar of verstuur nie. Skrap dit sodra u dit klaar gebruik het.</value>
|
<value>Hierdie uitstuur bevat u kluisdata in ’n ongeënkripteerde formaat. U behoort dit nie oor onbeveiligde kanale (soos e-pos) te bewaar of verstuur nie. Skrap dit sodra u dit klaar gebruik het.</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -2026,6 +2047,21 @@
|
|||||||
<data name="UpdatePasswordError" xml:space="preserve">
|
<data name="UpdatePasswordError" xml:space="preserve">
|
||||||
<value>Kan nie tans u wagwoord bywerk nie</value>
|
<value>Kan nie tans u wagwoord bywerk nie</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="RemoveMasterPassword" xml:space="preserve">
|
||||||
|
<value>Verwyder hoofwagwoord</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveMasterPasswordWarning" xml:space="preserve">
|
||||||
|
<value>{0} gebruik SSO met klantbestuurde enkripsie. Deur voort te gaan word u hoofwagwoord van u rekening verwyder en word SSO vereis om aan te teken.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveMasterPasswordWarning2" xml:space="preserve">
|
||||||
|
<value>Indien u nie u hoofwagwoord wil verwyder nie, kan u hierdie organisasie verlaat.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LeaveOrganization" xml:space="preserve">
|
||||||
|
<value>Verlaat organisasie</value>
|
||||||
|
</data>
|
||||||
|
<data name="LeaveOrganizationName" xml:space="preserve">
|
||||||
|
<value>Verlaat {0}?</value>
|
||||||
|
</data>
|
||||||
<data name="Fido2Title" xml:space="preserve">
|
<data name="Fido2Title" xml:space="preserve">
|
||||||
<value>FIDO2 WebAuthn</value>
|
<value>FIDO2 WebAuthn</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -2056,4 +2092,28 @@
|
|||||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||||
<value>Een of meer organisasiebeleide verhoed u om u persoonlike kluis uit te stuur.</value>
|
<value>Een of meer organisasiebeleide verhoed u om u persoonlike kluis uit te stuur.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DeleteAccount" xml:space="preserve">
|
||||||
|
<value>Skrap rekening</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||||
|
<value>Die skrap van u rekening is permanent</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||||
|
<value>U rekening en alle bybehorende data sal geskrap en onherwinbaar word. Is u seker u wil voortgaan?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeletingYourAccount" xml:space="preserve">
|
||||||
|
<value>U rekening word geskrap</value>
|
||||||
|
</data>
|
||||||
|
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||||
|
<value>U rekening is permanent geskrap</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||||
|
<value>Ongeldige bevestigingskode.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendCode" xml:space="preserve">
|
||||||
|
<value>Verstuur kode</value>
|
||||||
|
</data>
|
||||||
|
<data name="Sending" xml:space="preserve">
|
||||||
|
<value>Verstuur</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -1059,6 +1059,9 @@
|
|||||||
<data name="LastName" xml:space="preserve">
|
<data name="LastName" xml:space="preserve">
|
||||||
<value>Soyad</value>
|
<value>Soyad</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="FullName" xml:space="preserve">
|
||||||
|
<value>Tam ad</value>
|
||||||
|
</data>
|
||||||
<data name="LicenseNumber" xml:space="preserve">
|
<data name="LicenseNumber" xml:space="preserve">
|
||||||
<value>Lisenziya nömrəsi</value>
|
<value>Lisenziya nömrəsi</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1183,6 +1186,9 @@
|
|||||||
<data name="FieldTypeHidden" xml:space="preserve">
|
<data name="FieldTypeHidden" xml:space="preserve">
|
||||||
<value>Gizli</value>
|
<value>Gizli</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="FieldTypeLinked" xml:space="preserve">
|
||||||
|
<value>Əlaqə yaradıldı</value>
|
||||||
|
</data>
|
||||||
<data name="FieldTypeText" xml:space="preserve">
|
<data name="FieldTypeText" xml:space="preserve">
|
||||||
<value>Mətn</value>
|
<value>Mətn</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1449,6 +1455,9 @@
|
|||||||
<data name="Unlock" xml:space="preserve">
|
<data name="Unlock" xml:space="preserve">
|
||||||
<value>Kilidi aç</value>
|
<value>Kilidi aç</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="UnlockVault" xml:space="preserve">
|
||||||
|
<value>Anbar kilidini aç</value>
|
||||||
|
</data>
|
||||||
<data name="ThirtyMinutes" xml:space="preserve">
|
<data name="ThirtyMinutes" xml:space="preserve">
|
||||||
<value>30 dəqiqə</value>
|
<value>30 dəqiqə</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1465,6 +1474,9 @@
|
|||||||
<data name="VaultLockedPIN" xml:space="preserve">
|
<data name="VaultLockedPIN" xml:space="preserve">
|
||||||
<value>Anbarınız kilidlənib. Davam etmək üçün PIN kodunuzu təsdiqləyin.</value>
|
<value>Anbarınız kilidlənib. Davam etmək üçün PIN kodunuzu təsdiqləyin.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||||
|
<value>Anbarınız kilidlənib. Davam etmək üçün kimliyinizi təsdiqləyin.</value>
|
||||||
|
</data>
|
||||||
<data name="Dark" xml:space="preserve">
|
<data name="Dark" xml:space="preserve">
|
||||||
<value>Tünd</value>
|
<value>Tünd</value>
|
||||||
<comment>A dark color</comment>
|
<comment>A dark color</comment>
|
||||||
@@ -1605,6 +1617,15 @@
|
|||||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||||
<value>Anbar verilənlərinizi ixrac etmək üçün ana parolunuzu daxil edin.</value>
|
<value>Anbar verilənlərinizi ixrac etmək üçün ana parolunuzu daxil edin.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||||
|
<value>E-poçtunuza bir təsdiqləmə kodu göndərin</value>
|
||||||
|
</data>
|
||||||
|
<data name="CodeSent" xml:space="preserve">
|
||||||
|
<value>Kod göndərildi!</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||||
|
<value>Davam etmək üçün kimliyinizi təsdiqləyin.</value>
|
||||||
|
</data>
|
||||||
<data name="ExportVaultWarning" xml:space="preserve">
|
<data name="ExportVaultWarning" xml:space="preserve">
|
||||||
<value>Bu ixrac faylındakı anbar verilənləriniz şifrələnməmiş formatdadır. İxrac edilən faylı saxlamamalı və etibarsız yollarla (e-poçt kimi) göndərməməlisiniz. Bu faylı işiniz bitdikdən sonra dərhal silin.</value>
|
<value>Bu ixrac faylındakı anbar verilənləriniz şifrələnməmiş formatdadır. İxrac edilən faylı saxlamamalı və etibarsız yollarla (e-poçt kimi) göndərməməlisiniz. Bu faylı işiniz bitdikdən sonra dərhal silin.</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -2026,6 +2047,21 @@
|
|||||||
<data name="UpdatePasswordError" xml:space="preserve">
|
<data name="UpdatePasswordError" xml:space="preserve">
|
||||||
<value>Hazırda parol yenilənə bilmir</value>
|
<value>Hazırda parol yenilənə bilmir</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="RemoveMasterPassword" xml:space="preserve">
|
||||||
|
<value>Ana parolu sil</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveMasterPasswordWarning" xml:space="preserve">
|
||||||
|
<value>{0} müştəri tərəfindən idarə edilən şifrələmə ilə SSO istifadə edir. Davam etsəniz, Ana Parol hesabınızdan silinəcək və giriş etmək üçün SSO tələb olunacaq.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveMasterPasswordWarning2" xml:space="preserve">
|
||||||
|
<value>Ana Parolu silmək istəmirsinizsə, bu təşkilatı tərk edə bilərsiniz.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LeaveOrganization" xml:space="preserve">
|
||||||
|
<value>Təşkilatı tərk et</value>
|
||||||
|
</data>
|
||||||
|
<data name="LeaveOrganizationName" xml:space="preserve">
|
||||||
|
<value>{0} tərk edilsin?</value>
|
||||||
|
</data>
|
||||||
<data name="Fido2Title" xml:space="preserve">
|
<data name="Fido2Title" xml:space="preserve">
|
||||||
<value>FIDO2 WebAuthn</value>
|
<value>FIDO2 WebAuthn</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -2056,4 +2092,28 @@
|
|||||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||||
<value>Bir və ya daha çox təşkilat siyasəti, fərdi anbarınızı ixrac etməyinizin qarşısını alır.</value>
|
<value>Bir və ya daha çox təşkilat siyasəti, fərdi anbarınızı ixrac etməyinizin qarşısını alır.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DeleteAccount" xml:space="preserve">
|
||||||
|
<value>Hesabı sil</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||||
|
<value>Hesabınız birdəfəlik silinəcək</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||||
|
<value>Hesabınız və əlaqəli bütün verilənlər silinəcək və bərpa oluna bilməyəcək. Davam etmək istədiyinizə əminsiniz?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeletingYourAccount" xml:space="preserve">
|
||||||
|
<value>Hesabınız silinir</value>
|
||||||
|
</data>
|
||||||
|
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||||
|
<value>Hesabınız birdəfəlik silindi</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||||
|
<value>Etibarsız təsdiqləmə kodu</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendCode" xml:space="preserve">
|
||||||
|
<value>Kod göndər</value>
|
||||||
|
</data>
|
||||||
|
<data name="Sending" xml:space="preserve">
|
||||||
|
<value>Göndərilir</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -1059,6 +1059,9 @@
|
|||||||
<data name="LastName" xml:space="preserve">
|
<data name="LastName" xml:space="preserve">
|
||||||
<value>Прозвішча</value>
|
<value>Прозвішча</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="FullName" xml:space="preserve">
|
||||||
|
<value>Full Name</value>
|
||||||
|
</data>
|
||||||
<data name="LicenseNumber" xml:space="preserve">
|
<data name="LicenseNumber" xml:space="preserve">
|
||||||
<value>Нумар ліцэнзіі</value>
|
<value>Нумар ліцэнзіі</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1183,6 +1186,9 @@
|
|||||||
<data name="FieldTypeHidden" xml:space="preserve">
|
<data name="FieldTypeHidden" xml:space="preserve">
|
||||||
<value>Схавана</value>
|
<value>Схавана</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="FieldTypeLinked" xml:space="preserve">
|
||||||
|
<value>Linked</value>
|
||||||
|
</data>
|
||||||
<data name="FieldTypeText" xml:space="preserve">
|
<data name="FieldTypeText" xml:space="preserve">
|
||||||
<value>Тэкст</value>
|
<value>Тэкст</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1449,6 +1455,9 @@
|
|||||||
<data name="Unlock" xml:space="preserve">
|
<data name="Unlock" xml:space="preserve">
|
||||||
<value>Разблакіраваць</value>
|
<value>Разблакіраваць</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="UnlockVault" xml:space="preserve">
|
||||||
|
<value>Unlock Vault</value>
|
||||||
|
</data>
|
||||||
<data name="ThirtyMinutes" xml:space="preserve">
|
<data name="ThirtyMinutes" xml:space="preserve">
|
||||||
<value>30 хвілін</value>
|
<value>30 хвілін</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1465,6 +1474,9 @@
|
|||||||
<data name="VaultLockedPIN" xml:space="preserve">
|
<data name="VaultLockedPIN" xml:space="preserve">
|
||||||
<value>Ваша сховішча заблакіравана. Каб працягнуць, увядзіце PIN-код.</value>
|
<value>Ваша сховішча заблакіравана. Каб працягнуць, увядзіце PIN-код.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||||
|
<value>Your vault is locked. Verify your identity to continue.</value>
|
||||||
|
</data>
|
||||||
<data name="Dark" xml:space="preserve">
|
<data name="Dark" xml:space="preserve">
|
||||||
<value>Цёмная</value>
|
<value>Цёмная</value>
|
||||||
<comment>A dark color</comment>
|
<comment>A dark color</comment>
|
||||||
@@ -1605,6 +1617,15 @@
|
|||||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||||
<value>Увядзіце ваш асноўны пароль для экспарту даных са сховішча.</value>
|
<value>Увядзіце ваш асноўны пароль для экспарту даных са сховішча.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||||
|
<value>Send a verification code to your email</value>
|
||||||
|
</data>
|
||||||
|
<data name="CodeSent" xml:space="preserve">
|
||||||
|
<value>Code Sent!</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||||
|
<value>Confirm your identity to continue.</value>
|
||||||
|
</data>
|
||||||
<data name="ExportVaultWarning" xml:space="preserve">
|
<data name="ExportVaultWarning" xml:space="preserve">
|
||||||
<value>Экспартуемы файл утрымлівае даныя вашага сховішча ў незашыфраваным фармаце. Яго не варта захоўваць ці адпраўляць па небяспечным каналам (напрыклад, па электроннай пошце). Выдаліце яго адразу пасля выкарыстання.</value>
|
<value>Экспартуемы файл утрымлівае даныя вашага сховішча ў незашыфраваным фармаце. Яго не варта захоўваць ці адпраўляць па небяспечным каналам (напрыклад, па электроннай пошце). Выдаліце яго адразу пасля выкарыстання.</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -2026,6 +2047,21 @@
|
|||||||
<data name="UpdatePasswordError" xml:space="preserve">
|
<data name="UpdatePasswordError" xml:space="preserve">
|
||||||
<value>Currently unable to update password</value>
|
<value>Currently unable to update password</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="RemoveMasterPassword" xml:space="preserve">
|
||||||
|
<value>Remove Master Password</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveMasterPasswordWarning" xml:space="preserve">
|
||||||
|
<value>{0} is using SSO with customer-managed encryption. Continuing will remove your Master Password from your account and require SSO to login.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveMasterPasswordWarning2" xml:space="preserve">
|
||||||
|
<value>If you do not want to remove your Master Password, you may leave this organization.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LeaveOrganization" xml:space="preserve">
|
||||||
|
<value>Leave Organization</value>
|
||||||
|
</data>
|
||||||
|
<data name="LeaveOrganizationName" xml:space="preserve">
|
||||||
|
<value>Leave {0}?</value>
|
||||||
|
</data>
|
||||||
<data name="Fido2Title" xml:space="preserve">
|
<data name="Fido2Title" xml:space="preserve">
|
||||||
<value>FIDO2 WebAuthn</value>
|
<value>FIDO2 WebAuthn</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -2056,4 +2092,28 @@
|
|||||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||||
<value>One or more organization policies prevents your from exporting your personal vault.</value>
|
<value>One or more organization policies prevents your from exporting your personal vault.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DeleteAccount" xml:space="preserve">
|
||||||
|
<value>Delete Account</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||||
|
<value>Deleting your account is permanent</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||||
|
<value>Your account and all associated data will be erased and unrecoverable. Are you sure you want to continue?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeletingYourAccount" xml:space="preserve">
|
||||||
|
<value>Deleting your account</value>
|
||||||
|
</data>
|
||||||
|
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||||
|
<value>Your account has been permanently deleted</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||||
|
<value>Invalid Verification Code.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendCode" xml:space="preserve">
|
||||||
|
<value>Send Code</value>
|
||||||
|
</data>
|
||||||
|
<data name="Sending" xml:space="preserve">
|
||||||
|
<value>Sending</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -1059,6 +1059,9 @@
|
|||||||
<data name="LastName" xml:space="preserve">
|
<data name="LastName" xml:space="preserve">
|
||||||
<value>Фамилно име</value>
|
<value>Фамилно име</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="FullName" xml:space="preserve">
|
||||||
|
<value>Пълно име</value>
|
||||||
|
</data>
|
||||||
<data name="LicenseNumber" xml:space="preserve">
|
<data name="LicenseNumber" xml:space="preserve">
|
||||||
<value>Номер на лиценза</value>
|
<value>Номер на лиценза</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1183,6 +1186,9 @@
|
|||||||
<data name="FieldTypeHidden" xml:space="preserve">
|
<data name="FieldTypeHidden" xml:space="preserve">
|
||||||
<value>Скрито</value>
|
<value>Скрито</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="FieldTypeLinked" xml:space="preserve">
|
||||||
|
<value>Свързано</value>
|
||||||
|
</data>
|
||||||
<data name="FieldTypeText" xml:space="preserve">
|
<data name="FieldTypeText" xml:space="preserve">
|
||||||
<value>Текст</value>
|
<value>Текст</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1449,6 +1455,9 @@
|
|||||||
<data name="Unlock" xml:space="preserve">
|
<data name="Unlock" xml:space="preserve">
|
||||||
<value>Отключване</value>
|
<value>Отключване</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="UnlockVault" xml:space="preserve">
|
||||||
|
<value>Отключване на трезора</value>
|
||||||
|
</data>
|
||||||
<data name="ThirtyMinutes" xml:space="preserve">
|
<data name="ThirtyMinutes" xml:space="preserve">
|
||||||
<value>30 минути</value>
|
<value>30 минути</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1465,6 +1474,9 @@
|
|||||||
<data name="VaultLockedPIN" xml:space="preserve">
|
<data name="VaultLockedPIN" xml:space="preserve">
|
||||||
<value>Трезорът е заключен. Въведете своя ПИН, за да продължите.</value>
|
<value>Трезорът е заключен. Въведете своя ПИН, за да продължите.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="VaultLockedIdentity" xml:space="preserve">
|
||||||
|
<value>Трезорът е заключен. Потвърдете самоличността си, за да продължите.</value>
|
||||||
|
</data>
|
||||||
<data name="Dark" xml:space="preserve">
|
<data name="Dark" xml:space="preserve">
|
||||||
<value>Тъмен</value>
|
<value>Тъмен</value>
|
||||||
<comment>A dark color</comment>
|
<comment>A dark color</comment>
|
||||||
@@ -1605,6 +1617,15 @@
|
|||||||
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
|
||||||
<value>Въведете главната парола, за да изнесете данните.</value>
|
<value>Въведете главната парола, за да изнесете данните.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SendVerificationCodeToEmail" xml:space="preserve">
|
||||||
|
<value>Изпращане на код за потвърждаване до Вашата ел. поща</value>
|
||||||
|
</data>
|
||||||
|
<data name="CodeSent" xml:space="preserve">
|
||||||
|
<value>Кодът е изпратен!</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmYourIdentity" xml:space="preserve">
|
||||||
|
<value>Потвърдете самоличността си, за да продължите.</value>
|
||||||
|
</data>
|
||||||
<data name="ExportVaultWarning" xml:space="preserve">
|
<data name="ExportVaultWarning" xml:space="preserve">
|
||||||
<value>Данните от трезора ви ще се изнесат в незащитен формат. Не го пращайте по незащитени канали като е-поща. Изтрийте файла незабавно след като свършите работата си с него.</value>
|
<value>Данните от трезора ви ще се изнесат в незащитен формат. Не го пращайте по незащитени канали като е-поща. Изтрийте файла незабавно след като свършите работата си с него.</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -2026,6 +2047,21 @@
|
|||||||
<data name="UpdatePasswordError" xml:space="preserve">
|
<data name="UpdatePasswordError" xml:space="preserve">
|
||||||
<value>В момента паролата не може да бъде обновена</value>
|
<value>В момента паролата не може да бъде обновена</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="RemoveMasterPassword" xml:space="preserve">
|
||||||
|
<value>Премахване на главната парола</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveMasterPasswordWarning" xml:space="preserve">
|
||||||
|
<value>{0} използва еднократно удостоверяване с шифроване, управлявано от клиента. Ако продължите, главната парола ще бъде премахната от регистрацията Ви и за да се впишете, вече ще трябва да използвате еднократното удостоверяване.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoveMasterPasswordWarning2" xml:space="preserve">
|
||||||
|
<value>Ако не искате да премахвате главната си парола, ще трябва да напуснете организацията.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LeaveOrganization" xml:space="preserve">
|
||||||
|
<value>Напускане на организацията</value>
|
||||||
|
</data>
|
||||||
|
<data name="LeaveOrganizationName" xml:space="preserve">
|
||||||
|
<value>Напускане на {0}?</value>
|
||||||
|
</data>
|
||||||
<data name="Fido2Title" xml:space="preserve">
|
<data name="Fido2Title" xml:space="preserve">
|
||||||
<value>FIDO2 WebAuthn</value>
|
<value>FIDO2 WebAuthn</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -2056,4 +2092,28 @@
|
|||||||
<data name="DisablePersonalVaultExportPolicyInEffect">
|
<data name="DisablePersonalVaultExportPolicyInEffect">
|
||||||
<value>Една или повече от настройките на организацията Ви не позволяват да изнасяте личния си трезор.</value>
|
<value>Една или повече от настройките на организацията Ви не позволяват да изнасяте личния си трезор.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DeleteAccount" xml:space="preserve">
|
||||||
|
<value>Изтриване на регистрацията</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeletingYourAccountIsPermanent" xml:space="preserve">
|
||||||
|
<value>Изтриването на регистрацията е необратимо</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAccountExplanation" xml:space="preserve">
|
||||||
|
<value>Регистрацията Ви и всички свързани с нея данни ще бъдат изтрити и няма да могат да бъдат възстановени. Наистина ли искате да продължите?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeletingYourAccount" xml:space="preserve">
|
||||||
|
<value>Изтриване на регистрацията Ви</value>
|
||||||
|
</data>
|
||||||
|
<data name="YourAccountHasBeenPermanentlyDeleted" xml:space="preserve">
|
||||||
|
<value>Регистрацията Ви беше изтрита необратимо</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidVerificationCode" xml:space="preserve">
|
||||||
|
<value>Грешен код за потвърждаване</value>
|
||||||
|
</data>
|
||||||
|
<data name="SendCode" xml:space="preserve">
|
||||||
|
<value>Изпращане на кода</value>
|
||||||
|
</data>
|
||||||
|
<data name="Sending" xml:space="preserve">
|
||||||
|
<value>Изпращане…</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user