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

Compare commits

..

4 Commits

Author SHA1 Message Date
Andre Rosado
7500073e7e Removed logs, added null or whitespace validation and improved formatting 2023-01-03 10:58:14 +00:00
aj-rosado
6b77fee72b Merge branch 'master' into fix-ignore-diacritics-in-search 2022-12-29 14:16:26 +00:00
noncenz
a0e62771fd Merge branch 'master' into fix-ignore-diacritics-in-search 2022-08-22 17:03:57 -04:00
noncenz
733464e05d Fix ignore diacritics in search
This change updates the search function to ignore diacritical marks in search results. Marks are stripped from both the search input and results.
2022-08-22 16:26:45 -04:00
415 changed files with 8002 additions and 34980 deletions

View File

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

19
.github/labeler.yml vendored
View File

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

57
.github/renovate.json vendored
View File

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

View File

@@ -7,13 +7,13 @@
<key>provisioningProfiles</key>
<dict>
<key>com.8bit.bitwarden</key>
<string>Dist: Bitwarden</string>
<string>Dist: Bitwarden 2021</string>
<key>com.8bit.bitwarden.autofill</key>
<string>Dist: Autofill</string>
<string>Dist: Autofill 2021</string>
<key>com.8bit.bitwarden.find-login-action-extension</key>
<string>Dist: Extension</string>
<string>Dist: Extension 2021</string>
<key>com.8bit.bitwarden.share-extension</key>
<string>Dist: Share Extension</string>
<string>Dist: Share Extension 2021</string>
<key>com.8bit.bitwarden.watchkitapp</key>
<string>Dist: Bitwarden Watch App</string>
<key>com.8bit.bitwarden.watchkitapp.watchkitextension</key>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -14,7 +14,7 @@ jobs:
# Feature request
- if: github.event.label.name == 'feature-request'
name: Feature request
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with:
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.
@@ -25,7 +25,7 @@ jobs:
# Intended behavior
- if: github.event.label.name == 'intended-behavior'
name: Intended behaviour
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1
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.
@@ -38,7 +38,7 @@ jobs:
# Customer support request
- if: github.event.label.name == 'customer-support'
name: Customer Support request
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1
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.
@@ -49,14 +49,14 @@ jobs:
# Resolved
- if: github.event.label.name == 'resolved'
name: Resolved
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with:
comment: |
Weve closed this issue, as it appears the original problem has been resolved. If this happens again or continues to be an problem, please respond to this issue with any additional detail to assist with reproduction and root cause analysis.
# Stale
- if: github.event.label.name == 'stale'
name: Stale
uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1
uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0
with:
comment: |
As we havent heard from you about this problem in some time, this issue will now be closed.

View File

@@ -4,10 +4,10 @@ name: Build
on:
push:
branches-ignore:
- "l10n_master"
- "gh-pages"
- 'l10n_master'
- 'gh-pages'
paths-ignore:
- ".github/workflows/**"
- '.github/workflows/**'
workflow_dispatch:
inputs: {}
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Set up CLOC
run: |
@@ -36,9 +36,7 @@ jobs:
hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }}
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
with:
submodules: 'true'
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Check if special branches exist
id: branch-check
@@ -64,24 +62,16 @@ jobs:
strategy:
fail-fast: false
matrix:
variant: ["prod", "qa"]
variant: ['prod', 'qa']
steps:
- name: Setup NuGet
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
with:
nuget-version: 5.9.0
- name: Set up .NET
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
with:
dotnet-version: '3.1.x'
- name: Set up MSBuild
uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1
- name: Setup Windows builder
run: choco install checksum --no-progress
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
- name: Work Around for broken Windows 2022 Runner Image
run: |
Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
@@ -110,7 +100,7 @@ jobs:
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
with:
fetch-depth: 0
- name: Decrypt secrets
@@ -158,17 +148,7 @@ jobs:
shell: pwsh
- name: Run Core tests
run: dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx"
shell: pwsh
- name: Report test results
uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 # v1.6.0
if: always()
with:
name: Test Results
path: "**/test-results.trx"
reporter: dotnet-trx
fail-on-error: true
run: dotnet test test/Core.Test/Core.Test.csproj
- name: Build Play Store publisher
if: ${{ matrix.variant == 'prod' }}
@@ -195,8 +175,8 @@ jobs:
run: |
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
$packageName = "com.x8bit.bitwarden";
if ("${{ matrix.variant }}" -ne "prod")
if ("${{ matrix.variant }}" -ne "prod")
{
$packageName = "com.x8bit.bitwarden.${{ matrix.variant }}";
}
@@ -237,7 +217,7 @@ jobs:
shell: pwsh
- name: Upload Prod .aab artifact
if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: com.x8bit.bitwarden.aab
path: ./com.x8bit.bitwarden.aab
@@ -245,7 +225,7 @@ jobs:
- name: Upload Prod .apk artifact
if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: com.x8bit.bitwarden.apk
path: ./com.x8bit.bitwarden.apk
@@ -253,40 +233,12 @@ jobs:
- name: Upload Other .apk artifact
if: ${{ matrix.variant != 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: com.x8bit.bitwarden.${{ matrix.variant }}.apk
path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk
if-no-files-found: error
- name: Create checksum for Prod .apk artifact
if: ${{ matrix.variant == 'prod' }}
run: |
checksum -f="./com.x8bit.bitwarden.apk" `
-t sha256 | Out-File -Encoding ASCII ./bw-android-apk-sha256.txt
- name: Create checksum for Other .apk artifact
if: ${{ matrix.variant != 'prod' }}
run: |
checksum -f="./com.x8bit.bitwarden.${{ matrix.variant }}.apk" `
-t sha256 | Out-File -Encoding ASCII ./bw-android-${{ matrix.variant }}-apk-sha256.txt
- name: Upload .apk sha file for prod
if: ${{ matrix.variant == 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
with:
name: bw-android-apk-sha256.txt
path: ./bw-android-apk-sha256.txt
if-no-files-found: error
- name: Upload .apk sha file for other
if: ${{ matrix.variant != 'prod' }}
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
with:
name: bw-android-${{ matrix.variant }}-apk-sha256.txt
path: ./bw-android-${{ matrix.variant }}-apk-sha256.txt
if-no-files-found: error
- name: Deploy to Play Store
if: ${{ matrix.variant == 'prod' && (( github.ref == 'refs/heads/master'
&& needs.setup.outputs.rc_branch_exists == 0
@@ -308,15 +260,12 @@ jobs:
runs-on: windows-2022
steps:
- name: Setup NuGet
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
with:
nuget-version: 5.9.0
- name: Set up MSBuild
uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1
- name: Setup Windows builder
run: choco install checksum --no-progress
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
- name: Work Around for broken Windows 2022 Runner Image
run: |
@@ -347,7 +296,7 @@ jobs:
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Decrypt secrets
env:
@@ -482,32 +431,20 @@ jobs:
shell: pwsh
- name: Upload F-Droid .apk artifact
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: com.x8bit.bitwarden-fdroid.apk
path: ./com.x8bit.bitwarden-fdroid.apk
if-no-files-found: error
- name: Create checksum for F-Droid artifact
run: |
checksum -f="./com.x8bit.bitwarden-fdroid.apk" `
-t sha256 | Out-File -Encoding ASCII ./bw-fdroid-apk-sha256.txt
- name: Upload F-Droid sha file
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
with:
name: bw-fdroid-apk-sha256.txt
path: ./bw-fdroid-apk-sha256.txt
if-no-files-found: error
ios:
name: Apple iOS
runs-on: macos-12
runs-on: macos-11
needs: setup
steps:
- name: Setup NuGet
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
with:
nuget-version: 5.9.0
@@ -520,19 +457,17 @@ jobs:
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
with:
submodules: 'true'
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Login to Azure - CI Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
- name: Login to Azure - Prod Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
env:
KEYVAULT: bitwarden-ci
KEYVAULT: bitwarden-prod-kv
SECRETS: |
appcenter-ios-token
run: |
@@ -568,8 +503,6 @@ jobs:
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_watch_app_extension.mobileprovision \
./.github/secrets/dist_watch_app_extension.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/watchOS/bitwarden/GoogleService-Info.plist ./.github/secrets/GoogleService-Info.plist.gpg
shell: bash
- name: Increment version
@@ -651,12 +584,15 @@ jobs:
echo "########################################"
echo "##### Build WatchApp with Release Configuration"
echo "########################################"
xcodebuild archive -workspace ./src/watchOS/bitwarden/bitwarden.xcodeproj/project.xcworkspace -configuration Release -scheme bitwarden\ WatchKit\ App -archivePath ./src/watchOS/bitwarden
echo "########################################"
echo "##### Done"
echo "########################################"
cd src/watchOS
ls -R
cd ../..
shell: bash
- name: Restore packages
@@ -694,16 +630,11 @@ jobs:
ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
EXPORT_PATH="./bitwarden-export"
WATCH_ARCHIVE_DSYMS_PATH="./src/watchOS/bitwarden.xcarchive/dSYMs/"
WATCH_DSYMS_EXPORT_PATH="$EXPORT_PATH/Watch_dSYMs"
cp -r -v $ARCHIVE_DSYMS_PATH $EXPORT_PATH
mkdir $WATCH_DSYMS_EXPORT_PATH
cp -r -v $WATCH_ARCHIVE_DSYMS_PATH $WATCH_DSYMS_EXPORT_PATH
cp -r $ARCHIVE_DSYMS_PATH $EXPORT_PATH
shell: bash
- name: Upload App Store .ipa & dSYMs artifacts
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden iOS
path: |
@@ -722,39 +653,23 @@ jobs:
- name: Upload dSYMs to App Center
if: |
(github.ref == 'refs/heads/master'
&& needs.setup.outputs.rc_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/hotfix-rc'
(github.ref == 'refs/heads/master'
&& needs.setup.outputs.rc_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/hotfix-rc'
env:
APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }}
run: appcenter crashes upload-symbols -a bitwarden/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
shell: bash
- name: Upload Watch dSYMs to Firebase Crashlytics
if: |
(github.ref == 'refs/heads/master'
&& needs.setup.outputs.rc_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/hotfix-rc'
run: |
echo "########################################"
echo "##### Uploading Watch dSYMs to Firebase"
echo "########################################"
find "$HOME/Library/Developer/XCode/DerivedData" -name "upload-symbols" -exec chmod +x {} \; -exec {} -gsp "./src/watchOS/bitwarden/GoogleService-Info.plist" -p ios "./bitwarden-export/Watch_dSYMs" \;
shell: bash
- name: Deploy to App Store
if: |
(github.ref == 'refs/heads/master'
&& needs.setup.outputs.rc_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/hotfix-rc'
(github.ref == 'refs/heads/master'
&& needs.setup.outputs.rc_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/hotfix-rc'
env:
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
@@ -776,17 +691,17 @@ jobs:
_CROWDIN_PROJECT_ID: "269690"
steps:
- name: Checkout repo
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Login to Azure - CI Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
- name: Login to Azure
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
env:
KEYVAULT: bitwarden-ci
KEYVAULT: bitwarden-prod-kv
SECRETS: |
crowdin-api-token
run: |
@@ -798,7 +713,7 @@ jobs:
done
- name: Upload Sources
uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0
uses: crowdin/github-action@9237b4cb361788dfce63feb2e2f15c09e2fe7415
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
@@ -822,9 +737,9 @@ jobs:
steps:
- name: Check if any job failed
if: |
(github.ref == 'refs/heads/master')
|| (github.ref == 'refs/heads/rc')
|| (github.ref == 'refs/heads/hotfix-rc')
(github.ref == 'refs/heads/master')
|| (github.ref == 'refs/heads/rc')
|| (github.ref == 'refs/heads/hotfix-rc')
env:
CLOC_STATUS: ${{ needs.cloc.result }}
ANDROID_STATUS: ${{ needs.android.result }}
@@ -844,17 +759,17 @@ jobs:
exit 1
fi
- name: Login to Azure - CI Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
- name: Login to Azure - Prod Subscription
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
if: failure()
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
if: failure()
env:
KEYVAULT: bitwarden-ci
KEYVAULT: bitwarden-prod-kv
SECRETS: |
devops-alerts-slack-webhook-url
run: |
@@ -866,7 +781,7 @@ jobs:
done
- name: Notify Slack on failure
uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0
uses: act10ns/slack@da3191ebe2e67f49b46880b4633f5591a96d1d33
if: failure()
env:
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

1
.gitignore vendored
View File

@@ -30,7 +30,6 @@ Components/
[Rr]eleases/
x64/
x86/
!src/lib/x86/
build/
bld/
[Bb]in/

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "lib/MessagePack"]
path = lib/MessagePack
url = https://github.com/bitwarden/MessagePack.git

View File

@@ -23,3 +23,15 @@ Interested in contributing in a big way? Consider joining our team! We're hiring
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.
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

View File

@@ -1,11 +0,0 @@
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_41_29)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M70.214 34C70.7096 34 71.1457 34.1883 71.5124 34.555C71.8791 34.9217 72.0674 35.3479 72.0971 35.8534V50.4336H71.0669V51.9453H72.0971V58.0938C72.0971 59.749 71.7701 61.3942 71.1258 63.0295C70.4816 64.6549 69.6788 66.1019 68.7274 67.3706C67.766 68.6293 66.6262 69.8582 65.308 71.0575C63.98 72.2567 62.7609 73.2478 61.6409 74.0407C60.521 74.8237 59.3515 75.5769 58.1324 76.2806C56.9134 76.9843 56.0511 77.4699 55.5357 77.7177C55.0303 77.9655 54.614 78.1538 54.3068 78.2926C54.0788 78.4115 53.8211 78.471 53.5535 78.471C53.2859 78.471 53.0282 78.4115 52.8003 78.2926C52.5297 78.1791 52.1822 78.0118 51.7511 77.8042C51.6927 77.7761 51.6328 77.7473 51.5713 77.7177C51.0559 77.46 50.1937 76.9843 48.9746 76.2806C47.7555 75.5769 46.586 74.8336 45.4661 74.0407C44.3461 73.2478 43.1172 72.2567 41.799 71.0575C40.4709 69.8682 39.3311 68.6392 38.3797 67.3706C37.4183 66.1119 36.6155 64.6648 35.9713 63.0295C35.3271 61.4041 35 59.749 35 58.0938V35.8534C35 35.3479 35.1883 34.9217 35.555 34.555C35.9217 34.1883 36.3479 34 36.8534 34H70.214ZM67.211 57.5H70.1118V59H67.177C66.4282 66.7468 53.5337 73.2875 53.5337 73.2875V38.7573H67.211V50.4336H70.1118V51.9219H67.211V53.8027H69.895V55.291H67.211V57.5Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M58 46C56.3431 46 55 47.3431 55 49V60C55 61.6569 56.3431 63 58 63H107C108.657 63 110 61.6569 110 60V49C110 47.3431 108.657 46 107 46H58ZM59.7817 50.4336H57.1157V59H60.3208C60.9692 59 61.5278 58.9023 61.9966 58.707C62.4692 58.5078 62.8325 58.2227 63.0864 57.8516C63.3403 57.4805 63.4673 57.0352 63.4673 56.5156C63.4673 56.0664 63.397 55.707 63.2563 55.4375C63.1196 55.1641 62.936 54.957 62.7056 54.8164C62.4751 54.6719 62.2173 54.5703 61.9321 54.5117V54.4531C62.2134 54.4023 62.4517 54.293 62.647 54.125C62.8423 53.957 62.9907 53.7422 63.0923 53.4805C63.1978 53.2188 63.2505 52.9258 63.2505 52.6016C63.2505 51.7969 62.9575 51.2344 62.3716 50.9141C61.7856 50.5938 60.9224 50.4336 59.7817 50.4336ZM59.9868 53.8262H58.9321V51.9219H59.8872C60.4067 51.9219 60.7856 51.9941 61.0239 52.1387C61.2661 52.2793 61.3872 52.5137 61.3872 52.8418C61.3872 53.166 61.2856 53.4121 61.0825 53.5801C60.8794 53.7441 60.5142 53.8262 59.9868 53.8262ZM58.9321 57.5V55.2676H60.0571C60.4438 55.2676 60.7466 55.3125 60.9653 55.4023C61.188 55.4922 61.3462 55.6172 61.4399 55.7773C61.5337 55.9375 61.5806 56.123 61.5806 56.334C61.5806 56.6895 61.4731 56.9727 61.2583 57.1836C61.0435 57.3945 60.6626 57.5 60.1157 57.5H58.9321ZM65.1782 59H70.1118V57.5H66.9946V55.291H69.895V53.8027H66.9946V51.9219H70.1118V50.4336H65.1782V59ZM73.3931 59H75.2095V51.9453H77.5356V50.4336H71.0669V51.9453H73.3931V59ZM83.4771 56.9609L84.0981 59H86.0552L83.02 50.3984H80.7993L77.7759 59H79.7329L80.354 56.9609H83.4771ZM82.4224 53.4453L83.0435 55.4375H80.811L81.4263 53.4453C81.4536 53.3555 81.4985 53.2051 81.561 52.9941C81.6235 52.7832 81.688 52.5605 81.7544 52.3262C81.8247 52.0879 81.8794 51.8887 81.9185 51.7285C81.9575 51.8887 82.0083 52.0781 82.0708 52.2969C82.1372 52.5117 82.2017 52.7246 82.2642 52.9355C82.3306 53.1426 82.3833 53.3125 82.4224 53.4453Z" fill="#6795E8"/>
</g>
<defs>
<clipPath id="clip0_41_29">
<rect width="108" height="108" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -1,17 +0,0 @@
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_36_10)">
<path d="M71.5717 34.6343L113 76.0625L89.709 119.077L40.6492 70.0168L53.5336 77.4501L70.8779 60.1057L71.5717 34.6343Z" fill="url(#paint0_linear_36_10)"/>
<path d="M71.5124 34.555C71.1457 34.1883 70.7096 34 70.214 34H36.8534C36.3479 34 35.9217 34.1883 35.555 34.555C35.1883 34.9217 35 35.3479 35 35.8534V58.0938C35 59.749 35.3271 61.4041 35.9713 63.0295C36.6155 64.6648 37.4183 66.1119 38.3797 67.3706C39.3311 68.6392 40.4709 69.8682 41.799 71.0575C43.1172 72.2567 44.3461 73.2478 45.4661 74.0407C46.586 74.8336 47.7555 75.5769 48.9746 76.2806C50.1937 76.9843 51.0559 77.46 51.5713 77.7177C52.0867 77.9655 52.493 78.1637 52.8003 78.2926C53.0282 78.4115 53.2859 78.471 53.5535 78.471C53.8211 78.471 54.0788 78.4115 54.3068 78.2926C54.614 78.1538 55.0303 77.9655 55.5357 77.7177C56.0511 77.4699 56.9134 76.9843 58.1324 76.2806C59.3515 75.5769 60.521 74.8237 61.6409 74.0407C62.7609 73.2478 63.98 72.2567 65.308 71.0575C66.6262 69.8582 67.766 68.6293 68.7274 67.3706C69.6788 66.1019 70.4816 64.6549 71.1258 63.0295C71.7701 61.3942 72.0971 59.749 72.0971 58.0938V35.8534C72.0674 35.3479 71.8791 34.9217 71.5124 34.555ZM67.211 58.3019C67.211 66.3497 53.5337 73.2875 53.5337 73.2875V38.7573H67.211C67.211 38.7573 67.211 50.2542 67.211 58.3019Z" fill="white"/>
<path d="M55 49C55 47.3431 56.3431 46 58 46H107C108.657 46 110 47.3431 110 49V60C110 61.6569 108.657 63 107 63H58C56.3431 63 55 61.6569 55 60V49Z" fill="#6795E8"/>
<path d="M57.116 50.4336H59.782C60.9226 50.4336 61.7859 50.5938 62.3718 50.9141C62.9578 51.2344 63.2507 51.7969 63.2507 52.6016C63.2507 52.9258 63.198 53.2188 63.0925 53.4805C62.991 53.7422 62.8425 53.957 62.6472 54.125C62.4519 54.293 62.2136 54.4023 61.9324 54.4531V54.5117C62.2175 54.5703 62.4753 54.6719 62.7058 54.8164C62.9363 54.957 63.1199 55.1641 63.2566 55.4375C63.3972 55.707 63.4675 56.0664 63.4675 56.5156C63.4675 57.0352 63.3406 57.4805 63.0867 57.8516C62.8328 58.2227 62.4695 58.5078 61.9968 58.707C61.5281 58.9023 60.9695 59 60.321 59H57.116V50.4336ZM58.9324 53.8262H59.9871C60.5144 53.8262 60.8796 53.7441 61.0828 53.5801C61.2859 53.4121 61.3875 53.166 61.3875 52.8418C61.3875 52.5137 61.2664 52.2793 61.0242 52.1387C60.7859 51.9941 60.407 51.9219 59.8875 51.9219H58.9324V53.8262ZM58.9324 55.2676V57.5H60.116C60.6628 57.5 61.0437 57.3945 61.2585 57.1836C61.4734 56.9727 61.5808 56.6895 61.5808 56.334C61.5808 56.123 61.5339 55.9375 61.4402 55.7773C61.3464 55.6172 61.1882 55.4922 60.9656 55.4023C60.7468 55.3125 60.4441 55.2676 60.0574 55.2676H58.9324ZM70.1121 59H65.1785V50.4336H70.1121V51.9219H66.9949V53.8027H69.8953V55.291H66.9949V57.5H70.1121V59ZM75.2097 59H73.3933V51.9453H71.0671V50.4336H77.5359V51.9453H75.2097V59ZM84.0984 59L83.4773 56.9609H80.3542L79.7332 59H77.7761L80.7996 50.3984H83.0203L86.0554 59H84.0984ZM83.0437 55.4375L82.4226 53.4453C82.3835 53.3125 82.3308 53.1426 82.2644 52.9355C82.2019 52.7246 82.1375 52.5117 82.071 52.2969C82.0085 52.0781 81.9578 51.8887 81.9187 51.7285C81.8796 51.8887 81.825 52.0879 81.7546 52.3262C81.6882 52.5605 81.6238 52.7832 81.5613 52.9941C81.4988 53.2051 81.4539 53.3555 81.4265 53.4453L80.8113 55.4375H83.0437Z" fill="#212529"/>
</g>
<defs>
<linearGradient id="paint0_linear_36_10" x1="37.8512" y1="38.8122" x2="89.011" y2="89.972" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0.247059"/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<clipPath id="clip0_36_10">
<rect width="108" height="108" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -1,11 +0,0 @@
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_41_21)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M70.214 34C70.7096 34 71.1457 34.1883 71.5124 34.555C71.8791 34.9217 72.0674 35.3479 72.0971 35.8534V50.4336H71.647L72.0971 51.7604V58.0938C72.0971 59.749 71.7701 61.3942 71.1258 63.0295C70.4816 64.6549 69.6788 66.1019 68.7274 67.3706C67.766 68.6293 66.6262 69.8582 65.308 71.0575C63.98 72.2567 62.7609 73.2478 61.6409 74.0407C60.521 74.8237 59.3515 75.5769 58.1324 76.2806C56.9134 76.9843 56.0511 77.4699 55.5357 77.7177C55.0303 77.9655 54.614 78.1538 54.3068 78.2926C54.0788 78.4115 53.8211 78.471 53.5535 78.471C53.2859 78.471 53.0282 78.4115 52.8003 78.2926C52.5297 78.1791 52.1822 78.0118 51.7511 77.8042C51.6927 77.7761 51.6328 77.7473 51.5713 77.7177C51.0559 77.46 50.1937 76.9843 48.9746 76.2806C47.7555 75.5769 46.586 74.8336 45.4661 74.0407C44.3461 73.2478 43.1172 72.2567 41.799 71.0575C40.4709 69.8682 39.3311 68.6392 38.3797 67.3706C37.4183 66.1119 36.6155 64.6648 35.9713 63.0295C35.3271 61.4041 35 59.749 35 58.0938V35.8534C35 35.3479 35.1883 34.9217 35.555 34.555C35.9217 34.1883 36.3479 34 36.8534 34H70.214ZM67.177 59C66.4282 66.7468 53.5337 73.2875 53.5337 73.2875V38.7573H67.211V50.4336H70.9321V51.9219H67.8149V53.8027H70.7153V55.291H67.8149V57.5H70.9321V59H67.177Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M58 46C56.3431 46 55 47.3431 55 49V60C55 61.6569 56.3431 63 58 63H107C108.657 63 110 61.6569 110 60V49C110 47.3431 108.657 46 107 46H58ZM63.6665 57.0547C64.0376 56.4062 64.2231 55.5996 64.2231 54.6348C64.2231 53.7168 64.0415 52.9473 63.6782 52.3262C63.3149 51.7012 62.8032 51.2305 62.1431 50.9141C61.4829 50.5938 60.7036 50.4336 59.8052 50.4336H57.1157V59H59.5415C60.5259 59 61.3677 58.8379 62.0669 58.5137C62.7661 58.1855 63.2993 57.6992 63.6665 57.0547ZM62.0552 53.123C62.2427 53.5293 62.3364 54.0488 62.3364 54.6816C62.3364 55.6152 62.1196 56.3184 61.686 56.791C61.2563 57.2637 60.5981 57.5 59.7114 57.5H58.9321V51.9219H59.8989C60.4302 51.9219 60.8755 52.0195 61.2349 52.2148C61.5981 52.4102 61.8716 52.7129 62.0552 53.123ZM65.9985 59H70.9321V57.5H67.8149V55.291H70.7153V53.8027H67.8149V51.9219H70.9321V50.4336H65.9985V59ZM76.5337 59L79.4458 50.4336H77.6118L75.9888 55.5312C75.9614 55.6211 75.9165 55.7852 75.854 56.0234C75.7954 56.2578 75.7349 56.5059 75.6724 56.7676C75.6138 57.0293 75.5728 57.2461 75.5493 57.418C75.5259 57.2461 75.481 57.0293 75.4146 56.7676C75.3521 56.502 75.2896 56.252 75.2271 56.0176C75.1646 55.7793 75.1196 55.6172 75.0923 55.5312L73.481 50.4336H71.647L74.5532 59H76.5337Z" fill="#2DA49D"/>
</g>
<defs>
<clipPath id="clip0_41_21">
<rect width="108" height="108" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -1,17 +0,0 @@
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_36_2)">
<path d="M71.5717 34.6343L113 76.0625L89.709 119.077L40.6492 70.0168L53.5336 77.4501L70.8779 60.1057L71.5717 34.6343Z" fill="url(#paint0_linear_36_2)"/>
<path d="M71.5124 34.555C71.1457 34.1883 70.7096 34 70.214 34H36.8534C36.3479 34 35.9217 34.1883 35.555 34.555C35.1883 34.9217 35 35.3479 35 35.8534V58.0938C35 59.749 35.3271 61.4041 35.9713 63.0295C36.6155 64.6648 37.4183 66.1119 38.3797 67.3706C39.3311 68.6392 40.4709 69.8682 41.799 71.0575C43.1172 72.2567 44.3461 73.2478 45.4661 74.0407C46.586 74.8336 47.7555 75.5769 48.9746 76.2806C50.1937 76.9843 51.0559 77.46 51.5713 77.7177C52.0867 77.9655 52.493 78.1637 52.8003 78.2926C53.0282 78.4115 53.2859 78.471 53.5535 78.471C53.8211 78.471 54.0788 78.4115 54.3068 78.2926C54.614 78.1538 55.0303 77.9655 55.5357 77.7177C56.0511 77.4699 56.9134 76.9843 58.1324 76.2806C59.3515 75.5769 60.521 74.8237 61.6409 74.0407C62.7609 73.2478 63.98 72.2567 65.308 71.0575C66.6262 69.8582 67.766 68.6293 68.7274 67.3706C69.6788 66.1019 70.4816 64.6549 71.1258 63.0295C71.7701 61.3942 72.0971 59.749 72.0971 58.0938V35.8534C72.0674 35.3479 71.8791 34.9217 71.5124 34.555ZM67.211 58.3019C67.211 66.3497 53.5337 73.2875 53.5337 73.2875V38.7573H67.211C67.211 38.7573 67.211 50.2542 67.211 58.3019Z" fill="white"/>
<path d="M55 49C55 47.3431 56.3431 46 58 46H107C108.657 46 110 47.3431 110 49V60C110 61.6569 108.657 63 107 63H58C56.3431 63 55 61.6569 55 60V49Z" fill="#2DA49D"/>
<path d="M64.2234 54.6348C64.2234 55.5996 64.0378 56.4062 63.6667 57.0547C63.2996 57.6992 62.7664 58.1855 62.0671 58.5137C61.3679 58.8379 60.5261 59 59.5417 59H57.116V50.4336H59.8054C60.7039 50.4336 61.4832 50.5938 62.1433 50.9141C62.8035 51.2305 63.3152 51.7012 63.6785 52.3262C64.0417 52.9473 64.2234 53.7168 64.2234 54.6348ZM62.3367 54.6816C62.3367 54.0488 62.2429 53.5293 62.0554 53.123C61.8718 52.7129 61.5984 52.4102 61.2351 52.2148C60.8757 52.0195 60.4304 51.9219 59.8992 51.9219H58.9324V57.5H59.7117C60.5984 57.5 61.2566 57.2637 61.6863 56.791C62.1199 56.3184 62.3367 55.6152 62.3367 54.6816ZM70.9324 59H65.9988V50.4336H70.9324V51.9219H67.8152V53.8027H70.7156V55.291H67.8152V57.5H70.9324V59ZM79.446 50.4336L76.5339 59H74.5535L71.6472 50.4336H73.4812L75.0925 55.5312C75.1199 55.6172 75.1648 55.7793 75.2273 56.0176C75.2898 56.252 75.3523 56.502 75.4148 56.7676C75.4812 57.0293 75.5261 57.2461 75.5496 57.418C75.573 57.2461 75.614 57.0293 75.6726 56.7676C75.7351 56.5059 75.7957 56.2578 75.8542 56.0234C75.9167 55.7852 75.9617 55.6211 75.989 55.5312L77.6121 50.4336H79.446Z" fill="#212529"/>
</g>
<defs>
<linearGradient id="paint0_linear_36_2" x1="37.8512" y1="38.8122" x2="89.011" y2="89.972" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0.247059"/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<clipPath id="clip0_36_2">
<rect width="108" height="108" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -1,11 +0,0 @@
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_41_13)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M70.214 34C70.7096 34 71.1457 34.1883 71.5124 34.555C71.8791 34.9217 72.0674 35.3479 72.0971 35.8534V52.9823L70.8325 49.3984H68.6118L67.211 53.3838V38.7573H53.5337V73.2875C53.5337 73.2875 67.211 66.3497 67.211 58.3019V58H67.5454L68.1665 55.9609H71.2896L71.9106 58H72.0971V58.0938C72.0971 59.749 71.7701 61.3942 71.1258 63.0295C70.4816 64.6549 69.6788 66.1019 68.7274 67.3706C67.766 68.6293 66.6262 69.8582 65.308 71.0575C63.98 72.2567 62.7609 73.2478 61.6409 74.0407C60.521 74.8237 59.3515 75.5769 58.1324 76.2806C56.9134 76.9843 56.0511 77.4699 55.5357 77.7177C55.0303 77.9655 54.614 78.1538 54.3068 78.2926C54.0788 78.4115 53.8211 78.471 53.5535 78.471C53.2859 78.471 53.0282 78.4115 52.8003 78.2926C52.5297 78.1791 52.1822 78.0118 51.7511 77.8042C51.6927 77.7761 51.6328 77.7473 51.5713 77.7177C51.0559 77.46 50.1937 76.9843 48.9746 76.2806C47.7555 75.5769 46.586 74.8336 45.4661 74.0407C44.3461 73.2478 43.1172 72.2567 41.799 71.0575C40.4709 69.8682 39.3311 68.6392 38.3797 67.3706C37.4183 66.1119 36.6155 64.6648 35.9713 63.0295C35.3271 61.4041 35 59.749 35 58.0938V35.8534C35 35.3479 35.1883 34.9217 35.555 34.555C35.9217 34.1883 36.3479 34 36.8534 34H70.214ZM70.2349 52.4453L70.856 54.4375H68.6235L69.2388 52.4453C69.2661 52.3555 69.311 52.2051 69.3735 51.9941C69.436 51.7832 69.5005 51.5605 69.5669 51.3262C69.6372 51.0879 69.6919 50.8887 69.731 50.7285C69.77 50.8887 69.8208 51.0781 69.8833 51.2969C69.9497 51.5117 70.0142 51.7246 70.0767 51.9355C70.1431 52.1426 70.1958 52.3125 70.2349 52.4453Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M58 46C56.3431 46 55 47.3431 55 49V60C55 61.6569 56.3431 63 58 63H107C108.657 63 110 61.6569 110 60V49C110 47.3431 108.657 46 107 46H58ZM64.6626 55.457C64.8149 54.9258 64.8911 54.3418 64.8911 53.7051C64.8911 52.8145 64.7446 52.0391 64.4517 51.3789C64.1626 50.7188 63.7173 50.207 63.1157 49.8438C62.5181 49.4805 61.7544 49.2988 60.8247 49.2988C59.8911 49.2988 59.1216 49.4805 58.5161 49.8438C57.9106 50.207 57.4614 50.7188 57.1685 51.3789C56.8794 52.0352 56.7349 52.8066 56.7349 53.6934C56.7349 54.3574 56.8169 54.9609 56.981 55.5039C57.145 56.0469 57.3931 56.5137 57.7251 56.9043C58.061 57.2949 58.4849 57.5957 58.9966 57.8066C59.5083 58.0137 60.1138 58.1172 60.813 58.1172H60.8774H60.9478L62.5181 60.0391H64.8442L62.7817 57.7363C63.2622 57.5176 63.6587 57.2148 63.9712 56.8281C64.2837 56.4414 64.5142 55.9844 64.6626 55.457ZM58.8618 55.252C58.7134 54.8184 58.6392 54.3027 58.6392 53.7051C58.6392 53.1035 58.7134 52.5879 58.8618 52.1582C59.0142 51.7246 59.2505 51.3926 59.5708 51.1621C59.895 50.9277 60.313 50.8105 60.8247 50.8105C61.5942 50.8105 62.147 51.0684 62.4829 51.584C62.8188 52.0996 62.9868 52.8066 62.9868 53.7051C62.9868 54.3027 62.9126 54.8184 62.7642 55.252C62.6196 55.6816 62.3872 56.0137 62.0669 56.248C61.7466 56.4785 61.3286 56.5938 60.813 56.5938C60.3052 56.5938 59.8911 56.4785 59.5708 56.248C59.2505 56.0137 59.0142 55.6816 58.8618 55.252ZM71.2896 55.9609L71.9106 58H73.8677L70.8325 49.3984H68.6118L65.5884 58H67.5454L68.1665 55.9609H71.2896ZM70.2349 52.4453L70.856 54.4375H68.6235L69.2388 52.4453C69.2661 52.3555 69.311 52.2051 69.3735 51.9941C69.436 51.7832 69.5005 51.5605 69.5669 51.3262C69.6372 51.0879 69.6919 50.8887 69.731 50.7285C69.77 50.8887 69.8208 51.0781 69.8833 51.2969C69.9497 51.5117 70.0142 51.7246 70.0767 51.9355C70.1431 52.1426 70.1958 52.3125 70.2349 52.4453Z" fill="#C32998"/>
</g>
<defs>
<clipPath id="clip0_41_13">
<rect width="108" height="108" rx="34" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -1,17 +0,0 @@
<svg width="108" height="108" viewBox="0 0 108 108" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_34_2)">
<path d="M71.5717 34.6343L113 76.0625L89.709 119.077L40.6492 70.0168L53.5336 77.4501L70.8779 60.1057L71.5717 34.6343Z" fill="url(#paint0_linear_34_2)"/>
<path d="M71.5124 34.555C71.1457 34.1883 70.7096 34 70.214 34H36.8534C36.3479 34 35.9217 34.1883 35.555 34.555C35.1883 34.9217 35 35.3479 35 35.8534V58.0938C35 59.749 35.3271 61.4041 35.9713 63.0295C36.6155 64.6648 37.4183 66.1119 38.3797 67.3706C39.3311 68.6392 40.4709 69.8682 41.799 71.0575C43.1172 72.2567 44.3461 73.2478 45.4661 74.0407C46.586 74.8336 47.7555 75.5769 48.9746 76.2806C50.1937 76.9843 51.0559 77.46 51.5713 77.7177C52.0867 77.9655 52.493 78.1637 52.8003 78.2926C53.0282 78.4115 53.2859 78.471 53.5535 78.471C53.8211 78.471 54.0788 78.4115 54.3068 78.2926C54.614 78.1538 55.0303 77.9655 55.5357 77.7177C56.0511 77.4699 56.9134 76.9843 58.1324 76.2806C59.3515 75.5769 60.521 74.8237 61.6409 74.0407C62.7609 73.2478 63.98 72.2567 65.308 71.0575C66.6262 69.8582 67.766 68.6293 68.7274 67.3706C69.6788 66.1019 70.4816 64.6549 71.1258 63.0295C71.7701 61.3942 72.0971 59.749 72.0971 58.0938V35.8534C72.0674 35.3479 71.8791 34.9217 71.5124 34.555ZM67.211 58.3019C67.211 66.3497 53.5337 73.2875 53.5337 73.2875V38.7573H67.211C67.211 38.7573 67.211 50.2542 67.211 58.3019Z" fill="white"/>
<path d="M55 49C55 47.3431 56.3431 46 58 46H107C108.657 46 110 47.3431 110 49V60C110 61.6569 108.657 63 107 63H58C56.3431 63 55 61.6569 55 60V49Z" fill="#C32998"/>
<path d="M64.8914 53.7051C64.8914 54.3418 64.8152 54.9258 64.6628 55.457C64.5144 55.9844 64.2839 56.4414 63.9714 56.8281C63.6589 57.2148 63.2625 57.5176 62.782 57.7363L64.8445 60.0391H62.5183L60.948 58.1172C60.9207 58.1172 60.8972 58.1172 60.8777 58.1172C60.8582 58.1172 60.8367 58.1172 60.8132 58.1172C60.114 58.1172 59.5085 58.0137 58.9968 57.8066C58.4851 57.5957 58.0613 57.2949 57.7253 56.9043C57.3933 56.5137 57.1453 56.0469 56.9812 55.5039C56.8171 54.9609 56.7351 54.3574 56.7351 53.6934C56.7351 52.8066 56.8796 52.0352 57.1687 51.3789C57.4617 50.7188 57.9109 50.207 58.5164 49.8438C59.1218 49.4805 59.8914 49.2988 60.825 49.2988C61.7546 49.2988 62.5183 49.4805 63.116 49.8438C63.7175 50.207 64.1628 50.7188 64.4519 51.3789C64.7449 52.0391 64.8914 52.8145 64.8914 53.7051ZM58.6394 53.7051C58.6394 54.3027 58.7136 54.8184 58.8621 55.252C59.0144 55.6816 59.2507 56.0137 59.571 56.248C59.8914 56.4785 60.3054 56.5938 60.8132 56.5938C61.3289 56.5938 61.7468 56.4785 62.0671 56.248C62.3875 56.0137 62.6199 55.6816 62.7644 55.252C62.9128 54.8184 62.9871 54.3027 62.9871 53.7051C62.9871 52.8066 62.8191 52.0996 62.4832 51.584C62.1472 51.0684 61.5945 50.8105 60.825 50.8105C60.3132 50.8105 59.8953 50.9277 59.571 51.1621C59.2507 51.3926 59.0144 51.7246 58.8621 52.1582C58.7136 52.5879 58.6394 53.1035 58.6394 53.7051ZM71.9109 58L71.2898 55.9609H68.1667L67.5457 58H65.5886L68.6121 49.3984H70.8328L73.8679 58H71.9109ZM70.8562 54.4375L70.2351 52.4453C70.196 52.3125 70.1433 52.1426 70.0769 51.9355C70.0144 51.7246 69.95 51.5117 69.8835 51.2969C69.821 51.0781 69.7703 50.8887 69.7312 50.7285C69.6921 50.8887 69.6375 51.0879 69.5671 51.3262C69.5007 51.5605 69.4363 51.7832 69.3738 51.9941C69.3113 52.2051 69.2664 52.3555 69.239 52.4453L68.6238 54.4375H70.8562Z" fill="white"/>
</g>
<defs>
<linearGradient id="paint0_linear_34_2" x1="37.8512" y1="38.8122" x2="89.011" y2="89.972" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0.247059"/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<clipPath id="clip0_34_2">
<rect width="108" height="108" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,136 +0,0 @@
#! /bin/sh
function print_example() {
echo "Example"
echo " icons ios ~/AppIcon.pdf ~/Icons/"
}
function print_usage() {
echo "Usage"
echo " icons <ios|watch|complication|macos> in-file.pdf (out-dir)"
}
function command_exists() {
if type "$1" >/dev/null 2>&1; then
return 1
else
return 0
fi
}
if command_exists "sips" == 0 ; then
echo "sips tool not found"
exit 1
fi
if [ "$1" = "--help" ] || [ "$1" = "-h" ] ; then
print_usage
exit 0
fi
PLATFORM="$1"
FILE="$2"
if [ -z "$PLATFORM" ] || [ -z "$FILE" ] ; then
echo "Error: missing arguments"
echo ""
print_usage
echo ""
print_example
exit 1
fi
DIR="$3"
if [ -z "$DIR" ] ; then
DIR=$(dirname $FILE)
fi
# Create directory if needed
mkdir -p "$DIR"
if [[ "$PLATFORM" == *"ios"* ]] ; then # iOS
sips -s format png -Z '180' "${FILE}" --out "${DIR}"/Icon-180.png
sips -s format png -Z '29' "${FILE}" --out "${DIR}"/Icon-29.png
sips -s format png -Z '58' "${FILE}" --out "${DIR}"/Icon-58.png
sips -s format png -Z '120' "${FILE}" --out "${DIR}"/Icon-120.png
sips -s format png -Z '87' "${FILE}" --out "${DIR}"/Icon-87.png
sips -s format png -Z '40' "${FILE}" --out "${DIR}"/Icon-40.png
sips -s format png -Z '80' "${FILE}" --out "${DIR}"/Icon-80.png
sips -s format png -Z '76' "${FILE}" --out "${DIR}"/Icon-76.png
sips -s format png -Z '152' "${FILE}" --out "${DIR}"/Icon-152.png
sips -s format png -Z '167' "${FILE}" --out "${DIR}"/Icon-167.png
sips -s format png -Z '60' "${FILE}" --out "${DIR}"/Icon-60.png
sips -s format png -Z '20' "${FILE}" --out "${DIR}"/Icon-20.png
sips -s format png -Z '1024' "${FILE}" --out "${DIR}"/Icon-1024.png
# https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_ref-Asset_Catalog_Format/AppIconType.html
contents_json='{"images":[{"size":"20x20","idiom":"iphone","filename":"iPhoneNotification@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"iPhoneNotification@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"iPhoneSettings@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"iPhoneSettings@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"iPhoneSpotlight@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"iPhoneSpotlight@3x.png","scale":"3x"},{"size":"60x60","idiom":"iphone","filename":"iPhone@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"iPhone@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"iPadNotification.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"iPadNotification@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"iPadSettings.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"iPadSettings@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"iPadSpotlight.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"iPadSpotlight@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"iPad.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"iPad@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"iPadPro@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"AppStoreMarketing.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}'
echo $contents_json > "${DIR}"/Contents.json
fi
if [[ "$PLATFORM" == *"watch"* ]] ; then # Apple Watch
sips -s format png -Z '48' "${FILE}" --out "${DIR}"/Watch38mmNotificationCenter.png
sips -s format png -Z '55' "${FILE}" --out "${DIR}"/Watch42mmNotificationCenter.png
sips -s format png -Z '66' "${FILE}" --out "${DIR}"/Watch66NotificationCenter.png
sips -s format png -Z '58' "${FILE}" --out "${DIR}"/WatchCompanionSettings@2x.png
sips -s format png -Z '87' "${FILE}" --out "${DIR}"/WatchCompanionSettings@3x.png
sips -s format png -Z '80' "${FILE}" --out "${DIR}"/Watch38MM42MMHomeScreen.png
sips -s format png -Z '88' "${FILE}" --out "${DIR}"/Watch40MMHomeScreen.png
sips -s format png -Z '92' "${FILE}" --out "${DIR}"/Watch41MMHomeScreen.png
sips -s format png -Z '100' "${FILE}" --out "${DIR}"/Watch44MMHomeScreen.png
sips -s format png -Z '102' "${FILE}" --out "${DIR}"/Watch45MMHomeScreen.png
sips -s format png -Z '108' "${FILE}" --out "${DIR}"/Watch49MMHomeScreen.png
sips -s format png -Z '172' "${FILE}" --out "${DIR}"/Watch38MMShortLook.png
sips -s format png -Z '196' "${FILE}" --out "${DIR}"/Watch40MM42MMShortLook.png
sips -s format png -Z '216' "${FILE}" --out "${DIR}"/Watch44MMShortLook.png
sips -s format png -Z '234' "${FILE}" --out "${DIR}"/Watch234ShortLook.png
sips -s format png -Z '258' "${FILE}" --out "${DIR}"/Watch258ShortLook.png
sips -s format png -Z '1024' "${FILE}" --out "${DIR}"/WatchAppStore.png
# https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_ref-Asset_Catalog_Format/AppIconType.html
contents_json='{"images":[{"size":"24x24","idiom":"watch","scale":"2x","filename":"Watch38mmNotificationCenter.png","role":"notificationCenter","subtype":"38mm"},{"size":"27.5x27.5","idiom":"watch","scale":"2x","filename":"Watch42mmNotificationCenter.png","role":"notificationCenter","subtype":"42mm"},{"size":"29x29","idiom":"watch","filename":"WatchCompanionSettings@2x.png","role":"companionSettings","scale":"2x"},{"size":"29x29","idiom":"watch","filename":"WatchCompanionSettings@3x.png","role":"companionSettings","scale":"3x"},{"size":"40x40","idiom":"watch","filename":"Watch38MM42MMHomeScreen.png","scale":"2x","role":"appLauncher","subtype":"38mm"},{"size":"44x44","idiom":"watch","scale":"2x","filename":"Watch40MMHomeScreen.png","role":"appLauncher","subtype":"40mm"},{"size":"50x50","idiom":"watch","scale":"2x","filename":"Watch44MMHomeScreen.png","role":"appLauncher","subtype":"44mm"},{"size":"86x86","idiom":"watch","scale":"2x","filename":"Watch38MMShortLook.png","role":"quickLook","subtype":"38mm"},{"size":"98x98","idiom":"watch","scale":"2x","filename":"Watch40MM42MMShortLook.png","role":"quickLook","subtype":"42mm"},{"size":"108x108","idiom":"watch","scale":"2x","filename":"Watch44MMShortLook.png","role":"quickLook","subtype":"44mm"},{"idiom":"watch-marketing","filename":"WatchAppStore.png","size":"1024x1024","scale":"1x"}],"info":{"version":1,"author":"xcode"}}'
echo $contents_json > "${DIR}"/Contents.json
fi
if [[ "$PLATFORM" == *"complication"* ]] ; then # Apple Watch
sips -s format png -Z '32' "${FILE}" --out "${DIR}"/Circular38mm2x.png
sips -s format png -Z '36' "${FILE}" --out "${DIR}"/Circular40mm2x.png
sips -s format png -Z '36' "${FILE}" --out "${DIR}"/Circular42mm2x.png
sips -s format png -Z '40' "${FILE}" --out "${DIR}"/Circular44mm2x.png
sips -s format png -Z '182' "${FILE}" --out "${DIR}"/ExtraLarge38mm2x.png
sips -s format png -Z '203' "${FILE}" --out "${DIR}"/ExtraLarge40mm2x.png
sips -s format png -Z '203' "${FILE}" --out "${DIR}"/ExtraLarge42mm2x.png
sips -s format png -Z '224' "${FILE}" --out "${DIR}"/ExtraLarge44mm2x.png
sips -s format png -Z '84' "${FILE}" --out "${DIR}"/GraphicBezel40mm2x.png
sips -s format png -Z '84' "${FILE}" --out "${DIR}"/GraphicBezel42mm2x.png
sips -s format png -Z '94' "${FILE}" --out "${DIR}"/GraphicBezel44mm2x.png
sips -s format png -Z '84' "${FILE}" --out "${DIR}"/GraphicCircular40mm2x.png
sips -s format png -Z '84' "${FILE}" --out "${DIR}"/GraphicCircular42mm2x.png
sips -s format png -Z '94' "${FILE}" --out "${DIR}"/GraphicCircular44mm2x.png
sips -s format png -Z '40' "${FILE}" --out "${DIR}"/GraphicCorner40mm2x.png
sips -s format png -Z '40' "${FILE}" --out "${DIR}"/GraphicCorner42mm2x.png
sips -s format png -Z '44' "${FILE}" --out "${DIR}"/GraphicCorner44mm2x.png
sips -s format png -Z '52' "${FILE}" --out "${DIR}"/GraphicModular38mm2x.png
sips -s format png -Z '58' "${FILE}" --out "${DIR}"/GraphicModular40mm2x.png
sips -s format png -Z '58' "${FILE}" --out "${DIR}"/GraphicModular42mm2x.png
sips -s format png -Z '64' "${FILE}" --out "${DIR}"/GraphicModular44mm2x.png
sips -s format png -Z '40' "${FILE}" --out "${DIR}"/GraphicUtilitarian38mm2x.png
sips -s format png -Z '44' "${FILE}" --out "${DIR}"/GraphicUtilitarian40mm2x.png
sips -s format png -Z '44' "${FILE}" --out "${DIR}"/GraphicUtilitarian42mm2x.png
sips -s format png -Z '50' "${FILE}" --out "${DIR}"/GraphicUtilitarian44mm2x.png
sips -s format png -Z '206' "${FILE}" --out "${DIR}"/GraphicExtraLarge38mm2x.png
sips -s format png -Z '264' "${FILE}" --out "${DIR}"/GraphicExtraLarge44mm2x.png
echo "NOTE: Graphic Extra Large is not generated since that is not rectangular"
fi
if [[ "$PLATFORM" == *"macos"* ]] ; then # macOS
sips -s format png -Z '1024' "${FILE}" --out "${DIR}"/icon_512x512@2x.png
sips -s format png -Z '512' "${FILE}" --out "${DIR}"/icon_512x512.png
sips -s format png -Z '512' "${FILE}" --out "${DIR}"/icon_256x256@2x.png
sips -s format png -Z '256' "${FILE}" --out "${DIR}"/icon_256x256.png
sips -s format png -Z '256' "${FILE}" --out "${DIR}"/icon_128x128@2x.png
sips -s format png -Z '128' "${FILE}" --out "${DIR}"/icon_128x128.png
sips -s format png -Z '64' "${FILE}" --out "${DIR}"/icon_32x32@2x.png
sips -s format png -Z '32' "${FILE}" --out "${DIR}"/icon_32x32.png
sips -s format png -Z '32' "${FILE}" --out "${DIR}"/icon_16x16@2x.png
sips -s format png -Z '16' "${FILE}" --out "${DIR}"/icon_16x16.png
fi

View File

@@ -4,7 +4,6 @@
#addin nuget:?package=Cake.Incubator&version=7.0.0
#tool dotnet:?package=GitVersion.Tool&version=5.10.3
using Path = System.IO.Path;
using System.Text.RegularExpressions;
var debugScript = Argument<bool>("debugScript", false);
var target = Argument("target", "Default");
@@ -36,7 +35,6 @@ VariantConfig GetVariant() => variant.ToLower() switch{
GitVersion _gitVersion; //will be set by GetGitInfo task
var _slnPath = Path.Combine(""); //base path used to access files. If build.cake file is moved, just update this
string _androidPackageName = string.Empty; //will be set by UpdateAndroidManifest task
string _iOSVersionName = string.Empty; //will be set by UpdateiOSPlist task
string CreateFeatureBranch(string prevVersionName, GitVersion git) => $"{prevVersionName}-{git.BranchName.Replace("/","-")}";
string GetVersionName(string prevVersionName, VariantConfig buildVariant, GitVersion git) => buildVariant is Prod? prevVersionName : CreateFeatureBranch(prevVersionName, git);
int CreateBuildNumber(int previousNumber) => ++previousNumber;
@@ -165,8 +163,7 @@ enum iOSProjectType
MainApp,
Autofill,
Extension,
ShareExtension,
WatchApp
ShareExtension
}
string GetiOSBundleId(VariantConfig buildVariant, iOSProjectType projectType) => projectType switch
@@ -174,7 +171,6 @@ string GetiOSBundleId(VariantConfig buildVariant, iOSProjectType projectType) =>
iOSProjectType.Autofill => $"{buildVariant.iOSBundleId}.autofill",
iOSProjectType.Extension => $"{buildVariant.iOSBundleId}.find-login-action-extension",
iOSProjectType.ShareExtension => $"{buildVariant.iOSBundleId}.share-extension",
iOSProjectType.WatchApp => $"{buildVariant.iOSBundleId}.watchkitapp",
_ => buildVariant.iOSBundleId
};
@@ -209,7 +205,6 @@ private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, Gi
if(projectType == iOSProjectType.MainApp)
{
_iOSVersionName = newVersionName;
plist["CFBundleURLTypes"][0]["CFBundleURLName"] = $"{buildVariant.iOSBundleId}.url";
}
@@ -245,79 +240,10 @@ private void UpdateiOSEntitlementsPlist(string entitlementsPath, VariantConfig b
Information($"{entitlementsPath} updated with success!");
}
private void UpdateWatchKitAppInfoPlist(string plistPath, VariantConfig buildVariant)
{
var plistFile = File(plistPath);
dynamic plist = DeserializePlist(plistFile);
var prevBundleId = plist["NSExtension"]["NSExtensionAttributes"]["WKAppBundleIdentifier"];
var newBundleId = GetiOSBundleId(buildVariant, iOSProjectType.WatchApp);
plist["NSExtension"]["NSExtensionAttributes"]["WKAppBundleIdentifier"] = newBundleId;
SerializePlist(plistFile, plist);
Information($"Changed Bundle Identifier from {prevBundleId} to {newBundleId}");
Information($"{plistPath} updated with success!");
}
private void UpdateWatchPbxproj(string pbxprojPath, string newVersion)
{
var fileText = FileReadText(pbxprojPath);
if (string.IsNullOrEmpty(fileText))
{
throw new Exception($"Couldn't find {pbxprojPath}");
}
const string pattern = @"MARKETING_VERSION = [^;]*;";
fileText = Regex.Replace(fileText, pattern, $"MARKETING_VERSION = {newVersion};");
FileWriteText(pbxprojPath, fileText);
Information($"{pbxprojPath} modified successfully.");
}
/// <summary>
/// Updates the target icons on the given appiconset target
/// taking as source the icon in appIcons/iOS folder for the giving variant
/// </summary>
/// <param name="target">It can be <ios|watch|complication|macos></param>
/// <param name="appiconsetTarget">Folder to copy the generated icons to</param>
private void UpdateAppleIcons(string target, string appiconsetTarget)
{
Information($"Updating {target} App Icons");
var iconsTempDirPath = Path.Combine(_slnPath, "appIcons", "temp");
CreateDirectory(iconsTempDirPath);
var arguments = new ProcessArgumentBuilder();
arguments.Append(target);
arguments.Append(Path.Combine(_slnPath, "appIcons", "iOS", $"{variant}.png"));
arguments.Append(iconsTempDirPath);
using(var process = StartAndReturnProcess(Path.Combine(_slnPath, "appIcons", "icongen.sh"),
new ProcessSettings { Arguments = arguments }))
{
process.WaitForExit();
Information("Exit code: {0}", process.GetExitCode());
}
var generatedIconsPath = Path.Combine(iconsTempDirPath, "*.png");
CopyFiles(generatedIconsPath, appiconsetTarget);
DeleteDirectory(iconsTempDirPath, new DeleteDirectorySettings {
Recursive = true,
Force = true
});
Information($"{target} App Icons have been updated");
}
Task("UpdateiOSIcons")
Task("UpdateiOSIcon")
.Does(()=>{
UpdateAppleIcons("ios", Path.Combine(_slnPath, "src", "iOS", "Resources", "Assets.xcassets", "AppIcons.appiconset"));
UpdateAppleIcons("watch", Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden WatchKit App", "Assets.xcassets", "AppIcon.appiconset"));
// TODO: Update complication icons when they start working
//TODO we'll implement variant icons later
Information($"Updating IOS App Icon");
});
Task("UpdateiOSPlist")
@@ -370,10 +296,8 @@ Task("UpdateiOSCodeFiles")
var fileList = new string[] {
Path.Combine(_slnPath, "src", "iOS.Core", "Utilities", "iOSCoreHelpers.cs"),
Path.Combine(_slnPath, "src", "iOS.Core", "Constants.cs"),
Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden.xcodeproj", "project.pbxproj"),
Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden WatchKit Extension", "Helpers", "KeychainHelper.swift"),
Path.Combine(".github", "resources", "export-options-ad-hoc.plist"),
Path.Combine(".github", "resources", "export-options-app-store.plist")
Path.Combine(".github", "resources", "export-options-app-store.plist"),
};
foreach(string path in fileList)
@@ -381,22 +305,6 @@ Task("UpdateiOSCodeFiles")
ReplaceInFile(path, "com.8bit.bitwarden", buildVariant.iOSBundleId);
}
});
Task("UpdateWatchProject")
.IsDependentOn("UpdateiOSPlist")
.WithCriteria(() => !string.IsNullOrEmpty(_iOSVersionName))
.Does(()=> {
var watchProjectPath = Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden.xcodeproj", "project.pbxproj");
UpdateWatchPbxproj(watchProjectPath, _iOSVersionName);
});
Task("UpdateWatchKitAppInfoPlist")
.Does(()=> {
var buildVariant = GetVariant();
var infoPath = Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden WatchKit Extension", "Info.plist");
UpdateWatchKitAppInfoPlist(infoPath, buildVariant);
});
#endregion iOS
#region Main Tasks
@@ -410,14 +318,12 @@ Task("Android")
});
Task("iOS")
.IsDependentOn("UpdateiOSIcons")
//.IsDependentOn("UpdateiOSIcon")
.IsDependentOn("UpdateiOSPlist")
.IsDependentOn("UpdateiOSAutofillPlist")
.IsDependentOn("UpdateiOSExtensionPlist")
.IsDependentOn("UpdateiOSShareExtensionPlist")
.IsDependentOn("UpdateiOSCodeFiles")
.IsDependentOn("UpdateWatchProject")
.IsDependentOn("UpdateWatchKitAppInfoPlist")
.Does(()=>
{
Information("iOS app updated");

Submodule lib/MessagePack deleted from 1ecb15e311

Binary file not shown.

View File

@@ -76,7 +76,6 @@ namespace Bit.Droid.Accessibility
new Browser("com.opera.touch", "addressbarEdit"),
new Browser("com.qflair.browserq", "url"),
new Browser("com.qwant.liberty", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v4)
new Browser("com.rainsee.create", "search_box"),
new Browser("com.sec.android.app.sbrowser", "location_bar_edit_text"),
new Browser("com.sec.android.app.sbrowser.beta", "location_bar_edit_text"),
new Browser("com.stoutner.privacybrowser.free", "url_edittext"),
@@ -86,9 +85,6 @@ namespace Bit.Droid.Accessibility
new Browser("com.vivaldi.browser.sopranos", "url_bar"),
new Browser("com.yandex.browser", "bro_omnibar_address_title_text,bro_omnibox_collapsed_title",
(s) => s.Split(new char[]{' ', ' '}).FirstOrDefault()), // 0 = Regular Space, 1 = No-break space (00A0)
new Browser("com.yjllq.internet", "search_box"),
new Browser("com.yjllq.kito", "search_box"),
new Browser("com.yujian.ResideMenuDemo", "search_box"),
new Browser("com.z28j.feel", "g2"),
new Browser("idm.internet.download.manager", "search"),
new Browser("idm.internet.download.manager.adm.lite", "search"),
@@ -136,7 +132,6 @@ namespace Bit.Droid.Accessibility
new Browser("com.htc.sense.browser", "title"),
new Browser("com.jerky.browser2", "enterUrl"),
new Browser("com.ksmobile.cb", "address_bar_edit_text"),
new Browser("com.lemurbrowser.exts","url_bar"),
new Browser("com.linkbubble.playstore", "url_text"),
new Browser("com.mx.browser", "address_editor_with_progress"),
new Browser("com.mx.browser.tablet", "address_editor_with_progress"),

View File

@@ -78,20 +78,21 @@
<Version>1.9.0</Version>
</PackageReference>
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.5.1.1" />
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.16" />
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.19" />
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.10.0" />
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.14" />
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.17" />
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.9.0.1" />
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0.15" />
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.3.1.1" />
<PackageReference Include="Xamarin.Essentials">
<Version>1.7.5</Version>
<Version>1.7.3</Version>
</PackageReference>
<PackageReference Include="Xamarin.Firebase.Messaging">
<Version>123.1.1.1</Version>
<Version>123.0.8</Version>
</PackageReference>
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.8.0" />
<PackageReference Include="Xamarin.Google.Dagger" Version="2.44.2.1" />
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.6.1.1" />
<PackageReference Include="Xamarin.Google.Dagger" Version="2.41.0.2" />
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
<Version>118.0.1.3</Version>
<Version>118.0.1.2</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
@@ -159,7 +160,6 @@
<Compile Include="Constants.cs" />
<Compile Include="Effects\RemoveFontPaddingEffect.cs" />
<Compile Include="Services\WatchDeviceService.cs" />
<Compile Include="Renderers\CustomLabelRenderer.cs" />
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\bwi-font.ttf" />
@@ -169,10 +169,6 @@
<GoogleServicesJson Include="google-services.json" />
<GoogleServicesJson Include="google-services.json.enc" />
<None Include="fdroid-keystore.jks.enc" />
<AndroidNativeLibrary Include="lib\arm64-v8a\libargon2.so" />
<AndroidNativeLibrary Include="lib\armeabi-v7a\libargon2.so" />
<AndroidNativeLibrary Include="lib\x86\libargon2.so" />
<AndroidNativeLibrary Include="lib\x86_64\libargon2.so" />
<None Include="Properties\AndroidManifest.xml" />
<None Include="upload-keystore.jks.enc" />
</ItemGroup>

View File

@@ -79,7 +79,6 @@ namespace Bit.Droid.Autofill
"com.jamal2367.styx",
"com.kiwibrowser.browser",
"com.kiwibrowser.browser.dev",
"com.lemurbrowser.exts",
"com.microsoft.emmx",
"com.microsoft.emmx.beta",
"com.microsoft.emmx.canary",
@@ -97,7 +96,6 @@ namespace Bit.Droid.Autofill
"com.opera.touch",
"com.qflair.browserq",
"com.qwant.liberty",
"com.rainsee.create",
"com.sec.android.app.sbrowser",
"com.sec.android.app.sbrowser.beta",
"com.stoutner.privacybrowser.free",
@@ -106,9 +104,6 @@ namespace Bit.Droid.Autofill
"com.vivaldi.browser.snapshot",
"com.vivaldi.browser.sopranos",
"com.yandex.browser",
"com.yjllq.internet",
"com.yjllq.kito",
"com.yujian.ResideMenuDemo",
"com.z28j.feel",
"idm.internet.download.manager",
"idm.internet.download.manager.adm.lite",

View File

@@ -12,7 +12,6 @@ namespace Bit.Droid.Autofill
private List<Field> _passwordFields = null;
private List<Field> _usernameFields = null;
private HashSet<string> _ignoreSearchTerms = new HashSet<string> { "search", "find", "recipient", "edit" };
private HashSet<string> _usernameTerms = new HashSet<string> { "email", "phone", "username" };
private HashSet<string> _passwordTerms = new HashSet<string> { "password", "pswd" };
public List<AutofillId> AutofillIds { get; private set; } = new List<AutofillId>();
@@ -54,14 +53,15 @@ namespace Bit.Droid.Autofill
if (HintToFieldsMap.ContainsKey(View.AutofillHintPassword))
{
_passwordFields.AddRange(HintToFieldsMap[View.AutofillHintPassword]);
return _passwordFields;
}
}
_passwordFields = Fields.Where(f => FieldIsPassword(f)).ToList();
if (!_passwordFields.Any())
else
{
_passwordFields = Fields.Where(f => FieldHasPasswordTerms(f)).ToList();
_passwordFields = Fields.Where(f => FieldIsPassword(f)).ToList();
if (!_passwordFields.Any())
{
_passwordFields = Fields.Where(f => FieldHasPasswordTerms(f)).ToList();
}
}
return _passwordFields;
}
@@ -86,26 +86,19 @@ namespace Bit.Droid.Autofill
{
_usernameFields.AddRange(HintToFieldsMap[View.AutofillHintUsername]);
}
if (_usernameFields.Any())
{
return _usernameFields;
}
}
foreach (var passwordField in PasswordFields)
else
{
var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId)
.LastOrDefault();
if (usernameField != null)
foreach (var passwordField in PasswordFields)
{
_usernameFields.Add(usernameField);
var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId)
.LastOrDefault();
if (usernameField != null)
{
_usernameFields.Add(usernameField);
}
}
}
if (!_usernameFields.Any())
{
_usernameFields = Fields.Where(f => FieldIsUsername(f)).ToList();
}
return _usernameFields;
}
}
@@ -328,23 +321,13 @@ namespace Bit.Droid.Autofill
}
return inputTypePassword && !ValueContainsAnyTerms(f.IdEntry, _ignoreSearchTerms) &&
!ValueContainsAnyTerms(f.Hint, _ignoreSearchTerms) && !FieldIsUsername(f);
!ValueContainsAnyTerms(f.Hint, _ignoreSearchTerms);
}
private bool FieldHasPasswordTerms(Field f)
{
return ValueContainsAnyTerms(f.IdEntry, _passwordTerms) || ValueContainsAnyTerms(f.Hint, _passwordTerms);
}
private bool FieldIsUsername(Field f)
{
return f.InputType.HasFlag(InputTypes.TextVariationWebEmailAddress) || FieldHasUsernameTerms(f);
}
private bool FieldHasUsernameTerms(Field f)
{
return ValueContainsAnyTerms(f.IdEntry, _usernameTerms) || ValueContainsAnyTerms(f.Hint, _usernameTerms);
}
private bool ValueContainsAnyTerms(string value, HashSet<string> terms)
{
@@ -356,4 +339,4 @@ namespace Bit.Droid.Autofill
return terms.Any(t => lowerValue.Contains(t));
}
}
}
}

View File

@@ -170,7 +170,7 @@ namespace Bit.Droid
{
if (intent?.GetStringExtra("uri") is string uri)
{
_messagingService.Send(App.App.POP_ALL_AND_GO_TO_AUTOFILL_CIPHERS_MESSAGE);
_messagingService.Send("popAllAndGoToAutofillCiphers");
if (_appOptions != null)
{
_appOptions.Uri = uri;
@@ -178,7 +178,7 @@ namespace Bit.Droid
}
else if (intent.GetBooleanExtra("generatorTile", false))
{
_messagingService.Send(App.App.POP_ALL_AND_GO_TO_TAB_GENERATOR_MESSAGE);
_messagingService.Send("popAllAndGoToTabGenerator");
if (_appOptions != null)
{
_appOptions.GeneratorTile = true;
@@ -186,7 +186,7 @@ namespace Bit.Droid
}
else if (intent.GetBooleanExtra("myVaultTile", false))
{
_messagingService.Send(App.App.POP_ALL_AND_GO_TO_TAB_MYVAULT_MESSAGE);
_messagingService.Send("popAllAndGoToTabMyVault");
if (_appOptions != null)
{
_appOptions.MyVaultTile = true;
@@ -198,7 +198,7 @@ namespace Bit.Droid
{
_appOptions.CreateSend = GetCreateSendRequest(intent);
}
_messagingService.Send(App.App.POP_ALL_AND_GO_TO_TAB_SEND_MESSAGE);
_messagingService.Send("popAllAndGoToTabSend");
}
else
{

View File

@@ -21,7 +21,6 @@ using Bit.App.Utilities;
using Bit.App.Pages;
using Bit.App.Utilities.AccountManagement;
using Bit.App.Controls;
using Bit.Core.Enums;
#if !FDROID
using Android.Gms.Security;
#endif
@@ -82,8 +81,7 @@ namespace Bit.Droid
ServiceContainer.Resolve<IAuthService>("authService"),
ServiceContainer.Resolve<ILogger>("logger"),
ServiceContainer.Resolve<IMessagingService>("messagingService"),
ServiceContainer.Resolve<IWatchDeviceService>(),
ServiceContainer.Resolve<IConditionedAwaiterManager>());
ServiceContainer.Resolve<IWatchDeviceService>());
ServiceContainer.Register<IAccountsManager>("accountsManager", accountsManager);
}
#if !FDROID
@@ -145,10 +143,9 @@ namespace Bit.Droid
var secureStorageService = new SecureStorageService();
var cryptoPrimitiveService = new CryptoPrimitiveService();
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
var storageMediatorService = new StorageMediatorService(mobileStorageService, secureStorageService, preferencesStorage);
var stateService = new StateService(mobileStorageService, secureStorageService, storageMediatorService, messagingService);
var stateService = new StateService(mobileStorageService, secureStorageService, messagingService);
var stateMigrationService =
new StateMigrationService(DeviceType.Android, liteDbStorage, preferencesStorage, secureStorageService);
new StateMigrationService(liteDbStorage, preferencesStorage, secureStorageService);
var clipboardService = new ClipboardService(stateService);
var deviceActionService = new DeviceActionService(stateService, messagingService);
var fileService = new FileService(stateService, broadcasterService);
@@ -156,12 +153,11 @@ namespace Bit.Droid
messagingService, broadcasterService);
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService,
platformUtilsService, new LazyResolve<IEventService>());
var biometricService = new BiometricService(stateService);
var biometricService = new BiometricService();
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
ServiceContainer.Register<ISynchronousStorageService>(preferencesStorage);
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
ServiceContainer.Register<IMessagingService>("messagingService", messagingService);
ServiceContainer.Register<ILocalizeService>("localizeService", localizeService);
@@ -169,7 +165,6 @@ namespace Bit.Droid
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
ServiceContainer.Register<IStorageMediatorService>(storageMediatorService);
ServiceContainer.Register<IStateService>("stateService", stateService);
ServiceContainer.Register<IStateMigrationService>("stateMigrationService", stateMigrationService);
ServiceContainer.Register<IClipboardService>("clipboardService", clipboardService);
@@ -202,9 +197,7 @@ namespace Bit.Droid
private void Bootstrap()
{
var locale = ServiceContainer.Resolve<IStateService>().GetLocale();
(ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService)
.Init(locale != null ? new System.Globalization.CultureInfo(locale) : null);
(ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService).Init();
ServiceContainer.Resolve<IAuthService>("authService").Init();
// Note: This is not awaited
var bootstrapTask = BootstrapAsync();

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.7.0" 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.12.0" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
@@ -40,16 +40,4 @@
</intent-filter>
</activity>
</application>
<!-- Support for Xamarin.Essentials.Browser.OpenAsync (for Android > 11) -->
<!-- Related docs: https://learn.microsoft.com/en-us/xamarin/essentials/open-browser?tabs=android -->
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>
</manifest>

View File

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

View File

@@ -92,9 +92,6 @@
<compatibility-package
android:name="com.kiwibrowser.browser.dev"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.lemurbrowser.exts"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.microsoft.emmx"
android:maxLongVersionCode="10000000000"/>
@@ -146,9 +143,6 @@
<compatibility-package
android:name="com.qwant.liberty"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.rainsee.create"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.sec.android.app.sbrowser"
android:maxLongVersionCode="10000000000"/>
@@ -173,15 +167,6 @@
<compatibility-package
android:name="com.yandex.browser"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.yjllq.internet"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.yjllq.kito"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.yujian.ResideMenuDemo"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.z28j.feel"
android:maxLongVersionCode="10000000000"/>

View File

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

View File

@@ -49,6 +49,12 @@ namespace Bit.Droid.Services
}
}
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 clipboardManager = Application.Context.GetSystemService(Context.ClipboardService) as ClipboardManager;

View File

@@ -5,8 +5,6 @@ using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using System;
using System.Runtime.InteropServices;
using Java.Lang;
namespace Bit.Droid.Services
{
@@ -35,19 +33,5 @@ namespace Bit.Droid.Services
generator.Init(password, salt, iterations);
return ((KeyParameter)generator.GenerateDerivedMacParameters(keySize)).GetKey();
}
public byte[] Argon2id(byte[] password, byte[] salt, int iterations, int memory, int parallelism)
{
JavaSystem.LoadLibrary("argon2");
int keySize = 32;
var key = new byte[keySize];
argon2id_hash_raw(iterations, memory, parallelism,
password, password.Length, salt, salt.Length, key, key.Length);
return key;
}
[DllImport("argon2", EntryPoint = "argon2id_hash_raw")]
private static extern int argon2id_hash_raw(int timeCost, int memoryCost, int parallelism,
byte[] pwd, int pwdlen, byte[] salt, int saltlen, byte[] hash, int hashlen);
}
}

View File

@@ -502,6 +502,11 @@ namespace Bit.Droid.Services
public async Task SetScreenCaptureAllowedAsync()
{
if (CoreHelpers.ForceScreenCaptureEnabled())
{
return;
}
var activity = CrossCurrentActivity.Current?.Activity;
if (await _stateService.GetScreenCaptureAllowedAsync())
{

View File

@@ -1,5 +1,4 @@
using System;
using System.Diagnostics;
using System.Globalization;
using Bit.App.Abstractions;
using Bit.App.Models;
@@ -26,13 +25,13 @@ namespace Bit.Droid.Services
try
{
var fallback = ToDotnetFallbackLanguage(new PlatformCulture(netLanguage));
Debug.WriteLine(netLanguage + " failed, trying " + fallback + " (" + e1.Message + ")");
Console.WriteLine(netLanguage + " failed, trying " + fallback + " (" + e1.Message + ")");
ci = new CultureInfo(fallback);
}
catch (CultureNotFoundException e2)
{
// iOS language not valid .NET culture, falling back to English
Debug.WriteLine(netLanguage + " couldn't be set, using 'en' (" + e2.Message + ")");
Console.WriteLine(netLanguage + " couldn't be set, using 'en' (" + e2.Message + ")");
ci = new CultureInfo("en");
}
}
@@ -41,7 +40,7 @@ namespace Bit.Droid.Services
private string AndroidToDotnetLanguage(string androidLanguage)
{
Debug.WriteLine("Android Language:" + androidLanguage);
Console.WriteLine("Android Language:" + androidLanguage);
var netLanguage = androidLanguage;
if (androidLanguage.StartsWith("zh"))
{
@@ -80,13 +79,13 @@ namespace Bit.Droid.Services
// ONLY use cultures that have been tested and known to work
}
}
Debug.WriteLine(".NET Language/Locale:" + netLanguage);
Console.WriteLine(".NET Language/Locale:" + netLanguage);
return netLanguage;
}
private string ToDotnetFallbackLanguage(PlatformCulture platCulture)
{
Debug.WriteLine(".NET Fallback Language:" + platCulture.LanguageCode);
Console.WriteLine(".NET Fallback Language:" + platCulture.LanguageCode);
var netLanguage = platCulture.LanguageCode; // use the first part of the identifier (two chars, usually);
switch (platCulture.LanguageCode)
{
@@ -96,7 +95,7 @@ namespace Bit.Droid.Services
// add more application-specific cases here (if required)
// ONLY use cultures that have been tested and known to work
}
Debug.WriteLine(".NET Fallback Language/Locale:" + netLanguage + " (application-specific)");
Console.WriteLine(".NET Fallback Language/Locale:" + netLanguage + " (application-specific)");
return netLanguage;
}

View File

@@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Bit.App.Services;
using Bit.Core.Abstractions;
using Bit.Core.Models;
namespace Bit.Droid.Services
{
@@ -21,7 +22,7 @@ namespace Bit.Droid.Services
protected override bool CanSendData => false;
protected override Task SendDataToWatchAsync(byte[] rawData) => throw new NotImplementedException();
protected override Task SendDataToWatchAsync(WatchDTO watchDto) => throw new NotImplementedException();
protected override void ConnectToWatch() => throw new NotImplementedException();
}

Binary file not shown.

Binary file not shown.

View File

@@ -8,7 +8,6 @@ namespace Bit.App.Abstractions
{
void Init(Func<AppOptions> getOptionsFunc, IAccountsManagerHost accountsManagerHost);
Task NavigateOnAccountChangeAsync(bool? isAuthed = null);
Task StartDefaultNavigationFlowAsync(Action<AppOptions> appOptionsAction);
Task LogOutAsync(string userId, bool userInitiated, bool expired);
Task PromptToSwitchToExistingAccountAsync(string userId);
}

View File

@@ -1,9 +0,0 @@
using System;
namespace Bit.App.Abstractions
{
public interface IDeepLinkContext
{
bool OnNewUri(Uri uri);
}
}

View File

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

View File

@@ -23,11 +23,6 @@ namespace Bit.App
{
public partial class App : Application, IAccountsManagerHost
{
public const string POP_ALL_AND_GO_TO_TAB_GENERATOR_MESSAGE = "popAllAndGoToTabGenerator";
public const string POP_ALL_AND_GO_TO_TAB_MYVAULT_MESSAGE = "popAllAndGoToTabMyVault";
public const string POP_ALL_AND_GO_TO_TAB_SEND_MESSAGE = "popAllAndGoToTabSend";
public const string POP_ALL_AND_GO_TO_AUTOFILL_CIPHERS_MESSAGE = "popAllAndGoToAutofillCiphers";
private readonly IBroadcasterService _broadcasterService;
private readonly IMessagingService _messagingService;
private readonly IStateService _stateService;
@@ -38,7 +33,6 @@ namespace Bit.App
private readonly IFileService _fileService;
private readonly IAccountsManager _accountsManager;
private readonly IPushNotificationService _pushNotificationService;
private readonly IConfigService _configService;
private static bool _isResumed;
// these variables are static because the app is launching new activities on notification click, creating new instances of App.
private static bool _pendingCheckPasswordlessLoginRequests;
@@ -62,7 +56,6 @@ namespace Bit.App
_fileService = ServiceContainer.Resolve<IFileService>();
_accountsManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>();
_configService = ServiceContainer.Resolve<IConfigService>();
_accountsManager.Init(() => Options, this);
@@ -110,18 +103,12 @@ namespace Bit.App
await Task.Delay(1000);
await _accountsManager.NavigateOnAccountChangeAsync();
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_GENERATOR_MESSAGE ||
message.Command == POP_ALL_AND_GO_TO_TAB_MYVAULT_MESSAGE ||
message.Command == POP_ALL_AND_GO_TO_TAB_SEND_MESSAGE ||
message.Command == POP_ALL_AND_GO_TO_AUTOFILL_CIPHERS_MESSAGE ||
message.Command == DeepLinkContext.NEW_OTP_MESSAGE)
else if (message.Command == "popAllAndGoToTabGenerator" ||
message.Command == "popAllAndGoToTabMyVault" ||
message.Command == "popAllAndGoToTabSend" ||
message.Command == "popAllAndGoToAutofillCiphers")
{
if (message.Command == DeepLinkContext.NEW_OTP_MESSAGE)
{
Options.OtpData = new OtpData((string)message.Data);
}
await Device.InvokeOnMainThreadAsync(async () =>
Device.BeginInvokeOnMainThread(async () =>
{
if (Current.MainPage is TabsPage tabsPage)
{
@@ -129,29 +116,24 @@ namespace Bit.App
{
await tabsPage.Navigation.PopModalAsync(false);
}
if (message.Command == POP_ALL_AND_GO_TO_AUTOFILL_CIPHERS_MESSAGE)
if (message.Command == "popAllAndGoToAutofillCiphers")
{
Current.MainPage = new NavigationPage(new CipherSelectionPage(Options));
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_MYVAULT_MESSAGE)
else if (message.Command == "popAllAndGoToTabMyVault")
{
Options.MyVaultTile = false;
tabsPage.ResetToVaultPage();
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_GENERATOR_MESSAGE)
else if (message.Command == "popAllAndGoToTabGenerator")
{
Options.GeneratorTile = false;
tabsPage.ResetToGeneratorPage();
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_SEND_MESSAGE)
else if (message.Command == "popAllAndGoToTabSend")
{
tabsPage.ResetToSendPage();
}
else if (message.Command == DeepLinkContext.NEW_OTP_MESSAGE)
{
tabsPage.ResetToVaultPage();
await tabsPage.Navigation.PushModalAsync(new NavigationPage(new CipherSelectionPage(Options)));
}
}
});
}
@@ -163,18 +145,6 @@ namespace Bit.App
new NavigationPage(new RemoveMasterPasswordPage()));
});
}
else if (message.Command == Constants.ForceUpdatePassword)
{
Device.BeginInvokeOnMainThread(async () =>
{
await Application.Current.MainPage.Navigation.PushModalAsync(
new NavigationPage(new UpdateTempPasswordPage()));
});
}
else if (message.Command == "syncCompleted")
{
await _configService.GetAsync(true);
}
else if (message.Command == Constants.PasswordlessLoginRequestKey
|| message.Command == "unlocked"
|| message.Command == AccountsManagerMessageCommands.ACCOUNT_SWITCH_COMPLETED)
@@ -231,7 +201,7 @@ namespace Bit.App
Id = loginRequestData.Id,
IpAddress = loginRequestData.RequestIpAddress,
Email = await _stateService.GetEmailAsync(),
FingerprintPhrase = loginRequestData.FingerprintPhrase,
FingerprintPhrase = loginRequestData.RequestFingerprint,
RequestDate = loginRequestData.CreationDate,
DeviceType = loginRequestData.RequestDeviceType,
Origin = loginRequestData.Origin
@@ -299,8 +269,6 @@ namespace Bit.App
// Reset delay on every start
_vaultTimeoutService.DelayLockAndLogoutMs = null;
}
await _configService.GetAsync();
_messagingService.Send("startEventTimer");
}
@@ -340,7 +308,6 @@ namespace Bit.App
private async Task SleptAsync()
{
await _vaultTimeoutService.CheckVaultTimeoutAsync();
await ClearSensitiveFieldsAsync();
_messagingService.Send("stopEventTimer");
}
@@ -348,7 +315,6 @@ namespace Bit.App
{
await _stateService.CheckExtensionActiveUserAndSwitchIfNeededAsync();
await _vaultTimeoutService.CheckVaultTimeoutAsync();
await ClearSensitiveFieldsAsync();
_messagingService.Send("startEventTimer");
await UpdateThemeAsync();
await ClearCacheIfNeededAsync();
@@ -369,14 +335,6 @@ namespace Bit.App
});
}
private async Task ClearSensitiveFieldsAsync()
{
await Device.InvokeOnMainThreadAsync(() =>
{
_messagingService.Send(Constants.ClearSensitiveFields);
});
}
private void SetCulture()
{
// Calendars are removed by linker. ref https://bugzilla.xamarin.com/show_bug.cgi?id=59077
@@ -501,7 +459,14 @@ namespace Bit.App
switch (navTarget)
{
case NavigationTarget.HomeLogin:
Current.MainPage = new NavigationPage(new HomePage(Options));
if (navParams is HomeNavigationParams homeParams)
{
Current.MainPage = new NavigationPage(new HomePage(Options, homeParams.ShouldCheckRememberEmail));
}
else
{
Current.MainPage = new NavigationPage(new HomePage(Options));
}
break;
case NavigationTarget.Login:
if (navParams is LoginNavigationParams loginParams)
@@ -526,8 +491,7 @@ namespace Bit.App
Current.MainPage = new NavigationPage(new CipherAddEditPage(appOptions: Options));
break;
case NavigationTarget.AutofillCiphers:
case NavigationTarget.OtpCipherSelection:
Current.MainPage = new NavigationPage(new CipherSelectionPage(Options));
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
break;
case NavigationTarget.SendAddEdit:
Current.MainPage = new NavigationPage(new SendAddEditPage(Options));

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ namespace Bit.App.Controls
{
AccountView = accountView;
AvatarImageSource = ServiceContainer.Resolve<IAvatarImageSourcePool>("avatarImageSourcePool")
?.GetOrCreateAvatar(AccountView.UserId, AccountView.Name, AccountView.Email, AccountView.AvatarColor);
?.GetOrCreateAvatar(AccountView.UserId, AccountView.Name, AccountView.Email);
}
public AccountView AccountView

View File

@@ -13,7 +13,6 @@ namespace Bit.App.Controls
{
private readonly string _text;
private readonly string _id;
private readonly string _color;
public override bool Equals(object obj)
{
@@ -24,7 +23,7 @@ namespace Bit.App.Controls
if (obj is AvatarImageSource avatar)
{
return avatar._id == _id && avatar._text == _text && avatar._color == _color;
return avatar._id == _id && avatar._text == _text;
}
return base.Equals(obj);
@@ -32,7 +31,7 @@ namespace Bit.App.Controls
public override int GetHashCode() => _id?.GetHashCode() ?? _text?.GetHashCode() ?? -1;
public AvatarImageSource(string userId = null, string name = null, string email = null, string color = null)
public AvatarImageSource(string userId = null, string name = null, string email = null)
{
_id = userId;
_text = name;
@@ -40,7 +39,6 @@ namespace Bit.App.Controls
{
_text = email;
}
_color = color;
}
public override Func<CancellationToken, Task<Stream>> Stream => GetStreamAsync;
@@ -73,7 +71,7 @@ namespace Bit.App.Controls
chars = upperCaseText = _text.ToUpper();
}
var bgColor = _color ?? CoreHelpers.StringToColor(_id ?? upperCaseText, "#33ffffff");
var bgColor = CoreHelpers.StringToColor(_id ?? upperCaseText, "#33ffffff");
var textColor = CoreHelpers.TextColorFromBgColor(bgColor);
var size = 50;

View File

@@ -5,19 +5,19 @@ namespace Bit.App.Controls
{
public interface IAvatarImageSourcePool
{
AvatarImageSource GetOrCreateAvatar(string userId, string name, string email, string color);
AvatarImageSource GetOrCreateAvatar(string userId, string name, string email);
}
public class AvatarImageSourcePool : IAvatarImageSourcePool
{
private readonly ConcurrentDictionary<string, AvatarImageSource> _cache = new ConcurrentDictionary<string, AvatarImageSource>();
public AvatarImageSource GetOrCreateAvatar(string userId, string name, string email, string color)
public AvatarImageSource GetOrCreateAvatar(string userId, string name, string email)
{
var key = $"{userId}{name}{email}{color}";
var key = $"{userId}{name}{email}";
if (!_cache.TryGetValue(key, out var avatar))
{
avatar = new AvatarImageSource(userId, name, email, color);
avatar = new AvatarImageSource(userId, name, email);
if (!_cache.TryAdd(key, avatar)
&&
!_cache.TryGetValue(key, out avatar)) // If add fails another thread created the avatar in between the first try get and the try add.

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,4 @@
using System.Linq;
using Xamarin.CommunityToolkit.Converters;
using Xamarin.Forms;
using Xamarin.Forms;
namespace Bit.App.Controls
{
@@ -8,13 +6,4 @@ namespace Bit.App.Controls
{
public string ExtraDataForLogging { get; set; }
}
public class SelectionChangedEventArgsConverter : BaseNullableConverterOneWay<SelectionChangedEventArgs, object>
{
public override object? ConvertFrom(SelectionChangedEventArgs? value)
{
return value?.CurrentSelection.FirstOrDefault();
}
}
}

View File

@@ -1,11 +0,0 @@
using System.Collections.Generic;
namespace Bit.App.Controls
{
public interface IPasswordStrengthable
{
string Password { get; }
List<string> UserInputs { get; }
}
}

View File

@@ -1,17 +0,0 @@
using Bit.Core.Attributes;
namespace Bit.App.Controls
{
public enum PasswordStrengthLevel
{
[LocalizableEnum("Weak")]
VeryWeak,
[LocalizableEnum("Weak")]
Weak,
[LocalizableEnum("Good")]
Good,
[LocalizableEnum("Strong")]
Strong
}
}

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<StackLayout
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="controls:PasswordStrengthViewModel"
x:Class="Bit.App.Controls.PasswordStrengthProgressBar"
StyleClass="box">
<StackLayout.Resources>
<ResourceDictionary>
<u:LocalizableEnumConverter x:Key="localizableEnum" />
</ResourceDictionary>
</StackLayout.Resources>
<ProgressBar
x:Name="_progressBar"
u:ProgressBarExtensions.AnimatedProgress="{Binding PasswordStrength}"
ScaleY="2" />
<Label
x:Name="_progressLabel"
Text="{Binding PasswordStrengthLevel, Converter={StaticResource localizableEnum}, TargetNullValue=' ' }"
StyleClass="box-footer-label" />
</StackLayout>

View File

@@ -1,108 +0,0 @@
using System;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public partial class PasswordStrengthProgressBar : StackLayout
{
public static readonly BindableProperty PasswordStrengthLevelProperty = BindableProperty.Create(
nameof(PasswordStrengthLevel),
typeof(PasswordStrengthLevel),
typeof(PasswordStrengthProgressBar),
propertyChanged: OnControlPropertyChanged);
public static readonly BindableProperty VeryWeakColorProperty = BindableProperty.Create(
nameof(VeryWeakColor),
typeof(Color),
typeof(PasswordStrengthProgressBar),
propertyChanged: OnControlPropertyChanged);
public static readonly BindableProperty WeakColorProperty = BindableProperty.Create(
nameof(WeakColor),
typeof(Color),
typeof(PasswordStrengthProgressBar),
propertyChanged: OnControlPropertyChanged);
public static readonly BindableProperty GoodColorProperty = BindableProperty.Create(
nameof(GoodColor),
typeof(Color),
typeof(PasswordStrengthProgressBar),
propertyChanged: OnControlPropertyChanged);
public static readonly BindableProperty StrongColorProperty = BindableProperty.Create(
nameof(StrongColor),
typeof(Color),
typeof(PasswordStrengthProgressBar),
propertyChanged: OnControlPropertyChanged);
public PasswordStrengthLevel? PasswordStrengthLevel
{
get { return (PasswordStrengthLevel?)GetValue(PasswordStrengthLevelProperty); }
set { SetValue(PasswordStrengthLevelProperty, value); }
}
public Color VeryWeakColor
{
get { return (Color)GetValue(VeryWeakColorProperty); }
set { SetValue(VeryWeakColorProperty, value); }
}
public Color WeakColor
{
get { return (Color)GetValue(WeakColorProperty); }
set { SetValue(WeakColorProperty, value); }
}
public Color GoodColor
{
get { return (Color)GetValue(GoodColorProperty); }
set { SetValue(GoodColorProperty, value); }
}
public Color StrongColor
{
get { return (Color)GetValue(StrongColorProperty); }
set { SetValue(StrongColorProperty, value); }
}
public PasswordStrengthProgressBar()
{
InitializeComponent();
SetBinding(PasswordStrengthProgressBar.PasswordStrengthLevelProperty, new Binding() { Path = nameof(PasswordStrengthViewModel.PasswordStrengthLevel) });
UpdateColors();
}
private static void OnControlPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
(bindable as PasswordStrengthProgressBar)?.UpdateColors();
}
public void UpdateColors()
{
if (_progressBar == null || _progressLabel == null)
{
return;
}
_progressBar.ProgressColor = GetColorForStrength();
_progressLabel.TextColor = _progressBar.ProgressColor;
}
private Color GetColorForStrength()
{
switch (PasswordStrengthLevel)
{
case Controls.PasswordStrengthLevel.VeryWeak:
return VeryWeakColor;
case Controls.PasswordStrengthLevel.Weak:
return WeakColor;
case Controls.PasswordStrengthLevel.Good:
return GoodColor;
case Controls.PasswordStrengthLevel.Strong:
return StrongColor;
default:
return Color.Transparent;
}
}
}
}

View File

@@ -1,67 +0,0 @@
using System.Collections.Generic;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class PasswordStrengthViewModel : ExtendedViewModel
{
private readonly IPasswordGenerationService _passwordGenerationService;
private readonly IPasswordStrengthable _passwordStrengthable;
private double _passwordStrength;
private Color _passwordColor;
private PasswordStrengthLevel? _passwordStrengthLevel;
public PasswordStrengthViewModel(IPasswordStrengthable passwordStrengthable)
{
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>();
_passwordStrengthable = passwordStrengthable;
}
public double PasswordStrength
{
get => _passwordStrength;
set => SetProperty(ref _passwordStrength, value);
}
public PasswordStrengthLevel? PasswordStrengthLevel
{
get => _passwordStrengthLevel;
set => SetProperty(ref _passwordStrengthLevel, value);
}
public List<string> GetPasswordStrengthUserInput(string email) => _passwordGenerationService.GetPasswordStrengthUserInput(email);
public void CalculatePasswordStrength()
{
if (string.IsNullOrEmpty(_passwordStrengthable.Password))
{
PasswordStrength = 0;
PasswordStrengthLevel = null;
return;
}
var passwordStrength = _passwordGenerationService.PasswordStrength(_passwordStrengthable.Password, _passwordStrengthable.UserInputs);
// The passwordStrength.Score is 0..4, convertion was made to be used as a progress directly by the control 0..1
PasswordStrength = (passwordStrength.Score + 1f) / 5f;
if (PasswordStrength <= 0.4f)
{
PasswordStrengthLevel = Controls.PasswordStrengthLevel.VeryWeak;
}
else if (PasswordStrength <= 0.6f)
{
PasswordStrengthLevel = Controls.PasswordStrengthLevel.Weak;
}
else if (PasswordStrength <= 0.8f)
{
PasswordStrengthLevel = Controls.PasswordStrengthLevel.Good;
}
else
{
PasswordStrengthLevel = Controls.PasswordStrengthLevel.Strong;
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
using System;
using Bit.Core.Enums;
using Bit.Core.Utilities;
namespace Bit.App.Models
{
@@ -24,7 +23,6 @@ namespace Bit.App.Models
public Tuple<SendType, string, byte[], string> CreateSend { get; set; }
public bool CopyInsteadOfShareAfterSaving { get; set; }
public bool HideAccountSwitcher { get; set; }
public OtpData? OtpData { get; set; }
public void SetAllFrom(AppOptions o)
{
@@ -50,7 +48,6 @@ namespace Bit.App.Models
CreateSend = o.CreateSend;
CopyInsteadOfShareAfterSaving = o.CopyInsteadOfShareAfterSaving;
HideAccountSwitcher = o.HideAccountSwitcher;
OtpData = o.OtpData;
}
}
}

View File

@@ -1,8 +1,9 @@
using System.Text;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Models.Domain;
using Bit.Core.Utilities;
@@ -70,13 +71,13 @@ namespace Bit.App.Pages
set => SetProperty(ref _policy, value);
}
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string ShowPasswordIcon => ShowPassword ? "" : "";
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
public string MasterPassword { get; set; }
public string ConfirmMasterPassword { get; set; }
public string Hint { get; set; }
public virtual async Task InitAsync(bool forceSync = false)
public async Task InitAsync(bool forceSync = false)
{
if (forceSync)
{
@@ -146,8 +147,8 @@ namespace Bit.App.Pages
}
if (IsPolicyInEffect)
{
var userInputs = _passwordGenerationService.GetPasswordStrengthUserInput(await _stateService.GetEmailAsync());
var passwordStrength = _passwordGenerationService.PasswordStrength(MasterPassword, userInputs);
var userInput = await GetPasswordStrengthUserInput();
var passwordStrength = _passwordGenerationService.PasswordStrength(MasterPassword, userInput);
if (!await _policyService.EvaluateMasterPassword(passwordStrength.Score, MasterPassword, Policy))
{
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordPolicyValidationMessage,
@@ -157,9 +158,9 @@ namespace Bit.App.Pages
}
else
{
if (MasterPassword.Length < Constants.MasterPasswordMinimumChars)
if (MasterPassword.Length < 8)
{
await _platformUtilsService.ShowDialogAsync(string.Format(AppResources.MasterPasswordLengthValMessageX, Constants.MasterPasswordMinimumChars),
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordLengthValMessage,
AppResources.MasterPasswordPolicyValidationTitle, AppResources.Ok);
return false;
}
@@ -173,5 +174,19 @@ namespace Bit.App.Pages
return true;
}
private async Task<List<string>> GetPasswordStrengthUserInput()
{
var email = await _stateService.GetEmailAsync();
List<string> userInput = null;
var atPosition = email.IndexOf('@');
if (atPosition > -1)
{
var rx = new Regex("/[^A-Za-z0-9]/", RegexOptions.Compiled);
var data = rx.Split(email.Substring(0, atPosition).Trim().ToLower());
userInput = new List<string>(data);
}
return userInput;
}
}
}

View File

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

View File

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

View File

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

View File

@@ -15,15 +15,14 @@ namespace Bit.App.Pages
private readonly AppOptions _appOptions;
private IBroadcasterService _broadcasterService;
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
public HomePage(AppOptions appOptions = null)
public HomePage(AppOptions appOptions = null, bool shouldCheckRememberEmail = true)
{
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_appOptions = appOptions;
InitializeComponent();
_vm = BindingContext as HomeViewModel;
_vm.Page = this;
_vm.ShouldCheckRememberEmail = shouldCheckRememberEmail;
_vm.ShowCancelButton = _appOptions?.IosExtension ?? false;
_vm.StartLoginAction = async () => await StartLoginAsync();
_vm.StartRegisterAction = () => Device.BeginInvokeOnMainThread(async () => await StartRegisterAsync());
@@ -72,14 +71,8 @@ namespace Bit.App.Pages
});
}
});
try
{
await _vm.UpdateEnvironment();
}
catch (Exception ex)
{
_logger.Value?.Exception(ex);
}
_vm.CheckNavigateLoginStep();
}
protected override bool OnBackButtonPressed()
@@ -138,6 +131,14 @@ namespace Bit.App.Pages
await Navigation.PushModalAsync(new NavigationPage(page));
}
private void Environment_Clicked(object sender, EventArgs e)
{
if (DoOnce())
{
_vm.StartEnvironmentAction();
}
}
private async Task StartEnvironmentAsync()
{
await _accountListOverlay.HideAsync();

View File

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

View File

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

View File

@@ -3,8 +3,6 @@ using System.Threading.Tasks;
using Bit.App.Models;
using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Xamarin.Forms;
@@ -12,7 +10,6 @@ namespace Bit.App.Pages
{
public partial class LockPage : BaseContentPage
{
private readonly IBroadcasterService _broadcasterService;
private readonly AppOptions _appOptions;
private readonly bool _autoPromptBiometric;
private readonly LockPageViewModel _vm;
@@ -25,7 +22,6 @@ namespace Bit.App.Pages
_appOptions = appOptions;
_autoPromptBiometric = autoPromptBiometric;
InitializeComponent();
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>();
_vm = BindingContext as LockPageViewModel;
_vm.Page = this;
_vm.UnlockedAction = () => Device.BeginInvokeOnMainThread(async () => await UnlockedAsync());
@@ -68,13 +64,6 @@ namespace Bit.App.Pages
protected override async void OnAppearing()
{
base.OnAppearing();
_broadcasterService.Subscribe(nameof(LockPage), message =>
{
if (message.Command == Constants.ClearSensitiveFields)
{
Device.BeginInvokeOnMainThread(_vm.ResetPinPasswordFields);
}
});
if (_appeared)
{
return;
@@ -140,7 +129,6 @@ namespace Bit.App.Pages
base.OnDisappearing();
_accountAvatar?.OnDisappearing();
_broadcasterService.Unsubscribe(nameof(LockPage));
}
private void Unlock_Clicked(object sender, EventArgs e)

View File

@@ -9,7 +9,6 @@ using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Request;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.Helpers;
using Xamarin.Forms;
@@ -31,12 +30,8 @@ namespace Bit.App.Pages
private readonly ILogger _logger;
private readonly IWatchDeviceService _watchDeviceService;
private readonly WeakEventManager<int?> _secretEntryFocusWeakEventManager = new WeakEventManager<int?>();
private readonly IPolicyService _policyService;
private readonly IPasswordGenerationService _passwordGenerationService;
private string _email;
private string _masterPassword;
private string _pin;
private bool _showPassword;
private bool _pinLock;
private bool _biometricLock;
@@ -63,8 +58,6 @@ namespace Bit.App.Pages
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
_logger = ServiceContainer.Resolve<ILogger>("logger");
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
_policyService = ServiceContainer.Resolve<IPolicyService>();
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>();
PageTitle = AppResources.VerifyMasterPassword;
TogglePasswordCommand = new Command(TogglePassword);
@@ -77,18 +70,6 @@ namespace Bit.App.Pages
};
}
public string MasterPassword
{
get => _masterPassword;
set => SetProperty(ref _masterPassword, value);
}
public string Pin
{
get => _pin;
set => SetProperty(ref _pin, value);
}
public bool ShowPassword
{
get => _showPassword;
@@ -153,6 +134,8 @@ namespace Bit.App.Pages
public Command TogglePasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
public string MasterPassword { get; set; }
public string Pin { get; set; }
public Action UnlockedAction { get; set; }
public event Action<int?> FocusSecretEntry
{
@@ -209,7 +192,7 @@ namespace Bit.App.Pages
if (BiometricLock)
{
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
BiometricIntegrityValid = await _biometricService.ValidateIntegrityAsync();
if (!_biometricIntegrityValid)
{
BiometricButtonVisible = false;
@@ -245,7 +228,8 @@ namespace Bit.App.Pages
}
ShowPassword = false;
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
var kdf = await _stateService.GetKdfTypeAsync();
var kdfIterations = await _stateService.GetKdfIterationsAsync();
if (PinLock)
{
@@ -255,7 +239,7 @@ namespace Bit.App.Pages
if (_isPinProtected)
{
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
kdfConfig,
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
await _stateService.GetPinProtectedKeyAsync());
var encKey = await _cryptoService.GetEncKeyAsync(key);
var protectedPin = await _stateService.GetProtectedPinAsync();
@@ -270,7 +254,8 @@ namespace Bit.App.Pages
}
else
{
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email, kdfConfig);
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
failed = false;
Pin = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
@@ -295,10 +280,9 @@ namespace Bit.App.Pages
}
else
{
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdfConfig);
var key = await _cryptoService.MakeKeyAsync(MasterPassword, _email, kdf, kdfIterations);
var storedKeyHash = await _cryptoService.GetKeyHashAsync();
var passwordValid = false;
MasterPasswordPolicyOptions enforcedMasterPasswordOptions = null;
if (storedKeyHash != null)
{
@@ -310,11 +294,9 @@ namespace Bit.App.Pages
var keyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
var request = new PasswordVerificationRequest();
request.MasterPasswordHash = keyHash;
try
{
var response = await _apiService.PostAccountVerifyPasswordAsync(request);
enforcedMasterPasswordOptions = response.MasterPasswordPolicy;
await _apiService.PostAccountVerifyPasswordAsync(request);
passwordValid = true;
var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
await _cryptoService.SetKeyHashAsync(localKeyHash);
@@ -332,17 +314,10 @@ namespace Bit.App.Pages
var protectedPin = await _stateService.GetProtectedPinAsync();
var encKey = await _cryptoService.GetEncKeyAsync(key);
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, kdfConfig);
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email,
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
}
if (await RequirePasswordChangeAsync(enforcedMasterPasswordOptions))
{
// Save the ForcePasswordResetReason to force a password reset after unlock
await _stateService.SetForcePasswordResetReasonAsync(
ForcePasswordResetReason.WeakMasterPasswordOnLogin);
}
MasterPassword = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
await SetKeyAndContinueAsync(key);
@@ -367,37 +342,6 @@ namespace Bit.App.Pages
}
}
/// <summary>
/// Checks if the master password requires updating to meet the enforced policy requirements
/// </summary>
/// <param name="options"></param>
private async Task<bool> RequirePasswordChangeAsync(MasterPasswordPolicyOptions options = null)
{
// If no policy options are provided, attempt to load them from the policy service
var enforcedOptions = options ?? await _policyService.GetMasterPasswordPolicyOptions();
// No policy to enforce on login/unlock
if (!(enforcedOptions is { EnforceOnLogin: true }))
{
return false;
}
var strength = _passwordGenerationService.PasswordStrength(
MasterPassword, _passwordGenerationService.GetPasswordStrengthUserInput(_email))?.Score;
if (!strength.HasValue)
{
_logger.Error("Unable to evaluate master password strength during unlock");
return false;
}
return !await _policyService.EvaluateMasterPassword(
strength.Value,
MasterPassword,
enforcedOptions
);
}
public async Task LogOutAsync()
{
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.LogoutConfirmation,
@@ -408,20 +352,6 @@ namespace Bit.App.Pages
}
}
public void ResetPinPasswordFields()
{
try
{
MasterPassword = string.Empty;
Pin = string.Empty;
ShowPassword = false;
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
}
public void TogglePassword()
{
ShowPassword = !ShowPassword;
@@ -431,8 +361,7 @@ namespace Bit.App.Pages
public async Task PromptBiometricAsync()
{
BiometricIntegrityValid = await _platformUtilsService.IsBiometricIntegrityValidAsync();
BiometricButtonVisible = BiometricIntegrityValid;
BiometricIntegrityValid = await _biometricService.ValidateIntegrityAsync();
if (!BiometricLock || !BiometricIntegrityValid)
{
return;

View File

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

View File

@@ -2,7 +2,6 @@
using System.Threading.Tasks;
using Bit.App.Models;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Services;
using Bit.Core.Utilities;
@@ -13,7 +12,6 @@ namespace Bit.App.Pages
{
public partial class LoginPage : BaseContentPage
{
private readonly IBroadcasterService _broadcasterService;
private readonly LoginPageViewModel _vm;
private readonly AppOptions _appOptions;
@@ -25,7 +23,6 @@ namespace Bit.App.Pages
{
_appOptions = appOptions;
InitializeComponent();
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>();
_vm = BindingContext as LoginPageViewModel;
_vm.Page = this;
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
@@ -73,13 +70,6 @@ namespace Bit.App.Pages
protected override async void OnAppearing()
{
base.OnAppearing();
_broadcasterService.Subscribe(nameof(LoginPage), message =>
{
if (message.Command == Constants.ClearSensitiveFields)
{
Device.BeginInvokeOnMainThread(_vm.ResetPasswordField);
}
});
_mainContent.Content = _mainLayout;
_accountAvatar?.OnAppearing();
@@ -114,7 +104,6 @@ namespace Bit.App.Pages
base.OnDisappearing();
_accountAvatar?.OnDisappearing();
_broadcasterService.Unsubscribe(nameof(LoginPage));
}
private async void LogIn_Clicked(object sender, EventArgs e)

View File

@@ -40,8 +40,6 @@ namespace Bit.App.Pages
private string _masterPassword;
private bool _isEmailEnabled;
private bool _isKnownDevice;
private bool _isExecutingLogin;
private string _environmentHostName;
public LoginPageViewModel()
{
@@ -116,16 +114,6 @@ namespace Bit.App.Pages
set => SetProperty(ref _isKnownDevice, value);
}
public string EnvironmentDomainName
{
get => _environmentHostName;
set => SetProperty(ref _environmentHostName, value,
additionalPropertyNames: new string[]
{
nameof(LoggingInAsText)
});
}
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
public Command LogInCommand { get; }
public Command TogglePasswordCommand { get; }
@@ -133,7 +121,7 @@ namespace Bit.App.Pages
public ICommand LogInWithDeviceCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
public string LoggingInAsText => string.Format(AppResources.LoggingInAsXOnY, Email, EnvironmentDomainName);
public string LoggingInAsText => string.Format(AppResources.LoggingInAsX, Email);
public bool IsIosExtension { get; set; }
public bool CanRemoveAccount { get; set; }
public Action StartTwoFactorAction { get; set; }
@@ -155,28 +143,21 @@ namespace Bit.App.Pages
try
{
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
await _stateService.SetPreLoginEmailAsync(Email);
await AccountSwitchingOverlayViewModel.RefreshAccountViewsAsync();
if (string.IsNullOrWhiteSpace(Email))
{
Email = await _stateService.GetRememberedEmailAsync();
}
var deviceIdentifier = await _appIdService.GetAppIdAsync();
// TODO uncomment to enable login with device
//IsKnownDevice = await _apiService.GetKnownDeviceAsync(Email, deviceIdentifier);
CanRemoveAccount = await _stateService.GetActiveUserEmailAsync() != Email;
EnvironmentDomainName = CoreHelpers.GetDomain((await _stateService.GetPreAuthEnvironmentUrlsAsync())?.Base);
IsKnownDevice = await _apiService.GetKnownDeviceAsync(Email, await _appIdService.GetAppIdAsync());
}
catch (ApiException apiEx) when (apiEx.Error.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
_logger.Exception(apiEx);
await _deviceActionService.HideLoadingAsync();
}
catch (Exception ex)
{
HandleException(ex);
}
finally
{
await _deviceActionService.HideLoadingAsync();
}
}
public async Task LogInAsync(bool showLoading = true, bool checkForExistingAccount = false)
@@ -211,7 +192,6 @@ namespace Bit.App.Pages
ShowPassword = false;
try
{
_isExecutingLogin = true;
if (checkForExistingAccount)
{
var userId = await _stateService.GetUserIdAsync(Email);
@@ -273,26 +253,6 @@ namespace Bit.App.Pages
AppResources.AnErrorHasOccurred, AppResources.Ok);
}
}
finally
{
_isExecutingLogin = false;
}
}
public void ResetPasswordField()
{
try
{
if (!_isExecutingLogin)
{
MasterPassword = string.Empty;
ShowPassword = false;
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
}
private async Task MoreAsync()

View File

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

View File

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

View File

@@ -171,7 +171,7 @@ namespace Bit.App.Pages
var response = await _authService.PasswordlessCreateLoginRequestAsync(_email);
if (response != null)
{
FingerprintPhrase = response.FingerprintPhrase;
FingerprintPhrase = response.RequestFingerprint;
_requestId = response.Id;
_requestAccessCode = response.RequestAccessCode;
_requestKeyPair = response.RequestKeyPair;

View File

@@ -118,7 +118,7 @@ namespace Bit.App.Pages
}
var loginRequestData = await _authService.GetPasswordlessLoginRequestByIdAsync(LoginRequest.Id);
if (loginRequestData.IsAnswered)
if (loginRequestData.RequestApproved.HasValue && loginRequestData.ResponseDate.HasValue)
{
await _platformUtilsService.ShowDialogAsync(AppResources.ThisRequestIsNoLongerValid);
await Page.Navigation.PopModalAsync();

View File

@@ -1,5 +1,4 @@
using System;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions;
@@ -28,7 +27,6 @@ namespace Bit.App.Pages
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStateService _stateService;
private readonly ILogger _logger;
private readonly IOrganizationService _organizationService;
private string _orgIdentifier;
@@ -44,7 +42,6 @@ namespace Bit.App.Pages
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_logger = ServiceContainer.Resolve<ILogger>("logger");
_organizationService = ServiceContainer.Resolve<IOrganizationService>();
PageTitle = AppResources.Bitwarden;
@@ -66,25 +63,9 @@ namespace Bit.App.Pages
public async Task InitAsync()
{
try
if (string.IsNullOrWhiteSpace(OrgIdentifier))
{
if (await TryClaimedDomainLogin())
{
return;
}
if (string.IsNullOrWhiteSpace(OrgIdentifier))
{
OrgIdentifier = await _stateService.GetRememberedOrgIdentifierAsync();
}
}
catch (Exception ex)
{
_logger.Exception(ex);
}
finally
{
await _deviceActionService.HideLoadingAsync();
OrgIdentifier = await _stateService.GetRememberedOrgIdentifierAsync();
}
}
@@ -121,8 +102,9 @@ namespace Bit.App.Pages
var ssoToken = response.Token;
var passwordOptions = PasswordGenerationOptions.CreateDefault
.WithLength(64);
var passwordOptions = new PasswordGenerationOptions(true);
passwordOptions.Length = 64;
var codeVerifier = await _passwordGenerationService.GeneratePasswordAsync(passwordOptions);
var codeVerifierHash = await _cryptoFunctionService.HashAsync(codeVerifier, CryptoHashAlgorithm.Sha256);
@@ -225,37 +207,5 @@ namespace Bit.App.Pages
AppResources.AnErrorHasOccurred);
}
}
private async Task<bool> TryClaimedDomainLogin()
{
try
{
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
var userEmail = await _stateService.GetPreLoginEmailAsync();
var claimedDomainOrgDetails = await _organizationService.GetClaimedOrganizationDomainAsync(userEmail);
await _deviceActionService.HideLoadingAsync();
if (claimedDomainOrgDetails == null || !claimedDomainOrgDetails.SsoAvailable)
{
return false;
}
if (string.IsNullOrEmpty(claimedDomainOrgDetails.OrganizationIdentifier))
{
await _platformUtilsService.ShowDialogAsync(AppResources.OrganizationSsoIdentifierRequired, AppResources.AnErrorHasOccurred);
return false;
}
OrgIdentifier = claimedDomainOrgDetails.OrganizationIdentifier;
await LogInAsync();
return true;
}
catch (Exception ex)
{
HandleException(ex);
}
return false;
}
}
}

View File

@@ -25,7 +25,7 @@
</ContentPage.ToolbarItems>
<ScrollView>
<StackLayout Spacing="10">
<StackLayout Spacing="20">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row">
<Label
@@ -35,8 +35,7 @@
x:Name="_email"
Text="{Binding Email}"
Keyboard="Email"
StyleClass="box-value"
AutomationId="EmailAddressEntry"/>
StyleClass="box-value" />
</StackLayout>
<Grid StyleClass="box-row">
<Grid.RowDefinitions>
@@ -60,8 +59,7 @@
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
AutomationId="MasterPasswordEntry"/>
Grid.Column="0" />
<controls:IconButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
@@ -71,23 +69,11 @@
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"
AutomationId="PasswordVisibilityToggle"/>
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
</Grid>
<Label
StyleClass="box-sub-label"
Margin="0,0,0,10">
<Label.FormattedText>
<FormattedString>
<Span Text="{u:I18n Important}" TextColor="{DynamicResource InfoColor}"/>
<Span Text=": " TextColor="{DynamicResource InfoColor}"/>
<Span Text="{Binding MasterPasswordMininumCharactersDescription}" TextColor="{DynamicResource MutedColor}"/>
</FormattedString>
</Label.FormattedText>
</Label>
<controls:PasswordStrengthProgressBar
BindingContext="{Binding PasswordStrengthViewModel}"
Margin="0,0"/>
Text="{u:I18n MasterPasswordDescription}"
StyleClass="box-footer-label" />
</StackLayout>
<StackLayout StyleClass="box">
<Grid StyleClass="box-row">
@@ -112,8 +98,7 @@
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
AutomationId="ConfirmMasterPasswordEntry"/>
Grid.Column="0" />
<controls:IconButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
@@ -122,7 +107,6 @@
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationId="ConfirmPasswordVisibilityToggle"
AutomationProperties.Name="{u:I18n ToggleVisibility}"
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}" />
</Grid>
@@ -135,34 +119,20 @@
Text="{Binding Hint}"
StyleClass="box-value"
ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}"
AutomationId="MasterPasswordHintLabel" />
ReturnCommand="{Binding SubmitCommand}" />
</StackLayout>
<Label
Text="{u:I18n MasterPasswordHintDescription}"
StyleClass="box-footer-label" />
</StackLayout>
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-switch">
<Switch
IsToggled="{Binding CheckExposedMasterPassword}"
StyleClass="box-value"
HorizontalOptions="Start"
Margin="0, 0, 10, 0"
AutomationId="CheckExposedMasterPasswordToggle"/>
<Label
Text="{u:I18n CheckKnownDataBreachesForThisPassword}"
StyleClass="box-footer-label"
VerticalOptions="Center"/>
</StackLayout>
<StackLayout StyleClass="box-row, box-row-switch"
IsVisible="{Binding ShowTerms}">
<Switch
IsToggled="{Binding AcceptPolicies}"
StyleClass="box-value"
HorizontalOptions="Start"
Margin="0, 0, 10, 0"
AutomationId="AcceptPoliciesToggle"/>
Margin="0, 0, 10, 0"/>
<Label StyleClass="box-footer-label"
HorizontalOptions="Fill">
<Label.FormattedText>

View File

@@ -1,36 +1,28 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Request;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class RegisterPageViewModel : CaptchaProtectedViewModel, IPasswordStrengthable
public class RegisterPageViewModel : CaptchaProtectedViewModel
{
private readonly IDeviceActionService _deviceActionService;
private readonly II18nService _i18nService;
private readonly IEnvironmentService _environmentService;
private readonly IAuditService _auditService;
private readonly IApiService _apiService;
private readonly ICryptoService _cryptoService;
private readonly IPlatformUtilsService _platformUtilsService;
private string _email;
private string _masterPassword;
private bool _showPassword;
private bool _acceptPolicies;
private bool _checkExposedMasterPassword;
public RegisterPageViewModel()
{
@@ -40,15 +32,12 @@ namespace Bit.App.Pages
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
_auditService = ServiceContainer.Resolve<IAuditService>();
PageTitle = AppResources.CreateAccount;
TogglePasswordCommand = new Command(TogglePassword);
ToggleConfirmPasswordCommand = new Command(ToggleConfirmPassword);
SubmitCommand = new Command(async () => await SubmitAsync());
ShowTerms = !_platformUtilsService.IsSelfHost();
PasswordStrengthViewModel = new PasswordStrengthViewModel(this);
CheckExposedMasterPassword = true;
}
public ICommand PoliciesClickCommand => new Command<string>((url) =>
@@ -72,34 +61,6 @@ namespace Bit.App.Pages
get => _acceptPolicies;
set => SetProperty(ref _acceptPolicies, value);
}
public bool CheckExposedMasterPassword
{
get => _checkExposedMasterPassword;
set => SetProperty(ref _checkExposedMasterPassword, value);
}
public string MasterPassword
{
get => _masterPassword;
set
{
SetProperty(ref _masterPassword, value);
PasswordStrengthViewModel.CalculatePasswordStrength();
}
}
public string Email
{
get => _email;
set => SetProperty(ref _email, value);
}
public string Password => MasterPassword;
public List<string> UserInputs => PasswordStrengthViewModel.GetPasswordStrengthUserInput(Email);
public string MasterPasswordMininumCharactersDescription => string.Format(AppResources.YourMasterPasswordCannotBeRecoveredIfYouForgetItXCharactersMinimum,
Constants.MasterPasswordMinimumChars);
public PasswordStrengthViewModel PasswordStrengthViewModel { get; }
public bool ShowTerms { get; set; }
public Command SubmitCommand { get; }
public Command TogglePasswordCommand { get; }
@@ -107,10 +68,13 @@ namespace Bit.App.Pages
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
public string Name { get; set; }
public string Email { get; set; }
public string MasterPassword { get; set; }
public string ConfirmMasterPassword { get; set; }
public string Hint { get; set; }
public Action RegistrationSuccess { get; set; }
public Action CloseAction { get; set; }
protected override II18nService i18nService => _i18nService;
protected override IEnvironmentService environmentService => _environmentService;
protected override IDeviceActionService deviceActionService => _deviceActionService;
@@ -146,9 +110,9 @@ namespace Bit.App.Pages
AppResources.Ok);
return;
}
if (MasterPassword.Length < Constants.MasterPasswordMinimumChars)
if (MasterPassword.Length < 8)
{
await _platformUtilsService.ShowDialogAsync(string.Format(AppResources.MasterPasswordLengthValMessageX, Constants.MasterPasswordMinimumChars),
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordLengthValMessage,
AppResources.AnErrorHasOccurred, AppResources.Ok);
return;
}
@@ -164,10 +128,8 @@ namespace Bit.App.Pages
AppResources.AnErrorHasOccurred, AppResources.Ok);
return;
}
if (await IsPasswordWeakOrExposed())
{
return;
}
// TODO: Password strength check?
if (showLoading)
{
@@ -176,8 +138,9 @@ namespace Bit.App.Pages
Name = string.IsNullOrWhiteSpace(Name) ? null : Name;
Email = Email.Trim().ToLower();
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
var key = await _cryptoService.MakeKeyAsync(MasterPassword, Email, kdfConfig);
var kdf = KdfType.PBKDF2_SHA256;
var kdfIterations = 100_000;
var key = await _cryptoService.MakeKeyAsync(MasterPassword, Email, kdf, kdfIterations);
var encKey = await _cryptoService.MakeEncKeyAsync(key);
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, key);
var keys = await _cryptoService.MakeKeyPairAsync(encKey.Item1);
@@ -188,10 +151,8 @@ namespace Bit.App.Pages
MasterPasswordHash = hashedPassword,
MasterPasswordHint = Hint,
Key = encKey.Item2.EncryptedString,
Kdf = kdfConfig.Type,
KdfIterations = kdfConfig.Iterations,
KdfMemory = kdfConfig.Memory,
KdfParallelism = kdfConfig.Parallelism,
Kdf = kdf,
KdfIterations = kdfIterations,
Keys = new KeysRequest
{
PublicKey = keys.Item1,
@@ -199,7 +160,6 @@ namespace Bit.App.Pages
},
CaptchaResponse = _captchaToken,
};
// TODO: org invite?
try
@@ -248,43 +208,5 @@ namespace Bit.App.Pages
entry.Focus();
entry.CursorPosition = String.IsNullOrEmpty(ConfirmMasterPassword) ? 0 : ConfirmMasterPassword.Length;
}
private async Task<bool> IsPasswordWeakOrExposed()
{
try
{
var title = string.Empty;
var message = string.Empty;
var exposedPassword = CheckExposedMasterPassword ? await _auditService.PasswordLeakedAsync(MasterPassword) > 0 : false;
var weakPassword = PasswordStrengthViewModel.PasswordStrengthLevel <= PasswordStrengthLevel.Weak;
if (exposedPassword && weakPassword)
{
title = AppResources.WeakAndExposedMasterPassword;
message = AppResources.WeakPasswordIdentifiedAndFoundInADataBreachAlertDescription;
}
else if (exposedPassword)
{
title = AppResources.ExposedMasterPassword;
message = AppResources.PasswordFoundInADataBreachAlertDescription;
}
else if (weakPassword)
{
title = AppResources.WeakMasterPassword;
message = AppResources.WeakPasswordIdentifiedUseAStrongPasswordToProtectYourAccount;
}
if (exposedPassword || weakPassword)
{
return !await _platformUtilsService.ShowDialogAsync(message, title, AppResources.Yes, AppResources.No);
}
}
catch (Exception ex)
{
HandleException(ex);
}
return false;
}
}
}

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