mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
78 Commits
v2.17.0
...
bug/ps-675
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b54371dacd | ||
|
|
e51233bf9b | ||
|
|
f9cbe43627 | ||
|
|
5579817f9f | ||
|
|
51a5f58258 | ||
|
|
388ad4e840 | ||
|
|
48a8d9ae35 | ||
|
|
dd6003bd4f | ||
|
|
fba407f3b6 | ||
|
|
88b406544b | ||
|
|
3438ed94ce | ||
|
|
ec71b21264 | ||
|
|
b223f5f16e | ||
|
|
0a64e4c918 | ||
|
|
9b41db962e | ||
|
|
43d3c7b5d7 | ||
|
|
8168089591 | ||
|
|
6b55fc3032 | ||
|
|
87ab42b155 | ||
|
|
98130e89de | ||
|
|
121f0e3628 | ||
|
|
8a3d88b3ce | ||
|
|
b8b41fe847 | ||
|
|
5bbef3ee16 | ||
|
|
9a2b6c8ec9 | ||
|
|
5272c99643 | ||
|
|
43e9515a03 | ||
|
|
7e9b7398c8 | ||
|
|
58d7b001a5 | ||
|
|
a259560d29 | ||
|
|
22c746543a | ||
|
|
bcbc2738ca | ||
|
|
604e3b6892 | ||
|
|
b081a8c634 | ||
|
|
c251b950d1 | ||
|
|
1bbe8d8b98 | ||
|
|
cdc41d3bef | ||
|
|
769851adfe | ||
|
|
c988175e50 | ||
|
|
04539af2a6 | ||
|
|
e0efcfbe45 | ||
|
|
57213607a7 | ||
|
|
2cab62fda5 | ||
|
|
99828c7ead | ||
|
|
ab6dde4a11 | ||
|
|
80bd8ba9d1 | ||
|
|
35853a3815 | ||
|
|
cfbbea59e9 | ||
|
|
14d4b2ee29 | ||
|
|
b6ad3527db | ||
|
|
88f6b60b97 | ||
|
|
1f58b0cabe | ||
|
|
284d728023 | ||
|
|
0796bf17ce | ||
|
|
4bd06d2393 | ||
|
|
a3a508eb83 | ||
|
|
f10307c72d | ||
|
|
840925c479 | ||
|
|
fdcb2d76c9 | ||
|
|
4734fe4e43 | ||
|
|
383eee6ec7 | ||
|
|
1d9671bc5c | ||
|
|
22b00bcb33 | ||
|
|
c1748acf39 | ||
|
|
507c3faea1 | ||
|
|
020a5c072d | ||
|
|
c47aad0412 | ||
|
|
7c83d7b37b | ||
|
|
4d4e246a47 | ||
|
|
612e458071 | ||
|
|
a2ec263116 | ||
|
|
5ade10d1fe | ||
|
|
5008e1daa8 | ||
|
|
ad7c656868 | ||
|
|
bdd0ea007b | ||
|
|
bf33f23c12 | ||
|
|
c043528a16 | ||
|
|
fd74164f82 |
12
.config/dotnet-tools.json
Normal file
12
.config/dotnet-tools.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"dotnet-format": {
|
||||||
|
"version": "5.1.250801",
|
||||||
|
"commands": [
|
||||||
|
"dotnet-format"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ root = true
|
|||||||
# Don't use tabs for indentation.
|
# Don't use tabs for indentation.
|
||||||
[*]
|
[*]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
end_of_line = lf
|
||||||
# (Please don't specify an indent_size here; that has too many unintended consequences.)
|
# (Please don't specify an indent_size here; that has too many unintended consequences.)
|
||||||
|
|
||||||
# Code files
|
# Code files
|
||||||
|
|||||||
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# .NET format https://github.com/bitwarden/mobile/pull/1738
|
||||||
|
04539af2a66668b6e85476d5cf318c9150ec4357
|
||||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,7 +1,7 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# Set default behavior to automatically normalize line endings.
|
# Set default behavior to automatically normalize line endings.
|
||||||
###############################################################################
|
###############################################################################
|
||||||
* text=auto
|
* text=auto eol=lf
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Set default behavior for command prompt diff.
|
# Set default behavior for command prompt diff.
|
||||||
|
|||||||
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -21,12 +21,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Testing requirements
|
|
||||||
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Before you submit
|
## Before you submit
|
||||||
|
- [ ] I have checked for formatting errors (`dotnet tool run dotnet-format --check`) (required)
|
||||||
- [ ] I have added **unit tests** where it makes sense to do so (encouraged but not required)
|
- [ ] I have added **unit tests** where it makes sense to do so (encouraged but not required)
|
||||||
- [ ] This change requires a **documentation update** (notify the documentation team)
|
- [ ] This change requires a **documentation update** (notify the documentation team)
|
||||||
- [ ] This change has particular **deployment requirements** (notify the DevOps team)
|
- [ ] This change has particular **deployment requirements** (notify the DevOps team)
|
||||||
|
|||||||
64
.github/workflows/automatic-issue-responses.yml
vendored
Normal file
64
.github/workflows/automatic-issue-responses.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
name: Automatic responses
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types:
|
||||||
|
- labeled
|
||||||
|
jobs:
|
||||||
|
close-issue:
|
||||||
|
name: 'Close issue with automatic response'
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
steps:
|
||||||
|
# Feature request
|
||||||
|
- if: github.event.label.name == 'feature-request'
|
||||||
|
name: Feature request
|
||||||
|
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
|
||||||
|
with:
|
||||||
|
comment: |
|
||||||
|
We use GitHub issues as a place to track bugs and other development related issues. The [Bitwarden Community Forums](https://community.bitwarden.com/) has a [Feature Requests](https://community.bitwarden.com/c/feature-requests) section for submitting, voting for, and discussing requests like this one.
|
||||||
|
|
||||||
|
Please [sign up on our forums](https://community.bitwarden.com/signup) and search to see if this request already exists. If so, you can vote for it and contribute to any discussions about it. If not, you can re-create the request there so that it can be properly tracked.
|
||||||
|
|
||||||
|
This issue will now be closed. Thanks!
|
||||||
|
# Intended behavior
|
||||||
|
- if: github.event.label.name == 'intended-behavior'
|
||||||
|
name: Intended behaviour
|
||||||
|
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
|
||||||
|
with:
|
||||||
|
comment: |
|
||||||
|
Your issue appears to be describing the intended behavior of the software. If you want this to be changed, it would be a feature request.
|
||||||
|
|
||||||
|
We use GitHub issues as a place to track bugs and other development related issues. The [Bitwarden Community Forums](https://community.bitwarden.com/) has a [Feature Requests](https://community.bitwarden.com/c/feature-requests) section for submitting, voting for, and discussing requests like this one.
|
||||||
|
|
||||||
|
Please [sign up on our forums](https://community.bitwarden.com/signup) and search to see if this request already exists. If so, you can vote for it and contribute to any discussions about it. If not, you can re-create the request there so that it can be properly tracked.
|
||||||
|
|
||||||
|
This issue will now be closed. Thanks!
|
||||||
|
# Customer support request
|
||||||
|
- if: github.event.label.name == 'customer-support'
|
||||||
|
name: Customer Support request
|
||||||
|
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
|
||||||
|
with:
|
||||||
|
comment: |
|
||||||
|
We use GitHub issues as a place to track bugs and other development related issues. Your issue appears to be a support request, or would otherwise be better handled by our dedicated Customer Success team.
|
||||||
|
|
||||||
|
Please contact us using our [Contact page](https://bitwarden.com/contact). You can include a link to this issue in the message content.
|
||||||
|
|
||||||
|
Alternatively, you can also search for an answer in our [help documentation](https://bitwarden.com/help/) or get help from other Bitwarden users on our [community forums](https://community.bitwarden.com/c/support/). The issue here will be closed.
|
||||||
|
# Resolved
|
||||||
|
- if: github.event.label.name == 'resolved'
|
||||||
|
name: Resolved
|
||||||
|
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
|
||||||
|
with:
|
||||||
|
comment: |
|
||||||
|
We’ve closed this issue, as it appears the original problem has been resolved. If this happens again or continues to be an problem, please respond to this issue with any additional detail to assist with reproduction and root cause analysis.
|
||||||
|
# Stale
|
||||||
|
- if: github.event.label.name == 'stale'
|
||||||
|
name: Stale
|
||||||
|
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
|
||||||
|
with:
|
||||||
|
comment: |
|
||||||
|
As we haven’t heard from you about this problem in some time, this issue will now be closed.
|
||||||
|
|
||||||
|
If this happens again or continues to be an problem, please respond to this issue with any additional detail to assist with reproduction and root cause analysis.
|
||||||
87
.github/workflows/build.yml
vendored
87
.github/workflows/build.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||||
|
|
||||||
- name: Set up CLOC
|
- name: Set up CLOC
|
||||||
run: |
|
run: |
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }}
|
hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||||
|
|
||||||
- name: Check if special branches exist
|
- name: Check if special branches exist
|
||||||
id: branch-check
|
id: branch-check
|
||||||
@@ -47,7 +47,7 @@ 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 hotfix) ]]; then
|
if [[ $(git ls-remote --heads origin hotfix-rc) ]]; then
|
||||||
echo "::set-output name=hotfix_branch_exists::1"
|
echo "::set-output name=hotfix_branch_exists::1"
|
||||||
else
|
else
|
||||||
echo "::set-output name=hotfix_branch_exists::0"
|
echo "::set-output name=hotfix_branch_exists::0"
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
needs: setup
|
needs: setup
|
||||||
steps:
|
steps:
|
||||||
- name: Set up MSBuild
|
- name: Set up MSBuild
|
||||||
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d # v1
|
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
@@ -72,7 +72,7 @@ jobs:
|
|||||||
echo "GitHub event: $GITHUB_EVENT"
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||||
|
|
||||||
- name: Decrypt secrets
|
- name: Decrypt secrets
|
||||||
env:
|
env:
|
||||||
@@ -105,6 +105,14 @@ jobs:
|
|||||||
- name: Restore packages
|
- name: Restore packages
|
||||||
run: nuget restore
|
run: nuget restore
|
||||||
|
|
||||||
|
- name: Restore tools
|
||||||
|
run: dotnet tool restore
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Verify Format
|
||||||
|
run: dotnet tool run dotnet-format --check
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
- name: Run Core tests
|
- name: Run Core tests
|
||||||
run: dotnet test test/Core.Test/Core.Test.csproj
|
run: dotnet test test/Core.Test/Core.Test.csproj
|
||||||
|
|
||||||
@@ -167,14 +175,14 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Upload Play Store .aab artifact
|
- name: Upload Play Store .aab artifact
|
||||||
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
|
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden.aab
|
name: com.x8bit.bitwarden.aab
|
||||||
path: ./com.x8bit.bitwarden.aab
|
path: ./com.x8bit.bitwarden.aab
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload Play Store .apk artifact
|
- name: Upload Play Store .apk artifact
|
||||||
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
|
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden.apk
|
name: com.x8bit.bitwarden.apk
|
||||||
path: ./com.x8bit.bitwarden.apk
|
path: ./com.x8bit.bitwarden.apk
|
||||||
@@ -186,7 +194,7 @@ jobs:
|
|||||||
&& needs.setup.outputs.rc_branch_exists == 0
|
&& needs.setup.outputs.rc_branch_exists == 0
|
||||||
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| github.ref == 'refs/heads/hotfix'
|
|| github.ref == 'refs/heads/hotfix-rc'
|
||||||
run: |
|
run: |
|
||||||
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/netcoreapp3.1/Publisher.dll"
|
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/netcoreapp3.1/Publisher.dll"
|
||||||
CREDS_PATH="$HOME/secrets/play_creds.json"
|
CREDS_PATH="$HOME/secrets/play_creds.json"
|
||||||
@@ -202,7 +210,7 @@ jobs:
|
|||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
steps:
|
steps:
|
||||||
- name: Set up MSBuild
|
- name: Set up MSBuild
|
||||||
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d # v1
|
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
@@ -213,7 +221,7 @@ jobs:
|
|||||||
echo "GitHub event: $GITHUB_EVENT"
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||||
|
|
||||||
- name: Decrypt secrets
|
- name: Decrypt secrets
|
||||||
env:
|
env:
|
||||||
@@ -241,6 +249,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
|
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
|
||||||
$appPath = $($env:GITHUB_WORKSPACE + "/src/App/App.csproj");
|
$appPath = $($env:GITHUB_WORKSPACE + "/src/App/App.csproj");
|
||||||
|
$corePath = $($env:GITHUB_WORKSPACE + "/src/Core/Core.csproj");
|
||||||
|
|
||||||
$androidManifest = $($env:GITHUB_WORKSPACE + "/src/Android/Properties/AndroidManifest.xml");
|
$androidManifest = $($env:GITHUB_WORKSPACE + "/src/Android/Properties/AndroidManifest.xml");
|
||||||
|
|
||||||
@@ -296,16 +305,16 @@ jobs:
|
|||||||
$xml.Save($androidPath);
|
$xml.Save($androidPath);
|
||||||
|
|
||||||
Write-Output "########################################"
|
Write-Output "########################################"
|
||||||
Write-Output "##### Uninstall from App.csproj"
|
Write-Output "##### Uninstall from Core.csproj"
|
||||||
Write-Output "########################################"
|
Write-Output "########################################"
|
||||||
|
|
||||||
$xml=New-Object XML;
|
$xml=New-Object XML;
|
||||||
$xml.Load($appPath);
|
$xml.Load($corePath);
|
||||||
|
|
||||||
$appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
|
$appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
|
||||||
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
|
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
|
||||||
|
|
||||||
$xml.Save($appPath);
|
$xml.Save($corePath);
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Restore packages
|
- name: Restore packages
|
||||||
@@ -347,7 +356,7 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Upload F-Droid .apk artifact
|
- name: Upload F-Droid .apk artifact
|
||||||
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
|
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden-fdroid.apk
|
name: com.x8bit.bitwarden-fdroid.apk
|
||||||
path: ./com.x8bit.bitwarden-fdroid.apk
|
path: ./com.x8bit.bitwarden-fdroid.apk
|
||||||
@@ -368,16 +377,16 @@ jobs:
|
|||||||
echo "GitHub event: $GITHUB_EVENT"
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||||
|
|
||||||
- name: Login to Azure - Prod Subscription
|
- name: Login to Azure - Prod Subscription
|
||||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
- name: Retrieve secrets
|
- name: Retrieve secrets
|
||||||
id: retrieve-secrets
|
id: retrieve-secrets
|
||||||
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f
|
||||||
with:
|
with:
|
||||||
keyvault: "bitwarden-prod-kv"
|
keyvault: "bitwarden-prod-kv"
|
||||||
secrets: "appcenter-ios-token"
|
secrets: "appcenter-ios-token"
|
||||||
@@ -399,7 +408,8 @@ jobs:
|
|||||||
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
--output $HOME/secrets/dist_extension.mobileprovision ./.github/secrets/dist_extension.mobileprovision.gpg
|
--output $HOME/secrets/dist_extension.mobileprovision ./.github/secrets/dist_extension.mobileprovision.gpg
|
||||||
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
--output $HOME/secrets/dist_share_extension.mobileprovision ./.github/secrets/dist_share_extension.mobileprovision.gpg
|
--output $HOME/secrets/dist_share_extension.mobileprovision \
|
||||||
|
./.github/secrets/dist_share_extension.mobileprovision.gpg
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Increment version
|
- name: Increment version
|
||||||
@@ -495,7 +505,7 @@ jobs:
|
|||||||
-exportOptionsPlist $EXPORT_OPTIONS_PATH
|
-exportOptionsPlist $EXPORT_OPTIONS_PATH
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Copy all dSYMs files to upload
|
- name: Copy all dSYMs files to upload
|
||||||
run: |
|
run: |
|
||||||
ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
|
ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
|
||||||
EXPORT_PATH="./bitwarden-export"
|
EXPORT_PATH="./bitwarden-export"
|
||||||
@@ -504,7 +514,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Upload App Store .ipa & dSYMs artifacts
|
- name: Upload App Store .ipa & dSYMs artifacts
|
||||||
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
|
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
|
||||||
with:
|
with:
|
||||||
name: Bitwarden iOS
|
name: Bitwarden iOS
|
||||||
path: |
|
path: |
|
||||||
@@ -514,15 +524,12 @@ jobs:
|
|||||||
|
|
||||||
- name: Install AppCenter CLI
|
- name: Install AppCenter CLI
|
||||||
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.hotfix_branch_exists == 0)
|
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| github.ref == 'refs/heads/hotfix'
|
|| github.ref == 'refs/heads/hotfix-rc'
|
||||||
uses: actions/setup-node@v2
|
run: npm install -g appcenter-cli
|
||||||
with:
|
|
||||||
node-version: '14'
|
|
||||||
- run: npm install -g appcenter-cli
|
|
||||||
|
|
||||||
- name: Upload dSYMs to App Center
|
- name: Upload dSYMs to App Center
|
||||||
if: |
|
if: |
|
||||||
@@ -530,10 +537,10 @@ jobs:
|
|||||||
&& needs.setup.outputs.rc_branch_exists == 0
|
&& needs.setup.outputs.rc_branch_exists == 0
|
||||||
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| github.ref == 'refs/heads/hotfix'
|
|| github.ref == 'refs/heads/hotfix-rc'
|
||||||
env:
|
env:
|
||||||
APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }}
|
APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }}
|
||||||
run: appcenter crashes upload-symbols -a kspearrin/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
|
run: appcenter crashes upload-symbols -a bitwarden/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Deploy to App Store
|
- name: Deploy to App Store
|
||||||
@@ -542,7 +549,7 @@ jobs:
|
|||||||
&& needs.setup.outputs.rc_branch_exists == 0
|
&& needs.setup.outputs.rc_branch_exists == 0
|
||||||
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| github.ref == 'refs/heads/hotfix'
|
|| github.ref == 'refs/heads/hotfix-rc'
|
||||||
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 }}
|
||||||
@@ -564,22 +571,22 @@ jobs:
|
|||||||
_CROWDIN_PROJECT_ID: "269690"
|
_CROWDIN_PROJECT_ID: "269690"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||||
|
|
||||||
- name: Login to Azure
|
- name: Login to Azure
|
||||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
- name: Retrieve secrets
|
- name: Retrieve secrets
|
||||||
id: retrieve-secrets
|
id: retrieve-secrets
|
||||||
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f
|
||||||
with:
|
with:
|
||||||
keyvault: "bitwarden-prod-kv"
|
keyvault: "bitwarden-prod-kv"
|
||||||
secrets: "crowdin-api-token"
|
secrets: "crowdin-api-token"
|
||||||
|
|
||||||
- name: Upload Sources
|
- name: Upload Sources
|
||||||
uses: crowdin/github-action@e39093fd75daae7859c68eded4b43d42ec78d8ea # v1.3.2
|
uses: crowdin/github-action@9237b4cb361788dfce63feb2e2f15c09e2fe7415
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
||||||
@@ -605,7 +612,7 @@ jobs:
|
|||||||
if: |
|
if: |
|
||||||
(github.ref == 'refs/heads/master')
|
(github.ref == 'refs/heads/master')
|
||||||
|| (github.ref == 'refs/heads/rc')
|
|| (github.ref == 'refs/heads/rc')
|
||||||
|| (github.ref == 'refs/heads/hotfix')
|
|| (github.ref == 'refs/heads/hotfix-rc')
|
||||||
env:
|
env:
|
||||||
CLOC_STATUS: ${{ needs.cloc.result }}
|
CLOC_STATUS: ${{ needs.cloc.result }}
|
||||||
ANDROID_STATUS: ${{ needs.android.result }}
|
ANDROID_STATUS: ${{ needs.android.result }}
|
||||||
@@ -626,21 +633,21 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Login to Azure - Prod Subscription
|
- name: Login to Azure - Prod Subscription
|
||||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
- name: Retrieve secrets
|
- name: Retrieve secrets
|
||||||
id: retrieve-secrets
|
id: retrieve-secrets
|
||||||
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
keyvault: "bitwarden-prod-kv"
|
keyvault: "bitwarden-prod-kv"
|
||||||
secrets: "devops-alerts-slack-webhook-url"
|
secrets: "devops-alerts-slack-webhook-url"
|
||||||
|
|
||||||
- name: Notify Slack on failure
|
- name: Notify Slack on failure
|
||||||
uses: act10ns/slack@e4e71685b9b239384b0f676a63c32367f59c2522 # v1.2.2
|
uses: act10ns/slack@da3191ebe2e67f49b46880b4633f5591a96d1d33
|
||||||
if: failure()
|
if: failure()
|
||||||
env:
|
env:
|
||||||
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
|
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
|
||||||
|
|||||||
46
.github/workflows/release.yml
vendored
46
.github/workflows/release.yml
vendored
@@ -12,7 +12,7 @@ on:
|
|||||||
options:
|
options:
|
||||||
- Initial Release
|
- Initial Release
|
||||||
- Redeploy
|
- Redeploy
|
||||||
- dry-run
|
- Dry Run
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
@@ -22,11 +22,11 @@ jobs:
|
|||||||
branch-name: ${{ steps.branch.outputs.branch-name }}
|
branch-name: ${{ steps.branch.outputs.branch-name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Branch check
|
- name: Branch check
|
||||||
if: github.event.inputs.release_type != 'dry-run'
|
if: github.event.inputs.release_type != 'Dry Run'
|
||||||
run: |
|
run: |
|
||||||
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix" ]]; then
|
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
|
||||||
echo "==================================="
|
echo "==================================="
|
||||||
echo "[!] Can only release from the 'rc' or 'hotfix' branches"
|
echo "[!] Can only release from the 'rc' or 'hotfix-rc' branches"
|
||||||
echo "==================================="
|
echo "==================================="
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -34,29 +34,13 @@ jobs:
|
|||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||||
|
|
||||||
- name: Retrieve Mobile release version
|
- name: Check Release Version
|
||||||
id: retrieve-mobile-version
|
id: version
|
||||||
run: |
|
uses: bitwarden/gh-actions/release-version-check@8f055ef543c7433c967a1b9b04a0f230923233bb
|
||||||
ver=$(sed -E -n '/^<manifest/s/^.*[ ]android:versionName="([^"]+)".*$/\1/p' \
|
with:
|
||||||
./src/Android/Properties/AndroidManifest.xml | tr -d '"')
|
release-type: ${{ github.event.inputs.release_type }}
|
||||||
echo "::set-output name=mobile_version::${ver}"
|
project-type: xamarin
|
||||||
shell: bash
|
file: src/Android/Properties/AndroidManifest.xml
|
||||||
|
|
||||||
- name: Check to make sure Mobile release version has been bumped
|
|
||||||
if: ${{ github.event.inputs.release_type == 'Initial Release' }}
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
latest_ver=$(hub release -L 1 -f '%T')
|
|
||||||
latest_ver=${latest_ver:1}
|
|
||||||
echo "Latest version: $latest_ver"
|
|
||||||
ver=${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
|
||||||
echo "Version: $ver"
|
|
||||||
if [ "$latest_ver" = "$ver" ]; then
|
|
||||||
echo "Version has not been bumped!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Get branch name
|
- name: Get branch name
|
||||||
id: branch
|
id: branch
|
||||||
@@ -75,7 +59,7 @@ jobs:
|
|||||||
run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS
|
run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
if: github.event.inputs.release_type != 'dry-run'
|
if: github.event.inputs.release_type != 'Dry Run'
|
||||||
uses: ncipollo/release-action@40bb172bd05f266cf9ba4ff965cb61e9ee5f6d01 # v1.9.0
|
uses: ncipollo/release-action@40bb172bd05f266cf9ba4ff965cb61e9ee5f6d01 # v1.9.0
|
||||||
with:
|
with:
|
||||||
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
|
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
|
||||||
@@ -83,8 +67,8 @@ jobs:
|
|||||||
./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk,
|
./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk,
|
||||||
./Bitwarden iOS.zip"
|
./Bitwarden iOS.zip"
|
||||||
commit: ${{ github.sha }}
|
commit: ${{ github.sha }}
|
||||||
tag: v${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
tag: v${{ steps.version.outputs.version }}
|
||||||
name: Version ${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
name: Version ${{ steps.version.outputs.version }}
|
||||||
body: "<insert release notes here>"
|
body: "<insert release notes here>"
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
draft: true
|
draft: true
|
||||||
@@ -171,5 +155,5 @@ jobs:
|
|||||||
cd $GITHUB_WORKSPACE
|
cd $GITHUB_WORKSPACE
|
||||||
|
|
||||||
- name: Deploy to gh-pages
|
- name: Deploy to gh-pages
|
||||||
if: github.event.inputs.release_type != 'dry-run'
|
if: github.event.inputs.release_type != 'Dry Run'
|
||||||
run: npm run deploy
|
run: npm run deploy
|
||||||
|
|||||||
30
.github/workflows/stale-bot.yml
vendored
Normal file
30
.github/workflows/stale-bot.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: 'Close stale issues and PRs'
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule: # Run once a day at 5.23am (arbitrary but should avoid peak loads on the hour)
|
||||||
|
- cron: '23 5 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
name: 'Check for stale issues and PRs'
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- name: 'Run stale action'
|
||||||
|
uses: actions/stale@3cc123766321e9f15a6676375c154ccffb12a358 # v5.0.0
|
||||||
|
with:
|
||||||
|
stale-issue-label: 'needs-reply'
|
||||||
|
stale-pr-label: 'needs-changes'
|
||||||
|
days-before-stale: -1 # Do not apply the stale labels automatically, this is a manual process
|
||||||
|
days-before-issue-close: 14 # Close issue if no further activity after X days
|
||||||
|
days-before-pr-close: 21 # Close PR if no further activity after X days
|
||||||
|
close-issue-message: |
|
||||||
|
We need more information before we can help you with your problem. As we haven’t heard from you recently, this issue will be closed.
|
||||||
|
|
||||||
|
If this happens again or continues to be an problem, please respond to this issue with the information we’ve requested and anything else relevant.
|
||||||
|
close-pr-message: |
|
||||||
|
We can’t merge your pull request until you make the changes we’ve requested. As we haven’t heard from you recently, this pull request will be closed.
|
||||||
|
|
||||||
|
If you’re still working on this, please respond here after you’ve made the changes we’ve requested and our team will re-open it for further review.
|
||||||
|
|
||||||
|
Please make sure to resolve any conflicts with the master branch before requesting another review.
|
||||||
@@ -1,40 +1,3 @@
|
|||||||
# How to Contribute
|
# How to Contribute
|
||||||
|
|
||||||
Contributions of all kinds are welcome!
|
Our [Contributing Guidelines](https://contributing.bitwarden.com/contributing/) are located in our [Contributing Documentation](https://contributing.bitwarden.com/). The documentation also includes recommended tooling, code style tips, and lots of other great information to get you started.
|
||||||
|
|
||||||
Please visit our [Community Forums](https://community.bitwarden.com/) for general community discussion and the development roadmap.
|
|
||||||
|
|
||||||
Here is how you can get involved:
|
|
||||||
|
|
||||||
* **Request a new feature:** Go to the [Feature Requests category](https://community.bitwarden.com/c/feature-requests/) of the Community Forums. Please search existing feature requests before making a new one
|
|
||||||
|
|
||||||
* **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code
|
|
||||||
|
|
||||||
* **Report a bug or submit a bugfix:** Use Github issues and pull requests
|
|
||||||
|
|
||||||
* **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help)
|
|
||||||
|
|
||||||
* **Help other users:** Go to the [User-to-User Support category](https://community.bitwarden.com/c/support/) on the Community Forums
|
|
||||||
|
|
||||||
* **Translate:** See the localization (i10n) section below
|
|
||||||
|
|
||||||
## Contributor Agreement
|
|
||||||
|
|
||||||
Please sign the [Contributor Agreement](https://cla-assistant.io/bitwarden/mobile) if you intend on contributing to any Github repository. Pull requests cannot be accepted and merged unless the author has signed the Contributor Agreement.
|
|
||||||
|
|
||||||
## Pull Request Guidelines
|
|
||||||
|
|
||||||
* commit any pull requests against the `master` branch
|
|
||||||
* include a link to your Community Forums post
|
|
||||||
|
|
||||||
# Localization (l10n)
|
|
||||||
|
|
||||||
[](https://crowdin.com/project/bitwarden-mobile)
|
|
||||||
|
|
||||||
We use a translation tool called [Crowdin](https://crowdin.com) to help manage our localization efforts across many different languages.
|
|
||||||
|
|
||||||
If you are interested in helping translate the Bitwarden mobile app into another language (or make a translation correction), please register an account at Crowdin and join our project here: https://crowdin.com/project/bitwarden-mobile
|
|
||||||
|
|
||||||
If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/profile/kspearrin).
|
|
||||||
|
|
||||||
You can read Crowdin's getting started guide for translators here: https://support.crowdin.com/crowdin-intro/
|
|
||||||
|
|||||||
26
README.md
26
README.md
@@ -12,16 +12,7 @@ The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin
|
|||||||
|
|
||||||
# Build/Run
|
# Build/Run
|
||||||
|
|
||||||
**Requirements**
|
Please refer to the [Mobile section](https://contributing.bitwarden.com/clients/mobile) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started.
|
||||||
|
|
||||||
- [Visual Studio](https://visualstudio.microsoft.com/)
|
|
||||||
- [Xamarin](https://docs.microsoft.com/en-us/xamarin/get-started/installation/?pivots=windows)
|
|
||||||
|
|
||||||
**Run the app**
|
|
||||||
|
|
||||||
- Open the solution file in Visual Studio.
|
|
||||||
- Restore the nuget packages.
|
|
||||||
- Build and run the app.
|
|
||||||
|
|
||||||
# We're Hiring!
|
# We're Hiring!
|
||||||
|
|
||||||
@@ -29,7 +20,18 @@ Interested in contributing in a big way? Consider joining our team! We're hiring
|
|||||||
|
|
||||||
# Contribute
|
# Contribute
|
||||||
|
|
||||||
Code contributions are welcome! Visual Studio with Xamarin is required to work on this project. Please commit any pull requests against the `master` branch.
|
Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [Contributing Guidelines](https://contributing.bitwarden.com/contributing/). Check out the [Contributing Documentation](https://contributing.bitwarden.com/) for how to get started with your first contribution.
|
||||||
Learn more about how to contribute by reading the [`CONTRIBUTING.md`](CONTRIBUTING.md) file.
|
|
||||||
|
|
||||||
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file.
|
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file.
|
||||||
|
|
||||||
|
### Dotnet-format
|
||||||
|
|
||||||
|
We recently migrated to using dotnet-format as code formatter. All previous branches will need to updated to avoid large merge conflicts using the following steps:
|
||||||
|
|
||||||
|
1. Check out your local Branch
|
||||||
|
2. Run `git merge e0efcfbe45b2a27c73e9593bfd7a71fad2aa7a35`
|
||||||
|
3. Resolve any merge conflicts, commit.
|
||||||
|
4. Run `dotnet tool run dotnet-format`
|
||||||
|
5. Commit
|
||||||
|
6. Run `git merge -Xours 04539af2a66668b6e85476d5cf318c9150ec4357`
|
||||||
|
7. Push
|
||||||
|
|||||||
42
SECURITY.md
42
SECURITY.md
@@ -1,39 +1,11 @@
|
|||||||
Bitwarden believes that working with security researchers across the globe is crucial to keeping our
|
Bitwarden believes that working with security researchers across the globe is crucial to keeping our users safe. If you believe you've found a security issue in our product or service, we encourage you to please submit a report through our [HackerOne Program](https://hackerone.com/bitwarden/). We welcome working with you to resolve the issue promptly. Thanks in advance!
|
||||||
users safe. If you believe you've found a security issue in our product or service, we encourage you to
|
|
||||||
notify us. We welcome working with you to resolve the issue promptly. Thanks in advance!
|
|
||||||
|
|
||||||
# Disclosure Policy
|
# Disclosure Policy
|
||||||
|
|
||||||
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every
|
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every effort to quickly resolve the issue.
|
||||||
effort to quickly resolve the issue.
|
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a third-party. We may publicly disclose the issue before resolving it, if appropriate.
|
||||||
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a
|
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our service. Only interact with accounts you own or with explicit permission of the account holder.
|
||||||
third-party. We may publicly disclose the issue before resolving it, if appropriate.
|
- If you would like to encrypt your report, please use the PGP key with long ID `0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
|
||||||
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or
|
|
||||||
degradation of our service. Only interact with accounts you own or with explicit permission of the
|
|
||||||
account holder.
|
|
||||||
- If you would like to encrypt your report, please use the PGP key with long ID
|
|
||||||
`0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
|
|
||||||
|
|
||||||
# In-scope
|
|
||||||
|
|
||||||
- Security issues in any current release of Bitwarden. This includes the web vault, browser extension,
|
|
||||||
and mobile apps (iOS and Android). Product downloads are available at https://bitwarden.com. Source
|
|
||||||
code is available at https://github.com/bitwarden.
|
|
||||||
|
|
||||||
# Exclusions
|
|
||||||
|
|
||||||
The following bug classes are out-of scope:
|
|
||||||
|
|
||||||
- Bugs that are already reported on any of Bitwarden's issue trackers (https://github.com/bitwarden),
|
|
||||||
or that we already know of. Note that some of our issue tracking is private.
|
|
||||||
- Issues in an upstream software dependency (ex: Xamarin, ASP.NET) which are already reported to the
|
|
||||||
upstream maintainer.
|
|
||||||
- Attacks requiring physical access to a user's device.
|
|
||||||
- Self-XSS
|
|
||||||
- Issues related to software or protocols not under Bitwarden's control
|
|
||||||
- Vulnerabilities in outdated versions of Bitwarden
|
|
||||||
- Missing security best practices that do not directly lead to a vulnerability
|
|
||||||
- Issues that do not have any impact on the general public
|
|
||||||
|
|
||||||
While researching, we'd like to ask you to refrain from:
|
While researching, we'd like to ask you to refrain from:
|
||||||
|
|
||||||
@@ -42,4 +14,8 @@ While researching, we'd like to ask you to refrain from:
|
|||||||
- Social engineering (including phishing) of Bitwarden staff or contractors
|
- Social engineering (including phishing) of Bitwarden staff or contractors
|
||||||
- Any physical attempts against Bitwarden property or data centers
|
- Any physical attempts against Bitwarden property or data centers
|
||||||
|
|
||||||
|
# We want to help you!
|
||||||
|
|
||||||
|
If you have something that you feel is close to exploitation, or if you'd like some information regarding the internal API, or generally have any questions regarding the app that would help in your efforts, please email us at https://bitwarden.com/contact and ask for that information. As stated above, Bitwarden wants to help you find issues, and is more than willing to help.
|
||||||
|
|
||||||
Thank you for helping keep Bitwarden and our users safe!
|
Thank you for helping keep Bitwarden and our users safe!
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.2.5.2" />
|
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.2.5.2" />
|
||||||
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.8" />
|
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.8" />
|
||||||
<PackageReference Include="Xamarin.Essentials">
|
<PackageReference Include="Xamarin.Essentials">
|
||||||
<Version>1.7.1</Version>
|
<Version>1.7.3</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Firebase.Messaging">
|
<PackageReference Include="Xamarin.Firebase.Messaging">
|
||||||
<Version>122.0.0</Version>
|
<Version>122.0.0</Version>
|
||||||
@@ -145,12 +145,12 @@
|
|||||||
<Compile Include="Tiles\GeneratorTileService.cs" />
|
<Compile Include="Tiles\GeneratorTileService.cs" />
|
||||||
<Compile Include="Tiles\MyVaultTileService.cs" />
|
<Compile Include="Tiles\MyVaultTileService.cs" />
|
||||||
<Compile Include="Utilities\AndroidHelpers.cs" />
|
<Compile Include="Utilities\AndroidHelpers.cs" />
|
||||||
<Compile Include="Utilities\AppCenterHelper.cs" />
|
|
||||||
<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="Services\ClipboardService.cs" />
|
||||||
<Compile Include="Utilities\IntentExtensions.cs" />
|
<Compile Include="Utilities\IntentExtensions.cs" />
|
||||||
|
<Compile Include="Renderers\CustomPageRenderer.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidAsset Include="Assets\bwi-font.ttf" />
|
<AndroidAsset Include="Assets\bwi-font.ttf" />
|
||||||
|
|||||||
@@ -69,16 +69,20 @@ namespace Bit.Droid
|
|||||||
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
Window.AddFlags(Android.Views.WindowManagerFlags.Secure);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !DEBUG && !FDROID
|
ServiceContainer.Resolve<ILogger>("logger").InitAsync();
|
||||||
var appCenterHelper = new AppCenterHelper(_appIdService, _stateService);
|
|
||||||
var appCenterTask = appCenterHelper.InitAsync();
|
var toplayout = Window?.DecorView?.RootView;
|
||||||
#endif
|
if (toplayout != null)
|
||||||
|
{
|
||||||
|
toplayout.FilterTouchesWhenObscured = true;
|
||||||
|
}
|
||||||
|
|
||||||
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
|
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
|
||||||
Xamarin.Forms.Forms.Init(this, savedInstanceState);
|
Xamarin.Forms.Forms.Init(this, savedInstanceState);
|
||||||
_appOptions = GetOptions();
|
_appOptions = GetOptions();
|
||||||
LoadApplication(new App.App(_appOptions));
|
LoadApplication(new App.App(_appOptions));
|
||||||
|
|
||||||
|
|
||||||
_broadcasterService.Subscribe(_activityKey, (message) =>
|
_broadcasterService.Subscribe(_activityKey, (message) =>
|
||||||
{
|
{
|
||||||
if (message.Command == "startEventTimer")
|
if (message.Command == "startEventTimer")
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ using Bit.Core.Abstractions;
|
|||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Droid.Services;
|
using Bit.Droid.Services;
|
||||||
using Bit.Droid.Utilities;
|
|
||||||
using Plugin.CurrentActivity;
|
using Plugin.CurrentActivity;
|
||||||
using Plugin.Fingerprint;
|
using Plugin.Fingerprint;
|
||||||
using Xamarin.Android.Net;
|
using Xamarin.Android.Net;
|
||||||
@@ -20,6 +19,7 @@ using System.Net.Http;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.App.Pages;
|
using Bit.App.Pages;
|
||||||
|
using Bit.App.Utilities.AccountManagement;
|
||||||
#if !FDROID
|
#if !FDROID
|
||||||
using Android.Gms.Security;
|
using Android.Gms.Security;
|
||||||
#endif
|
#endif
|
||||||
@@ -62,6 +62,15 @@ namespace Bit.Droid
|
|||||||
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
||||||
ServiceContainer.Resolve<ICryptoService>("cryptoService"));
|
ServiceContainer.Resolve<ICryptoService>("cryptoService"));
|
||||||
ServiceContainer.Register<IVerificationActionsFlowHelper>("verificationActionsFlowHelper", verificationActionsFlowHelper);
|
ServiceContainer.Register<IVerificationActionsFlowHelper>("verificationActionsFlowHelper", verificationActionsFlowHelper);
|
||||||
|
|
||||||
|
var accountsManager = new AccountsManager(
|
||||||
|
ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"),
|
||||||
|
ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService"),
|
||||||
|
ServiceContainer.Resolve<IStorageService>("secureStorageService"),
|
||||||
|
ServiceContainer.Resolve<IStateService>("stateService"),
|
||||||
|
ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"),
|
||||||
|
ServiceContainer.Resolve<IAuthService>("authService"));
|
||||||
|
ServiceContainer.Register<IAccountsManager>("accountsManager", accountsManager);
|
||||||
}
|
}
|
||||||
#if !FDROID
|
#if !FDROID
|
||||||
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
|
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
|
||||||
@@ -124,10 +133,11 @@ namespace Bit.Droid
|
|||||||
var stateService = new StateService(mobileStorageService, secureStorageService);
|
var stateService = new StateService(mobileStorageService, secureStorageService);
|
||||||
var stateMigrationService =
|
var stateMigrationService =
|
||||||
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
|
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
|
||||||
var deviceActionService = new DeviceActionService(stateService, messagingService,
|
var clipboardService = new ClipboardService(stateService);
|
||||||
|
var deviceActionService = new DeviceActionService(clipboardService, stateService, messagingService,
|
||||||
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
|
||||||
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
|
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, clipboardService,
|
||||||
broadcasterService);
|
messagingService, broadcasterService);
|
||||||
var biometricService = new BiometricService();
|
var biometricService = new BiometricService();
|
||||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||||
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||||
@@ -142,7 +152,7 @@ namespace Bit.Droid
|
|||||||
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
|
||||||
ServiceContainer.Register<IStateService>("stateService", stateService);
|
ServiceContainer.Register<IStateService>("stateService", stateService);
|
||||||
ServiceContainer.Register<IStateMigrationService>("stateMigrationService", stateMigrationService);
|
ServiceContainer.Register<IStateMigrationService>("stateMigrationService", stateMigrationService);
|
||||||
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(stateService));
|
ServiceContainer.Register<IClipboardService>("clipboardService", clipboardService);
|
||||||
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);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2.16.3" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2022.05.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
|
||||||
|
|
||||||
|
|||||||
31
src/Android/Renderers/CustomPageRenderer.cs
Normal file
31
src/Android/Renderers/CustomPageRenderer.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using AndroidX.AppCompat.Widget;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.Droid.Renderers;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(ContentPage), typeof(CustomPageRenderer))]
|
||||||
|
namespace Bit.Droid.Renderers
|
||||||
|
{
|
||||||
|
public class CustomPageRenderer : PageRenderer
|
||||||
|
{
|
||||||
|
public CustomPageRenderer(Context context) : base(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
|
||||||
|
Activity context = (Activity)this.Context;
|
||||||
|
var toolbar = context.FindViewById<Toolbar>(Resource.Id.toolbar);
|
||||||
|
if(toolbar != null)
|
||||||
|
{
|
||||||
|
toolbar.NavigationContentDescription = AppResources.TapToGoBack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,4 +2,5 @@
|
|||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
@@ -2,4 +2,5 @@
|
|||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Security.Keystore;
|
using Android.Security.Keystore;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Java.Security;
|
using Java.Security;
|
||||||
using Javax.Crypto;
|
using Javax.Crypto;
|
||||||
|
|
||||||
@@ -43,23 +42,22 @@ namespace Bit.Droid.Services
|
|||||||
|
|
||||||
public Task<bool> ValidateIntegrityAsync(string bioIntegrityKey = null)
|
public Task<bool> ValidateIntegrityAsync(string bioIntegrityKey = null)
|
||||||
{
|
{
|
||||||
// bioIntegrityKey used in iOS only
|
|
||||||
if (Build.VERSION.SdkInt < BuildVersionCodes.M)
|
if (Build.VERSION.SdkInt < BuildVersionCodes.M)
|
||||||
{
|
{
|
||||||
return Task.FromResult(true);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_keystore.Load(null);
|
|
||||||
IKey key = _keystore.GetKey(KeyName, null);
|
|
||||||
Cipher cipher = Cipher.GetInstance(Transformation);
|
|
||||||
|
|
||||||
if (key == null || cipher == null)
|
|
||||||
{
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_keystore.Load(null);
|
||||||
|
var key = _keystore.GetKey(KeyName, null);
|
||||||
|
var cipher = Cipher.GetInstance(Transformation);
|
||||||
|
|
||||||
|
if (key == null || cipher == null)
|
||||||
|
{
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
cipher.Init(CipherMode.EncryptMode, key);
|
cipher.Init(CipherMode.EncryptMode, key);
|
||||||
}
|
}
|
||||||
catch (KeyPermanentlyInvalidatedException e)
|
catch (KeyPermanentlyInvalidatedException e)
|
||||||
@@ -75,6 +73,7 @@ namespace Bit.Droid.Services
|
|||||||
catch (InvalidKeyException e)
|
catch (InvalidKeyException e)
|
||||||
{
|
{
|
||||||
// Fallback for old bitwarden users without a key
|
// Fallback for old bitwarden users without a key
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(e);
|
||||||
CreateKey();
|
CreateKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,10 +94,11 @@ namespace Bit.Droid.Services
|
|||||||
keyGen.Init(keyGenSpec);
|
keyGen.Init(keyGenSpec);
|
||||||
keyGen.GenerateKey();
|
keyGen.GenerateKey();
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
// Catch silently to allow biometrics to function on devices that are in a state where key generation
|
// Catch silently to allow biometrics to function on devices that are in a state where key generation
|
||||||
// is not functioning
|
// is not functioning
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Bit.Core;
|
using Android.OS;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Droid.Receivers;
|
using Bit.Droid.Receivers;
|
||||||
using Plugin.CurrentActivity;
|
using Plugin.CurrentActivity;
|
||||||
@@ -26,13 +26,41 @@ namespace Bit.Droid.Services
|
|||||||
PendingIntentFlags.UpdateCurrent));
|
PendingIntentFlags.UpdateCurrent));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CopyTextAsync(string text, int expiresInMs = -1)
|
public async Task CopyTextAsync(string text, int expiresInMs = -1, bool isSensitive = true)
|
||||||
{
|
{
|
||||||
await Clipboard.SetTextAsync(text);
|
// Xamarin.Essentials.Clipboard currently doesn't support the IS_SENSITIVE flag for API 33+
|
||||||
|
if ((int)Build.VERSION.SdkInt < 33)
|
||||||
|
{
|
||||||
|
await Clipboard.SetTextAsync(text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CopyToClipboard(text, isSensitive);
|
||||||
|
}
|
||||||
|
|
||||||
await ClearClipboardAlarmAsync(expiresInMs);
|
await ClearClipboardAlarmAsync(expiresInMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsCopyNotificationHandledByPlatform()
|
||||||
|
{
|
||||||
|
// Android 13+ provides built-in notification when text is copied to the clipboard
|
||||||
|
return (int)Build.VERSION.SdkInt >= 33;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CopyToClipboard(string text, bool isSensitive = true)
|
||||||
|
{
|
||||||
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
|
var clipboardManager = activity.GetSystemService(
|
||||||
|
Context.ClipboardService) as Android.Content.ClipboardManager;
|
||||||
|
var clipData = ClipData.NewPlainText("bitwarden", text);
|
||||||
|
if (isSensitive)
|
||||||
|
{
|
||||||
|
clipData.Description.Extras ??= new PersistableBundle();
|
||||||
|
clipData.Description.Extras.PutBoolean("android.content.extra.IS_SENSITIVE", true);
|
||||||
|
}
|
||||||
|
clipboardManager.PrimaryClip = clipData;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ClearClipboardAlarmAsync(int expiresInMs = -1)
|
private async Task ClearClipboardAlarmAsync(int expiresInMs = -1)
|
||||||
{
|
{
|
||||||
var clearMs = expiresInMs;
|
var clearMs = expiresInMs;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace Bit.Droid.Services
|
|||||||
{
|
{
|
||||||
public class DeviceActionService : IDeviceActionService
|
public class DeviceActionService : IDeviceActionService
|
||||||
{
|
{
|
||||||
|
private readonly IClipboardService _clipboardService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
@@ -47,11 +48,13 @@ namespace Bit.Droid.Services
|
|||||||
private string _userAgent;
|
private string _userAgent;
|
||||||
|
|
||||||
public DeviceActionService(
|
public DeviceActionService(
|
||||||
|
IClipboardService clipboardService,
|
||||||
IStateService stateService,
|
IStateService stateService,
|
||||||
IMessagingService messagingService,
|
IMessagingService messagingService,
|
||||||
IBroadcasterService broadcasterService,
|
IBroadcasterService broadcasterService,
|
||||||
Func<IEventService> eventServiceFunc)
|
Func<IEventService> eventServiceFunc)
|
||||||
{
|
{
|
||||||
|
_clipboardService = clipboardService;
|
||||||
_stateService = stateService;
|
_stateService = stateService;
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
_broadcasterService = broadcasterService;
|
_broadcasterService = broadcasterService;
|
||||||
@@ -674,7 +677,7 @@ namespace Bit.Droid.Services
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var data = new Intent();
|
var data = new Intent();
|
||||||
if (cipher == null)
|
if (cipher?.Login == null)
|
||||||
{
|
{
|
||||||
data.PutExtra("canceled", "true");
|
data.PutExtra("canceled", "true");
|
||||||
}
|
}
|
||||||
@@ -734,6 +737,11 @@ namespace Bit.Droid.Services
|
|||||||
return Accessibility.AccessibilityHelpers.OverlayPermitted();
|
return Accessibility.AccessibilityHelpers.OverlayPermitted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasAutofillService()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public void OpenAccessibilityOverlayPermissionSettings()
|
public void OpenAccessibilityOverlayPermissionSettings()
|
||||||
{
|
{
|
||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||||
@@ -924,24 +932,21 @@ namespace Bit.Droid.Services
|
|||||||
var totp = await totpService.GetCodeAsync(cipher.Login.Totp);
|
var totp = await totpService.GetCodeAsync(cipher.Login.Totp);
|
||||||
if (totp != null)
|
if (totp != null)
|
||||||
{
|
{
|
||||||
CopyToClipboard(totp);
|
await _clipboardService.CopyTextAsync(totp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyToClipboard(string text)
|
|
||||||
{
|
|
||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
|
||||||
var clipboardManager = activity.GetSystemService(
|
|
||||||
Context.ClipboardService) as Android.Content.ClipboardManager;
|
|
||||||
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float GetSystemFontSizeScale()
|
public float GetSystemFontSizeScale()
|
||||||
{
|
{
|
||||||
var activity = CrossCurrentActivity.Current?.Activity as MainActivity;
|
var activity = CrossCurrentActivity.Current?.Activity as MainActivity;
|
||||||
return activity?.Resources?.Configuration?.FontScale ?? 1;
|
return activity?.Resources?.Configuration?.FontScale ?? 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task OnAccountSwitchCompleteAsync()
|
||||||
|
{
|
||||||
|
// for any Android-specific cleanup required after switching accounts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
#if !FDROID
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AppCenter;
|
|
||||||
using Microsoft.AppCenter.Crashes;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Bit.Droid.Utilities
|
|
||||||
{
|
|
||||||
public class AppCenterHelper
|
|
||||||
{
|
|
||||||
private const string AppSecret = "d3834185-b4a6-4347-9047-b86c65293d42";
|
|
||||||
|
|
||||||
private readonly IAppIdService _appIdService;
|
|
||||||
private readonly IStateService _stateService;
|
|
||||||
|
|
||||||
private string _userId;
|
|
||||||
private string _appId;
|
|
||||||
|
|
||||||
public AppCenterHelper(
|
|
||||||
IAppIdService appIdService,
|
|
||||||
IStateService stateService)
|
|
||||||
{
|
|
||||||
_appIdService = appIdService;
|
|
||||||
_stateService = stateService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task InitAsync()
|
|
||||||
{
|
|
||||||
_userId = await _stateService.GetActiveUserIdAsync();
|
|
||||||
_appId = await _appIdService.GetAppIdAsync();
|
|
||||||
|
|
||||||
AppCenter.Start(AppSecret, typeof(Crashes));
|
|
||||||
AppCenter.SetUserId(_userId);
|
|
||||||
|
|
||||||
Crashes.GetErrorAttachments = (ErrorReport report) =>
|
|
||||||
{
|
|
||||||
return new ErrorAttachmentLog[]
|
|
||||||
{
|
|
||||||
ErrorAttachmentLog.AttachmentWithText(Description, "crshdesc.txt"),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Description
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return JsonConvert.SerializeObject(new
|
|
||||||
{
|
|
||||||
AppId = _appId,
|
|
||||||
UserId = _userId
|
|
||||||
}, Formatting.Indented);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
12
src/App/Abstractions/IAccountsManager.cs
Normal file
12
src/App/Abstractions/IAccountsManager.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Models;
|
||||||
|
|
||||||
|
namespace Bit.App.Abstractions
|
||||||
|
{
|
||||||
|
public interface IAccountsManager
|
||||||
|
{
|
||||||
|
void Init(Func<AppOptions> getOptionsFunc, IAccountsManagerHost accountsManagerHost);
|
||||||
|
Task NavigateOnAccountChangeAsync(bool? isAuthed = null);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/App/Abstractions/IAccountsManagerHost.cs
Normal file
14
src/App/Abstractions/IAccountsManagerHost.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.App.Abstractions
|
||||||
|
{
|
||||||
|
public interface INavigationParams { }
|
||||||
|
|
||||||
|
public interface IAccountsManagerHost
|
||||||
|
{
|
||||||
|
Task SetPreviousPageInfoAsync();
|
||||||
|
void Navigate(NavigationTarget navTarget, INavigationParams navParams = null);
|
||||||
|
Task UpdateThemeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using Bit.Core.Enums;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Bit.App.Abstractions
|
namespace Bit.App.Abstractions
|
||||||
{
|
{
|
||||||
@@ -35,6 +35,7 @@ namespace Bit.App.Abstractions
|
|||||||
void Background();
|
void Background();
|
||||||
bool AutofillAccessibilityServiceRunning();
|
bool AutofillAccessibilityServiceRunning();
|
||||||
bool AutofillAccessibilityOverlayPermitted();
|
bool AutofillAccessibilityOverlayPermitted();
|
||||||
|
bool HasAutofillService();
|
||||||
bool AutofillServiceEnabled();
|
bool AutofillServiceEnabled();
|
||||||
void DisableAutofillService();
|
void DisableAutofillService();
|
||||||
bool AutofillServicesEnabled();
|
bool AutofillServicesEnabled();
|
||||||
@@ -46,5 +47,6 @@ namespace Bit.App.Abstractions
|
|||||||
void CloseMainApp();
|
void CloseMainApp();
|
||||||
bool SupportsFido2();
|
bool SupportsFido2();
|
||||||
float GetSystemFontSizeScale();
|
float GetSystemFontSizeScale();
|
||||||
|
Task OnAccountSwitchCompleteAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace Bit.App.Abstractions
|
|||||||
string[] ProtectedFields { get; }
|
string[] ProtectedFields { get; }
|
||||||
|
|
||||||
Task<bool> ShowPasswordPromptAsync();
|
Task<bool> ShowPasswordPromptAsync();
|
||||||
|
|
||||||
Task<(string password, bool valid)> ShowPasswordPromptAndGetItAsync();
|
Task<(string password, bool valid)> ShowPasswordPromptAndGetItAsync();
|
||||||
|
|
||||||
Task<bool> Enabled();
|
Task<bool> Enabled();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Newtonsoft.Json.Linq;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Bit.App.Abstractions
|
namespace Bit.App.Abstractions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,13 +13,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
|
<PackageReference Include="Plugin.Fingerprint" Version="2.1.5" />
|
||||||
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
|
||||||
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.80.3" />
|
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.80.3" />
|
||||||
<PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.0" />
|
<PackageReference Include="Xamarin.CommunityToolkit" Version="2.0.2" />
|
||||||
<PackageReference Include="Xamarin.Essentials" Version="1.7.1" />
|
<PackageReference Include="Xamarin.Essentials" Version="1.7.3" />
|
||||||
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
|
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
|
||||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2337" />
|
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2478" />
|
||||||
<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>
|
||||||
@@ -129,6 +128,7 @@
|
|||||||
<Folder Include="Resources\" />
|
<Folder Include="Resources\" />
|
||||||
<Folder Include="Behaviors\" />
|
<Folder Include="Behaviors\" />
|
||||||
<Folder Include="Controls\AccountSwitchingOverlay\" />
|
<Folder Include="Controls\AccountSwitchingOverlay\" />
|
||||||
|
<Folder Include="Utilities\AccountManagement\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -421,5 +421,6 @@
|
|||||||
<None Remove="Behaviors\" />
|
<None Remove="Behaviors\" />
|
||||||
<None Remove="Xamarin.CommunityToolkit" />
|
<None Remove="Xamarin.CommunityToolkit" />
|
||||||
<None Remove="Controls\AccountSwitchingOverlay\" />
|
<None Remove="Controls\AccountSwitchingOverlay\" />
|
||||||
|
<None Remove="Utilities\AccountManagement\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
using Bit.App.Abstractions;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Pages;
|
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.App.Utilities.AccountManagement;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Xaml;
|
using Xamarin.Forms.Xaml;
|
||||||
|
|
||||||
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
|
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
|
||||||
namespace Bit.App
|
namespace Bit.App
|
||||||
{
|
{
|
||||||
public partial class App : Application
|
public partial class App : Application, IAccountsManagerHost
|
||||||
{
|
{
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
@@ -27,6 +28,7 @@ namespace Bit.App
|
|||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly IStorageService _secureStorageService;
|
private readonly IStorageService _secureStorageService;
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
|
private readonly IAccountsManager _accountsManager;
|
||||||
|
|
||||||
private static bool _isResumed;
|
private static bool _isResumed;
|
||||||
|
|
||||||
@@ -47,6 +49,9 @@ namespace Bit.App
|
|||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
|
_accountsManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
|
||||||
|
|
||||||
|
_accountsManager.Init(() => Options, this);
|
||||||
|
|
||||||
Bootstrap();
|
Bootstrap();
|
||||||
_broadcasterService.Subscribe(nameof(App), async (message) =>
|
_broadcasterService.Subscribe(nameof(App), async (message) =>
|
||||||
@@ -71,27 +76,6 @@ namespace Bit.App
|
|||||||
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
|
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (message.Command == "locked")
|
|
||||||
{
|
|
||||||
var (userId, userInitiated) =
|
|
||||||
message.Data as Tuple<string, bool> ?? new Tuple<string, bool>(null, false);
|
|
||||||
Device.BeginInvokeOnMainThread(async () => await LockedAsync(userId, userInitiated));
|
|
||||||
}
|
|
||||||
else if (message.Command == "lockVault")
|
|
||||||
{
|
|
||||||
await _vaultTimeoutService.LockAsync(true);
|
|
||||||
}
|
|
||||||
else if (message.Command == "logout")
|
|
||||||
{
|
|
||||||
var (userId, userInitiated, expired) =
|
|
||||||
message.Data as Tuple<string, bool, bool> ?? new Tuple<string, bool, bool>(null, true, false);
|
|
||||||
Device.BeginInvokeOnMainThread(async () => await LogOutAsync(userId, userInitiated, expired));
|
|
||||||
}
|
|
||||||
else if (message.Command == "loggedOut")
|
|
||||||
{
|
|
||||||
// Clean up old migrated key if they ever log out.
|
|
||||||
await _secureStorageService.RemoveAsync("oldKey");
|
|
||||||
}
|
|
||||||
else if (message.Command == "resumed")
|
else if (message.Command == "resumed")
|
||||||
{
|
{
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
@@ -106,22 +90,10 @@ namespace Bit.App
|
|||||||
await SleptAsync();
|
await SleptAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (message.Command == "addAccount")
|
|
||||||
{
|
|
||||||
await AddAccount();
|
|
||||||
}
|
|
||||||
else if (message.Command == "accountAdded")
|
|
||||||
{
|
|
||||||
await UpdateThemeAsync();
|
|
||||||
}
|
|
||||||
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);
|
||||||
await SetMainPageAsync();
|
await _accountsManager.NavigateOnAccountChangeAsync();
|
||||||
}
|
}
|
||||||
else if (message.Command == "popAllAndGoToTabGenerator" ||
|
else if (message.Command == "popAllAndGoToTabGenerator" ||
|
||||||
message.Command == "popAllAndGoToTabMyVault" ||
|
message.Command == "popAllAndGoToTabMyVault" ||
|
||||||
@@ -165,7 +137,6 @@ namespace Bit.App
|
|||||||
new NavigationPage(new RemoveMasterPasswordPage()));
|
new NavigationPage(new RemoveMasterPasswordPage()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +176,10 @@ namespace Bit.App
|
|||||||
{
|
{
|
||||||
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||||
}
|
}
|
||||||
SetTabsPageFromAutofill(isLocked);
|
if (!SetTabsPageFromAutofill(isLocked))
|
||||||
|
{
|
||||||
|
ClearAutofillUri();
|
||||||
|
}
|
||||||
await SleptAsync();
|
await SleptAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,102 +231,6 @@ namespace Bit.App
|
|||||||
new System.Globalization.UmAlQuraCalendar();
|
new System.Globalization.UmAlQuraCalendar();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LogOutAsync(string userId, bool userInitiated, bool expired)
|
|
||||||
{
|
|
||||||
await AppHelpers.LogOutAsync(userId, userInitiated);
|
|
||||||
await SetMainPageAsync();
|
|
||||||
_authService.LogOut(() =>
|
|
||||||
{
|
|
||||||
if (expired)
|
|
||||||
{
|
|
||||||
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AddAccount()
|
|
||||||
{
|
|
||||||
Device.BeginInvokeOnMainThread(async () =>
|
|
||||||
{
|
|
||||||
Options.HideAccountSwitcher = false;
|
|
||||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SwitchedAccountAsync()
|
|
||||||
{
|
|
||||||
await AppHelpers.OnAccountSwitchAsync();
|
|
||||||
Device.BeginInvokeOnMainThread(async () =>
|
|
||||||
{
|
|
||||||
if (await _vaultTimeoutService.ShouldTimeoutAsync())
|
|
||||||
{
|
|
||||||
await _vaultTimeoutService.ExecuteTimeoutActionAsync();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await SetMainPageAsync();
|
|
||||||
}
|
|
||||||
await Task.Delay(50);
|
|
||||||
await UpdateThemeAsync();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SetMainPageAsync()
|
|
||||||
{
|
|
||||||
var authed = await _stateService.IsAuthenticatedAsync();
|
|
||||||
if (authed)
|
|
||||||
{
|
|
||||||
if (await _vaultTimeoutService.IsLoggedOutByTimeoutAsync() ||
|
|
||||||
await _vaultTimeoutService.ShouldLogOutByTimeoutAsync())
|
|
||||||
{
|
|
||||||
// TODO implement orgIdentifier flow to SSO Login page, same as email flow below
|
|
||||||
// var orgIdentifier = await _stateService.GetOrgIdentifierAsync();
|
|
||||||
|
|
||||||
var email = await _stateService.GetEmailAsync();
|
|
||||||
Options.HideAccountSwitcher = await _stateService.GetActiveUserIdAsync() == null;
|
|
||||||
Current.MainPage = new NavigationPage(new LoginPage(email, Options));
|
|
||||||
}
|
|
||||||
else if (await _vaultTimeoutService.IsLockedAsync() ||
|
|
||||||
await _vaultTimeoutService.ShouldLockAsync())
|
|
||||||
{
|
|
||||||
Current.MainPage = new NavigationPage(new LockPage(Options));
|
|
||||||
}
|
|
||||||
else if (Options.FromAutofillFramework && Options.SaveType.HasValue)
|
|
||||||
{
|
|
||||||
Current.MainPage = new NavigationPage(new AddEditPage(appOptions: Options));
|
|
||||||
}
|
|
||||||
else if (Options.Uri != null)
|
|
||||||
{
|
|
||||||
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
|
|
||||||
}
|
|
||||||
else if (Options.CreateSend != null)
|
|
||||||
{
|
|
||||||
Current.MainPage = new NavigationPage(new SendAddEditPage(Options));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Current.MainPage = new TabsPage(Options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Options.HideAccountSwitcher = await _stateService.GetActiveUserIdAsync() == null;
|
|
||||||
if (await _vaultTimeoutService.IsLoggedOutByTimeoutAsync() ||
|
|
||||||
await _vaultTimeoutService.ShouldLogOutByTimeoutAsync())
|
|
||||||
{
|
|
||||||
// TODO implement orgIdentifier flow to SSO Login page, same as email flow below
|
|
||||||
// var orgIdentifier = await _stateService.GetOrgIdentifierAsync();
|
|
||||||
|
|
||||||
var email = await _stateService.GetEmailAsync();
|
|
||||||
Current.MainPage = new NavigationPage(new LoginPage(email, Options));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ClearCacheIfNeededAsync()
|
private async Task ClearCacheIfNeededAsync()
|
||||||
{
|
{
|
||||||
var lastClear = await _stateService.GetLastFileCacheClearAsync();
|
var lastClear = await _stateService.GetLastFileCacheClearAsync();
|
||||||
@@ -362,7 +240,15 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetTabsPageFromAutofill(bool isLocked)
|
private void ClearAutofillUri()
|
||||||
|
{
|
||||||
|
if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri))
|
||||||
|
{
|
||||||
|
Options.Uri = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetTabsPageFromAutofill(bool isLocked)
|
||||||
{
|
{
|
||||||
if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri) &&
|
if (Device.RuntimePlatform == Device.Android && !string.IsNullOrWhiteSpace(Options.Uri) &&
|
||||||
!Options.FromAutofillFramework)
|
!Options.FromAutofillFramework)
|
||||||
@@ -382,7 +268,9 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Prime()
|
private void Prime()
|
||||||
@@ -404,7 +292,7 @@ namespace Bit.App
|
|||||||
UpdateThemeAsync();
|
UpdateThemeAsync();
|
||||||
};
|
};
|
||||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||||
var mainPageTask = SetMainPageAsync();
|
var mainPageTask = _accountsManager.NavigateOnAccountChangeAsync();
|
||||||
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,22 +313,8 @@ namespace Bit.App
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LockedAsync(string userId, bool autoPromptBiometric)
|
public async Task SetPreviousPageInfoAsync()
|
||||||
{
|
{
|
||||||
if (!await _stateService.IsActiveAccountAsync(userId))
|
|
||||||
{
|
|
||||||
_platformUtilsService.ShowToast("info", null, AppResources.AccountLockedSuccessfully);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
|
|
||||||
{
|
|
||||||
var vaultTimeout = await _stateService.GetVaultTimeoutAsync();
|
|
||||||
if (vaultTimeout == 0)
|
|
||||||
{
|
|
||||||
autoPromptBiometric = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PreviousPageInfo lastPageBeforeLock = null;
|
PreviousPageInfo lastPageBeforeLock = null;
|
||||||
if (Current.MainPage is TabbedPage tabbedPage && tabbedPage.Navigation.ModalStack.Count > 0)
|
if (Current.MainPage is TabbedPage tabbedPage && tabbedPage.Navigation.ModalStack.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -466,8 +340,44 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
|
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
|
||||||
var lockPage = new LockPage(Options, autoPromptBiometric);
|
}
|
||||||
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
|
|
||||||
|
public void Navigate(NavigationTarget navTarget, INavigationParams navParams)
|
||||||
|
{
|
||||||
|
switch (navTarget)
|
||||||
|
{
|
||||||
|
case NavigationTarget.HomeLogin:
|
||||||
|
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||||
|
break;
|
||||||
|
case NavigationTarget.Login:
|
||||||
|
if (navParams is LoginNavigationParams loginParams)
|
||||||
|
{
|
||||||
|
Current.MainPage = new NavigationPage(new LoginPage(loginParams.Email, Options));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NavigationTarget.Lock:
|
||||||
|
if (navParams is LockNavigationParams lockParams)
|
||||||
|
{
|
||||||
|
Current.MainPage = new NavigationPage(new LockPage(Options, lockParams.AutoPromptBiometric));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Current.MainPage = new NavigationPage(new LockPage(Options));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NavigationTarget.Home:
|
||||||
|
Current.MainPage = new TabsPage(Options);
|
||||||
|
break;
|
||||||
|
case NavigationTarget.AddEditCipher:
|
||||||
|
Current.MainPage = new NavigationPage(new AddEditPage(appOptions: Options));
|
||||||
|
break;
|
||||||
|
case NavigationTarget.AutofillCiphers:
|
||||||
|
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
|
||||||
|
break;
|
||||||
|
case NavigationTarget.SendAddEdit:
|
||||||
|
Current.MainPage = new NavigationPage(new SendAddEditPage(Options));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ namespace Bit.App.Controls
|
|||||||
|
|
||||||
public int AccountListRowHeight => Device.RuntimePlatform == Device.Android ? 74 : 70;
|
public int AccountListRowHeight => Device.RuntimePlatform == Device.Android ? 74 : 70;
|
||||||
|
|
||||||
|
public bool LongPressAccountEnabled { get; set; } = true;
|
||||||
|
|
||||||
public async Task ToggleVisibilityAsync()
|
public async Task ToggleVisibilityAsync()
|
||||||
{
|
{
|
||||||
if (IsVisible)
|
if (IsVisible)
|
||||||
@@ -99,7 +101,7 @@ namespace Bit.App.Controls
|
|||||||
Opacity = 0;
|
Opacity = 0;
|
||||||
IsVisible = true;
|
IsVisible = true;
|
||||||
this.FadeTo(1, 100);
|
this.FadeTo(1, 100);
|
||||||
|
|
||||||
if (Device.RuntimePlatform == Device.Android && MainFab != null)
|
if (Device.RuntimePlatform == Device.Android && MainFab != null)
|
||||||
{
|
{
|
||||||
// start fab fade-out
|
// start fab fade-out
|
||||||
@@ -167,7 +169,7 @@ namespace Bit.App.Controls
|
|||||||
|
|
||||||
private async Task LongPressAccountAsync(AccountViewCellViewModel item)
|
private async Task LongPressAccountAsync(AccountViewCellViewModel item)
|
||||||
{
|
{
|
||||||
if (!item.IsAccount)
|
if (!LongPressAccountEnabled || !item.IsAccount)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ namespace Bit.App.Controls
|
|||||||
{
|
{
|
||||||
_stateService = stateService;
|
_stateService = stateService;
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
|
|
||||||
SelectAccountCommand = new AsyncCommand<AccountViewCellViewModel>(SelectAccountAsync,
|
SelectAccountCommand = new AsyncCommand<AccountViewCellViewModel>(SelectAccountAsync,
|
||||||
onException: ex => logger.Exception(ex),
|
onException: ex => logger.Exception(ex),
|
||||||
allowsMultipleExecutions: false);
|
allowsMultipleExecutions: false);
|
||||||
|
|
||||||
LongPressAccountCommand = new AsyncCommand<Tuple<ContentPage, AccountViewCellViewModel>>(LongPressAccountAsync,
|
LongPressAccountCommand = new AsyncCommand<Tuple<ContentPage, AccountViewCellViewModel>>(LongPressAccountAsync,
|
||||||
onException: ex => logger.Exception(ex),
|
onException: ex => logger.Exception(ex),
|
||||||
allowsMultipleExecutions: false);
|
allowsMultipleExecutions: false);
|
||||||
|
|||||||
@@ -59,26 +59,26 @@
|
|||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Text="{Binding AccountView.Email}"
|
Text="{Binding AccountView.Email}"
|
||||||
IsVisible="{Binding IsActive}"
|
IsVisible="{Binding IsActive}"
|
||||||
StyleClass="list-title"
|
StyleClass="accountlist-title, accountlist-title-platform"
|
||||||
LineBreakMode="TailTruncation" />
|
LineBreakMode="TailTruncation" />
|
||||||
<Label
|
<Label
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Text="{Binding AccountView.Email}"
|
Text="{Binding AccountView.Email}"
|
||||||
IsVisible="{Binding IsActive, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding IsActive, Converter={StaticResource inverseBool}}"
|
||||||
StyleClass="list-title"
|
StyleClass="accountlist-title, accountlist-title-platform"
|
||||||
TextColor="{DynamicResource MutedColor}"
|
TextColor="{DynamicResource MutedColor}"
|
||||||
LineBreakMode="TailTruncation" />
|
LineBreakMode="TailTruncation" />
|
||||||
<Label
|
<Label
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
IsVisible="{Binding ShowHostname}"
|
IsVisible="{Binding ShowHostname}"
|
||||||
Text="{Binding AccountView.Hostname}"
|
Text="{Binding AccountView.Hostname}"
|
||||||
StyleClass="list-sub"
|
StyleClass="accountlist-sub, accountlist-sub-platform"
|
||||||
LineBreakMode="TailTruncation" />
|
LineBreakMode="TailTruncation" />
|
||||||
<Label
|
<Label
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Text="{u:I18n AccountUnlocked}"
|
Text="{u:I18n AccountUnlocked}"
|
||||||
IsVisible="{Binding IsUnlockedAndNotActive}"
|
IsVisible="{Binding IsUnlockedAndNotActive}"
|
||||||
StyleClass="list-sub"
|
StyleClass="accountlist-sub, accountlist-sub-platform"
|
||||||
FontAttributes="Italic"
|
FontAttributes="Italic"
|
||||||
TextTransform="Lowercase"
|
TextTransform="Lowercase"
|
||||||
LineBreakMode="TailTruncation" />
|
LineBreakMode="TailTruncation" />
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Text="{u:I18n AccountLocked}"
|
Text="{u:I18n AccountLocked}"
|
||||||
IsVisible="{Binding IsLockedAndNotActive}"
|
IsVisible="{Binding IsLockedAndNotActive}"
|
||||||
StyleClass="list-sub"
|
StyleClass="accountlist-sub, accountlist-sub-platform"
|
||||||
FontAttributes="Italic"
|
FontAttributes="Italic"
|
||||||
TextTransform="Lowercase"
|
TextTransform="Lowercase"
|
||||||
LineBreakMode="TailTruncation" />
|
LineBreakMode="TailTruncation" />
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Text="{u:I18n AccountLoggedOut}"
|
Text="{u:I18n AccountLoggedOut}"
|
||||||
IsVisible="{Binding IsLoggedOutAndNotActive}"
|
IsVisible="{Binding IsLoggedOutAndNotActive}"
|
||||||
StyleClass="list-sub"
|
StyleClass="accountlist-sub, accountlist-sub-platform"
|
||||||
FontAttributes="Italic"
|
FontAttributes="Italic"
|
||||||
TextTransform="Lowercase"
|
TextTransform="Lowercase"
|
||||||
LineBreakMode="TailTruncation" />
|
LineBreakMode="TailTruncation" />
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
AutomationProperties.IsInAccessibleTree="False" />
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n AddAccount}"
|
Text="{u:I18n AddAccount}"
|
||||||
StyleClass="list-title"
|
StyleClass="accountlist-title, accountlist-title-platform"
|
||||||
LineBreakMode="TailTruncation"
|
LineBreakMode="TailTruncation"
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
Grid.Column="1" />
|
Grid.Column="1" />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
using System.Windows.Input;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using System.Windows.Input;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
namespace Bit.App.Controls
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ namespace Bit.App.Controls
|
|||||||
upperData = _data.ToUpper();
|
upperData = _data.ToUpper();
|
||||||
chars = GetFirstLetters(upperData, 2);
|
chars = GetFirstLetters(upperData, 2);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chars = upperData = _data.ToUpper();
|
||||||
|
}
|
||||||
|
|
||||||
var bgColor = StringToColor(upperData);
|
var bgColor = StringToColor(upperData);
|
||||||
var textColor = Color.White;
|
var textColor = Color.White;
|
||||||
@@ -108,13 +112,13 @@ namespace Bit.App.Controls
|
|||||||
|
|
||||||
private string GetFirstLetters(string data, int charCount)
|
private string GetFirstLetters(string data, int charCount)
|
||||||
{
|
{
|
||||||
var parts = data.Split();
|
var parts = data.Split();
|
||||||
if (parts.Length > 1 && charCount <= 2)
|
if (parts.Length > 1 && charCount <= 2)
|
||||||
{
|
{
|
||||||
var text = "";
|
var text = "";
|
||||||
for (int i = 0; i < charCount; i++)
|
for (int i = 0; i < charCount; i++)
|
||||||
{
|
{
|
||||||
text += parts[i].Substring(0,1);
|
text += parts[i].Substring(0, 1);
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
@@ -122,7 +126,7 @@ namespace Bit.App.Controls
|
|||||||
{
|
{
|
||||||
return data.Substring(0, 2);
|
return data.Substring(0, 2);
|
||||||
}
|
}
|
||||||
return null;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color StringToColor(string str)
|
private Color StringToColor(string str)
|
||||||
|
|||||||
@@ -108,7 +108,7 @@
|
|||||||
<controls:MiButton
|
<controls:MiButton
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Text=""
|
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
|
||||||
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
||||||
Clicked="MoreButton_Clicked"
|
Clicked="MoreButton_Clicked"
|
||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
|
|||||||
@@ -4,5 +4,6 @@ namespace Bit.App.Controls
|
|||||||
{
|
{
|
||||||
public class ExtendedCollectionView : CollectionView
|
public class ExtendedCollectionView : CollectionView
|
||||||
{
|
{
|
||||||
|
public string ExtraDataForLogging { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Bit.App.Controls
|
|||||||
{
|
{
|
||||||
public static readonly BindableProperty StepperBackgroundColorProperty = BindableProperty.Create(
|
public static readonly BindableProperty StepperBackgroundColorProperty = BindableProperty.Create(
|
||||||
nameof(StepperBackgroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default);
|
nameof(StepperBackgroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default);
|
||||||
|
|
||||||
public static readonly BindableProperty StepperForegroundColorProperty = BindableProperty.Create(
|
public static readonly BindableProperty StepperForegroundColorProperty = BindableProperty.Create(
|
||||||
nameof(StepperForegroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default);
|
nameof(StepperForegroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default);
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ namespace Bit.App.Controls
|
|||||||
get => (Color)GetValue(StepperBackgroundColorProperty);
|
get => (Color)GetValue(StepperBackgroundColorProperty);
|
||||||
set => SetValue(StepperBackgroundColorProperty, value);
|
set => SetValue(StepperBackgroundColorProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color StepperForegroundColor
|
public Color StepperForegroundColor
|
||||||
{
|
{
|
||||||
get => (Color)GetValue(StepperForegroundColorProperty);
|
get => (Color)GetValue(StepperForegroundColorProperty);
|
||||||
|
|||||||
@@ -122,7 +122,7 @@
|
|||||||
<controls:MiButton
|
<controls:MiButton
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Text=""
|
Text="{Binding Source={x:Static core:BitwardenIcons.ViewCellMenu}}"
|
||||||
IsVisible="{Binding ShowOptions, Mode=OneWay}"
|
IsVisible="{Binding ShowOptions, Mode=OneWay}"
|
||||||
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
|
||||||
Clicked="MoreButton_Clicked"
|
Clicked="MoreButton_Clicked"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@@ -13,7 +13,7 @@ namespace Bit.App.Controls
|
|||||||
|
|
||||||
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
|
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
|
||||||
nameof(ButtonCommand), typeof(Command<SendView>), typeof(SendViewCell));
|
nameof(ButtonCommand), typeof(Command<SendView>), typeof(SendViewCell));
|
||||||
|
|
||||||
public static readonly BindableProperty ShowOptionsProperty = BindableProperty.Create(
|
public static readonly BindableProperty ShowOptionsProperty = BindableProperty.Create(
|
||||||
nameof(ShowOptions), typeof(bool), typeof(SendViewCell), true, BindingMode.OneWay);
|
nameof(ShowOptions), typeof(bool), typeof(SendViewCell), true, BindingMode.OneWay);
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ namespace Bit.App.Effects
|
|||||||
{
|
{
|
||||||
public class FabShadowEffect : RoutingEffect
|
public class FabShadowEffect : RoutingEffect
|
||||||
{
|
{
|
||||||
public FabShadowEffect()
|
public FabShadowEffect()
|
||||||
: base("Bitwarden.FabShadowEffect")
|
: base("Bitwarden.FabShadowEffect")
|
||||||
{ }
|
{ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
using Bit.App.Resources;
|
using System.Collections.Generic;
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -46,7 +46,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
get => _showPassword;
|
get => _showPassword;
|
||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new[] { nameof(ShowPasswordIcon) });
|
additionalPropertyNames: new[]
|
||||||
|
{
|
||||||
|
nameof(ShowPasswordIcon),
|
||||||
|
nameof(PasswordVisibilityAccessibilityText)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPolicyInEffect
|
public bool IsPolicyInEffect
|
||||||
@@ -66,8 +70,10 @@ namespace Bit.App.Pages
|
|||||||
get => _policy;
|
get => _policy;
|
||||||
set => SetProperty(ref _policy, value);
|
set => SetProperty(ref _policy, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||||
|
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
|
||||||
|
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
|
||||||
public string MasterPassword { get; set; }
|
public string MasterPassword { get; set; }
|
||||||
public string ConfirmMasterPassword { get; set; }
|
public string ConfirmMasterPassword { get; set; }
|
||||||
public string Hint { get; set; }
|
public string Hint { get; set; }
|
||||||
@@ -84,7 +90,7 @@ namespace Bit.App.Pages
|
|||||||
await CheckPasswordPolicy();
|
await CheckPasswordPolicy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckPasswordPolicy()
|
private async Task CheckPasswordPolicy()
|
||||||
{
|
{
|
||||||
Policy = await _policyService.GetMasterPasswordPolicyOptions();
|
Policy = await _policyService.GetMasterPasswordPolicyOptions();
|
||||||
@@ -166,7 +172,7 @@ namespace Bit.App.Pages
|
|||||||
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Bit.Core.Abstractions;
|
using System;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Bit.App.Resources;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Bit.App.Abstractions;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
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.Threading.Tasks;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png";
|
_logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Cancel_Clicked(object sender, EventArgs e)
|
private void Cancel_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
@@ -117,7 +117,7 @@ namespace Bit.App.Pages
|
|||||||
_vm.StartRegisterAction();
|
_vm.StartRegisterAction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartRegisterAsync()
|
private async Task StartRegisterAsync()
|
||||||
{
|
{
|
||||||
var page = new RegisterPage(this);
|
var page = new RegisterPage(this);
|
||||||
@@ -145,7 +145,7 @@ namespace Bit.App.Pages
|
|||||||
_vm.StartEnvironmentAction();
|
_vm.StartEnvironmentAction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartEnvironmentAsync()
|
private async Task StartEnvironmentAsync()
|
||||||
{
|
{
|
||||||
await _accountListOverlay.HideAsync();
|
await _accountListOverlay.HideAsync();
|
||||||
|
|||||||
@@ -80,7 +80,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="_passwordGrid"
|
x:Name="_passwordGrid"
|
||||||
@@ -119,7 +119,8 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"
|
||||||
|
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
StyleClass="box-row"
|
StyleClass="box-row"
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new string[]
|
additionalPropertyNames: new string[]
|
||||||
{
|
{
|
||||||
nameof(ShowPasswordIcon)
|
nameof(ShowPasswordIcon),
|
||||||
|
nameof(PasswordVisibilityAccessibilityText),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +129,8 @@ namespace Bit.App.Pages
|
|||||||
public Command SubmitCommand { get; }
|
public Command SubmitCommand { get; }
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
|
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
|
||||||
|
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
|
||||||
public string MasterPassword { get; set; }
|
public string MasterPassword { get; set; }
|
||||||
public string Pin { get; set; }
|
public string Pin { get; set; }
|
||||||
public Action UnlockedAction { get; set; }
|
public Action UnlockedAction { get; set; }
|
||||||
@@ -272,7 +275,7 @@ namespace Bit.App.Pages
|
|||||||
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdf, kdfIterations);
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdf, kdfIterations);
|
||||||
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
|
||||||
var passwordValid = false;
|
var passwordValid = false;
|
||||||
|
|
||||||
if (storedKeyHash != null)
|
if (storedKeyHash != null)
|
||||||
{
|
{
|
||||||
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, key);
|
passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(MasterPassword, key);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout Padding="10, 0">
|
<StackLayout Padding="10, 0">
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ namespace Bit.App.Pages
|
|||||||
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 Task UpdateTempPasswordAsync()
|
private async Task UpdateTempPasswordAsync()
|
||||||
{
|
{
|
||||||
var page = new UpdateTempPasswordPage();
|
var page = new UpdateTempPasswordPage();
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new string[]
|
additionalPropertyNames: new string[]
|
||||||
{
|
{
|
||||||
nameof(ShowPasswordIcon)
|
nameof(ShowPasswordIcon),
|
||||||
|
nameof(PasswordVisibilityAccessibilityText)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ namespace Bit.App.Pages
|
|||||||
get => _showCancelButton;
|
get => _showCancelButton;
|
||||||
set => SetProperty(ref _showCancelButton, value);
|
set => SetProperty(ref _showCancelButton, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Email
|
public string Email
|
||||||
{
|
{
|
||||||
get => _email;
|
get => _email;
|
||||||
@@ -85,6 +86,8 @@ namespace Bit.App.Pages
|
|||||||
public Command LogInCommand { get; }
|
public Command LogInCommand { get; }
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
|
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
|
||||||
|
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
|
||||||
public Action StartTwoFactorAction { get; set; }
|
public Action StartTwoFactorAction { get; set; }
|
||||||
public Action LogInSuccessAction { get; set; }
|
public Action LogInSuccessAction { get; set; }
|
||||||
public Action UpdateTempPasswordAction { get; set; }
|
public Action UpdateTempPasswordAction { get; set; }
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Bit.App.Models;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Models;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -99,7 +99,7 @@ namespace Bit.App.Pages
|
|||||||
var page = new SetPasswordPage(_appOptions, _vm.OrgIdentifier);
|
var page = new SetPasswordPage(_appOptions, _vm.OrgIdentifier);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateTempPasswordAsync()
|
private async Task UpdateTempPasswordAsync()
|
||||||
{
|
{
|
||||||
var page = new UpdateTempPasswordPage();
|
var page = new UpdateTempPasswordPage();
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
using Bit.App.Abstractions;
|
using System;
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ namespace Bit.App.Pages
|
|||||||
else if (response.ResetMasterPassword)
|
else if (response.ResetMasterPassword)
|
||||||
{
|
{
|
||||||
StartSetPasswordAction?.Invoke();
|
StartSetPasswordAction?.Invoke();
|
||||||
}
|
}
|
||||||
else if (response.ForcePasswordReset)
|
else if (response.ForcePasswordReset)
|
||||||
{
|
{
|
||||||
UpdateTempPasswordAction?.Invoke();
|
UpdateTempPasswordAction?.Invoke();
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n MasterPasswordDescription}"
|
Text="{u:I18n MasterPasswordDescription}"
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<StackLayout StyleClass="box-row">
|
<StackLayout StyleClass="box-row">
|
||||||
<Label
|
<Label
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace Bit.App.Pages
|
|||||||
await _vm.SubmitAsync();
|
await _vm.SubmitAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RegistrationSuccessAsync(HomePage homePage)
|
private async Task RegistrationSuccessAsync(HomePage homePage)
|
||||||
{
|
{
|
||||||
if (homePage != null)
|
if (homePage != null)
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
using Bit.App.Abstractions;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
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;
|
||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using Bit.Core;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -39,7 +39,7 @@ namespace Bit.App.Pages
|
|||||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||||
ShowTerms = !_platformUtilsService.IsSelfHost();
|
ShowTerms = !_platformUtilsService.IsSelfHost();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommand PoliciesClickCommand => new Command<string>((url) =>
|
public ICommand PoliciesClickCommand => new Command<string>((url) =>
|
||||||
{
|
{
|
||||||
_platformUtilsService.LaunchUri(url);
|
_platformUtilsService.LaunchUri(url);
|
||||||
@@ -51,28 +51,31 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new string[]
|
additionalPropertyNames: new string[]
|
||||||
{
|
{
|
||||||
nameof(ShowPasswordIcon)
|
nameof(ShowPasswordIcon),
|
||||||
|
nameof(PasswordVisibilityAccessibilityText)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AcceptPolicies
|
public bool AcceptPolicies
|
||||||
{
|
{
|
||||||
get => _acceptPolicies;
|
get => _acceptPolicies;
|
||||||
set => SetProperty(ref _acceptPolicies, value);
|
set => SetProperty(ref _acceptPolicies, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Thickness SwitchMargin
|
public Thickness SwitchMargin
|
||||||
{
|
{
|
||||||
get => Device.RuntimePlatform == Device.Android
|
get => Device.RuntimePlatform == Device.Android
|
||||||
? new Thickness(0, 0, 0, 0)
|
? new Thickness(0, 0, 0, 0)
|
||||||
: new Thickness(0, 0, 10, 0);
|
: new Thickness(0, 0, 10, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowTerms { get; set; }
|
public bool ShowTerms { get; set; }
|
||||||
public Command SubmitCommand { get; }
|
public Command SubmitCommand { get; }
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public Command ToggleConfirmPasswordCommand { get; }
|
public Command ToggleConfirmPasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
|
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
|
||||||
|
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
public string MasterPassword { get; set; }
|
public string MasterPassword { get; set; }
|
||||||
@@ -136,7 +139,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Password strength check?
|
// TODO: Password strength check?
|
||||||
|
|
||||||
if (showLoading)
|
if (showLoading)
|
||||||
{
|
{
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||||
|
|||||||
@@ -107,7 +107,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n MasterPasswordDescription}"
|
Text="{u:I18n MasterPasswordDescription}"
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<StackLayout StyleClass="box-row">
|
<StackLayout StyleClass="box-row">
|
||||||
<Label
|
<Label
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
using Bit.App.Abstractions;
|
using System;
|
||||||
using Bit.App.Resources;
|
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Exceptions;
|
|
||||||
using Bit.Core.Models.Request;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Models.Request;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -55,7 +55,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
get => _showPassword;
|
get => _showPassword;
|
||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new[] { nameof(ShowPasswordIcon) });
|
additionalPropertyNames: new[]
|
||||||
|
{
|
||||||
|
nameof(ShowPasswordIcon),
|
||||||
|
nameof(PasswordVisibilityAccessibilityText)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPolicyInEffect
|
public bool IsPolicyInEffect
|
||||||
@@ -63,7 +67,7 @@ namespace Bit.App.Pages
|
|||||||
get => _isPolicyInEffect;
|
get => _isPolicyInEffect;
|
||||||
set => SetProperty(ref _isPolicyInEffect, value);
|
set => SetProperty(ref _isPolicyInEffect, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ResetPasswordAutoEnroll
|
public bool ResetPasswordAutoEnroll
|
||||||
{
|
{
|
||||||
get => _resetPasswordAutoEnroll;
|
get => _resetPasswordAutoEnroll;
|
||||||
@@ -86,6 +90,8 @@ namespace Bit.App.Pages
|
|||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public Command ToggleConfirmPasswordCommand { get; }
|
public Command ToggleConfirmPasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
|
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
|
||||||
|
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
|
||||||
public string MasterPassword { get; set; }
|
public string MasterPassword { get; set; }
|
||||||
public string ConfirmMasterPassword { get; set; }
|
public string ConfirmMasterPassword { get; set; }
|
||||||
public string Hint { get; set; }
|
public string Hint { get; set; }
|
||||||
@@ -220,7 +226,7 @@ namespace Bit.App.Pages
|
|||||||
// Enroll user
|
// Enroll user
|
||||||
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
SetPasswordSuccessAction?.Invoke();
|
SetPasswordSuccessAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using Bit.App.Controls;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Controls;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -47,7 +47,8 @@ namespace Bit.App.Pages
|
|||||||
if (Device.RuntimePlatform == Device.iOS)
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
ToolbarItems.Add(_moreItem);
|
ToolbarItems.Add(_moreItem);
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
ToolbarItems.Add(_useAnotherTwoStepMethod);
|
ToolbarItems.Add(_useAnotherTwoStepMethod);
|
||||||
}
|
}
|
||||||
@@ -92,7 +93,8 @@ namespace Bit.App.Pages
|
|||||||
if (_vm.TotpMethod)
|
if (_vm.TotpMethod)
|
||||||
{
|
{
|
||||||
RequestFocus(_totpEntry);
|
RequestFocus(_totpEntry);
|
||||||
} else if (_vm.YubikeyMethod)
|
}
|
||||||
|
else if (_vm.YubikeyMethod)
|
||||||
{
|
{
|
||||||
RequestFocus(_yubikeyTokenEntry);
|
RequestFocus(_yubikeyTokenEntry);
|
||||||
}
|
}
|
||||||
@@ -187,7 +189,7 @@ namespace Bit.App.Pages
|
|||||||
var page = new SetPasswordPage(_appOptions, _orgIdentifier);
|
var page = new SetPasswordPage(_appOptions, _orgIdentifier);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateTempPasswordAsync()
|
private async Task UpdateTempPasswordAsync()
|
||||||
{
|
{
|
||||||
var page = new UpdateTempPasswordPage();
|
var page = new UpdateTempPasswordPage();
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
using Bit.App.Abstractions;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@@ -29,6 +29,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly II18nService _i18nService;
|
private readonly II18nService _i18nService;
|
||||||
|
private readonly IAppIdService _appIdService;
|
||||||
|
|
||||||
private TwoFactorProviderType? _selectedProviderType;
|
private TwoFactorProviderType? _selectedProviderType;
|
||||||
private string _totpInstruction;
|
private string _totpInstruction;
|
||||||
@@ -49,6 +50,7 @@ namespace Bit.App.Pages
|
|||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||||
|
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
|
||||||
|
|
||||||
PageTitle = AppResources.TwoStepLogin;
|
PageTitle = AppResources.TwoStepLogin;
|
||||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||||
@@ -113,7 +115,7 @@ namespace Bit.App.Pages
|
|||||||
public Action StartSetPasswordAction { get; set; }
|
public Action StartSetPasswordAction { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
public Action UpdateTempPasswordAction { get; set; }
|
public Action UpdateTempPasswordAction { get; set; }
|
||||||
|
|
||||||
protected override II18nService i18nService => _i18nService;
|
protected override II18nService i18nService => _i18nService;
|
||||||
protected override IEnvironmentService environmentService => _environmentService;
|
protected override IEnvironmentService environmentService => _environmentService;
|
||||||
protected override IDeviceActionService deviceActionService => _deviceActionService;
|
protected override IDeviceActionService deviceActionService => _deviceActionService;
|
||||||
@@ -293,7 +295,7 @@ namespace Bit.App.Pages
|
|||||||
await _deviceActionService.ShowLoadingAsync(AppResources.Validating);
|
await _deviceActionService.ShowLoadingAsync(AppResources.Validating);
|
||||||
}
|
}
|
||||||
var result = await _authService.LogInTwoFactorAsync(SelectedProviderType.Value, Token, _captchaToken, Remember);
|
var result = await _authService.LogInTwoFactorAsync(SelectedProviderType.Value, Token, _captchaToken, Remember);
|
||||||
|
|
||||||
if (result.CaptchaNeeded)
|
if (result.CaptchaNeeded)
|
||||||
{
|
{
|
||||||
if (await HandleCaptchaAsync(result.CaptchaSiteKey))
|
if (await HandleCaptchaAsync(result.CaptchaSiteKey))
|
||||||
@@ -304,12 +306,12 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_captchaToken = null;
|
_captchaToken = null;
|
||||||
|
|
||||||
var task = Task.Run(() => _syncService.FullSyncAsync(true));
|
var task = Task.Run(() => _syncService.FullSyncAsync(true));
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
_messagingService.Send("listenYubiKeyOTP", false);
|
_messagingService.Send("listenYubiKeyOTP", false);
|
||||||
_broadcasterService.Unsubscribe(nameof(TwoFactorPage));
|
_broadcasterService.Unsubscribe(nameof(TwoFactorPage));
|
||||||
|
|
||||||
if (_authingWithSso && result.ResetMasterPassword)
|
if (_authingWithSso && result.ResetMasterPassword)
|
||||||
{
|
{
|
||||||
StartSetPasswordAction?.Invoke();
|
StartSetPasswordAction?.Invoke();
|
||||||
@@ -317,7 +319,7 @@ namespace Bit.App.Pages
|
|||||||
else if (result.ForcePasswordReset)
|
else if (result.ForcePasswordReset)
|
||||||
{
|
{
|
||||||
UpdateTempPasswordAction?.Invoke();
|
UpdateTempPasswordAction?.Invoke();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TwoFactorAuthSuccessAction?.Invoke();
|
TwoFactorAuthSuccessAction?.Invoke();
|
||||||
@@ -380,7 +382,8 @@ namespace Bit.App.Pages
|
|||||||
var request = new TwoFactorEmailRequest
|
var request = new TwoFactorEmailRequest
|
||||||
{
|
{
|
||||||
Email = _authService.Email,
|
Email = _authService.Email,
|
||||||
MasterPasswordHash = _authService.MasterPasswordHash
|
MasterPasswordHash = _authService.MasterPasswordHash,
|
||||||
|
DeviceIdentifier = await _appIdService.GetAppIdAsync()
|
||||||
};
|
};
|
||||||
await _apiService.PostTwoFactorEmailAsync(request);
|
await _apiService.PostTwoFactorEmailAsync(request);
|
||||||
if (showLoading)
|
if (showLoading)
|
||||||
|
|||||||
@@ -105,7 +105,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box">
|
<StackLayout StyleClass="box">
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<StackLayout StyleClass="box-row">
|
<StackLayout StyleClass="box-row">
|
||||||
<Label
|
<Label
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
using System;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
|
||||||
using Bit.App.Resources;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -14,29 +14,29 @@ namespace Bit.App.Pages
|
|||||||
private readonly string _pageName;
|
private readonly string _pageName;
|
||||||
|
|
||||||
public UpdateTempPasswordPage()
|
public UpdateTempPasswordPage()
|
||||||
{
|
{
|
||||||
// Service Init
|
// Service Init
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
|
|
||||||
// Binding
|
// Binding
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks);
|
_pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks);
|
||||||
_vm = BindingContext as UpdateTempPasswordPageViewModel;
|
_vm = BindingContext as UpdateTempPasswordPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
SetActivityIndicator();
|
SetActivityIndicator();
|
||||||
|
|
||||||
// Actions Declaration
|
// Actions Declaration
|
||||||
_vm.LogOutAction = () =>
|
_vm.LogOutAction = () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("logout");
|
_messagingService.Send("logout");
|
||||||
};
|
};
|
||||||
_vm.UpdateTempPasswordSuccessAction = () => Device.BeginInvokeOnMainThread(UpdateTempPasswordSuccess);
|
_vm.UpdateTempPasswordSuccessAction = () => Device.BeginInvokeOnMainThread(UpdateTempPasswordSuccess);
|
||||||
|
|
||||||
// Link fields that will be referenced in codebehind
|
// Link fields that will be referenced in codebehind
|
||||||
MasterPasswordEntry = _masterPassword;
|
MasterPasswordEntry = _masterPassword;
|
||||||
ConfirmMasterPasswordEntry = _confirmMasterPassword;
|
ConfirmMasterPasswordEntry = _confirmMasterPassword;
|
||||||
|
|
||||||
// Return Types and Commands
|
// Return Types and Commands
|
||||||
_masterPassword.ReturnType = ReturnType.Next;
|
_masterPassword.ReturnType = ReturnType.Next;
|
||||||
_masterPassword.ReturnCommand = new Command(() => _confirmMasterPassword.Focus());
|
_masterPassword.ReturnCommand = new Command(() => _confirmMasterPassword.Focus());
|
||||||
@@ -77,7 +77,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateTempPasswordSuccess()
|
private void UpdateTempPasswordSuccess()
|
||||||
{
|
{
|
||||||
_messagingService.Send("logout");
|
_messagingService.Send("logout");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Bit.App.Resources;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@@ -16,7 +16,7 @@ namespace Bit.App.Pages
|
|||||||
ToggleConfirmPasswordCommand = new Command(ToggleConfirmPassword);
|
ToggleConfirmPasswordCommand = new Command(ToggleConfirmPassword);
|
||||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Command SubmitCommand { get; }
|
public Command SubmitCommand { get; }
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public Command ToggleConfirmPasswordCommand { get; }
|
public Command ToggleConfirmPasswordCommand { get; }
|
||||||
@@ -37,23 +37,23 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task SubmitAsync()
|
public async Task SubmitAsync()
|
||||||
{
|
{
|
||||||
if (!await ValidateMasterPasswordAsync())
|
if (!await ValidateMasterPasswordAsync())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve details for key generation
|
// Retrieve details for key generation
|
||||||
var kdf = await _stateService.GetKdfTypeAsync();
|
var kdf = await _stateService.GetKdfTypeAsync();
|
||||||
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
var kdfIterations = await _stateService.GetKdfIterationsAsync();
|
||||||
var email = await _stateService.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);
|
||||||
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
||||||
|
|
||||||
// Create new encKey for the User
|
// Create new encKey for the User
|
||||||
var newEncKey = await _cryptoService.RemakeEncKeyAsync(key);
|
var newEncKey = await _cryptoService.RemakeEncKeyAsync(key);
|
||||||
|
|
||||||
// Create request
|
// Create request
|
||||||
var request = new UpdateTempPasswordRequest
|
var request = new UpdateTempPasswordRequest
|
||||||
{
|
{
|
||||||
@@ -61,7 +61,7 @@ namespace Bit.App.Pages
|
|||||||
NewMasterPasswordHash = masterPasswordHash,
|
NewMasterPasswordHash = masterPasswordHash,
|
||||||
MasterPasswordHint = Hint
|
MasterPasswordHint = Hint
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initiate API action
|
// Initiate API action
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Bit.App.Pages
|
|||||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
PageTitle = AppResources.VerificationCode;
|
PageTitle = AppResources.VerificationCode;
|
||||||
|
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
MainActionCommand = new AsyncCommand(MainActionAsync, allowsMultipleExecutions: false);
|
MainActionCommand = new AsyncCommand(MainActionAsync, allowsMultipleExecutions: false);
|
||||||
RequestOTPCommand = new AsyncCommand(RequestOTPAsync, allowsMultipleExecutions: false);
|
RequestOTPCommand = new AsyncCommand(RequestOTPAsync, allowsMultipleExecutions: false);
|
||||||
@@ -60,7 +60,7 @@ namespace Bit.App.Pages
|
|||||||
get => _mainActionText;
|
get => _mainActionText;
|
||||||
set => SetProperty(ref _mainActionText, value);
|
set => SetProperty(ref _mainActionText, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SendCodeStatus
|
public string SendCodeStatus
|
||||||
{
|
{
|
||||||
get => _sendCodeStatus;
|
get => _sendCodeStatus;
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ namespace Bit.App.Pages
|
|||||||
protected async override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
|
|
||||||
if (IsThemeDirty)
|
if (IsThemeDirty)
|
||||||
{
|
{
|
||||||
UpdateOnThemeChanged();
|
UpdateOnThemeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
await SaveActivityAsync();
|
await SaveActivityAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
@@ -37,11 +37,14 @@ namespace Bit.App.Pages
|
|||||||
bool cancelled = false;
|
bool cancelled = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// PrefersEphemeralWebBrowserSession should be false to allow access to the hCaptcha accessibility
|
||||||
|
// cookie set in the default browser
|
||||||
|
// https://www.hcaptcha.com/accessibility
|
||||||
var options = new WebAuthenticatorOptions
|
var options = new WebAuthenticatorOptions
|
||||||
{
|
{
|
||||||
Url = new Uri(url),
|
Url = new Uri(url),
|
||||||
CallbackUrl = new Uri(callbackUri),
|
CallbackUrl = new Uri(callbackUri),
|
||||||
PrefersEphemeralWebBrowserSession = true,
|
PrefersEphemeralWebBrowserSession = false,
|
||||||
};
|
};
|
||||||
authResult = await WebAuthenticator.AuthenticateAsync(options);
|
authResult = await WebAuthenticator.AuthenticateAsync(options);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,8 @@
|
|||||||
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
|
||||||
ItemsSource="{Binding History}"
|
ItemsSource="{Binding History}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform"
|
||||||
|
ExtraDataForLogging="Generator History Page">
|
||||||
<CollectionView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="domain:GeneratedPasswordHistory">
|
<DataTemplate x:DataType="domain:GeneratedPasswordHistory">
|
||||||
<Grid
|
<Grid
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
|
|
||||||
await LoadOnAppearedAsync(_mainLayout, true, async () => {
|
await LoadOnAppearedAsync(_mainLayout, true, async () =>
|
||||||
|
{
|
||||||
await _vm.InitAsync();
|
await _vm.InitAsync();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,8 +41,11 @@ namespace Bit.App.Pages
|
|||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
var history = await _passwordGenerationService.GetHistoryAsync();
|
var history = await _passwordGenerationService.GetHistoryAsync();
|
||||||
History.ResetWithRange(history ?? new List<GeneratedPasswordHistory>());
|
Device.BeginInvokeOnMainThread(() =>
|
||||||
ShowNoData = History.Count == 0;
|
{
|
||||||
|
History.ResetWithRange(history ?? new List<GeneratedPasswordHistory>());
|
||||||
|
ShowNoData = History.Count == 0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ClearAsync()
|
public async Task ClearAsync()
|
||||||
@@ -55,8 +58,7 @@ namespace Bit.App.Pages
|
|||||||
private async void CopyAsync(GeneratedPasswordHistory ph)
|
private async void CopyAsync(GeneratedPasswordHistory ph)
|
||||||
{
|
{
|
||||||
await _clipboardService.CopyTextAsync(ph.Password);
|
await _clipboardService.CopyTextAsync(ph.Password);
|
||||||
_platformUtilsService.ShowToast("info", null,
|
_platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateOnThemeChanged()
|
public async Task UpdateOnThemeChanged()
|
||||||
|
|||||||
@@ -183,52 +183,68 @@
|
|||||||
<Label
|
<Label
|
||||||
Text="A-Z"
|
Text="A-Z"
|
||||||
StyleClass="box-label-regular"
|
StyleClass="box-label-regular"
|
||||||
HorizontalOptions="StartAndExpand" />
|
HorizontalOptions="StartAndExpand"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n UppercaseAtoZ}"/>
|
||||||
<Switch
|
<Switch
|
||||||
IsToggled="{Binding Uppercase}"
|
IsToggled="{Binding Uppercase}"
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.UseUppercase,
|
IsEnabled="{Binding EnforcedPolicyOptions.UseUppercase,
|
||||||
Converter={StaticResource inverseBool}}"
|
Converter={StaticResource inverseBool}}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End" />
|
HorizontalOptions="End"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n UppercaseAtoZ}"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
<BoxView StyleClass="box-row-separator" />
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
<Label
|
<Label
|
||||||
Text="a-z"
|
Text="a-z"
|
||||||
StyleClass="box-label-regular"
|
StyleClass="box-label-regular"
|
||||||
HorizontalOptions="StartAndExpand" />
|
HorizontalOptions="StartAndExpand"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n LowercaseAtoZ}"/>
|
||||||
<Switch
|
<Switch
|
||||||
IsToggled="{Binding Lowercase}"
|
IsToggled="{Binding Lowercase}"
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.UseLowercase,
|
IsEnabled="{Binding EnforcedPolicyOptions.UseLowercase,
|
||||||
Converter={StaticResource inverseBool}}"
|
Converter={StaticResource inverseBool}}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End" />
|
HorizontalOptions="End"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n LowercaseAtoZ}" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
<BoxView StyleClass="box-row-separator" />
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
<Label
|
<Label
|
||||||
Text="0-9"
|
Text="0-9"
|
||||||
StyleClass="box-label-regular"
|
StyleClass="box-label-regular"
|
||||||
HorizontalOptions="StartAndExpand" />
|
HorizontalOptions="StartAndExpand"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n NumbersZeroToNine}"/>
|
||||||
<Switch
|
<Switch
|
||||||
IsToggled="{Binding Number}"
|
IsToggled="{Binding Number}"
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.UseNumbers,
|
IsEnabled="{Binding EnforcedPolicyOptions.UseNumbers,
|
||||||
Converter={StaticResource inverseBool}}"
|
Converter={StaticResource inverseBool}}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End" />
|
HorizontalOptions="End"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n NumbersZeroToNine}"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
<BoxView StyleClass="box-row-separator" />
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
<Label
|
<Label
|
||||||
Text="!@#$%^&*"
|
Text="!@#$%^&*"
|
||||||
StyleClass="box-label-regular"
|
StyleClass="box-label-regular"
|
||||||
HorizontalOptions="StartAndExpand" />
|
HorizontalOptions="StartAndExpand"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n SpecialCharacters}"/>
|
||||||
<Switch
|
<Switch
|
||||||
IsToggled="{Binding Special}"
|
IsToggled="{Binding Special}"
|
||||||
IsEnabled="{Binding EnforcedPolicyOptions.UseSpecial,
|
IsEnabled="{Binding EnforcedPolicyOptions.UseSpecial,
|
||||||
Converter={StaticResource inverseBool}}"
|
Converter={StaticResource inverseBool}}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End" />
|
HorizontalOptions="End"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n SpecialCharacters}"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<BoxView StyleClass="box-row-separator" />
|
<BoxView StyleClass="box-row-separator" />
|
||||||
<StackLayout StyleClass="box-row, box-row-stepper">
|
<StackLayout StyleClass="box-row, box-row-stepper">
|
||||||
@@ -277,7 +293,7 @@
|
|||||||
StyleClass="box-label-regular"
|
StyleClass="box-label-regular"
|
||||||
HorizontalOptions="StartAndExpand" />
|
HorizontalOptions="StartAndExpand" />
|
||||||
<Switch
|
<Switch
|
||||||
IsToggled="{Binding AvoidAmbiguous}"
|
IsToggled="{Binding AvoidAmbiguousChars}"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
HorizontalOptions="End" />
|
HorizontalOptions="End" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Bit.App.Pages
|
|||||||
public partial class GeneratorPage : BaseContentPage, IThemeDirtablePage
|
public partial class GeneratorPage : BaseContentPage, IThemeDirtablePage
|
||||||
{
|
{
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
|
|
||||||
private GeneratorPageViewModel _vm;
|
private GeneratorPageViewModel _vm;
|
||||||
private readonly bool _fromTabPage;
|
private readonly bool _fromTabPage;
|
||||||
private readonly Action<string> _selectAction;
|
private readonly Action<string> _selectAction;
|
||||||
@@ -77,7 +77,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDisappearing()
|
protected override void OnDisappearing()
|
||||||
{
|
{
|
||||||
base.OnDisappearing();
|
base.OnDisappearing();
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using Bit.App.Resources;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -23,7 +22,7 @@ namespace Bit.App.Pages
|
|||||||
private bool _lowercase;
|
private bool _lowercase;
|
||||||
private bool _number;
|
private bool _number;
|
||||||
private bool _special;
|
private bool _special;
|
||||||
private bool _avoidAmbiguous;
|
private bool _allowAmbiguousChars;
|
||||||
private int _minNumber;
|
private int _minNumber;
|
||||||
private int _minSpecial;
|
private int _minSpecial;
|
||||||
private int _length = 5;
|
private int _length = 5;
|
||||||
@@ -130,19 +129,29 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AvoidAmbiguous
|
public bool AllowAmbiguousChars
|
||||||
{
|
{
|
||||||
get => _avoidAmbiguous;
|
get => _allowAmbiguousChars;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _avoidAmbiguous, value))
|
if (SetProperty(ref _allowAmbiguousChars, value,
|
||||||
|
additionalPropertyNames: new string[]
|
||||||
|
{
|
||||||
|
nameof(AvoidAmbiguousChars)
|
||||||
|
}))
|
||||||
{
|
{
|
||||||
_options.Ambiguous = !value;
|
_options.AllowAmbiguousChar = value;
|
||||||
var task = SaveOptionsAsync();
|
var task = SaveOptionsAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool AvoidAmbiguousChars
|
||||||
|
{
|
||||||
|
get => !AllowAmbiguousChars;
|
||||||
|
set => AllowAmbiguousChars = !value;
|
||||||
|
}
|
||||||
|
|
||||||
public int MinNumber
|
public int MinNumber
|
||||||
{
|
{
|
||||||
get => _minNumber;
|
get => _minNumber;
|
||||||
@@ -225,7 +234,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PasswordGeneratorPolicyOptions EnforcedPolicyOptions
|
public PasswordGeneratorPolicyOptions EnforcedPolicyOptions
|
||||||
{
|
{
|
||||||
get => _enforcedPolicyOptions;
|
get => _enforcedPolicyOptions;
|
||||||
@@ -309,13 +318,12 @@ namespace Bit.App.Pages
|
|||||||
public async Task CopyAsync()
|
public async Task CopyAsync()
|
||||||
{
|
{
|
||||||
await _clipboardService.CopyTextAsync(Password);
|
await _clipboardService.CopyTextAsync(Password);
|
||||||
_platformUtilsService.ShowToast("success", null,
|
_platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
||||||
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadFromOptions()
|
private void LoadFromOptions()
|
||||||
{
|
{
|
||||||
AvoidAmbiguous = !_options.Ambiguous.GetValueOrDefault();
|
AllowAmbiguousChars = _options.AllowAmbiguousChar.GetValueOrDefault();
|
||||||
TypeSelectedIndex = _options.Type == "passphrase" ? 1 : 0;
|
TypeSelectedIndex = _options.Type == "passphrase" ? 1 : 0;
|
||||||
IsPassword = TypeSelectedIndex == 0;
|
IsPassword = TypeSelectedIndex == 0;
|
||||||
MinNumber = _options.MinNumber.GetValueOrDefault();
|
MinNumber = _options.MinNumber.GetValueOrDefault();
|
||||||
@@ -333,7 +341,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private void SetOptions()
|
private void SetOptions()
|
||||||
{
|
{
|
||||||
_options.Ambiguous = !AvoidAmbiguous;
|
_options.AllowAmbiguousChar = AllowAmbiguousChars;
|
||||||
_options.Type = TypeSelectedIndex == 1 ? "passphrase" : "password";
|
_options.Type = TypeSelectedIndex == 1 ? "passphrase" : "password";
|
||||||
_options.MinNumber = MinNumber;
|
_options.MinNumber = MinNumber;
|
||||||
_options.MinSpecial = MinSpecial;
|
_options.MinSpecial = MinSpecial;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<pages:BaseContentPage
|
<pages:BaseContentPage
|
||||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
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.SendAddEditPage"
|
x:Class="Bit.App.Pages.SendAddEditPage"
|
||||||
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"
|
||||||
@@ -121,6 +122,7 @@
|
|||||||
Clicked="FileType_Clicked"
|
Clicked="FileType_Clicked"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n File}"
|
AutomationProperties.Name="{u:I18n File}"
|
||||||
|
AutomationProperties.HelpText="{Binding FileTypeAccessibilityLabel}"
|
||||||
Grid.Column="0">
|
Grid.Column="0">
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -132,6 +134,7 @@
|
|||||||
Clicked="TextType_Clicked"
|
Clicked="TextType_Clicked"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n Text}"
|
AutomationProperties.Name="{u:I18n Text}"
|
||||||
|
AutomationProperties.HelpText="{Binding TextTypeAccessibilityLabel}"
|
||||||
Grid.Column="1">
|
Grid.Column="1">
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -250,29 +253,33 @@
|
|||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
Spacing="0">
|
Spacing="0"
|
||||||
|
xct:TouchEffect.Command="{Binding ToggleOptionsCommand}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{Binding OptionsAccessilibityText}">
|
||||||
<Button
|
<Button
|
||||||
Text="{u:I18n Options}"
|
Text="{u:I18n Options}"
|
||||||
x:Name="_btnOptions"
|
x:Name="_btnOptions"
|
||||||
StyleClass="box-row-button"
|
StyleClass="box-row-button"
|
||||||
TextColor="{DynamicResource PrimaryColor}"
|
TextColor="{DynamicResource PrimaryColor}"
|
||||||
Margin="0" />
|
Margin="0"
|
||||||
|
AutomationProperties.IsInAccessibleTree="False"/>
|
||||||
<controls:IconButton
|
<controls:IconButton
|
||||||
x:Name="_btnOptionsUp"
|
x:Name="_btnOptionsUp"
|
||||||
Text="{Binding Source={x:Static core:BitwardenIcons.ChevronUp}}"
|
Text="{Binding Source={x:Static core:BitwardenIcons.ChevronUp}}"
|
||||||
StyleClass="box-row-button"
|
StyleClass="box-row-button"
|
||||||
TextColor="{DynamicResource PrimaryColor}"
|
TextColor="{DynamicResource PrimaryColor}"
|
||||||
Clicked="ToggleOptions_Clicked"
|
IsVisible="{Binding ShowOptions}"
|
||||||
IsVisible="False" />
|
AutomationProperties.IsInAccessibleTree="False"/>
|
||||||
<controls:IconButton
|
<controls:IconButton
|
||||||
x:Name="_btnOptionsDown"
|
x:Name="_btnOptionsDown"
|
||||||
Text="{Binding Source={x:Static core:BitwardenIcons.AngleDown}}"
|
Text="{Binding Source={x:Static core:BitwardenIcons.AngleDown}}"
|
||||||
StyleClass="box-row-button"
|
StyleClass="box-row-button"
|
||||||
TextColor="{DynamicResource PrimaryColor}"
|
TextColor="{DynamicResource PrimaryColor}"
|
||||||
Clicked="ToggleOptions_Clicked"
|
IsVisible="{Binding ShowOptions, Converter={StaticResource inverseBool}}"
|
||||||
IsVisible="False" />
|
AutomationProperties.IsInAccessibleTree="False"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout IsVisible="True">
|
<StackLayout IsVisible="{Binding ShowOptions}">
|
||||||
<StackLayout
|
<StackLayout
|
||||||
StyleClass="box-row"
|
StyleClass="box-row"
|
||||||
Margin="0,10,0,0">
|
Margin="0,10,0,0">
|
||||||
@@ -437,7 +444,7 @@
|
|||||||
Command="{Binding TogglePasswordCommand}"
|
Command="{Binding TogglePasswordCommand}"
|
||||||
Margin="10,0,0,0"
|
Margin="10,0,0,0"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n PasswordInfo}"
|
Text="{u:I18n PasswordInfo}"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
@@ -53,10 +53,9 @@ namespace Bit.App.Pages
|
|||||||
_vm.SegmentedButtonFontSize = 13;
|
_vm.SegmentedButtonFontSize = 13;
|
||||||
_vm.SegmentedButtonMargins = new Thickness(0, 10, 0, 0);
|
_vm.SegmentedButtonMargins = new Thickness(0, 10, 0, 0);
|
||||||
_vm.EditorMargins = new Thickness(0, 5, 0, 0);
|
_vm.EditorMargins = new Thickness(0, 5, 0, 0);
|
||||||
// Review this when https://github.com/bitwarden/mobile/pull/1454 workaround can be reverted
|
_btnOptions.WidthRequest = 70;
|
||||||
//_btnOptions.WidthRequest = 70;
|
_btnOptionsDown.WidthRequest = 30;
|
||||||
//_btnOptionsDown.WidthRequest = 30;
|
_btnOptionsUp.WidthRequest = 30;
|
||||||
//_btnOptionsUp.WidthRequest = 30;
|
|
||||||
}
|
}
|
||||||
else if (Device.RuntimePlatform == Device.iOS)
|
else if (Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
@@ -210,11 +209,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleOptions_Clicked(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
_vm.ToggleOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearExpirationDate_Clicked(object sender, EventArgs e)
|
private void ClearExpirationDate_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
@@ -338,10 +332,10 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
_vm.IsAddFromShare = true;
|
_vm.IsAddFromShare = true;
|
||||||
_vm.CopyInsteadOfShareAfterSaving = _appOptions.CopyInsteadOfShareAfterSaving;
|
_vm.CopyInsteadOfShareAfterSaving = _appOptions.CopyInsteadOfShareAfterSaving;
|
||||||
|
|
||||||
var name = _appOptions.CreateSend.Item2;
|
var name = _appOptions.CreateSend.Item2;
|
||||||
_vm.Send.Name = name;
|
_vm.Send.Name = name;
|
||||||
|
|
||||||
var type = _appOptions.CreateSend.Item1;
|
var type = _appOptions.CreateSend.Item1;
|
||||||
if (type == SendType.File)
|
if (type == SendType.File)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,10 +40,12 @@ namespace Bit.App.Pages
|
|||||||
private TimeSpan? _expirationTime;
|
private TimeSpan? _expirationTime;
|
||||||
private bool _isOverridingPickers;
|
private bool _isOverridingPickers;
|
||||||
private int? _maxAccessCount;
|
private int? _maxAccessCount;
|
||||||
private string[] _additionalSendProperties = new []
|
private string[] _additionalSendProperties = new[]
|
||||||
{
|
{
|
||||||
nameof(IsText),
|
nameof(IsText),
|
||||||
nameof(IsFile),
|
nameof(IsFile),
|
||||||
|
nameof(FileTypeAccessibilityLabel),
|
||||||
|
nameof(TextTypeAccessibilityLabel)
|
||||||
};
|
};
|
||||||
private bool _disableHideEmail;
|
private bool _disableHideEmail;
|
||||||
private bool _sendOptionsPolicyInEffect;
|
private bool _sendOptionsPolicyInEffect;
|
||||||
@@ -59,6 +61,7 @@ namespace Bit.App.Pages
|
|||||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
|
ToggleOptionsCommand = new Command(ToggleOptions);
|
||||||
|
|
||||||
TypeOptions = new List<KeyValuePair<string, SendType>>
|
TypeOptions = new List<KeyValuePair<string, SendType>>
|
||||||
{
|
{
|
||||||
@@ -89,6 +92,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Command TogglePasswordCommand { get; set; }
|
public Command TogglePasswordCommand { get; set; }
|
||||||
|
public Command ToggleOptionsCommand { get; set; }
|
||||||
public string SendId { get; set; }
|
public string SendId { get; set; }
|
||||||
public int SegmentedButtonHeight { get; set; }
|
public int SegmentedButtonHeight { get; set; }
|
||||||
public int SegmentedButtonFontSize { get; set; }
|
public int SegmentedButtonFontSize { get; set; }
|
||||||
@@ -102,6 +106,7 @@ namespace Bit.App.Pages
|
|||||||
public bool DisableHideEmailControl { get; set; }
|
public bool DisableHideEmailControl { get; set; }
|
||||||
public bool IsAddFromShare { get; set; }
|
public bool IsAddFromShare { get; set; }
|
||||||
public string ShareOnSaveText => CopyInsteadOfShareAfterSaving ? AppResources.CopySendLinkOnSave : AppResources.ShareOnSave;
|
public string ShareOnSaveText => CopyInsteadOfShareAfterSaving ? AppResources.CopySendLinkOnSave : AppResources.ShareOnSave;
|
||||||
|
public string OptionsAccessilibityText => ShowOptions ? AppResources.OptionsExpanded : AppResources.OptionsCollapsed;
|
||||||
public List<KeyValuePair<string, SendType>> TypeOptions { get; }
|
public List<KeyValuePair<string, SendType>> TypeOptions { get; }
|
||||||
public List<KeyValuePair<string, string>> DeletionTypeOptions { get; }
|
public List<KeyValuePair<string, string>> DeletionTypeOptions { get; }
|
||||||
public List<KeyValuePair<string, string>> ExpirationTypeOptions { get; }
|
public List<KeyValuePair<string, string>> ExpirationTypeOptions { get; }
|
||||||
@@ -134,7 +139,11 @@ namespace Bit.App.Pages
|
|||||||
public bool ShowOptions
|
public bool ShowOptions
|
||||||
{
|
{
|
||||||
get => _showOptions;
|
get => _showOptions;
|
||||||
set => SetProperty(ref _showOptions, value);
|
set => SetProperty(ref _showOptions, value,
|
||||||
|
additionalPropertyNames: new[]
|
||||||
|
{
|
||||||
|
nameof(OptionsAccessilibityText)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
public int ExpirationDateTypeSelectedIndex
|
public int ExpirationDateTypeSelectedIndex
|
||||||
{
|
{
|
||||||
@@ -209,9 +218,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
get => _showPassword;
|
get => _showPassword;
|
||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new []
|
additionalPropertyNames: new[]
|
||||||
{
|
{
|
||||||
nameof(ShowPasswordIcon)
|
nameof(ShowPasswordIcon),
|
||||||
|
nameof(PasswordVisibilityAccessibilityText)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public bool DisableHideEmail
|
public bool DisableHideEmail
|
||||||
@@ -231,13 +241,17 @@ namespace Bit.App.Pages
|
|||||||
public bool ShowDeletionCustomPickers => EditMode || DeletionDateTypeSelectedIndex == 6;
|
public bool ShowDeletionCustomPickers => EditMode || DeletionDateTypeSelectedIndex == 6;
|
||||||
public bool ShowExpirationCustomPickers => EditMode || ExpirationDateTypeSelectedIndex == 7;
|
public bool ShowExpirationCustomPickers => EditMode || ExpirationDateTypeSelectedIndex == 7;
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
|
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
|
||||||
|
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
|
||||||
|
public string FileTypeAccessibilityLabel => IsFile ? AppResources.FileTypeIsSelected : AppResources.FileTypeIsNotSelected;
|
||||||
|
public string TextTypeAccessibilityLabel => IsText ? AppResources.TextTypeIsSelected : AppResources.TextTypeIsNotSelected;
|
||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
|
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
|
||||||
_canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
_canAccessPremium = await _stateService.CanAccessPremiumAsync();
|
||||||
_emailVerified = await _stateService.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;
|
||||||
}
|
}
|
||||||
@@ -381,7 +395,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
UpdateSendData();
|
UpdateSendData();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(NewPassword))
|
if (string.IsNullOrWhiteSpace(NewPassword))
|
||||||
{
|
{
|
||||||
NewPassword = null;
|
NewPassword = null;
|
||||||
}
|
}
|
||||||
@@ -521,7 +535,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.SendFileEmailVerificationRequired);
|
await _platformUtilsService.ShowDialogAsync(AppResources.SendFileEmailVerificationRequired);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
|
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
|
||||||
{
|
{
|
||||||
_deviceActionService.CloseMainApp();
|
_deviceActionService.CloseMainApp();
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public interface ISendGroupingsPageListItem
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<?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"
|
||||||
x:Class="Bit.App.Pages.SendGroupingsPage"
|
x:Class="Bit.App.Pages.SendGroupingsPage"
|
||||||
@@ -73,7 +73,29 @@
|
|||||||
</controls:ExtendedStackLayout>
|
</controls:ExtendedStackLayout>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate
|
||||||
|
x:Key="headerTemplate"
|
||||||
|
x:DataType="pages:SendGroupingsPageHeaderListItem">
|
||||||
|
<StackLayout
|
||||||
|
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
|
||||||
|
StyleClass="list-row-header-container, list-row-header-container-platform">
|
||||||
|
<BoxView
|
||||||
|
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
|
||||||
|
<StackLayout StyleClass="list-row-header, list-row-header-platform">
|
||||||
|
<Label
|
||||||
|
Text="{Binding Title}"
|
||||||
|
StyleClass="list-header, list-header-platform" />
|
||||||
|
<Label
|
||||||
|
Text="{Binding ItemCount}"
|
||||||
|
StyleClass="list-header-sub" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView
|
||||||
|
StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
|
||||||
|
</StackLayout>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
<pages:SendGroupingsPageListItemSelector x:Key="sendListItemDataTemplateSelector"
|
<pages:SendGroupingsPageListItemSelector x:Key="sendListItemDataTemplateSelector"
|
||||||
|
HeaderTemplate="{StaticResource headerTemplate}"
|
||||||
SendTemplate="{StaticResource sendTemplate}"
|
SendTemplate="{StaticResource sendTemplate}"
|
||||||
GroupTemplate="{StaticResource sendGroupTemplate}" />
|
GroupTemplate="{StaticResource sendGroupTemplate}" />
|
||||||
|
|
||||||
@@ -114,33 +136,10 @@
|
|||||||
ItemsSource="{Binding GroupedSends}"
|
ItemsSource="{Binding GroupedSends}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
|
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
|
||||||
IsGrouped="True"
|
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
SelectionChanged="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform"
|
||||||
|
ExtraDataForLogging="Send Groupings Page" />
|
||||||
<CollectionView.GroupHeaderTemplate>
|
|
||||||
<DataTemplate x:DataType="pages:SendGroupingsPageListGroup">
|
|
||||||
<StackLayout
|
|
||||||
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
|
|
||||||
StyleClass="list-row-header-container, list-row-header-container-platform">
|
|
||||||
<BoxView
|
|
||||||
StyleClass="list-section-separator-top, list-section-separator-top-platform"
|
|
||||||
IsVisible="{Binding First, Converter={StaticResource inverseBool}}" />
|
|
||||||
<StackLayout StyleClass="list-row-header, list-row-header-platform">
|
|
||||||
<Label
|
|
||||||
Text="{Binding Name}"
|
|
||||||
StyleClass="list-header, list-header-platform" />
|
|
||||||
<Label
|
|
||||||
Text="{Binding ItemCount}"
|
|
||||||
StyleClass="list-header-sub" />
|
|
||||||
</StackLayout>
|
|
||||||
<BoxView
|
|
||||||
StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
|
|
||||||
</StackLayout>
|
|
||||||
</DataTemplate>
|
|
||||||
</CollectionView.GroupHeaderTemplate>
|
|
||||||
</controls:ExtendedCollectionView>
|
|
||||||
</RefreshView>
|
</RefreshView>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
@@ -133,7 +133,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
private async void RowSelected(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
((ExtendedCollectionView)sender).SelectedItem = null;
|
((ExtendedCollectionView)sender).SelectedItem = null;
|
||||||
@@ -160,7 +160,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
var page = new SendsPage(_vm.Filter, _vm.Type != null);
|
var page = new SendsPage(_vm.Filter, _vm.Type);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,7 +174,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _vaultTimeoutService.LockAsync(true, true);
|
await _vaultTimeoutService.LockAsync(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void About_Clicked(object sender, EventArgs e)
|
private void About_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
_vm.ShowAbout();
|
_vm.ShowAbout();
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class SendGroupingsPageHeaderListItem : ISendGroupingsPageListItem
|
||||||
|
{
|
||||||
|
public SendGroupingsPageHeaderListItem(string title, string itemCount)
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
ItemCount = itemCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Title { get; }
|
||||||
|
public string ItemCount { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ using Bit.Core.Models.View;
|
|||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class SendGroupingsPageListItem
|
public class SendGroupingsPageListItem : ISendGroupingsPageListItem
|
||||||
{
|
{
|
||||||
private string _icon;
|
private string _icon;
|
||||||
private string _name;
|
private string _name;
|
||||||
|
|||||||
@@ -4,11 +4,17 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
public class SendGroupingsPageListItemSelector : DataTemplateSelector
|
public class SendGroupingsPageListItemSelector : DataTemplateSelector
|
||||||
{
|
{
|
||||||
|
public DataTemplate HeaderTemplate { get; set; }
|
||||||
public DataTemplate SendTemplate { get; set; }
|
public DataTemplate SendTemplate { get; set; }
|
||||||
public DataTemplate GroupTemplate { get; set; }
|
public DataTemplate GroupTemplate { get; set; }
|
||||||
|
|
||||||
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
|
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
|
||||||
{
|
{
|
||||||
|
if (item is SendGroupingsPageHeaderListItem)
|
||||||
|
{
|
||||||
|
return HeaderTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
if (item is SendGroupingsPageListItem listItem)
|
if (item is SendGroupingsPageListItem listItem)
|
||||||
{
|
{
|
||||||
return listItem.Send != null ? SendTemplate : GroupTemplate;
|
return listItem.Send != null ? SendTemplate : GroupTemplate;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using Bit.Core.Abstractions;
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using DeviceType = Bit.Core.Enums.DeviceType;
|
using DeviceType = Bit.Core.Enums.DeviceType;
|
||||||
@@ -48,7 +49,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
Loading = true;
|
Loading = true;
|
||||||
PageTitle = AppResources.Send;
|
PageTitle = AppResources.Send;
|
||||||
GroupedSends = new ExtendedObservableCollection<SendGroupingsPageListGroup>();
|
GroupedSends = new ObservableRangeCollection<ISendGroupingsPageListItem>();
|
||||||
RefreshCommand = new Command(async () =>
|
RefreshCommand = new Command(async () =>
|
||||||
{
|
{
|
||||||
Refreshing = true;
|
Refreshing = true;
|
||||||
@@ -103,7 +104,7 @@ namespace Bit.App.Pages
|
|||||||
get => _showList;
|
get => _showList;
|
||||||
set => SetProperty(ref _showList, value);
|
set => SetProperty(ref _showList, value);
|
||||||
}
|
}
|
||||||
public ExtendedObservableCollection<SendGroupingsPageListGroup> GroupedSends { get; set; }
|
public ObservableRangeCollection<ISendGroupingsPageListItem> GroupedSends { get; set; }
|
||||||
public Command RefreshCommand { get; set; }
|
public Command RefreshCommand { get; set; }
|
||||||
public Command<SendView> SendOptionsCommand { get; set; }
|
public Command<SendView> SendOptionsCommand { get; set; }
|
||||||
public bool LoadedOnce { get; set; }
|
public bool LoadedOnce { get; set; }
|
||||||
@@ -135,7 +136,7 @@ namespace Bit.App.Pages
|
|||||||
ShowNoData = false;
|
ShowNoData = false;
|
||||||
Loading = true;
|
Loading = true;
|
||||||
ShowList = false;
|
ShowList = false;
|
||||||
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
SendEnabled = !await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||||
var groupedSends = new List<SendGroupingsPageListGroup>();
|
var groupedSends = new List<SendGroupingsPageListGroup>();
|
||||||
var page = Page as SendGroupingsPage;
|
var page = Page as SendGroupingsPage;
|
||||||
|
|
||||||
@@ -175,7 +176,49 @@ namespace Bit.App.Pages
|
|||||||
MainPage ? AppResources.AllSends : AppResources.Sends, sendsListItems.Count,
|
MainPage ? AppResources.AllSends : AppResources.Sends, sendsListItems.Count,
|
||||||
uppercaseGroupNames, !MainPage));
|
uppercaseGroupNames, !MainPage));
|
||||||
}
|
}
|
||||||
GroupedSends.ResetWithRange(groupedSends);
|
|
||||||
|
// TODO: refactor this
|
||||||
|
if (Device.RuntimePlatform == Device.Android
|
||||||
|
||
|
||||||
|
GroupedSends.Any())
|
||||||
|
{
|
||||||
|
var items = new List<ISendGroupingsPageListItem>();
|
||||||
|
foreach (var itemGroup in groupedSends)
|
||||||
|
{
|
||||||
|
items.Add(new SendGroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
|
||||||
|
items.AddRange(itemGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupedSends.ReplaceRange(items);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// HACK: we need this on iOS, so that it doesn't crash when adding coming from an empty list
|
||||||
|
var first = true;
|
||||||
|
var items = new List<ISendGroupingsPageListItem>();
|
||||||
|
foreach (var itemGroup in groupedSends)
|
||||||
|
{
|
||||||
|
if (!first)
|
||||||
|
{
|
||||||
|
items.Add(new SendGroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
items.AddRange(itemGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupedSends.Any())
|
||||||
|
{
|
||||||
|
GroupedSends.ReplaceRange(new List<ISendGroupingsPageListItem> { new SendGroupingsPageHeaderListItem(groupedSends[0].Name, groupedSends[0].ItemCount) });
|
||||||
|
GroupedSends.AddRange(items);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GroupedSends.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?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:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
@@ -66,7 +66,8 @@
|
|||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
SelectionChanged="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform"
|
||||||
|
ExtraDataForLogging="Sends Page">
|
||||||
<CollectionView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:SendView">
|
<DataTemplate x:DataType="views:SendView">
|
||||||
<controls:SendViewCell
|
<controls:SendViewCell
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
@@ -12,15 +13,22 @@ namespace Bit.App.Pages
|
|||||||
private SendsPageViewModel _vm;
|
private SendsPageViewModel _vm;
|
||||||
private bool _hasFocused;
|
private bool _hasFocused;
|
||||||
|
|
||||||
public SendsPage(Func<SendView, bool> filter, bool type = false)
|
public SendsPage(Func<SendView, bool> filter, SendType? type = null)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as SendsPageViewModel;
|
_vm = BindingContext as SendsPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_vm.Filter = filter;
|
_vm.Filter = filter;
|
||||||
if (type)
|
if (type != null)
|
||||||
{
|
{
|
||||||
_vm.PageTitle = AppResources.SearchType;
|
if (type == SendType.File)
|
||||||
|
{
|
||||||
|
_vm.PageTitle = AppResources.SearchFileSends;
|
||||||
|
}
|
||||||
|
else if (type == SendType.Text)
|
||||||
|
{
|
||||||
|
_vm.PageTitle = AppResources.SearchTextSends;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -33,6 +41,7 @@ namespace Bit.App.Pages
|
|||||||
_searchBar.Placeholder = AppResources.Search;
|
_searchBar.Placeholder = AppResources.Search;
|
||||||
_mainLayout.Children.Insert(0, _searchBar);
|
_mainLayout.Children.Insert(0, _searchBar);
|
||||||
_mainLayout.Children.Insert(1, _separator);
|
_mainLayout.Children.Insert(1, _separator);
|
||||||
|
ShowModalAnimationDelay = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ namespace Bit.App.Pages
|
|||||||
get => _sendEnabled;
|
get => _sendEnabled;
|
||||||
set => SetProperty(ref _sendEnabled, value);
|
set => SetProperty(ref _sendEnabled, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowNoData
|
public bool ShowNoData
|
||||||
{
|
{
|
||||||
get => _showNoData;
|
get => _showNoData;
|
||||||
set => SetProperty(ref _showNoData, value, additionalPropertyNames: new []
|
set => SetProperty(ref _showNoData, value, additionalPropertyNames: new[]
|
||||||
{
|
{
|
||||||
nameof(ShowSearchDirection)
|
nameof(ShowSearchDirection)
|
||||||
});
|
});
|
||||||
@@ -48,7 +48,7 @@ namespace Bit.App.Pages
|
|||||||
public bool ShowList
|
public bool ShowList
|
||||||
{
|
{
|
||||||
get => _showList;
|
get => _showList;
|
||||||
set => SetProperty(ref _showList, value, additionalPropertyNames: new []
|
set => SetProperty(ref _showList, value, additionalPropertyNames: new[]
|
||||||
{
|
{
|
||||||
nameof(ShowSearchDirection)
|
nameof(ShowSearchDirection)
|
||||||
});
|
});
|
||||||
@@ -58,7 +58,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
|
SendEnabled = !await AppHelpers.IsSendDisabledByPolicyAsync();
|
||||||
if (!string.IsNullOrWhiteSpace((Page as SendsPage).SearchBar.Text))
|
if (!string.IsNullOrWhiteSpace((Page as SendsPage).SearchBar.Text))
|
||||||
{
|
{
|
||||||
Search((Page as SendsPage).SearchBar.Text, 200);
|
Search((Page as SendsPage).SearchBar.Text, 200);
|
||||||
|
|||||||
@@ -49,12 +49,12 @@ namespace Bit.App.Pages
|
|||||||
_vm.ToggleAutofillService();
|
_vm.ToggleAutofillService();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleInlineAutofill(object sender, EventArgs e)
|
private void ToggleInlineAutofill(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
_vm.ToggleInlineAutofill();
|
_vm.ToggleInlineAutofill();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleAccessibility(object sender, EventArgs e)
|
private void ToggleAccessibility(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
@@ -62,7 +62,7 @@ namespace Bit.App.Pages
|
|||||||
_vm.ToggleAccessibility();
|
_vm.ToggleAccessibility();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleDrawOver(object sender, EventArgs e)
|
private void ToggleDrawOver(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly MobileI18nService _i18nService;
|
private readonly MobileI18nService _i18nService;
|
||||||
|
|
||||||
private bool _autofillServiceToggled;
|
private bool _autofillServiceToggled;
|
||||||
private bool _inlineAutofillToggled;
|
private bool _inlineAutofillToggled;
|
||||||
private bool _accessibilityToggled;
|
private bool _accessibilityToggled;
|
||||||
@@ -26,9 +26,9 @@ namespace Bit.App.Pages
|
|||||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
|
||||||
PageTitle = AppResources.AutofillServices;
|
PageTitle = AppResources.AutofillServices;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Autofill Service
|
#region Autofill Service
|
||||||
|
|
||||||
public bool AutofillServiceVisible
|
public bool AutofillServiceVisible
|
||||||
{
|
{
|
||||||
get => _deviceActionService.SystemMajorVersion() >= 26;
|
get => _deviceActionService.SystemMajorVersion() >= 26;
|
||||||
@@ -43,16 +43,16 @@ namespace Bit.App.Pages
|
|||||||
nameof(InlineAutofillEnabled)
|
nameof(InlineAutofillEnabled)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Inline Autofill
|
#region Inline Autofill
|
||||||
|
|
||||||
public bool InlineAutofillVisible
|
public bool InlineAutofillVisible
|
||||||
{
|
{
|
||||||
get => _deviceActionService.SystemMajorVersion() >= 30;
|
get => _deviceActionService.SystemMajorVersion() >= 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool InlineAutofillEnabled
|
public bool InlineAutofillEnabled
|
||||||
{
|
{
|
||||||
get => AutofillServiceToggled;
|
get => AutofillServiceToggled;
|
||||||
@@ -69,9 +69,9 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Accessibility
|
#region Accessibility
|
||||||
|
|
||||||
public string AccessibilityDescriptionLabel
|
public string AccessibilityDescriptionLabel
|
||||||
@@ -97,7 +97,7 @@ namespace Bit.App.Pages
|
|||||||
return _i18nService.T("AccessibilityDescription4");
|
return _i18nService.T("AccessibilityDescription4");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AccessibilityToggled
|
public bool AccessibilityToggled
|
||||||
{
|
{
|
||||||
get => _accessibilityToggled;
|
get => _accessibilityToggled;
|
||||||
@@ -109,9 +109,9 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Draw-Over
|
#region Draw-Over
|
||||||
|
|
||||||
public bool DrawOverVisible
|
public bool DrawOverVisible
|
||||||
{
|
{
|
||||||
get => _deviceActionService.SystemMajorVersion() >= 23;
|
get => _deviceActionService.SystemMajorVersion() >= 23;
|
||||||
@@ -135,12 +135,12 @@ namespace Bit.App.Pages
|
|||||||
return _i18nService.T("DrawOverDescription3");
|
return _i18nService.T("DrawOverDescription3");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DrawOverEnabled
|
public bool DrawOverEnabled
|
||||||
{
|
{
|
||||||
get => AccessibilityToggled;
|
get => AccessibilityToggled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DrawOverToggled
|
public bool DrawOverToggled
|
||||||
{
|
{
|
||||||
get => _drawOverToggled;
|
get => _drawOverToggled;
|
||||||
@@ -148,7 +148,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
InlineAutofillToggled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
InlineAutofillToggled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
|
||||||
@@ -189,14 +189,15 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
_deviceActionService.OpenAccessibilityOverlayPermissionSettings();
|
_deviceActionService.OpenAccessibilityOverlayPermissionSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateEnabled()
|
public void UpdateEnabled()
|
||||||
{
|
{
|
||||||
AutofillServiceToggled = _deviceActionService.AutofillServiceEnabled();
|
AutofillServiceToggled =
|
||||||
|
_deviceActionService.HasAutofillService() && _deviceActionService.AutofillServiceEnabled();
|
||||||
AccessibilityToggled = _deviceActionService.AutofillAccessibilityServiceRunning();
|
AccessibilityToggled = _deviceActionService.AutofillAccessibilityServiceRunning();
|
||||||
DrawOverToggled = _deviceActionService.AutofillAccessibilityOverlayPermitted();
|
DrawOverToggled = _deviceActionService.AutofillAccessibilityOverlayPermitted();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateInlineAutofillToggledAsync()
|
private async Task UpdateInlineAutofillToggledAsync()
|
||||||
{
|
{
|
||||||
if (_inited)
|
if (_inited)
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
AutomationProperties.Name="{Binding PasswordVisibilityAccessibilityText}"
|
||||||
IsVisible="{Binding UseOTPVerification, Converter={StaticResource inverseBool}}"/>
|
IsVisible="{Binding UseOTPVerification, Converter={StaticResource inverseBool}}"/>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n ConfirmYourIdentity}"
|
Text="{u:I18n ConfirmYourIdentity}"
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
|
using Bit.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;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Core;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -77,7 +77,7 @@ namespace Bit.App.Pages
|
|||||||
InstructionText = _i18nService.T("ExportVaultMasterPasswordDescription");
|
InstructionText = _i18nService.T("ExportVaultMasterPasswordDescription");
|
||||||
SecretName = _i18nService.T("MasterPassword");
|
SecretName = _i18nService.T("MasterPassword");
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateWarning();
|
UpdateWarning();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +109,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
get => _showPassword;
|
get => _showPassword;
|
||||||
set => SetProperty(ref _showPassword, value,
|
set => SetProperty(ref _showPassword, value,
|
||||||
additionalPropertyNames: new string[] {nameof(ShowPasswordIcon)});
|
additionalPropertyNames: new string[]
|
||||||
|
{
|
||||||
|
nameof(ShowPasswordIcon),
|
||||||
|
nameof(PasswordVisibilityAccessibilityText),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UseOTPVerification
|
public bool UseOTPVerification
|
||||||
@@ -139,6 +143,8 @@ namespace Bit.App.Pages
|
|||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
|
|
||||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||||
|
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.VisibilityTogglePasswordIsVisibleActivateToHide :
|
||||||
|
AppResources.VisibilityTogglePasswordIsNotVisibleActivateToHide;
|
||||||
|
|
||||||
public void TogglePassword()
|
public void TogglePassword()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Bit.App.Resources;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Bit.App.Resources;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
using Bit.App.Resources;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using Bit.App.Abstractions;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
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.Threading.Tasks;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
|
|||||||
@@ -38,7 +38,8 @@
|
|||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
SelectionChanged="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform"
|
||||||
|
ExtraDataForLogging="Folders Page">
|
||||||
<CollectionView.ItemTemplate>
|
<CollectionView.ItemTemplate>
|
||||||
<DataTemplate x:DataType="views:FolderView">
|
<DataTemplate x:DataType="views:FolderView">
|
||||||
<controls:ExtendedStackLayout
|
<controls:ExtendedStackLayout
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Bit.Core.Models.View;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
|
using Bit.Core.Models.View;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using Bit.App.Resources;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using Bit.App.Resources;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
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;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public interface ISettingsPageListItem
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -82,8 +82,26 @@
|
|||||||
</controls:ExtendedStackLayout>
|
</controls:ExtendedStackLayout>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate
|
||||||
|
x:Key="headerTemplate"
|
||||||
|
x:DataType="pages:SettingsPageHeaderListItem">
|
||||||
|
<StackLayout
|
||||||
|
Padding="0" Spacing="0" VerticalOptions="FillAndExpand"
|
||||||
|
StyleClass="list-row-header-container, list-row-header-container-platform">
|
||||||
|
<BoxView
|
||||||
|
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
|
||||||
|
<StackLayout StyleClass="list-row-header, list-row-header-platform">
|
||||||
|
<Label
|
||||||
|
Text="{Binding Title}"
|
||||||
|
StyleClass="list-header, list-header-platform" />
|
||||||
|
</StackLayout>
|
||||||
|
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
|
||||||
|
</StackLayout>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
<pages:SettingsPageListItemSelector
|
<pages:SettingsPageListItemSelector
|
||||||
x:Key="listItemDataTemplateSelector"
|
x:Key="listItemDataTemplateSelector"
|
||||||
|
HeaderTemplate="{StaticResource headerTemplate}"
|
||||||
RegularTemplate="{StaticResource regularTemplate}"
|
RegularTemplate="{StaticResource regularTemplate}"
|
||||||
TimePickerTemplate="{StaticResource timePickerTemplate}" />
|
TimePickerTemplate="{StaticResource timePickerTemplate}" />
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
@@ -93,28 +111,9 @@
|
|||||||
ItemsSource="{Binding GroupedItems}"
|
ItemsSource="{Binding GroupedItems}"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
|
||||||
IsGrouped="True"
|
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
SelectionChanged="RowSelected"
|
SelectionChanged="RowSelected"
|
||||||
StyleClass="list, list-platform">
|
StyleClass="list, list-platform"
|
||||||
|
ExtraDataForLogging="Settings Page" />
|
||||||
<CollectionView.GroupHeaderTemplate>
|
|
||||||
<DataTemplate x:DataType="pages:SettingsPageListGroup">
|
|
||||||
<StackLayout
|
|
||||||
Padding="0" Spacing="0" VerticalOptions="FillAndExpand"
|
|
||||||
StyleClass="list-row-header-container, list-row-header-container-platform">
|
|
||||||
<BoxView
|
|
||||||
StyleClass="list-section-separator-top, list-section-separator-top-platform"
|
|
||||||
IsVisible="{Binding First, Converter={StaticResource inverseBool}}" />
|
|
||||||
<StackLayout StyleClass="list-row-header, list-row-header-platform">
|
|
||||||
<Label
|
|
||||||
Text="{Binding Name}"
|
|
||||||
StyleClass="list-header, list-header-platform" />
|
|
||||||
</StackLayout>
|
|
||||||
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
|
|
||||||
</StackLayout>
|
|
||||||
</DataTemplate>
|
|
||||||
</CollectionView.GroupHeaderTemplate>
|
|
||||||
</controls:ExtendedCollectionView>
|
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
async void OnTimePickerPropertyChanged(object sender, PropertyChangedEventArgs args)
|
async void OnTimePickerPropertyChanged(object sender, PropertyChangedEventArgs args)
|
||||||
{
|
{
|
||||||
var s = (TimePicker) sender;
|
var s = (TimePicker)sender;
|
||||||
var time = s.Time.TotalMinutes;
|
var time = s.Time.TotalMinutes;
|
||||||
if (s.IsFocused && args.PropertyName == "Time")
|
if (s.IsFocused && args.PropertyName == "Time")
|
||||||
{
|
{
|
||||||
@@ -167,6 +167,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _vm.UpdatePinAsync();
|
await _vm.UpdatePinAsync();
|
||||||
}
|
}
|
||||||
|
else if (item.Name == AppResources.SubmitCrashLogs)
|
||||||
|
{
|
||||||
|
await _vm.LoggerReportingAsync();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var biometricName = AppResources.Biometrics;
|
var biometricName = AppResources.Biometrics;
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class SettingsPageHeaderListItem : ISettingsPageListItem
|
||||||
|
{
|
||||||
|
public SettingsPageHeaderListItem(string title)
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Title { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user