1
0
mirror of https://github.com/bitwarden/mobile synced 2026-01-26 06:13:24 +00:00

Compare commits

..

19 Commits

Author SHA1 Message Date
Federico Maccaroni
a4e599f680 WIP Fix toolbar item avatar update issue iOS 2022-01-21 11:08:12 -03:00
Matt Portune
d41e1a0bc3 logout fixes and token handling improvements 2022-01-19 16:15:09 -05:00
Matt Portune
e717992b2b account list status enhancements 2022-01-19 12:06:45 -05:00
Matt Portune
d52a1bf668 Merge branch 'master' into feature-accountswitch 2022-01-19 09:31:13 -05:00
Matt Portune
796cf6dc25 vault timeout fixes 2022-01-18 22:55:22 -05:00
Matt Portune
59e7969856 cleanup 2022-01-18 19:31:32 -05:00
Matt Portune
afecc1f569 fixes 2022-01-18 17:43:20 -05:00
Matt Portune
a3922f7fbb fixes 2022-01-18 17:21:24 -05:00
Matt Portune
7e288c62f9 switch fixes 2022-01-18 15:20:16 -05:00
Matt Portune
ed2bb9b94e fixes for theme handling on account switching and re-adding existing account 2022-01-18 14:50:04 -05:00
Matt Portune
1c6a8964fe fixed Send tests 2022-01-18 13:51:09 -05:00
Matt Portune
6aa96b8964 Merge branch 'master' into feature-accountswitch 2022-01-18 10:36:54 -05:00
Matt Portune
f6d4b0a443 updates to send test logic 2022-01-18 10:28:34 -05:00
Matt Portune
e20ae26808 wip 2022-01-17 23:15:32 -05:00
Matt Portune
e3a8d3bb55 Merge branch 'master' into feature-accountswitch 2022-01-17 21:59:39 -05:00
Matt Portune
65739489a7 wip 2022-01-17 21:58:04 -05:00
Matt Portune
120fb70039 WIP 2022-01-17 00:41:14 -05:00
Matt Portune
d1ece79d2e Merge branch 'master' into feature-accountswitch 2022-01-16 22:48:04 -05:00
Matt Portune
ebae2585f6 Account switching 2022-01-16 22:43:37 -05:00
319 changed files with 4945 additions and 6176 deletions

View File

@@ -12,8 +12,6 @@
<string>Dist: Autofill 2021</string>
<key>com.8bit.bitwarden.find-login-action-extension</key>
<string>Dist: Extension 2021</string>
<key>com.8bit.bitwarden.share-extension</key>
<string>Dist: Share Extension 2021</string>
</dict>
</dict>
</plist>

View File

@@ -366,18 +366,6 @@ jobs:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
- name: Login to Azure - Prod Subscription
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
with:
keyvault: "bitwarden-prod-kv"
secrets: "appcenter-ios-token"
- name: Decrypt secrets
env:
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
@@ -394,8 +382,6 @@ jobs:
--output $HOME/secrets/dist_bitwarden.mobileprovision ./.github/secrets/dist_bitwarden.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_extension.mobileprovision ./.github/secrets/dist_extension.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_share_extension.mobileprovision ./.github/secrets/dist_share_extension.mobileprovision.gpg
shell: bash
- name: Increment version
@@ -409,7 +395,6 @@ jobs:
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.ShareExtension/Info.plist
shell: bash
- name: Update Entitlements
@@ -443,7 +428,6 @@ jobs:
AUTOFILL_PROFILE_PATH=$HOME/secrets/dist_autofill.mobileprovision
BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_bitwarden.mobileprovision
EXTENSION_PROFILE_PATH=$HOME/secrets/dist_extension.mobileprovision
SHARE_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_share_extension.mobileprovision
PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles
mkdir -p "$PROFILES_DIR_PATH"
@@ -456,9 +440,6 @@ jobs:
EXTENSION_UUID=$(grep UUID -A1 -a $EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$EXTENSION_UUID.mobileprovision"
SHARE_EXTENSION_UUID=$(grep UUID -A1 -a $SHARE_EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $SHARE_EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$SHARE_EXTENSION_UUID.mobileprovision"
shell: bash
- name: Restore packages
@@ -491,48 +472,13 @@ jobs:
-exportOptionsPlist $EXPORT_OPTIONS_PATH
shell: bash
- name: Copy all dSYMs files to upload
run: |
ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
EXPORT_PATH="./bitwarden-export"
cp -r $ARCHIVE_DSYMS_PATH $EXPORT_PATH
shell: bash
- name: Upload App Store .ipa & dSYMs artifacts
- name: Upload App Store .ipa artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
with:
name: Bitwarden iOS
path: |
./bitwarden-export/Bitwarden.ipa
./bitwarden-export/dSYMs/*.*
name: Bitwarden.ipa
path: ./bitwarden-export/Bitwarden.ipa
if-no-files-found: error
- name: Install AppCenter CLI
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'
uses: actions/setup-node@v2
with:
node-version: '14'
- run: npm install -g appcenter-cli
- 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'
env:
APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }}
run: |
appcenter crashes upload-symbols -a kspearrin/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
shell: bash
- name: Deploy to App Store
if: |
(github.ref == 'refs/heads/master'

View File

@@ -35,7 +35,7 @@ jobs:
- name: Retrieve Mobile release version
id: retrieve-mobile-version
run: |
ver=$(sed -E -n '/^<manifest/s/^.*[ ]android:versionName="([^"]+)".*$/\1/p' ./src/Android/Properties/AndroidManifest.xml | tr -d '"')
ver=$(sed -n -e '/android:versionName/ s/.*\= *//p' ./src/Android/Properties/AndroidManifest.xml | tr -d '"')
echo "::set-output name=mobile_version::${ver}"
shell: bash

View File

@@ -44,12 +44,6 @@ jobs:
version: ${{ github.event.inputs.version_number }}
file_path: "./src/iOS.Extension/Info.plist"
- name: Bump Version - iOS.ShareExtension
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@03ad9a873c39cdc95dd8d77dbbda67f84db43945
with:

View File

@@ -38,14 +38,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS", "src\iOS\iOS.csproj",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Extension", "src\iOS.Extension\iOS.Extension.csproj", "{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "test\Common\Common.csproj", "{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\iOS.ShareExtension\iOS.ShareExtension.csproj", "{F8C3F648-EA5A-4719-8005-85D1690B1655}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -327,6 +325,35 @@ Global
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.Build.0 = Release|iPhone
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|Any CPU.Build.0 = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhone.Build.0 = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.ActiveCfg = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Build.0 = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Deploy.0 = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhone.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhone.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|Any CPU.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
@@ -387,65 +414,6 @@ Global
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.Build.0 = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|Any CPU.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|Any CPU.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.ActiveCfg = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.Build.0 = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|Any CPU.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|Any CPU.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.ActiveCfg = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.Build.0 = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.ActiveCfg = Debug|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.Build.0 = Debug|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhone.ActiveCfg = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhone.Build.0 = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.ActiveCfg = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.Build.0 = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.ActiveCfg = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.Build.0 = Release|iPhone
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|Any CPU.Build.0 = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhone.Build.0 = AppStore|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.ActiveCfg = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Build.0 = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Deploy.0 = Debug|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhone.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhone.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|Any CPU.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.ActiveCfg = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -460,10 +428,9 @@ Global
{E71F3053-056C-4381-9638-048ED73BDFF6} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{599E0201-420A-4C3E-A7BA-5349F72E0B15} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39} = {8904C536-C67D-420F-9971-51B26574C3AA}
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {D10CA4A9-F866-40E1-B658-F69051236C71}
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}

View File

@@ -10,7 +10,6 @@ using Android.Views;
using Android.Views.Accessibility;
using Android.Widget;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
@@ -25,7 +24,7 @@ namespace Bit.Droid.Accessibility
private const string BitwardenPackage = "com.x8bit.bitwarden";
private const string BitwardenWebsite = "vault.bitwarden.com";
private IStorageService _storageService;
private IStateService _stateService;
private IBroadcasterService _broadcasterService;
private DateTime? _lastSettingsReload = null;
private TimeSpan _settingsReloadSpan = TimeSpan.FromMinutes(1);
@@ -444,9 +443,9 @@ namespace Bit.Droid.Accessibility
private void LoadServices()
{
if (_storageService == null)
if (_stateService == null)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
}
if (_broadcasterService == null)
{
@@ -460,12 +459,12 @@ namespace Bit.Droid.Accessibility
if (_lastSettingsReload == null || (now - _lastSettingsReload.Value) > _settingsReloadSpan)
{
_lastSettingsReload = now;
var uris = await _storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
var uris = await _stateService.GetAutofillBlacklistedUrisAsync();
if (uris != null)
{
_blacklistedUris = new HashSet<string>(uris);
}
var isAutoFillTileAdded = await _storageService.GetAsync<bool?>(Constants.AutofillTileAdded);
var isAutoFillTileAdded = await _stateService.GetAutofillTileAddedAsync();
AccessibilityHelpers.IsAutofillTileAdded = isAutoFillTileAdded.GetValueOrDefault();
}
}

View File

@@ -153,7 +153,7 @@
<Compile Include="Utilities\IntentExtensions.cs" />
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\bwi-font.ttf" />
<AndroidAsset Include="Assets\FontAwesome.ttf" />
<AndroidAsset Include="Assets\RobotoMono_Regular.ttf" />
<AndroidAsset Include="Assets\MaterialIcons_Regular.ttf" />
<None Include="8bit.keystore.enc" />
@@ -171,7 +171,8 @@
<AndroidResource Include="Resources\drawable-xxhdpi\logo_legacy.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\logo_white_legacy.png" />
<AndroidResource Include="Resources\drawable\card.xml" />
<AndroidResource Include="Resources\drawable\cog.xml" />
<AndroidResource Include="Resources\drawable\cog_environment.xml" />
<AndroidResource Include="Resources\drawable\cog_settings.xml" />
<AndroidResource Include="Resources\drawable\icon.xml" />
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
<AndroidResource Include="Resources\drawable\ic_warning.xml" />
@@ -182,16 +183,15 @@
<AndroidResource Include="Resources\drawable\login.xml" />
<AndroidResource Include="Resources\drawable\logo.xml" />
<AndroidResource Include="Resources\drawable\logo_white.xml" />
<AndroidResource Include="Resources\drawable\send.xml" />
<AndroidResource Include="Resources\drawable\paper_plane.xml" />
<AndroidResource Include="Resources\drawable\pencil.xml" />
<AndroidResource Include="Resources\drawable\plus.xml" />
<AndroidResource Include="Resources\drawable\generate.xml" />
<AndroidResource Include="Resources\drawable\refresh.xml" />
<AndroidResource Include="Resources\drawable\search.xml" />
<AndroidResource Include="Resources\drawable\shield.xml" />
<AndroidResource Include="Resources\drawable-v23\splash_screen.xml" />
<AndroidResource Include="Resources\drawable-v23\splash_screen_dark.xml" />
<AndroidResource Include="Resources\drawable\switch_thumb.xml" />
<AndroidResource Include="Resources\layout\progress_dialog_layout.xml" />
<AndroidResource Include="Resources\layout\Tabbar.axml" />
<AndroidResource Include="Resources\layout\Toolbar.axml" />
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />

Binary file not shown.

Binary file not shown.

View File

@@ -160,7 +160,7 @@ namespace Bit.Droid.Autofill
return new List<FilledItem>();
}
public static FillResponse.Builder CreateFillResponse(Parser parser, List<FilledItem> items, bool locked,
public static FillResponse BuildFillResponse(Parser parser, List<FilledItem> items, bool locked,
bool inlineAutofillEnabled, FillRequest fillRequest = null)
{
// Acquire inline presentation specs on Android 11+
@@ -211,8 +211,9 @@ namespace Bit.Droid.Autofill
}
responseBuilder.AddDataset(BuildVaultDataset(parser.ApplicationContext, parser.FieldCollection,
parser.Uri, locked, inlinePresentationSpecs));
AddSaveInfo(parser, fillRequest, responseBuilder, parser.FieldCollection);
responseBuilder.SetIgnoredIds(parser.FieldCollection.IgnoreAutofillIds.ToArray());
return responseBuilder;
return responseBuilder.Build();
}
public static Dataset BuildDataset(Context context, FieldCollection fields, FilledItem filledItem,

View File

@@ -9,10 +9,6 @@ using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Utilities;
#if !FDROID
using Microsoft.AppCenter.Crashes;
#endif
using System;
using System.Collections.Generic;
using System.Linq;
@@ -26,145 +22,121 @@ namespace Bit.Droid.Autofill
{
private ICipherService _cipherService;
private IVaultTimeoutService _vaultTimeoutService;
private IStorageService _storageService;
private IPolicyService _policyService;
private IUserService _userService;
private IStateService _stateService;
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal,
FillCallback callback)
{
try
var structure = request.FillContexts?.LastOrDefault()?.Structure;
if (structure == null)
{
var structure = request.FillContexts?.LastOrDefault()?.Structure;
if (structure == null)
{
return;
}
var parser = new Parser(structure, ApplicationContext);
parser.Parse();
if (_storageService == null)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
}
var shouldAutofill = await parser.ShouldAutofillAsync(_storageService);
if (!shouldAutofill)
{
return;
}
var inlineAutofillEnabled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
if (_vaultTimeoutService == null)
{
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
}
List<FilledItem> items = null;
await _vaultTimeoutService.CheckVaultTimeoutAsync();
var locked = await _vaultTimeoutService.IsLockedAsync();
if (!locked)
{
if (_cipherService == null)
{
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
}
items = await AutofillHelpers.GetFillItemsAsync(parser, _cipherService);
}
// build response
var response = AutofillHelpers.CreateFillResponse(parser, items, locked, inlineAutofillEnabled, request);
var disableSavePrompt = await _storageService.GetAsync<bool?>(Constants.AutofillDisableSavePromptKey);
if (!disableSavePrompt.GetValueOrDefault())
{
AutofillHelpers.AddSaveInfo(parser, request, response, parser.FieldCollection);
}
callback.OnSuccess(response.Build());
return;
}
catch (Exception e)
var parser = new Parser(structure, ApplicationContext);
parser.Parse();
if (_stateService == null)
{
#if !FDROID
Crashes.TrackError(e);
#endif
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
}
var shouldAutofill = await parser.ShouldAutofillAsync(_stateService);
if (!shouldAutofill)
{
return;
}
var inlineAutofillEnabled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
if (_vaultTimeoutService == null)
{
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
}
List<FilledItem> items = null;
await _vaultTimeoutService.CheckVaultTimeoutAsync();
var locked = await _vaultTimeoutService.IsLockedAsync();
if (!locked)
{
if (_cipherService == null)
{
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
}
items = await AutofillHelpers.GetFillItemsAsync(parser, _cipherService);
}
// build response
var response = AutofillHelpers.BuildFillResponse(parser, items, locked, inlineAutofillEnabled, request);
callback.OnSuccess(response);
}
public async override void OnSaveRequest(SaveRequest request, SaveCallback callback)
{
try
var structure = request.FillContexts?.LastOrDefault()?.Structure;
if (structure == null)
{
var structure = request.FillContexts?.LastOrDefault()?.Structure;
if (structure == null)
{
return;
}
if (_storageService == null)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
}
var disableSavePrompt = await _storageService.GetAsync<bool?>(Constants.AutofillDisableSavePromptKey);
if (disableSavePrompt.GetValueOrDefault())
{
return;
}
_policyService ??= ServiceContainer.Resolve<IPolicyService>("policyService");
var personalOwnershipPolicyApplies = await _policyService.PolicyAppliesToUser(PolicyType.PersonalOwnership);
if (personalOwnershipPolicyApplies)
{
return;
}
var parser = new Parser(structure, ApplicationContext);
parser.Parse();
var savedItem = parser.FieldCollection.GetSavedItem();
if (savedItem == null)
{
Toast.MakeText(this, "Unable to save this form.", ToastLength.Short).Show();
return;
}
var intent = new Intent(this, typeof(MainActivity));
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTop);
intent.PutExtra("autofillFramework", true);
intent.PutExtra("autofillFrameworkSave", true);
intent.PutExtra("autofillFrameworkType", (int)savedItem.Type);
switch (savedItem.Type)
{
case CipherType.Login:
intent.PutExtra("autofillFrameworkName", parser.Uri
.Replace(Constants.AndroidAppProtocol, string.Empty)
.Replace("https://", string.Empty)
.Replace("http://", string.Empty));
intent.PutExtra("autofillFrameworkUri", parser.Uri);
intent.PutExtra("autofillFrameworkUsername", savedItem.Login.Username);
intent.PutExtra("autofillFrameworkPassword", savedItem.Login.Password);
break;
case CipherType.Card:
intent.PutExtra("autofillFrameworkCardName", savedItem.Card.Name);
intent.PutExtra("autofillFrameworkCardNumber", savedItem.Card.Number);
intent.PutExtra("autofillFrameworkCardExpMonth", savedItem.Card.ExpMonth);
intent.PutExtra("autofillFrameworkCardExpYear", savedItem.Card.ExpYear);
intent.PutExtra("autofillFrameworkCardCode", savedItem.Card.Code);
break;
default:
Toast.MakeText(this, "Unable to save this type of form.", ToastLength.Short).Show();
return;
}
StartActivity(intent);
return;
}
catch (Exception e)
if (_stateService == null)
{
#if !FDROID
Crashes.TrackError(e);
#endif
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
}
var disableSavePrompt = await _stateService.GetAutofillDisableSavePromptAsync();
if (disableSavePrompt.GetValueOrDefault())
{
return;
}
_policyService ??= ServiceContainer.Resolve<IPolicyService>("policyService");
var personalOwnershipPolicyApplies = await _policyService.PolicyAppliesToUser(PolicyType.PersonalOwnership);
if (personalOwnershipPolicyApplies)
{
return;
}
var parser = new Parser(structure, ApplicationContext);
parser.Parse();
var savedItem = parser.FieldCollection.GetSavedItem();
if (savedItem == null)
{
Toast.MakeText(this, "Unable to save this form.", ToastLength.Short).Show();
return;
}
var intent = new Intent(this, typeof(MainActivity));
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTop);
intent.PutExtra("autofillFramework", true);
intent.PutExtra("autofillFrameworkSave", true);
intent.PutExtra("autofillFrameworkType", (int)savedItem.Type);
switch (savedItem.Type)
{
case CipherType.Login:
intent.PutExtra("autofillFrameworkName", parser.Uri
.Replace(Constants.AndroidAppProtocol, string.Empty)
.Replace("https://", string.Empty)
.Replace("http://", string.Empty));
intent.PutExtra("autofillFrameworkUri", parser.Uri);
intent.PutExtra("autofillFrameworkUsername", savedItem.Login.Username);
intent.PutExtra("autofillFrameworkPassword", savedItem.Login.Password);
break;
case CipherType.Card:
intent.PutExtra("autofillFrameworkCardName", savedItem.Card.Name);
intent.PutExtra("autofillFrameworkCardNumber", savedItem.Card.Number);
intent.PutExtra("autofillFrameworkCardExpMonth", savedItem.Card.ExpMonth);
intent.PutExtra("autofillFrameworkCardExpYear", savedItem.Card.ExpYear);
intent.PutExtra("autofillFrameworkCardCode", savedItem.Card.Code);
break;
default:
Toast.MakeText(this, "Unable to save this type of form.", ToastLength.Short).Show();
return;
}
StartActivity(intent);
}
}
}

View File

@@ -80,13 +80,13 @@ namespace Bit.Droid.Autofill
}
}
public async Task<bool> ShouldAutofillAsync(IStorageService storageService)
public async Task<bool> ShouldAutofillAsync(IStateService stateService)
{
var fillable = !string.IsNullOrWhiteSpace(Uri) && !AutofillHelpers.BlacklistedUris.Contains(Uri) &&
FieldCollection != null && FieldCollection.Fillable;
if (fillable)
{
var blacklistedUris = await storageService.GetAsync<List<string>>(Constants.AutofillBlacklistedUrisKey);
var blacklistedUris = await stateService.GetAutofillBlacklistedUrisAsync();
if (blacklistedUris != null && blacklistedUris.Count > 0)
{
fillable = !new HashSet<string>(blacklistedUris).Contains(Uri);

View File

@@ -1,24 +1,25 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.App;
using Android.Content.PM;
using Android.Nfc;
using Android.OS;
using Android.Runtime;
using AndroidX.Core.Content;
using Bit.App.Abstractions;
using Bit.App.Models;
using Bit.App.Utilities;
using Android.OS;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using System.Linq;
using Bit.App.Abstractions;
using Bit.Core.Utilities;
using Bit.Droid.Receivers;
using Bit.Core.Abstractions;
using System.IO;
using System;
using Android.Content;
using Bit.Droid.Utilities;
using Bit.Droid.Receivers;
using Bit.App.Models;
using Bit.Core.Enums;
using Android.Nfc;
using System.Threading.Tasks;
using AndroidX.Core.Content;
using Bit.App.Utilities;
using ZXing.Net.Mobile.Android;
using Android.Util;
namespace Bit.Droid
{
@@ -32,7 +33,7 @@ namespace Bit.Droid
private IDeviceActionService _deviceActionService;
private IMessagingService _messagingService;
private IBroadcasterService _broadcasterService;
private IUserService _userService;
private IStateService _stateService;
private IAppIdService _appIdService;
private IEventService _eventService;
private PendingIntent _eventUploadPendingIntent;
@@ -53,7 +54,7 @@ namespace Bit.Droid
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_appIdService = ServiceContainer.Resolve<IAppIdService>("appIdService");
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
@@ -70,7 +71,7 @@ namespace Bit.Droid
}
#if !FDROID
var appCenterHelper = new AppCenterHelper(_appIdService, _userService);
var appCenterHelper = new AppCenterHelper(_appIdService, _stateService);
var appCenterTask = appCenterHelper.InitAsync();
#endif
@@ -119,9 +120,6 @@ namespace Bit.Droid
base.OnResume();
Xamarin.Essentials.Platform.OnResume();
AppearanceAdjustments();
ThemeManager.UpdateThemeOnPagesAsync();
if (_deviceActionService.SupportsNfc())
{
try
@@ -375,7 +373,7 @@ namespace Bit.Droid
{
Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor);
Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor);
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(true), ThemeManager.OsDarkModeEnabled());
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(), ThemeManager.OsDarkModeEnabled());
}
private void ExitApp()

View File

@@ -18,8 +18,6 @@ using Plugin.Fingerprint;
using Xamarin.Android.Net;
using System.Net.Http;
using System.Net;
using Bit.App.Utilities;
using Bit.App.Pages;
#if !FDROID
using Android.Gms.Security;
#endif
@@ -47,20 +45,6 @@ namespace Bit.Droid
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
ServiceContainer.Init(deviceActionService.DeviceUserAgent, Constants.ClearCiphersCacheKey,
Constants.AndroidAllClearCipherCacheKeys);
// TODO: Update when https://github.com/bitwarden/mobile/pull/1662 gets merged
var deleteAccountActionFlowExecutioner = new DeleteAccountActionFlowExecutioner(
ServiceContainer.Resolve<IApiService>("apiService"),
ServiceContainer.Resolve<IMessagingService>("messagingService"),
ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService"),
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"));
ServiceContainer.Register<IDeleteAccountActionFlowExecutioner>("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner);
var verificationActionsFlowHelper = new VerificationActionsFlowHelper(
ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService"),
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
ServiceContainer.Resolve<ICryptoService>("cryptoService"));
ServiceContainer.Register<IVerificationActionsFlowHelper>("verificationActionsFlowHelper", verificationActionsFlowHelper);
}
#if !FDROID
if (Build.VERSION.SdkInt <= BuildVersionCodes.Kitkat)
@@ -113,13 +97,14 @@ namespace Bit.Droid
var secureStorageService = new SecureStorageService();
var cryptoPrimitiveService = new CryptoPrimitiveService();
var mobileStorageService = new MobileStorageService(preferencesStorage, liteDbStorage);
var deviceActionService = new DeviceActionService(mobileStorageService, messagingService,
var stateService = new StateService(mobileStorageService, secureStorageService);
var deviceActionService = new DeviceActionService(stateService, messagingService,
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
broadcasterService);
var biometricService = new BiometricService();
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
var cryptoService = new CryptoService(mobileStorageService, secureStorageService, cryptoFunctionService);
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService);
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
@@ -129,7 +114,8 @@ namespace Bit.Droid
ServiceContainer.Register<ICryptoPrimitiveService>("cryptoPrimitiveService", cryptoPrimitiveService);
ServiceContainer.Register<IStorageService>("storageService", mobileStorageService);
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(mobileStorageService));
ServiceContainer.Register<IStateService>("stateService", stateService);
ServiceContainer.Register<IClipboardService>("clipboardService", new ClipboardService(stateService));
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
@@ -148,7 +134,7 @@ namespace Bit.Droid
ServiceContainer.Register<IPushNotificationListenerService>(
"pushNotificationListenerService", notificationListenerService);
var androidPushNotificationService = new AndroidPushNotificationService(
mobileStorageService, notificationListenerService);
stateService, notificationListenerService);
ServiceContainer.Register<IPushNotificationService>(
"pushNotificationService", androidPushNotificationService);
#endif
@@ -164,10 +150,6 @@ namespace Bit.Droid
private async Task BootstrapAsync()
{
var disableFavicon = await ServiceContainer.Resolve<IStorageService>("storageService")
.GetAsync<bool?>(Constants.DisableFaviconKey);
await ServiceContainer.Resolve<IStateService>("stateService").SaveAsync(
Constants.DisableFaviconKey, disableFavicon);
await ServiceContainer.Resolve<IEnvironmentService>("environmentService").SetUrlsFromStorageAsync();
}
}

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="2.16.2" 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="2.15.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>

View File

@@ -16,10 +16,10 @@ namespace Bit.Droid.Push
{
public async override void OnNewToken(string token)
{
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
var stateService = ServiceContainer.Resolve<IStateService>("stateService");
var pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>("pushNotificationService");
await storageService.SaveAsync(Core.Constants.PushRegisteredTokenKey, token);
await stateService.SetPushRegisteredTokenAsync(token);
await pushNotificationService.RegisterAsync();
}

View File

@@ -1,5 +1,4 @@
using System;
using Android.App;
using Android.App;
using Android.Content;
using Bit.App.Abstractions;
using Bit.App.Utilities;
@@ -14,9 +13,10 @@ namespace Bit.Droid.Receivers
{
public override async void OnReceive(Context context, Intent intent)
{
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
await AppHelpers.PerformUpdateTasksAsync(ServiceContainer.Resolve<ISyncService>("syncService"),
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"), storageService);
await AppHelpers.PerformUpdateTasksAsync(
ServiceContainer.Resolve<ISyncService>("syncService"),
ServiceContainer.Resolve<IDeviceActionService>("deviceActionService"),
ServiceContainer.Resolve<IStateService>("stateService"));
}
}
}

View File

@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="640dp"
android:height="512dp"
android:viewportWidth="640"
android:viewportHeight="512">
android:width="24dp"
android:height="24dp"
android:viewportWidth="2048"
android:viewportHeight="2048">
<path
android:fillColor="#FF777777"
android:pathData="M532.864 80h-425.728c-12.067 0.695-23.371 6.129-31.451 15.118s-12.279 20.808-11.686 32.882v256c-0.594 12.074 3.606 23.891 11.686 32.88 8.079 8.992 19.383 14.425 31.451 15.12h425.599c12.090-0.663 23.427-6.083 31.533-15.075 8.109-8.995 12.327-20.832 11.731-32.925v-256c0.595-12.073-3.606-23.891-11.683-32.882-8.080-8.99-19.385-14.424-31.453-15.118v0zM107.264 112h425.6c4.013 0.248 7.766 2.066 10.451 5.059 2.682 2.995 4.077 6.925 3.885 10.941v28.8c0.186 3.465-1.011 6.862-3.328 9.447-2.313 2.585-5.558 4.147-9.024 4.345h-429.728c-3.459-0.207-6.695-1.773-9.003-4.356s-3.501-5.976-3.317-9.436v-28.8c-0.192-4.016 1.204-7.946 3.886-10.941s6.437-4.812 10.45-5.059h0.128zM532.864 399.808h-425.728c-3.978-0.247-7.703-2.038-10.38-4.992-2.677-2.95-4.097-6.832-3.956-10.816v-167.424c-0.183-3.465 1.013-6.862 3.329-9.446s5.559-4.147 9.023-4.345h429.76c3.466 0.198 6.711 1.761 9.024 4.345 2.317 2.584 3.514 5.982 3.328 9.447v167.424c0.192 4.026-1.213 7.965-3.907 10.963-2.697 2.995-6.467 4.806-10.493 5.037v-0.192zM494.496 340.96h-75.296c-2.717 0.358-5.213 1.69-7.021 3.75s-2.803 4.711-2.803 7.45c0 2.743 0.995 5.389 2.803 7.45s4.304 3.395 7.021 3.75h75.424c2.72-0.355 5.216-1.69 7.024-3.75s2.803-4.707 2.803-7.45c0-2.739-0.995-5.389-2.803-7.45s-4.304-3.392-7.024-3.75h-0.128z" />
android:pathData="M1824,256C1868,256 1905.67,271.67 1937,303C1968.33,334.33 1984,372 1984,416L1984,1632C1984,1676 1968.33,1713.67 1937,1745C1905.67,1776.33 1868,1792 1824,1792L224,1792C180,1792 142.33,1776.33 111,1745C79.67,1713.67 64,1676 64,1632L64,416C64,372 79.67,334.33 111,303C142.33,271.67 180,256 224,256L1824,256ZM224,384C215.33,384 207.83,387.17 201.5,393.5C195.17,399.83 192,407.33 192,416L192,640L1856,640L1856,416C1856,407.33 1852.83,399.83 1846.5,393.5C1840.17,387.17 1832.67,384 1824,384L224,384ZM1824,1664C1832.67,1664 1840.17,1660.83 1846.5,1654.5C1852.83,1648.17 1856,1640.67 1856,1632L1856,1024L192,1024L192,1632C192,1640.67 195.17,1648.17 201.5,1654.5C207.83,1660.83 215.33,1664 224,1664L1824,1664ZM320,1536L320,1408L576,1408L576,1536L320,1536ZM704,1536L704,1408L1088,1408L1088,1536L704,1536Z" />
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="640dp"
android:height="512dp"
android:viewportWidth="640"
android:viewportHeight="512">
<path
android:fillColor="#FF000000"
android:pathData="M532.989 289.887l-3.872-2.528c-3.197-1.866-5.744-4.667-7.299-8.026-1.558-3.358-2.048-7.113-1.405-10.759v-24.64c-0.682-3.57-0.202-7.266 1.37-10.542s4.154-5.964 7.366-7.666l4.768-2.4c12.013-7.054 20.768-18.547 24.384-32 3.421-13.333 1.661-27.466-4.928-39.552l-25.056-43.872c-7.011-11.727-18.259-20.313-31.418-23.987-13.161-3.674-27.229-2.155-39.303 4.243l-4.384 2.208c-3.286 1.769-6.983 2.63-10.711 2.496-3.731-0.135-7.357-1.261-10.505-3.263-7.082-4.796-14.579-8.951-22.4-12.416-3.28-1.636-6.038-4.157-7.962-7.278s-2.935-6.719-2.918-10.386v-6.528c0.099-6.965-1.197-13.879-3.808-20.335s-6.49-12.326-11.401-17.264c-4.915-4.937-10.765-8.842-17.209-11.486s-13.351-3.972-20.317-3.907h-51.2c-6.952-0.043-13.842 1.301-20.267 3.954s-12.257 6.561-17.154 11.496c-4.896 4.935-8.758 10.797-11.361 17.243s-3.892 13.347-3.794 20.298v5.472c0.032 3.614-0.938 7.165-2.802 10.261s-4.55 5.614-7.758 7.275c-5.691 2.572-11.197 5.533-16.48 8.864l-6.080 3.584c-3.102 2.221-6.788 3.481-10.6 3.623s-7.582-0.839-10.84-2.823l-3.968-1.952c-5.856-3.516-12.377-5.778-19.153-6.642s-13.656-0.314-20.208 1.618c-13.446 3.716-24.885 12.58-31.84 24.672l-24.96 43.68c-3.566 6.048-5.867 12.757-6.763 19.721s-0.37 14.037 1.547 20.791c1.743 6.495 4.779 12.571 8.925 17.866s9.317 9.699 15.203 12.95l2.88 2.848 1.312 0.928c3.197 1.867 5.744 4.667 7.3 8.026s2.046 7.113 1.403 10.758v24.704c0.326 3.533-0.314 7.087-1.853 10.283s-3.918 5.913-6.883 7.861l-4.768 2.4c-11.724 7.217-20.258 18.63-23.866 31.917s-2.020 27.447 4.442 39.603l25.088 43.872c6.806 11.955 18.058 20.739 31.308 24.445 13.25 3.702 27.425 2.026 39.445-4.669l4.352-2.176c3.287-1.792 6.994-2.669 10.736-2.547 3.742 0.125 7.382 1.248 10.544 3.251 7.082 4.797 14.578 8.954 22.4 12.416 3.281 1.635 6.038 4.157 7.962 7.28 1.923 3.12 2.934 6.717 2.918 10.384v5.472c-0.102 6.954 1.185 13.859 3.788 20.31s6.468 12.317 11.368 17.251c4.901 4.938 10.738 8.845 17.169 11.495s13.327 3.987 20.282 3.936h51.2c6.957 0.051 13.856-1.286 20.288-3.936s12.272-6.557 17.175-11.491c4.902-4.938 8.771-10.8 11.379-17.251 2.605-6.451 3.897-13.357 3.798-20.313v-5.472c-0.032-3.613 0.938-7.165 2.803-10.259 1.863-3.098 4.547-5.616 7.757-7.277 5.683-2.567 11.181-5.526 16.448-8.864l1.376-0.8 4.704-2.784c3.111-2.211 6.803-3.466 10.618-3.606 3.815-0.144 7.587 0.832 10.854 2.807l3.968 1.952c5.993 3.568 12.653 5.878 19.565 6.791 6.915 0.912 13.945 0.409 20.659-1.478 6.599-1.805 12.755-4.95 18.080-9.248 5.325-4.295 9.706-9.645 12.864-15.712l24.96-43.68c3.504-5.907 5.757-12.474 6.615-19.289 0.861-6.816 0.307-13.735-1.622-20.327-3.584-13.397-12.298-24.846-24.256-31.873zM319.997 346.752c-17.949 0-35.495-5.322-50.419-15.296-14.924-9.971-26.556-24.144-33.424-40.727s-8.666-34.83-5.165-52.434c3.502-17.604 12.145-33.775 24.837-46.466s28.862-21.335 46.466-24.837c17.604-3.502 35.852-1.704 52.434 5.164s30.755 18.501 40.73 33.425c9.971 14.924 15.293 32.47 15.293 50.419 0 24.069-9.562 47.153-26.579 64.17-17.021 17.021-40.103 26.582-64.173 26.582z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1792"
android:viewportHeight="1792">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M1152,896q0,-106 -75,-181t-181,-75 -181,75 -75,181 75,181 181,75 181,-75 75,-181zM1664,787v222q0,12 -8,23t-20,13l-185,28q-19,54 -39,91 35,50 107,138 10,12 10,25t-9,23q-27,37 -99,108t-94,71q-12,0 -26,-9l-138,-108q-44,23 -91,38 -16,136 -29,186 -7,28 -36,28h-222q-14,0 -24.5,-8.5t-11.5,-21.5l-28,-184q-49,-16 -90,-37l-141,107q-10,9 -25,9 -14,0 -25,-11 -126,-114 -165,-168 -7,-10 -7,-23 0,-12 8,-23 15,-21 51,-66.5t54,-70.5q-27,-50 -41,-99l-183,-27q-13,-2 -21,-12.5t-8,-23.5v-222q0,-12 8,-23t19,-13l186,-28q14,-46 39,-92 -40,-57 -107,-138 -10,-12 -10,-24 0,-10 9,-23 26,-36 98.5,-107.5t94.5,-71.5q13,0 26,10l138,107q44,-23 91,-38 16,-136 29,-186 7,-28 36,-28h222q14,0 24.5,8.5t11.5,21.5l28,184q49,16 90,37l142,-107q9,-9 24,-9 13,0 25,10 129,119 165,170 7,8 7,22 0,12 -8,23 -15,21 -51,66.5t-54,70.5q26,50 41,98l183,28q13,2 21,12.5t8,23.5z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1792"
android:viewportHeight="1792">
<path
android:fillColor="#FF000000"
android:pathData="M1152,896q0,-106 -75,-181t-181,-75 -181,75 -75,181 75,181 181,75 181,-75 75,-181zM1664,787v222q0,12 -8,23t-20,13l-185,28q-19,54 -39,91 35,50 107,138 10,12 10,25t-9,23q-27,37 -99,108t-94,71q-12,0 -26,-9l-138,-108q-44,23 -91,38 -16,136 -29,186 -7,28 -36,28h-222q-14,0 -24.5,-8.5t-11.5,-21.5l-28,-184q-49,-16 -90,-37l-141,107q-10,9 -25,9 -14,0 -25,-11 -126,-114 -165,-168 -7,-10 -7,-23 0,-12 8,-23 15,-21 51,-66.5t54,-70.5q-27,-50 -41,-99l-183,-27q-13,-2 -21,-12.5t-8,-23.5v-222q0,-12 8,-23t19,-13l186,-28q14,-46 39,-92 -40,-57 -107,-138 -10,-12 -10,-24 0,-10 9,-23 26,-36 98.5,-107.5t94.5,-71.5q13,0 26,10l138,107q44,-23 91,-38 16,-136 29,-186 7,-28 36,-28h222q14,0 24.5,8.5t11.5,21.5l28,184q49,16 90,37l142,-107q9,-9 24,-9 13,0 25,10 129,119 165,170 7,8 7,22 0,12 -8,23 -15,21 -51,66.5t-54,70.5q26,50 41,98l183,28q13,2 21,12.5t8,23.5z" />
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="640dp"
android:height="512dp"
android:viewportWidth="640"
android:viewportHeight="512">
<path
android:fillColor="#FF000000"
android:pathData="M320.64 512.32c141.385 0 256-114.615 256-256s-114.615-256-256-256c-141.385 0-256 114.615-256 256s114.615 256 256 256zM488 278.402c3.315 0.796 6.199 2.832 8.055 5.69 1.859 2.858 2.547 6.318 1.93 9.671-8.887 36.99-28.976 70.334-57.529 95.474s-64.173 40.845-101.99 44.976c-6.4 0.64-12.8 0.928-19.2 0.928-34.070-0.307-67.349-10.301-95.948-28.819s-51.337-44.797-65.555-75.757c-0.339-0.387-0.768-0.688-1.248-0.877-0.48-0.185-0.998-0.253-1.511-0.195-0.512 0.055-1.003 0.233-1.43 0.522s-0.78 0.672-1.027 1.127l-8.032 16.576c-1.536 3.181-4.274 5.623-7.611 6.787s-6.999 0.957-10.181-0.579c-3.183-1.536-5.624-4.275-6.789-7.613-1.164-3.334-0.955-6.998 0.581-10.179l24.96-51.49c1.492-3.055 4.099-5.423 7.284-6.614s6.706-1.114 9.836 0.214l53.12 22.4c1.613 0.675 3.078 1.66 4.311 2.901s2.209 2.712 2.874 4.33c0.664 1.618 1.003 3.351 0.998 5.1s-0.356 3.479-1.030 5.094c-0.674 1.614-1.66 3.079-2.901 4.312s-2.712 2.208-4.33 2.873c-1.618 0.663-3.351 1.002-5.1 0.998-1.749-0.007-3.48-0.358-5.093-1.031l-20.832-8.738c-2.624-0.288-3.68 1.6-3.2 2.912 13.19 29.202 35.259 53.495 63.062 69.422s59.924 22.675 91.786 19.283c32.304-3.2 62.791-16.445 87.175-37.872s41.44-49.962 48.762-81.585c0.365-1.711 1.063-3.333 2.055-4.774s2.259-2.671 3.728-3.623c1.469-0.951 3.111-1.603 4.829-1.92 1.721-0.317 3.488-0.291 5.197 0.076zM502.947 157.296c3.331-1.163 6.989-0.962 10.173 0.56 1.622 0.746 3.075 1.811 4.275 3.131 1.203 1.32 2.125 2.867 2.717 4.551s0.838 3.469 0.726 5.25c-0.115 1.781-0.582 3.521-1.383 5.117l-24.928 51.488c-1.498 3.050-4.106 5.412-7.286 6.602-3.184 1.19-6.701 1.118-9.834-0.202l-53.152-22.4c-1.613-0.677-3.079-1.665-4.31-2.907s-2.205-2.715-2.87-4.335c-0.663-1.619-0.998-3.353-0.992-5.103 0.010-1.75 0.361-3.481 1.037-5.095 0.678-1.613 1.664-3.078 2.909-4.31 1.241-1.232 2.714-2.207 4.333-2.87s3.353-1 5.104-0.993c1.75 0.007 3.481 0.359 5.094 1.036l20.832 8.704c2.592 0.288 3.68-1.6 3.2-2.912-13.216-29.171-35.293-53.431-63.094-69.329s-59.911-22.621-91.753-19.216c-32.303 3.198-62.791 16.443-87.175 37.87s-41.437 49.961-48.761 81.585c-0.73 3.458-2.804 6.486-5.765 8.415s-6.569 2.603-10.027 1.874c-3.458-0.73-6.485-2.804-8.415-5.765s-2.603-6.569-1.874-10.027c8.919-36.975 29.038-70.293 57.608-95.403s64.194-40.785 102.008-44.886c6.4-0.64 12.8-0.928 19.2-0.928 34.055 0.283 67.325 10.252 95.923 28.742s51.347 44.737 65.581 75.674c0.339 0.394 0.768 0.7 1.255 0.891 0.483 0.19 1.005 0.261 1.523 0.204 0.515-0.056 1.011-0.238 1.443-0.53 0.429-0.291 0.784-0.683 1.027-1.142l8.032-16.576c1.549-3.173 4.285-5.606 7.619-6.768z" />
</vector>

View File

@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="640dp"
android:height="512dp"
android:viewportWidth="640"
android:viewportHeight="512">
android:width="20dp"
android:height="20dp"
android:viewportWidth="2048"
android:viewportHeight="2048">
<path
android:fillColor="#FF777777"
android:pathData="M261.411 257.983c8.966-7.451 15.421-17.482 18.488-28.73s2.595-23.167-1.349-34.138c-3.945-10.97-11.171-20.461-20.697-27.181s-20.891-10.345-32.548-10.381c-11.658-0.036-23.044 3.518-32.612 10.18s-16.852 16.107-20.863 27.053c-4.012 10.946-4.556 22.862-1.559 34.129s9.39 21.337 18.31 28.843c-13.738 5.811-25.873 14.849-35.375 26.346s-16.095 25.116-19.216 39.704c-0.78 3.654-0.728 7.44 0.153 11.075 0.881 3.632 2.567 7.021 4.935 9.917 2.31 2.864 5.236 5.175 8.559 6.759s6.96 2.403 10.642 2.394h132.928c3.682 0.010 7.318-0.81 10.642-2.394s6.248-3.894 8.558-6.759c2.366-2.896 4.053-6.285 4.939-9.917s0.948-7.418 0.181-11.075c-3.106-14.498-9.639-28.038-19.054-39.491s-21.436-20.483-35.058-26.334v0zM225.123 180.319c6.798 0 13.442 2.016 19.094 5.792s10.056 9.144 12.658 15.423c2.601 6.28 3.282 13.19 1.956 19.857s-4.6 12.791-9.406 17.597c-4.806 4.806-10.93 8.079-17.597 9.406s-13.577 0.645-19.857-1.956c-6.28-2.601-11.648-7.006-15.423-12.658s-5.792-12.297-5.792-19.094c0.008-9.113 3.632-17.85 10.076-24.292s15.18-10.067 24.292-10.076v0zM291.202 331.552h-132.928l-2.368-3.2c3.409-15.714 12.094-29.788 24.61-39.88s28.112-15.596 44.19-15.596c16.079 0 31.673 5.504 44.19 15.596s21.202 24.166 24.61 39.88l-2.304 3.2zM369.376 202.178h80.288c2.825-0.214 5.466-1.487 7.389-3.563 1.926-2.077 2.998-4.804 2.998-7.637s-1.072-5.56-2.998-7.637c-1.923-2.077-4.563-3.35-7.389-3.563h-80.288c-2.822 0.214-5.462 1.487-7.389 3.563s-2.995 4.804-2.995 7.637c0 2.833 1.069 5.56 2.995 7.637s4.567 3.35 7.389 3.563v0zM484.445 309.699h-115.2c-2.825 0.214-5.466 1.486-7.389 3.563-1.926 2.077-2.995 4.805-2.995 7.637s1.069 5.558 2.995 7.639c1.923 2.077 4.563 3.347 7.389 3.562h115.2c2.822-0.214 5.462-1.485 7.389-3.562 1.926-2.080 2.995-4.807 2.995-7.639s-1.069-5.56-2.995-7.637c-1.926-2.077-4.567-3.349-7.389-3.563v0zM484.448 244.738h-115.2c-2.973 0-5.821 1.18-7.92 3.28-2.103 2.101-3.28 4.95-3.28 7.919s1.178 5.819 3.28 7.92c2.099 2.101 4.947 3.281 7.92 3.281h115.2c2.969 0 5.817-1.18 7.92-3.281 2.099-2.101 3.28-4.949 3.28-7.919s-1.181-5.819-3.28-7.919c-2.103-2.1-4.95-3.28-7.92-3.28v0zM544 80h-448c-8.487 0-16.626 3.371-22.627 9.373s-9.373 14.141-9.373 22.627v288c0 8.486 3.371 16.627 9.373 22.627s14.141 9.373 22.627 9.373h448c8.486 0 16.627-3.373 22.627-9.373s9.373-14.141 9.373-22.627v-288c0-8.487-3.373-16.626-9.373-22.627s-14.141-9.373-22.627-9.373v0zM528 400h-416c-4.243 0-8.314-1.687-11.314-4.685-3.001-3.002-4.686-7.072-4.686-11.315v-256c0-4.243 1.686-8.314 4.686-11.314s7.070-4.686 11.314-4.686h418.4c2.816 0.64 13.376 3.712 13.6 13.824v258.176c0 4.243-1.687 8.313-4.685 11.315-3.001 2.998-7.072 4.685-11.315 4.685z" />
android:pathData="M896,1340C896,1376.67 885.5,1407.83 864.5,1433.5C843.5,1459.17 818.33,1472 789,1472L363,1472C333.67,1472 308.5,1459.17 287.5,1433.5C266.5,1407.83 256,1376.67 256,1340C256,1304 258.5,1270.5 263.5,1239.5C268.5,1208.5 276.67,1178.5 288,1149.5C299.33,1120.5 316.33,1097.67 339,1081C361.67,1064.33 388.67,1056 420,1056C462.67,1098.67 514.67,1120 576,1120C637.33,1120 689.33,1098.67 732,1056C763.33,1056 790.33,1064.33 813,1081C835.67,1097.67 852.67,1120.5 864,1149.5C875.33,1178.5 883.5,1208.5 888.5,1239.5C893.5,1270.5 896,1304 896,1340ZM768,896C768,949.33 749.33,994.67 712,1032C674.67,1069.33 629.33,1088 576,1088C522.67,1088 477.33,1069.33 440,1032C402.67,994.67 384,949.33 384,896C384,842.67 402.67,797.33 440,760C477.33,722.67 522.67,704 576,704C629.33,704 674.67,722.67 712,760C749.33,797.33 768,842.67 768,896ZM1792,1312L1792,1376C1792,1385.33 1789,1393 1783,1399C1777,1405 1769.33,1408 1760,1408L1056,1408C1046.67,1408 1039,1405 1033,1399C1027,1393 1024,1385.33 1024,1376L1024,1312C1024,1302.67 1027,1295 1033,1289C1039,1283 1046.67,1280 1056,1280L1760,1280C1769.33,1280 1777,1283 1783,1289C1789,1295 1792,1302.67 1792,1312ZM1408,1056L1408,1120C1408,1129.33 1405,1137 1399,1143C1393,1149 1385.33,1152 1376,1152L1056,1152C1046.67,1152 1039,1149 1033,1143C1027,1137 1024,1129.33 1024,1120L1024,1056C1024,1046.67 1027,1039 1033,1033C1039,1027 1046.67,1024 1056,1024L1376,1024C1385.33,1024 1393,1027 1399,1033C1405,1039 1408,1046.67 1408,1056ZM1792,1056L1792,1120C1792,1129.33 1789,1137 1783,1143C1777,1149 1769.33,1152 1760,1152L1568,1152C1558.67,1152 1551,1149 1545,1143C1539,1137 1536,1129.33 1536,1120L1536,1056C1536,1046.67 1539,1039 1545,1033C1551,1027 1558.67,1024 1568,1024L1760,1024C1769.33,1024 1777,1027 1783,1033C1789,1039 1792,1046.67 1792,1056ZM1792,800L1792,864C1792,873.33 1789,881 1783,887C1777,893 1769.33,896 1760,896L1056,896C1046.67,896 1039,893 1033,887C1027,881 1024,873.33 1024,864L1024,800C1024,790.67 1027,783 1033,777C1039,771 1046.67,768 1056,768L1760,768C1769.33,768 1777,771 1783,777C1789,783 1792,790.67 1792,800ZM1920,1632L1920,512L128,512L128,1632C128,1640.67 131.17,1648.17 137.5,1654.5C143.83,1660.83 151.33,1664 160,1664L1888,1664C1896.67,1664 1904.17,1660.83 1910.5,1654.5C1916.83,1648.17 1920,1640.67 1920,1632ZM2048,416L2048,1632C2048,1676 2032.33,1713.67 2001,1745C1969.67,1776.33 1932,1792 1888,1792L160,1792C116,1792 78.33,1776.33 47,1745C15.67,1713.67 0,1676 0,1632L0,416C0,372 15.67,334.33 47,303C78.33,271.67 116,256 160,256L1888,256C1932,256 1969.67,271.67 2001,303C2032.33,334.33 2048,372 2048,416Z" />
</vector>

View File

@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="640dp"
android:height="512dp"
android:viewportWidth="640"
android:viewportHeight="512">
<path
android:fillColor="#FF000000"
android:pathData="M320 421.763c-4.243 0-8.313-1.687-11.313-4.688s-4.687-7.069-4.687-11.312v-192.001c0-4.243 1.686-8.313 4.687-11.314s7.070-4.686 11.313-4.686c4.243 0 8.313 1.686 11.315 4.686 2.998 3 4.685 7.071 4.685 11.314v192.001c0 4.243-1.687 8.31-4.685 11.312-3.002 3.002-7.072 4.688-11.315 4.688zM320 142.049c10.481 0 18.976-8.495 18.976-18.976s-8.496-18.976-18.976-18.976c-10.48 0-18.976 8.496-18.976 18.976s8.496 18.976 18.976 18.976zM320 512c-50.632 0-100.127-15.014-142.226-43.142-42.099-28.131-74.911-68.112-94.287-114.889-19.376-46.779-24.446-98.252-14.568-147.911s34.26-95.274 70.062-131.076c35.802-35.802 81.417-60.184 131.076-70.061s101.134-4.808 147.911 14.568c46.777 19.376 86.758 52.188 114.89 94.287 28.128 42.099 43.143 91.594 43.143 142.226-0.077 67.872-27.072 132.944-75.065 180.935-47.99 47.993-113.063 74.989-180.935 75.066zM320 32c-44.303 0-87.611 13.138-124.447 37.751s-65.547 59.597-82.501 100.528c-16.954 40.93-21.39 85.969-12.747 129.422s29.977 83.365 61.304 114.693c31.327 31.325 71.24 52.659 114.692 61.303s88.49 4.208 129.422-12.745c40.931-16.957 75.913-45.667 100.528-82.502s37.75-80.145 37.75-124.448c-0.067-59.387-23.689-116.323-65.683-158.317s-98.928-65.615-158.317-65.683v0z"/>
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
</vector>

View File

@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="640dp"
android:height="512dp"
android:viewportWidth="640"
android:viewportHeight="512">
android:width="24dp"
android:height="24dp"
android:viewportWidth="1792"
android:viewportHeight="1792">
<path
android:fillColor="#FF000000"
android:pathData="M484.32 163.488h-14.528c-1.142-0.004-2.275-0.233-3.328-0.674-1.056-0.441-2.013-1.086-2.816-1.898-0.807-0.811-1.443-1.773-1.878-2.831-0.432-1.058-0.653-2.191-0.649-3.333v-6.4c0.675-35.916-11.907-70.822-35.341-98.047-23.437-27.225-56.083-44.861-91.699-49.536-19.685-1.895-39.55 0.355-58.314 6.603s-36.011 16.357-50.632 29.675c-14.62 13.318-26.29 29.55-34.255 47.651s-12.054 37.671-11.999 57.447v10.24c0 0.224-0.768 10.944-10.080 11.104h-13.088c-5.76 0.017-11.46 1.168-16.774 3.389s-10.139 5.467-14.198 9.553c-4.059 4.086-7.274 8.932-9.46 14.262s-3.3 11.036-3.278 16.796v260.576c0.025 11.606 4.632 22.733 12.817 30.96s19.29 12.893 30.895 12.976h328.608c11.6-0.077 22.701-4.733 30.883-12.957 8.179-8.224 12.781-19.347 12.797-30.947v-260.608c0.023-5.757-1.091-11.462-3.277-16.789-2.183-5.327-5.395-10.172-9.449-14.257-4.058-4.086-8.877-7.333-14.189-9.555s-11.008-3.377-16.765-3.399v0zM331.808 391.104v43.072c0.083 1.6-0.16 3.2-0.714 4.704-0.557 1.504-1.411 2.877-2.515 4.041-1.101 1.161-2.429 2.087-3.904 2.72-1.472 0.63-3.056 0.957-4.659 0.957-1.602 0-3.188-0.327-4.66-0.957-1.473-0.634-2.8-1.558-3.904-2.72-1.103-1.165-1.958-2.538-2.513-4.041s-0.798-3.104-0.715-4.704v-43.072c-5.285-2.653-9.519-7.014-12.019-12.375s-3.117-11.408-1.753-17.161c1.364-5.753 4.63-10.88 9.269-14.55 4.639-3.667 10.38-5.661 16.295-5.661s11.654 1.993 16.294 5.661c4.64 3.671 7.904 8.797 9.271 14.55 1.363 5.753 0.745 11.801-1.753 17.161s-6.733 9.721-12.019 12.375v0zM429.12 154.752c0.003 1.146-0.217 2.281-0.653 3.341s-1.075 2.023-1.885 2.834c-0.81 0.812-1.769 1.456-2.829 1.895-1.056 0.44-2.192 0.666-3.337 0.666h-196.16c-1.624 0.141-3.258-0.042-4.81-0.536s-2.992-1.291-4.235-2.344c-1.244-1.053-2.266-2.341-3.010-3.792s-1.192-3.032-1.321-4.656v-10.016c-0.043-16.317 3.56-32.439 10.547-47.185s17.179-27.745 29.833-38.047c12.654-10.302 27.451-17.647 43.308-21.497s32.375-4.11 48.344-0.758c24.841 5.834 46.941 19.992 62.624 40.121s24.003 45.020 23.584 70.534v9.44z" />
android:pathData="M640,768h512v-192q0,-106 -75,-181t-181,-75 -181,75 -75,181v192zM1472,864v576q0,40 -28,68t-68,28h-960q-40,0 -68,-28t-28,-68v-576q0,-40 28,-68t68,-28h32v-192q0,-184 132,-316t316,-132 316,132 132,316v192h32q40,0 68,28t28,68z" />
</vector>

View File

@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="640dp"
android:height="512dp"
android:viewportWidth="640"
android:viewportHeight="512">
android:width="24dp"
android:height="24dp"
android:viewportWidth="1792"
android:viewportHeight="1792">
<path
android:fillColor="#FF777777"
android:pathData="M571.2 205.825c-9.082-45.471-30.339-87.624-61.501-121.963-31.159-34.339-71.056-59.575-115.437-73.018-44.377-13.443-91.573-14.586-136.551-3.311s-86.052 34.55-118.84 67.338c-32.788 32.788-56.062 73.862-67.338 118.84s-10.132 92.173 3.311 136.551c13.442 44.381 38.679 84.279 73.018 115.437 34.339 31.161 76.492 52.419 121.964 61.501 41.367 8.409 84.172 6.442 124.594-5.728 40.419-12.166 77.197-34.157 107.046-64.007s51.84-66.627 64.007-107.046c12.169-40.422 14.138-83.228 5.728-124.594zM186.305 76.481c38.576-28.909 85.49-44.517 133.695-44.48 2.752 0 5.376 0.48 8.128 0.608 9.952 2.88 2.368 11.104 2.368 11.104-34.061 17.672-62.726 44.203-82.975 76.8-9.6 15.488-17.216 15.328-19.68 15.040-14.112-0.608-31.456-20.224-44.608-40.16-1.954-2.963-2.723-6.552-2.154-10.055s2.434-6.665 5.226-8.856v0zM253.505 461.184c-1.923 1.971-4.388 3.328-7.083 3.897s-5.497 0.327-8.054-0.697c-40.582-15.911-75.645-43.305-100.902-78.832s-39.611-77.643-41.306-121.199l9.44-13.888c30.432-11.36 60.416-17.504 68.512-10.496 3.744 3.2 1.184 13.12 0 17.216-22.88 74.143 36.288 93.183 58.624 100.383 2.464 0.8 4.576 1.504 6.4 2.112 23.392 9.024 36.736 22.112 39.712 38.848 0.56 11.645-1.404 23.274-5.757 34.087s-10.99 20.56-19.459 28.57h-0.128zM477.152 414.656c-41.667 41.728-98.183 65.226-157.152 65.344-9.798-0.067-19.581-0.807-29.279-2.208-1.884-0.298-3.663-1.063-5.178-2.221-1.514-1.161-2.716-2.678-3.498-4.419s-1.117-3.648-0.976-5.549c0.14-1.901 0.752-3.741 1.779-5.347v0c14.127-18.771 20.486-42.262 17.76-65.6-3.027-13.315-9.63-25.552-19.099-35.392s-21.44-16.909-34.63-20.448c-1.984-0.768-4.544-1.632-7.488-2.56-30.592-9.856-59.168-23.551-44.192-72.447 7.040-22.848 0.256-34.784-6.656-40.768-14.72-12.8-42.976-8.928-68.96-1.216-2.377 0.642-4.879 0.653-7.262 0.034s-4.563-1.849-6.326-3.566c-1.763-1.718-3.049-3.865-3.73-6.231s-0.734-4.868-0.155-7.26c7.821-33.432 23.251-64.608 45.088-91.104 1.379-1.599 3.102-2.864 5.042-3.698s4.043-1.216 6.151-1.118c2.109 0.099 4.167 0.675 6.020 1.688s3.452 2.431 4.674 4.152c14.304 20.224 35.040 42.4 57.6 43.392h1.6c8.108-0.433 15.946-3.062 22.676-7.605s12.098-10.829 15.532-18.188c25.389-38.702 62.678-68.096 106.239-83.744v0c9.504-2.946 19.648-3.124 29.248-0.512 29.693 11.669 56.55 29.538 78.784 52.416 1.798 1.852 3.053 4.159 3.635 6.674s0.467 5.138-0.333 7.591c-0.8 2.453-2.256 4.641-4.208 6.329s-4.327 2.81-6.87 3.247c-41.088 7.136-94.208 21.184-101.088 46.816-3.2 12.416 2.912 24.48 18.56 35.872 60.288 43.744 77.984 77.344 68.768 91.232-8.49 10.898-14.24 23.673-16.774 37.254-2.534 13.577-1.773 27.568 2.214 40.793 5.907 10.624 15.488 18.726 26.944 22.784 0 0 11.36 6.528 5.664 15.584h-0.128zM512 369.664c-2.966 3.639-6.743 6.531-11.024 8.448-4.285 1.917-8.96 2.807-13.648 2.592-4.595-0.729-8.992-2.409-12.909-4.925-3.917-2.519-7.267-5.821-9.843-9.699-7.584-14.624 2.432-38.944 13.312-55.359 11.296-17.12 16.736-55.68-74.304-121.76-7.648-5.536-10.592-10.080-10.080-11.936 3.2-11.808 52.608-27.072 108.8-34.336 2.845-0.366 5.735 0.123 8.298 1.405 2.567 1.282 4.691 3.299 6.102 5.794 18.557 33.828 27.891 71.942 27.066 110.518s-11.783 76.255-31.769 109.257v0z" />
android:pathData="M896,128q209,0 385.5,103t279.5,279.5 103,385.5 -103,385.5 -279.5,279.5 -385.5,103 -385.5,-103 -279.5,-279.5 -103,-385.5 103,-385.5 279.5,-279.5 385.5,-103zM1170,649q-2,1 -9.5,9.5t-13.5,9.5q2,0 4.5,-5t5,-11 3.5,-7q6,-7 22,-15 14,-6 52,-12 34,-8 51,11 -2,-2 9.5,-13t14.5,-12q3,-2 15,-4.5t15,-7.5l2,-22q-12,1 -17.5,-7t-6.5,-21q0,2 -6,8 0,-7 -4.5,-8t-11.5,1 -9,1q-10,-3 -15,-7.5t-8,-16.5 -4,-15q-2,-5 -9.5,-11t-9.5,-10q-1,-2 -2.5,-5.5t-3,-6.5 -4,-5.5 -5.5,-2.5 -7,5 -7.5,10 -4.5,5q-3,-2 -6,-1.5t-4.5,1 -4.5,3 -5,3.5q-3,2 -8.5,3t-8.5,2q15,-5 -1,-11 -10,-4 -16,-3 9,-4 7.5,-12t-8.5,-14h5q-1,-4 -8.5,-8.5t-17.5,-8.5 -13,-6q-8,-5 -34,-9.5t-33,-0.5q-5,6 -4.5,10.5t4,14 3.5,12.5q1,6 -5.5,13t-6.5,12q0,7 14,15.5t10,21.5q-3,8 -16,16t-16,12q-5,8 -1.5,18.5t10.5,16.5q2,2 1.5,4t-3.5,4.5 -5.5,4 -6.5,3.5l-3,2q-11,5 -20.5,-6t-13.5,-26q-7,-25 -16,-30 -23,-8 -29,1 -5,-13 -41,-26 -25,-9 -58,-4 6,-1 0,-15 -7,-15 -19,-12 3,-6 4,-17.5t1,-13.5q3,-13 12,-23 1,-1 7,-8.5t9.5,-13.5 0.5,-6q35,4 50,-11 5,-5 11.5,-17t10.5,-17q9,-6 14,-5.5t14.5,5.5 14.5,5q14,1 15.5,-11t-7.5,-20q12,1 3,-17 -4,-7 -8,-9 -12,-4 -27,5 -8,4 2,8 -1,-1 -9.5,10.5t-16.5,17.5 -16,-5q-1,-1 -5.5,-13.5t-9.5,-13.5q-8,0 -16,15 3,-8 -11,-15t-24,-8q19,-12 -8,-27 -7,-4 -20.5,-5t-19.5,4q-5,7 -5.5,11.5t5,8 10.5,5.5 11.5,4 8.5,3q14,10 8,14 -2,1 -8.5,3.5t-11.5,4.5 -6,4q-3,4 0,14t-2,14q-5,-5 -9,-17.5t-7,-16.5q7,9 -25,6l-10,-1q-4,0 -16,2t-20.5,1 -13.5,-8q-4,-8 0,-20 1,-4 4,-2 -4,-3 -11,-9.5t-10,-8.5q-46,15 -94,41 6,1 12,-1 5,-2 13,-6.5t10,-5.5q34,-14 42,-7l5,-5q14,16 20,25 -7,-4 -30,-1 -20,6 -22,12 7,12 5,18 -4,-3 -11.5,-10t-14.5,-11 -15,-5q-16,0 -22,1 -146,80 -235,222 7,7 12,8 4,1 5,9t2.5,11 11.5,-3q9,8 3,19 1,-1 44,27 19,17 21,21 3,11 -10,18 -1,-2 -9,-9t-9,-4q-3,5 0.5,18.5t10.5,12.5q-7,0 -9.5,16t-2.5,35.5 -1,23.5l2,1q-3,12 5.5,34.5t21.5,19.5q-13,3 20,43 6,8 8,9 3,2 12,7.5t15,10 10,10.5q4,5 10,22.5t14,23.5q-2,6 9.5,20t10.5,23q-1,0 -2.5,1t-2.5,1q3,7 15.5,14t15.5,13q1,3 2,10t3,11 8,2q2,-20 -24,-62 -15,-25 -17,-29 -3,-5 -5.5,-15.5t-4.5,-14.5q2,0 6,1.5t8.5,3.5 7.5,4 2,3q-3,7 2,17.5t12,18.5 17,19 12,13q6,6 14,19.5t0,13.5q9,0 20,10.5t17,19.5q5,8 8,26t5,24q2,7 8.5,13.5t12.5,9.5l16,8 13,7q5,2 18.5,10.5t21.5,11.5q10,4 16,4t14.5,-2.5 13.5,-3.5q15,-2 29,15t21,21q36,19 55,11 -2,1 0.5,7.5t8,15.5 9,14.5 5.5,8.5q5,6 18,15t18,15q6,-4 7,-9 -3,8 7,20t18,10q14,-3 14,-32 -31,15 -49,-18 0,-1 -2.5,-5.5t-4,-8.5 -2.5,-8.5 0,-7.5 5,-3q9,0 10,-3.5t-2,-12.5 -4,-13q-1,-8 -11,-20t-12,-15q-5,9 -16,8t-16,-9q0,1 -1.5,5.5t-1.5,6.5q-13,0 -15,-1 1,-3 2.5,-17.5t3.5,-22.5q1,-4 5.5,-12t7.5,-14.5 4,-12.5 -4.5,-9.5 -17.5,-2.5q-19,1 -26,20 -1,3 -3,10.5t-5,11.5 -9,7q-7,3 -24,2t-24,-5q-13,-8 -22.5,-29t-9.5,-37q0,-10 2.5,-26.5t3,-25 -5.5,-24.5q3,-2 9,-9.5t10,-10.5q2,-1 4.5,-1.5t4.5,0 4,-1.5 3,-6q-1,-1 -4,-3 -3,-3 -4,-3 7,3 28.5,-1.5t27.5,1.5q15,11 22,-2 0,-1 -2.5,-9.5t-0.5,-13.5q5,27 29,9 3,3 15.5,5t17.5,5q3,2 7,5.5t5.5,4.5 5,-0.5 8.5,-6.5q10,14 12,24 11,40 19,44 7,3 11,2t4.5,-9.5 0,-14 -1.5,-12.5l-1,-8v-18l-1,-8q-15,-3 -18.5,-12t1.5,-18.5 15,-18.5q1,-1 8,-3.5t15.5,-6.5 12.5,-8q21,-19 15,-35 7,0 11,-9 -1,0 -5,-3t-7.5,-5 -4.5,-2q9,-5 2,-16 5,-3 7.5,-11t7.5,-10q9,12 21,2 8,-8 1,-16 5,-7 20.5,-10.5t18.5,-9.5q7,2 8,-2t1,-12 3,-12q4,-5 15,-9t13,-5l17,-11q3,-4 0,-4 18,2 31,-11 10,-11 -6,-20 3,-6 -3,-9.5t-15,-5.5q3,-1 11.5,-0.5t10.5,-1.5q15,-10 -7,-16 -17,-5 -43,12zM1007,1526q206,-36 351,-189 -3,-3 -12.5,-4.5t-12.5,-3.5q-18,-7 -24,-8 1,-7 -2.5,-13t-8,-9 -12.5,-8 -11,-7q-2,-2 -7,-6t-7,-5.5 -7.5,-4.5 -8.5,-2 -10,1l-3,1q-3,1 -5.5,2.5t-5.5,3 -4,3 0,2.5q-21,-17 -36,-22 -5,-1 -11,-5.5t-10.5,-7 -10,-1.5 -11.5,7q-5,5 -6,15t-2,13q-7,-5 0,-17.5t2,-18.5q-3,-6 -10.5,-4.5t-12,4.5 -11.5,8.5 -9,6.5 -8.5,5.5 -8.5,7.5q-3,4 -6,12t-5,11q-2,-4 -11.5,-6.5t-9.5,-5.5q2,10 4,35t5,38q7,31 -12,48 -27,25 -29,40 -4,22 12,26 0,7 -8,20.5t-7,21.5q0,6 2,16z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="600"
android:viewportHeight="600">
<path
android:fillColor="#FF000000"
android:pathData="M508.757,52.805L68.978,306.52C51.804,316.388 53.987,340.299 71.065,347.51L171.925,389.827L444.522,149.585C449.741,144.936 457.141,152.052 452.682,157.46L224.111,435.94L224.111,512.32C224.111,534.712 251.152,543.536 264.436,527.312L324.686,453.967L442.909,503.496C456.382,509.189 471.753,500.744 474.22,486.227L542.536,76.336C545.762,57.17 525.172,43.317 508.757,52.805Z" />
</vector>

View File

@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="16dp"
android:viewportWidth="640"
android:viewportHeight="512">
android:width="24dp"
android:height="24dp"
android:viewportWidth="1792"
android:viewportHeight="1792">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M558.781 17.216c-6.342-6.397-14.083-11.239-22.608-14.148-8.528-2.909-17.613-3.805-26.544-2.619-20.425 3.183-39.178 13.174-53.216 28.352l-323.807 323.872c-11.904 11.872-21.977 25.453-29.888 40.288l-33.696 64.64c-4.079 6.957-5.793 15.046-4.887 23.059s4.382 15.517 9.911 21.389c3.263 3.216 7.133 5.753 11.386 7.462s8.8 2.554 13.382 2.489c6.858-0.067 13.599-1.792 19.648-5.024l64.512-33.632c14.925-7.888 28.573-17.984 40.48-29.952l323.744-323.808c15.187-14.022 25.191-32.763 28.384-53.184 1.159-8.938 0.246-18.022-2.669-26.549-2.912-8.529-7.747-16.273-14.131-22.634v0zM483.741 137.6l-283.103 282.976c-9.575 9.632-20.546 17.767-32.544 24.128l-64.576 33.664c-3.2 1.76-5.76 1.6-6.656 0.736s-0.992-3.2 0.768-6.688l33.728-64.512c6.355-11.949 14.466-22.873 24.064-32.416l282.975-282.688c0.599-0.624 1.315-1.121 2.112-1.461 0.793-0.34 1.648-0.514 2.512-0.514s1.721 0.175 2.515 0.514c0.793 0.34 1.514 0.837 2.109 1.461l36.032 35.2c0.659 0.612 1.187 1.352 1.549 2.175s0.55 1.712 0.557 2.611c0.007 0.899-0.17 1.79-0.522 2.618s-0.867 1.575-1.52 2.196v0zM524.381 96.928l-16.352 16.384c-1.034 0.882-2.352 1.365-3.712 1.365s-2.675-0.484-3.712-1.365l-38.4-37.568c-0.803-0.851-1.286-1.957-1.36-3.125-0.077-1.168 0.256-2.326 0.944-3.275l17.376-17.376c8.906-10.032 20.915-16.791 34.112-19.2 1.222-0.153 2.451-0.227 3.68-0.224 3.517-0.046 7.005 0.616 10.259 1.947s6.208 3.302 8.685 5.797c13.76 13.472 9.216 35.744-11.52 56.48v0.16z" />
android:pathData="M491,1536l91,-91 -235,-235 -91,91v107h128v128h107zM1014,608q0,-22 -22,-22 -10,0 -17,7l-542,542q-7,7 -7,17 0,22 22,22 10,0 17,-7l542,-542q7,-7 7,-17zM960,416l416,416 -832,832h-416v-416zM1643,512q0,53 -37,90l-166,166 -416,-416 166,-165q36,-38 90,-38 53,0 91,38l235,234q37,39 37,91z" />
</vector>

View File

@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="16dp"
android:viewportWidth="640"
android:viewportHeight="512">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M560 237.568h-208c-4.243 0-8.313-1.686-11.315-4.686-2.998-3-4.685-7.070-4.685-11.314v-205.568c0-4.244-1.687-8.313-4.685-11.313-3.002-3.001-7.072-4.686-11.315-4.686s-8.313 1.685-11.313 4.686c-3 3.001-4.687 7.070-4.687 11.313v205.568c0 4.244-1.686 8.314-4.687 11.314s-7.070 4.686-11.313 4.686h-208c-2.101 0-4.182 0.414-6.123 1.218s-3.705 1.982-5.191 3.469c-1.486 1.486-2.665 3.25-3.469 5.19s-1.218 4.022-1.218 6.123c0 2.101 0.414 4.182 1.218 6.123s1.983 3.705 3.469 5.19c1.486 1.486 3.249 2.664 5.191 3.469s4.022 1.218 6.123 1.218h208c4.243 0 8.313 1.686 11.313 4.687s4.687 7.070 4.687 11.313v210.432c0 4.243 1.686 8.313 4.687 11.315 3 2.998 7.070 4.685 11.313 4.685s8.313-1.687 11.315-4.685c2.998-3.002 4.685-7.072 4.685-11.315v-210.432c0-4.243 1.687-8.313 4.685-11.313 3.002-3 7.072-4.687 11.315-4.687h208c4.243 0 8.313-1.686 11.315-4.687 2.999-3 4.685-7.070 4.685-11.314s-1.687-8.314-4.685-11.314c-3.001-3-7.072-4.686-11.315-4.686v0z"/>
android:width="32dp"
android:height="32dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="128"
android:viewportHeight="128">
<path
android:fillColor="#FF000000"
android:pathData="M82.292,41.269C77.427,37.45 71.296,35.172 64.638,35.172C48.838,35.172 36.011,47.999 36.011,63.799C36.011,79.599 48.838,92.426 64.638,92.426C74.998,92.426 84.08,86.911 89.105,78.66C89.198,78.504 89.351,78.394 89.527,78.353C89.704,78.313 89.889,78.346 90.04,78.446C92.453,80.028 101.659,86.07 104.243,87.765C104.622,88.014 104.732,88.52 104.491,88.903C96.08,102.14 81.286,110.933 64.454,110.933C38.295,110.933 17.057,89.695 17.057,63.537C17.057,37.378 38.295,16.14 64.454,16.14C77.501,16.14 89.324,21.423 97.899,29.965L106.199,23.954C106.758,23.549 107.496,23.486 108.116,23.791C108.736,24.095 109.137,24.718 109.158,25.408C109.406,33.597 110.018,53.82 110.255,61.641C110.277,62.353 109.945,63.03 109.368,63.448C108.792,63.867 108.046,63.973 107.376,63.732C99.85,61.027 80.127,53.94 72.734,51.283C72.175,51.082 71.773,50.589 71.69,50.001C71.606,49.413 71.855,48.827 72.336,48.479L82.292,41.269Z" />
</vector>

View File

@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25dp"
android:height="20dp"
android:viewportWidth="640"
android:viewportHeight="512">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M560.509 400.576l-90.304-90.56c-4.509-4.373-7.69-9.932-9.175-16.037s-1.209-12.504 0.791-18.459c13.549-38.522 15.271-80.213 4.947-119.721-10.327-39.507-32.224-75.027-62.883-101.996s-68.681-44.158-109.185-49.358c-40.501-5.2-81.633 1.826-118.112 20.176s-66.639 47.186-86.609 82.803c-19.969 35.618-28.835 76.392-25.459 117.087s18.84 79.45 44.406 111.29c25.566 31.84 60.066 55.312 99.070 67.399 42.909 13.382 89.040 12.189 131.198-3.392 5.974-2.064 12.413-2.377 18.56-0.902 6.144 1.472 11.741 4.672 16.128 9.223l89.6 89.952c4.906 4.909 10.729 8.806 17.139 11.462 6.409 2.659 13.283 4.026 20.221 4.026 6.941 0 13.811-1.366 20.224-4.026 6.409-2.656 12.234-6.553 17.136-11.462l22.4-22.4c9.923-9.942 15.495-23.411 15.495-37.456s-5.571-27.514-15.495-37.456l-0.096-0.192zM104.318 253.344c-8.181-29.267-8.441-60.183-0.752-89.584s23.049-56.232 44.509-77.748c21.461-21.516 48.252-36.946 77.633-44.709s60.297-7.584 89.585 0.522c28.303 7.74 54.098 22.735 74.827 43.501 20.733 20.766 35.687 46.583 43.379 74.899 8.183 29.267 8.442 60.183 0.755 89.584-7.69 29.401-23.050 56.232-44.512 77.747-21.459 21.517-48.25 36.947-77.632 44.711-29.381 7.763-60.298 7.584-89.586-0.522-28.303-7.741-54.097-22.736-74.829-43.501s-35.684-46.582-43.379-74.899zM536.317 451.264l-22.4 22.4c-1.728 1.744-3.783 3.127-6.048 4.070s-4.697 1.427-7.152 1.427c-2.454 0-4.883-0.483-7.149-1.427s-4.32-2.327-6.051-4.070l-88.768-88.992c-1.351-1.376-2.394-3.024-3.069-4.832-0.672-1.808-0.957-3.735-0.835-5.661 0.118-1.926 0.643-3.805 1.536-5.514 0.896-1.709 2.137-3.213 3.648-4.409 9.808-8.221 18.829-17.337 26.944-27.232 1.19-1.526 2.688-2.787 4.397-3.696s3.591-1.443 5.52-1.575c1.93-0.131 3.869 0.151 5.683 0.822 1.815 0.669 3.469 1.719 4.848 3.072l88.896 89.088c3.488 3.529 5.443 8.288 5.443 13.248s-1.955 9.721-5.443 13.248v0.032z"/>
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="640dp"
android:height="512dp"
android:viewportWidth="640"
android:viewportHeight="512">
<path
android:fillColor="#FF000000"
android:pathData="M418.883 157.792c0 0 9.28-7.552 15.36-0.512 5.344 6.144-1.472 14.112-1.472 14.112l-176.226 194.976c-3.936 4.57-6.075 10.416-6.016 16.448l0.768 112.32c0.046 3.571 1.213 7.040 3.338 9.911s5.099 5.002 8.502 6.089c1.692 0.567 3.464 0.861 5.248 0.864 2.682 0.003 5.327-0.624 7.724-1.827 2.396-1.207 4.474-2.96 6.069-5.117l49.666-67.2c2.72-3.664 6.576-6.329 10.969-7.581 4.39-1.248 9.072-1.014 13.318 0.669l108.64 42.88c2.333 0.921 4.839 1.325 7.341 1.175 2.505-0.151 4.947-0.845 7.155-2.038 2.195-1.184 4.106-2.839 5.587-4.848 1.481-2.007 2.502-4.32 2.989-6.768l87.84-441.056c0.634-3.203 0.33-6.521-0.88-9.553s-3.27-5.65-5.936-7.535c-2.665-1.909-5.824-3.012-9.097-3.176s-6.525 0.616-9.366 2.248l-477.858 273.376c-2.722 1.565-4.957 3.854-6.457 6.613s-2.206 5.879-2.041 9.014c0.166 3.135 1.197 6.164 2.98 8.75s4.246 4.625 7.118 5.895l106.080 48.256c5.627 3.837 12.391 5.645 19.181 5.133 6.791-0.509 13.206-3.312 18.195-7.949z" />
</vector>

View File

@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="640dp"
android:height="512dp"
android:viewportWidth="640"
android:viewportHeight="512">
android:width="24dp"
android:height="24dp"
android:viewportWidth="1792"
android:viewportHeight="1792">
<path
android:fillColor="#FF000000"
android:pathData="M526.976 6.401c-1.929-2.029-4.253-3.644-6.826-4.745-2.576-1.101-5.351-1.663-8.151-1.655h-383.998c-2.802-0.016-5.576 0.543-8.153 1.645s-4.898 2.719-6.823 4.755c-2.031 1.928-3.647 4.252-4.748 6.827s-1.663 5.348-1.653 8.149v256c0.075 19.489 3.855 38.786 11.136 56.863 6.93 17.843 16.24 34.669 27.68 50.016 11.758 15.37 24.924 29.606 39.328 42.528 13.368 12.256 27.44 23.721 42.144 34.336 12.8 9.088 26.24 17.696 40.32 25.824s24.021 13.623 29.824 16.48c5.856 2.88 10.592 5.152 14.112 6.656 2.752 1.325 5.778 1.984 8.83 1.92 3.011 0.042 5.987-0.653 8.672-2.016 3.584-1.568 8.256-3.776 14.176-6.656s16-8.384 29.824-16.48c13.824-8.096 27.424-16.736 40.32-25.824 14.723-10.618 28.816-22.083 42.208-34.336 14.419-12.906 27.587-27.146 39.328-42.528 11.43-15.353 20.739-32.176 27.68-50.016 7.293-18.074 11.072-37.373 11.136-56.863v-256c0.013-2.784-0.544-5.541-1.641-8.101-1.095-2.559-2.704-4.867-4.726-6.779v0zM477.472 279.712c0 92.799-157.472 172.512-157.472 172.512v-397.375h157.472v224.864z" />
android:pathData="M1344,960v-640h-448v1137q119,-63 213,-137 235,-184 235,-360zM1536,192v768q0,86 -33.5,170.5t-83,150 -118,127.5 -126.5,103 -121,77.5 -89.5,49.5 -42.5,20q-12,6 -26,6t-26,-6q-16,-7 -42.5,-20t-89.5,-49.5 -121,-77.5 -126.5,-103 -118,-127.5 -83,-150 -33.5,-170.5v-768q0,-26 19,-45t45,-19h1152q26,0 45,19t19,45z" />
</vector>

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_centerInParent="true"
android:orientation="horizontal">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="10dp" />
<TextView
android:id="@+id/txtLoading"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="15dp"
android:gravity="center|left"
android:textSize="18sp" />
</LinearLayout>
</RelativeLayout>

View File

@@ -3,7 +3,6 @@ using System;
using System.Threading.Tasks;
using AndroidX.Core.App;
using Bit.App.Abstractions;
using Bit.Core;
using Bit.Core.Abstractions;
using Xamarin.Forms;
@@ -11,14 +10,14 @@ namespace Bit.Droid.Services
{
public class AndroidPushNotificationService : IPushNotificationService
{
private readonly IStorageService _storageService;
private readonly IStateService _stateService;
private readonly IPushNotificationListenerService _pushNotificationListenerService;
public AndroidPushNotificationService(
IStorageService storageService,
IStateService stateService,
IPushNotificationListenerService pushNotificationListenerService)
{
_storageService = storageService;
_stateService = stateService;
_pushNotificationListenerService = pushNotificationListenerService;
}
@@ -26,12 +25,12 @@ namespace Bit.Droid.Services
public async Task<string> GetTokenAsync()
{
return await _storageService.GetAsync<string>(Constants.PushCurrentTokenKey);
return await _stateService.GetPushCurrentTokenAsync();
}
public async Task RegisterAsync()
{
var registeredToken = await _storageService.GetAsync<string>(Constants.PushRegisteredTokenKey);
var registeredToken = await _stateService.GetPushRegisteredTokenAsync();
var currentToken = await GetTokenAsync();
if (!string.IsNullOrWhiteSpace(registeredToken) && registeredToken != currentToken)
{
@@ -39,7 +38,7 @@ namespace Bit.Droid.Services
}
else
{
await _storageService.SaveAsync(Constants.PushLastRegistrationDateKey, DateTime.UtcNow);
await _stateService.SetPushLastRegistrationDateAsync(DateTime.UtcNow);
}
}

View File

@@ -12,12 +12,12 @@ namespace Bit.Droid.Services
{
public class ClipboardService : IClipboardService
{
private readonly IStorageService _storageService;
private readonly IStateService _stateService;
private readonly Lazy<PendingIntent> _clearClipboardPendingIntent;
public ClipboardService(IStorageService storageService)
public ClipboardService(IStateService stateService)
{
_storageService = storageService;
_stateService = stateService;
_clearClipboardPendingIntent = new Lazy<PendingIntent>(() =>
PendingIntent.GetBroadcast(CrossCurrentActivity.Current.Activity,
@@ -39,7 +39,7 @@ namespace Bit.Droid.Services
if (clearMs < 0)
{
// if not set then we need to check if the user set this config
var clearSeconds = await _storageService.GetAsync<int?>(Constants.ClearClipboardKey);
var clearSeconds = await _stateService.GetClearClipboardAsync();
if (clearSeconds != null)
{
clearMs = clearSeconds.Value * 1000;

View File

@@ -13,7 +13,6 @@ using Android.OS;
using Android.Provider;
using Android.Text;
using Android.Text.Method;
using Android.Views;
using Android.Views.Autofill;
using Android.Views.InputMethods;
using Android.Webkit;
@@ -28,31 +27,28 @@ using Bit.Core.Enums;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Bit.Droid.Autofill;
using Bit.Droid.Utilities;
using Plugin.CurrentActivity;
namespace Bit.Droid.Services
{
public class DeviceActionService : IDeviceActionService
{
private readonly IStorageService _storageService;
private readonly IStateService _stateService;
private readonly IMessagingService _messagingService;
private readonly IBroadcasterService _broadcasterService;
private readonly Func<IEventService> _eventServiceFunc;
private AlertDialog _progressDialog;
object _progressDialogLock = new object();
private ProgressDialog _progressDialog;
private bool _cameraPermissionsDenied;
private Toast _toast;
private string _userAgent;
public DeviceActionService(
IStorageService storageService,
IStateService stateService,
IMessagingService messagingService,
IBroadcasterService broadcasterService,
Func<IEventService> eventServiceFunc)
{
_storageService = storageService;
_stateService = stateService;
_messagingService = messagingService;
_broadcasterService = broadcasterService;
_eventServiceFunc = eventServiceFunc;
@@ -112,101 +108,22 @@ namespace Bit.Droid.Services
{
await HideLoadingAsync();
}
var activity = CrossCurrentActivity.Current.Activity;
var inflater = (LayoutInflater)activity.GetSystemService(Context.LayoutInflaterService);
var dialogView = inflater.Inflate(Resource.Layout.progress_dialog_layout, null);
var txtLoading = dialogView.FindViewById<TextView>(Resource.Id.txtLoading);
txtLoading.Text = text;
txtLoading.SetTextColor(ThemeHelpers.TextColor);
_progressDialog = new AlertDialog.Builder(activity)
.SetView(dialogView)
.SetCancelable(false)
.Create();
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
_progressDialog = new ProgressDialog(activity);
_progressDialog.SetMessage(text);
_progressDialog.SetCancelable(false);
_progressDialog.Show();
}
public Task HideLoadingAsync()
{
// Based on https://github.com/redth-org/AndHUD/blob/master/AndHUD/AndHUD.cs
lock (_progressDialogLock)
if (_progressDialog != null)
{
if (_progressDialog is null)
{
return Task.CompletedTask;
}
void actionDismiss()
{
try
{
if (IsAlive(_progressDialog) && IsAlive(_progressDialog.Window))
{
_progressDialog.Hide();
_progressDialog.Dismiss();
}
}
catch
{
// ignore
}
_progressDialog = null;
}
// First try the SynchronizationContext
if (Application.SynchronizationContext != null)
{
Application.SynchronizationContext.Send(state => actionDismiss(), null);
return Task.CompletedTask;
}
// Otherwise try OwnerActivity on dialog
var ownerActivity = _progressDialog?.OwnerActivity;
if (IsAlive(ownerActivity))
{
ownerActivity.RunOnUiThread(actionDismiss);
return Task.CompletedTask;
}
// Otherwise try get it from the Window Context
if (_progressDialog?.Window?.Context is Activity windowActivity && IsAlive(windowActivity))
{
windowActivity.RunOnUiThread(actionDismiss);
return Task.CompletedTask;
}
// Finally if all else fails, let's see if current activity is MainActivity
if (CrossCurrentActivity.Current.Activity is MainActivity activity && IsAlive(activity))
{
activity.RunOnUiThread(actionDismiss);
return Task.CompletedTask;
}
return Task.CompletedTask;
_progressDialog.Dismiss();
_progressDialog.Dispose();
_progressDialog = null;
}
}
bool IsAlive(Java.Lang.Object @object)
{
if (@object == null)
return false;
if (@object.Handle == IntPtr.Zero)
return false;
if (@object is Activity activity)
{
if (activity.IsFinishing)
return false;
if (activity.IsDestroyed)
return false;
}
return true;
return Task.FromResult(0);
}
public bool OpenFile(byte[] fileData, string id, string fileName)
@@ -333,7 +250,7 @@ namespace Bit.Droid.Services
try
{
DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
await _storageService.SaveAsync(Constants.LastFileCacheClearKey, DateTime.UtcNow);
await _stateService.SetLastFileCacheClearAsync(DateTime.UtcNow);
}
catch (Exception) { }
}
@@ -916,9 +833,8 @@ namespace Bit.Droid.Services
{
if (!string.IsNullOrWhiteSpace(cipher?.Login?.Totp))
{
var userService = ServiceContainer.Resolve<IUserService>("userService");
var autoCopyDisabled = await _storageService.GetAsync<bool?>(Constants.DisableAutoTotpCopyKey);
var canAccessPremium = await userService.CanAccessPremiumAsync();
var autoCopyDisabled = await _stateService.GetDisableAutoTotpCopyAsync();
var canAccessPremium = await _stateService.CanAccessPremiumAsync();
if ((canAccessPremium || cipher.OrganizationUseTotp) && !autoCopyDisabled.GetValueOrDefault())
{
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");

View File

@@ -4,7 +4,6 @@ using Android.Content;
using Android.Runtime;
using Android.Service.QuickSettings;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Bit.Droid.Accessibility;
@@ -18,7 +17,7 @@ namespace Bit.Droid.Tile
[Register("com.x8bit.bitwarden.AutofillTileService")]
public class AutofillTileService : TileService
{
private IStorageService _storageService;
private IStateService _stateService;
public override void OnTileAdded()
{
@@ -59,11 +58,11 @@ namespace Bit.Droid.Tile
private void SetTileAdded(bool isAdded)
{
AccessibilityHelpers.IsAutofillTileAdded = isAdded;
if (_storageService == null)
if (_stateService == null)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
}
_storageService.SaveAsync(Constants.AutofillTileAdded, isAdded);
_stateService.SetAutofillTileAddedAsync(isAdded);
}
private void ScanAndFill()

View File

@@ -15,7 +15,7 @@ using Java.Lang;
namespace Bit.Droid.Tile
{
[Service(Permission = Android.Manifest.Permission.BindQuickSettingsTile, Label = "@string/PasswordGenerator",
Icon = "@drawable/generate")]
Icon = "@drawable/refresh")]
[IntentFilter(new string[] { ActionQsTile })]
[Register("com.x8bit.bitwarden.GeneratorTileService")]
public class GeneratorTileService : TileService

View File

@@ -12,22 +12,22 @@ namespace Bit.Droid.Utilities
private const string AppSecret = "d3834185-b4a6-4347-9047-b86c65293d42";
private readonly IAppIdService _appIdService;
private readonly IUserService _userService;
private readonly IStateService _stateService;
private string _userId;
private string _appId;
public AppCenterHelper(
IAppIdService appIdService,
IUserService userService)
IStateService stateService)
{
_appIdService = appIdService;
_userService = userService;
_stateService = stateService;
}
public async Task InitAsync()
{
_userId = await _userService.GetUserIdAsync();
_userId = await _stateService.GetActiveUserIdAsync();
_appId = await _appIdService.GetAppIdAsync();
AppCenter.Start(AppSecret, typeof(Crashes));

View File

@@ -36,10 +36,6 @@ namespace Bit.Droid.Utilities
{
get => ThemeManager.GetResourceColor("SwitchThumbColor").ToAndroid();
}
public static Color TextColor
{
get => ThemeManager.GetResourceColor("TextColor").ToAndroid();
}
public static void SetAppearance(string theme, bool osDarkModeEnabled)
{

View File

@@ -15,12 +15,13 @@
<ItemGroup>
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
<PackageReference Include="SkiaSharp.Views.Forms" Version="2.80.3" />
<PackageReference Include="Xamarin.CommunityToolkit" Version="1.3.2" />
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2125" />
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2291" />
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
<PackageReference Include="Xamarin.CommunityToolkit" Version="1.3.1" />
</ItemGroup>
<ItemGroup>
@@ -121,6 +122,7 @@
<DependentUpon>SendGroupingsPage.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Remove="Pages\Accounts\AccountsPopupPage.xaml.cs" />
</ItemGroup>
<ItemGroup>
@@ -132,6 +134,7 @@
<EmbeddedResource Update="Controls\CipherViewCell\CipherViewCell.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Remove="Pages\Accounts\AccountsPopupPage.xaml" />
</ItemGroup>
<ItemGroup>
@@ -415,6 +418,5 @@
<ItemGroup>
<None Remove="Behaviors\" />
<None Remove="Xamarin.CommunityToolkit" />
</ItemGroup>
</Project>

View File

@@ -4,11 +4,10 @@ using Bit.App.Pages;
using Bit.App.Resources;
using Bit.App.Services;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
using System;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
@@ -18,7 +17,6 @@ namespace Bit.App
{
public partial class App : Application
{
private readonly IUserService _userService;
private readonly IBroadcasterService _broadcasterService;
private readonly IMessagingService _messagingService;
private readonly IStateService _stateService;
@@ -26,7 +24,6 @@ namespace Bit.App
private readonly ISyncService _syncService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IAuthService _authService;
private readonly IStorageService _storageService;
private readonly IStorageService _secureStorageService;
private readonly IDeviceActionService _deviceActionService;
@@ -40,7 +37,6 @@ namespace Bit.App
Current = this;
return;
}
_userService = ServiceContainer.Resolve<IUserService>("userService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
@@ -48,7 +44,6 @@ namespace Bit.App
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_authService = ServiceContainer.Resolve<IAuthService>("authService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
@@ -85,8 +80,12 @@ namespace Bit.App
}
else if (message.Command == "logout")
{
var extras = message.Data as Tuple<string, bool, bool>;
var userId = extras?.Item1;
var userInitiated = extras?.Item2;
var expired = extras?.Item3;
Device.BeginInvokeOnMainThread(async () =>
await LogOutAsync((message.Data as bool?).GetValueOrDefault()));
await LogOutAsync(userId, userInitiated, expired));
}
else if (message.Command == "loggedOut")
{
@@ -97,7 +96,7 @@ namespace Bit.App
{
if (Device.RuntimePlatform == Device.iOS)
{
ResumedAsync().FireAndForget();
ResumedAsync();
}
}
else if (message.Command == "slept")
@@ -107,6 +106,18 @@ namespace Bit.App
await SleptAsync();
}
}
else if (message.Command == "addAccount")
{
await AddAccount();
}
else if (message.Command == "accountAdded")
{
UpdateTheme();
}
else if (message.Command == "switchedAccount")
{
await SwitchedAccountAsync();
}
else if (message.Command == "migrated")
{
await Task.Delay(1000);
@@ -168,7 +179,7 @@ namespace Bit.App
if (string.IsNullOrWhiteSpace(Options.Uri))
{
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
_storageService);
_stateService);
if (!updated)
{
SyncIfNeeded();
@@ -192,7 +203,7 @@ namespace Bit.App
var isLocked = await _vaultTimeoutService.IsLockedAsync();
if (!isLocked)
{
await _storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
}
SetTabsPageFromAutofill(isLocked);
await SleptAsync();
@@ -205,7 +216,7 @@ namespace Bit.App
_isResumed = true;
if (Device.RuntimePlatform == Device.Android)
{
ResumedAsync().FireAndForget();
ResumedAsync();
}
}
@@ -215,11 +226,11 @@ namespace Bit.App
_messagingService.Send("stopEventTimer");
}
private async Task ResumedAsync()
private async void ResumedAsync()
{
UpdateTheme();
await _vaultTimeoutService.CheckVaultTimeoutAsync();
_messagingService.Send("startEventTimer");
await UpdateThemeAsync();
await ClearCacheIfNeededAsync();
Prime();
SyncIfNeeded();
@@ -229,15 +240,6 @@ namespace Bit.App
}
}
public async Task UpdateThemeAsync()
{
await Device.InvokeOnMainThreadAsync(() =>
{
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
_messagingService.Send("updatedTheme");
});
}
private void SetCulture()
{
// Calendars are removed by linker. ref https://bugzilla.xamarin.com/show_bug.cgi?id=59077
@@ -246,27 +248,65 @@ namespace Bit.App
new System.Globalization.UmAlQuraCalendar();
}
private async Task LogOutAsync(bool expired)
private async Task LogOutAsync(string userId, bool? userInitiated, bool? expired)
{
await AppHelpers.LogOutAsync();
await AppHelpers.LogOutAsync(userId, userInitiated.GetValueOrDefault(true));
await SetMainPageAsync();
_authService.LogOut(() =>
{
Current.MainPage = new HomePage();
if (expired)
if (expired.GetValueOrDefault())
{
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
}
});
}
private async Task AddAccount()
{
Device.BeginInvokeOnMainThread(async () =>
{
Current.MainPage = new NavigationPage(new HomePage(Options));
});
}
private async Task SwitchedAccountAsync()
{
await AppHelpers.OnAccountSwitchAsync();
var shouldTimeout = await _vaultTimeoutService.ShouldTimeoutAsync();
Device.BeginInvokeOnMainThread(async () =>
{
if (shouldTimeout)
{
await _vaultTimeoutService.ExecuteTimeoutActionAsync();
}
else
{
await SetMainPageAsync();
}
await Task.Delay(50);
UpdateTheme();
});
}
private async Task SetMainPageAsync()
{
var authed = await _userService.IsAuthenticatedAsync();
var authed = await _stateService.IsAuthenticatedAsync();
if (authed)
{
if (await _vaultTimeoutService.IsLockedAsync())
var isLocked = await _vaultTimeoutService.IsLockedAsync();
var shouldTimeout = await _vaultTimeoutService.ShouldTimeoutAsync();
var vaultTimeoutAction = await _stateService.GetVaultTimeoutActionAsync();
if (isLocked || shouldTimeout)
{
Current.MainPage = new NavigationPage(new LockPage(Options));
if (vaultTimeoutAction == "logOut")
{
var email = await _stateService.GetEmailAsync();
Current.MainPage = new NavigationPage(new LoginPage(email, Options));
}
else
{
Current.MainPage = new NavigationPage(new LockPage(Options));
}
}
else if (Options.FromAutofillFramework && Options.SaveType.HasValue)
{
@@ -287,13 +327,13 @@ namespace Bit.App
}
else
{
Current.MainPage = new HomePage(Options);
Current.MainPage = new NavigationPage(new HomePage(Options));
}
}
private async Task ClearCacheIfNeededAsync()
{
var lastClear = await _storageService.GetAsync<DateTime?>(Constants.LastFileCacheClearKey);
var lastClear = await _stateService.GetLastFileCacheClearAsync();
if ((DateTime.UtcNow - lastClear.GetValueOrDefault(DateTime.MinValue)).TotalDays >= 1)
{
var task = Task.Run(() => _deviceActionService.ClearCacheAsync());
@@ -336,12 +376,12 @@ namespace Bit.App
{
InitializeComponent();
SetCulture();
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
ThemeManager.SetTheme(Current.Resources);
Current.RequestedThemeChanged += (s, a) =>
{
UpdateThemeAsync();
UpdateTheme();
};
Current.MainPage = new HomePage();
Current.MainPage = new NavigationPage(new HomePage(Options));
var mainPageTask = SetMainPageAsync();
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
}
@@ -363,12 +403,20 @@ namespace Bit.App
});
}
private void UpdateTheme()
{
Device.BeginInvokeOnMainThread(() =>
{
ThemeManager.SetTheme(Current.Resources);
_messagingService.Send("updatedTheme");
});
}
private async Task LockedAsync(bool autoPromptBiometric)
{
await _stateService.PurgeAsync();
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
{
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
var vaultTimeout = await _stateService.GetVaultTimeoutAsync();
if (vaultTimeout == 0)
{
autoPromptBiometric = false;
@@ -398,7 +446,7 @@ namespace Bit.App
}
}
}
await _storageService.SaveAsync(Constants.PreviousPageKey, lastPageBeforeLock);
await _stateService.SetPreviousPageInfoAsync(lastPageBeforeLock);
var lockPage = new LockPage(Options, autoPromptBiometric);
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
}

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
x:Class="Bit.App.Controls.AccountViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="controls:AccountViewCellViewModel">
<Grid RowSpacing="0"
ColumnSpacing="0">
<Grid.Resources>
<u:InverseBoolConverter x:Key="inverseBool" />
</Grid.Resources>
<Grid
IsVisible="{Binding IsAccount}"
VerticalOptions="CenterAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid
Grid.Row="0"
Grid.Column="0"
RowSpacing="3"
Margin="12,0,0,0"
VerticalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Grid.Column="0"
Grid.Row="0"
Text="{Binding AccountView.Email}"
StyleClass="list-title"
LineBreakMode="TailTruncation" />
<Label
Grid.Column="0"
Grid.Row="1"
Text="{Binding AccountView.Hostname}"
StyleClass="list-sub"
LineBreakMode="TailTruncation" />
</Grid>
<Label
Grid.Column="1"
Grid.Row="0"
Text="{Binding AuthStatusText}"
FontSize="Small"
FontAttributes="Italic"
LineBreakMode="NoWrap"
HorizontalOptions="End"
Margin="10,0,20,0"
VerticalOptions="Center" />
</Grid>
<Grid
IsVisible="{Binding IsAccount, Converter={StaticResource inverseBool}}"
VerticalOptions="CenterAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="36" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
VerticalOptions="Center"
Margin="12,0,0,0"
WidthRequest="{OnPlatform 14, iOS=14, Android=26}"
HeightRequest="{OnPlatform 14, iOS=14, Android=26}"
Source="plus.png"
xct:IconTintColorEffect.TintColor="{DynamicResource TextColor}"
AutomationProperties.IsInAccessibleTree="False" />
<Label
Text="Add Account"
StyleClass="list-title"
LineBreakMode="TailTruncation"
VerticalOptions="Center"
Grid.Column="1" />
</Grid>
</Grid>
</ViewCell>

View File

@@ -0,0 +1,35 @@
using Bit.Core.Models.View;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public partial class AccountViewCell : ViewCell
{
public static readonly BindableProperty AccountProperty = BindableProperty.Create(
nameof(Account), typeof(AccountView), typeof(AccountViewCell), default(AccountView), BindingMode.OneWay);
public AccountViewCell()
{
InitializeComponent();
}
public AccountView Account
{
get => GetValue(AccountProperty) as AccountView;
set => SetValue(AccountProperty, value);
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == AccountProperty.PropertyName)
{
if (Account == null)
{
return;
}
BindingContext = new AccountViewCellViewModel(Account);
}
}
}
}

View File

@@ -0,0 +1,31 @@
using Bit.Core.Models.View;
using Bit.Core.Utilities;
namespace Bit.App.Controls
{
public class AccountViewCellViewModel : ExtendedViewModel
{
private AccountView _accountView;
public AccountViewCellViewModel(AccountView accountView)
{
AccountView = accountView;
}
public AccountView AccountView
{
get => _accountView;
set => SetProperty(ref _accountView, value);
}
public bool IsAccount
{
get => AccountView.IsAccount;
}
public string AuthStatusText
{
get => AccountView.AuthStatus.ToString();
}
}
}

View File

@@ -0,0 +1,114 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SkiaSharp;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class AvatarImageSource : StreamImageSource
{
private string _data;
public AvatarImageSource(string data = null)
{
_data = data;
}
public override Func<CancellationToken, Task<Stream>> Stream => GetStreamAsync;
private Task<Stream> GetStreamAsync(CancellationToken userToken = new CancellationToken())
{
OnLoadingStarted();
userToken.Register(CancellationTokenSource.Cancel);
var result = Draw();
OnLoadingCompleted(CancellationTokenSource.IsCancellationRequested);
return Task.FromResult(result);
}
private Stream Draw()
{
string chars = null;
string upperData = null;
if (string.IsNullOrEmpty(_data))
{
chars = "..";
}
else if (_data?.Length > 2)
{
upperData = _data.ToUpper();
chars = upperData.Substring(0, 2).ToUpper();
}
var bgColor = StringToColor(upperData);
var textColor = Color.White;
var size = 50;
var bitmap = new SKBitmap(
size * 2,
size * 2,
SKImageInfo.PlatformColorType,
SKAlphaType.Premul);
var canvas = new SKCanvas(bitmap);
canvas.Clear(SKColors.Transparent);
var midX = canvas.LocalClipBounds.Size.ToSizeI().Width / 2;
var midY = canvas.LocalClipBounds.Size.ToSizeI().Height / 2;
var radius = midX - midX / 5;
var circlePaint = new SKPaint
{
IsAntialias = true,
Style = SKPaintStyle.Fill,
StrokeJoin = SKStrokeJoin.Miter,
Color = SKColor.Parse(bgColor.ToHex())
};
canvas.DrawCircle(midX, midY, radius, circlePaint);
var typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal);
var textSize = midX / 1.3f;
var textPaint = new SKPaint
{
IsAntialias = true,
Style = SKPaintStyle.Fill,
Color = SKColor.Parse(textColor.ToHex()),
TextSize = textSize,
TextAlign = SKTextAlign.Center,
Typeface = typeface
};
var rect = new SKRect();
textPaint.MeasureText(chars, ref rect);
canvas.DrawText(chars, midX, midY + rect.Height / 2, textPaint);
return SKImage.FromBitmap(bitmap).Encode(SKEncodedImageFormat.Png, 100).AsStream();
}
private Color StringToColor(string str)
{
if (str == null)
{
return Color.FromHex("#33ffffff");
}
var hash = 0;
for (var i = 0; i < str.Length; i++)
{
hash = str[i] + ((hash << 5) - hash);
}
var color = "#FF";
for (var i = 0; i < 3; i++)
{
var value = (hash >> (i * 8)) & 0xff;
var base16 = "00" + Convert.ToString(value, 16);
color += base16.Substring(base16.Length - 2);
}
//if (Device.RuntimePlatform == Device.iOS)
//{
// // TODO remove this once iOS ToolbarItem tint issue is solved
// return Color.FromHex("#33ffffff");
//}
return Color.FromHex(color);
}
}
}

View File

@@ -5,7 +5,6 @@
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:ff="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
StyleClass="list-row, list-row-platform"
RowSpacing="0"
ColumnSpacing="0"
@@ -28,7 +27,7 @@
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<controls:IconLabel
<controls:FaLabel
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
@@ -77,25 +76,25 @@
Text="{Binding Cipher.SubTitle}"
IsVisible="{Binding Source={RelativeSource Self}, Path=Text,
Converter={StaticResource stringHasValueConverter}}"/>
<controls:IconLabel
<controls:FaLabel
Grid.Column="1"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="{Binding Source={x:Static core:BitwardenIcons.Collection}}"
Text="&#xf1b2;"
IsVisible="{Binding Cipher.Shared, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Shared}" />
<controls:IconLabel
<controls:FaLabel
Grid.Column="2"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="{Binding Source={x:Static core:BitwardenIcons.Paperclip}}"
Text="&#xf0c6;"
IsVisible="{Binding Cipher.HasAttachments, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Attachments}" />

View File

@@ -0,0 +1,9 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedToolbarItem : ToolbarItem
{
public bool UseOriginalImage { get; set; }
}
}

View File

@@ -2,18 +2,18 @@
namespace Bit.App.Controls
{
public class IconButton : Button
public class FaButton : Button
{
public IconButton()
public FaButton()
{
Padding = 0;
switch (Device.RuntimePlatform)
{
case Device.iOS:
FontFamily = "bwi-font";
FontFamily = "FontAwesome";
break;
case Device.Android:
FontFamily = "bwi-font.ttf#bwi-font";
FontFamily = "FontAwesome.ttf#FontAwesome";
break;
}
}

View File

@@ -2,17 +2,17 @@
namespace Bit.App.Controls
{
public class IconLabel : Label
public class FaLabel : Label
{
public IconLabel()
public FaLabel()
{
switch (Device.RuntimePlatform)
{
case Device.iOS:
FontFamily = "bwi-font";
FontFamily = "FontAwesome";
break;
case Device.Android:
FontFamily = "bwi-font.ttf#bwi-font";
FontFamily = "FontAwesome.ttf#FontAwesome";
break;
}
}

View File

@@ -4,7 +4,6 @@
x:Class="Bit.App.Controls.SendViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
StyleClass="list-row, list-row-platform"
RowSpacing="0"
ColumnSpacing="0"
@@ -24,7 +23,7 @@
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<controls:IconLabel
<controls:FaLabel
Grid.Row="0"
Grid.Column="0"
HorizontalOptions="Center"
@@ -61,58 +60,58 @@
Grid.ColumnSpan="6"
StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Send.DisplayDate}" />
<controls:IconLabel
<controls:FaLabel
Grid.Column="1"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="{Binding Source={x:Static core:BitwardenIcons.ExclamationTriangle}}"
Text="&#xf071;"
IsVisible="{Binding Send.Disabled, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Disabled}" />
<controls:IconLabel
<controls:FaLabel
Grid.Column="2"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="{Binding Source={x:Static core:BitwardenIcons.Key}}"
Text="&#xf084;"
IsVisible="{Binding Send.HasPassword, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Password}" />
<controls:IconLabel
<controls:FaLabel
Grid.Column="3"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="{Binding Source={x:Static core:BitwardenIcons.Ban}}"
Text="&#xf05e;"
IsVisible="{Binding Send.MaxAccessCountReached, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n MaxAccessCountReached}" />
<controls:IconLabel
<controls:FaLabel
Grid.Column="4"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="{Binding Source={x:Static core:BitwardenIcons.Clock}}"
Text="&#xf017;"
IsVisible="{Binding Send.Expired, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Expired}" />
<controls:IconLabel
<controls:FaLabel
Grid.Column="5"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="{Binding Source={x:Static core:BitwardenIcons.Trash}}"
Text="&#xf1f8;"
IsVisible="{Binding Send.PendingDelete, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n PendingDelete}" />

View File

@@ -21,7 +21,6 @@ namespace Bit.App.Models
public string SaveCardCode { get; set; }
public bool IosExtension { get; set; }
public Tuple<SendType, string, byte[], string> CreateSend { get; set; }
public bool CopyInsteadOfShareAfterSaving { get; set; }
public void SetAllFrom(AppOptions o)
{
@@ -45,7 +44,6 @@ namespace Bit.App.Models
SaveCardCode = o.SaveCardCode;
IosExtension = o.IosExtension;
CreateSend = o.CreateSend;
CopyInsteadOfShareAfterSaving = o.CopyInsteadOfShareAfterSaving;
}
}
}

View File

@@ -14,7 +14,7 @@ namespace Bit.App.Pages
public class BaseChangePasswordViewModel : BaseViewModel
{
protected readonly IPlatformUtilsService _platformUtilsService;
protected readonly IUserService _userService;
protected readonly IStateService _stateService;
protected readonly IPolicyService _policyService;
protected readonly IPasswordGenerationService _passwordGenerationService;
protected readonly II18nService _i18nService;
@@ -31,7 +31,7 @@ namespace Bit.App.Pages
protected BaseChangePasswordViewModel()
{
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
_passwordGenerationService =
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
@@ -172,7 +172,7 @@ namespace Bit.App.Pages
private async Task<List<string>> GetPasswordStrengthUserInput()
{
var email = await _userService.GetEmailAsync();
var email = await _stateService.GetEmailAsync();
List<string> userInput = null;
var atPosition = email.IndexOf('@');
if (atPosition > -1)

View File

@@ -1,7 +1,6 @@
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Exceptions;
using Bit.Core.Utilities;
@@ -13,13 +12,21 @@ namespace Bit.App.Pages
{
public class DeleteAccountViewModel : BaseViewModel
{
readonly IApiService _apiService;
readonly IPasswordRepromptService _passwordRepromptService;
readonly IMessagingService _messagingService;
readonly ICryptoService _cryptoService;
readonly IPlatformUtilsService _platformUtilsService;
readonly IVerificationActionsFlowHelper _verificationActionsFlowHelper;
readonly IDeviceActionService _deviceActionService;
public DeleteAccountViewModel()
{
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_verificationActionsFlowHelper = ServiceContainer.Resolve<IVerificationActionsFlowHelper>("verificationActionsFlowHelper");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
PageTitle = AppResources.DeleteAccount;
}
@@ -35,53 +42,18 @@ namespace Bit.App.Pages
return;
}
await _verificationActionsFlowHelper
.Configure(VerificationFlowAction.DeleteAccount,
null,
AppResources.DeleteAccount,
true)
.ValidateAndExecuteAsync();
}
catch (System.Exception ex)
{
#if !FDROID
Crashes.TrackError(ex);
#endif
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred);
}
}
}
var (password, valid) = await _passwordRepromptService.ShowPasswordPromptAndGetItAsync();
if (!valid)
{
return;
}
public interface IDeleteAccountActionFlowExecutioner : IActionFlowExecutioner { }
public class DeleteAccountActionFlowExecutioner : IDeleteAccountActionFlowExecutioner
{
readonly IApiService _apiService;
readonly IMessagingService _messagingService;
readonly IPlatformUtilsService _platformUtilsService;
readonly IDeviceActionService _deviceActionService;
public DeleteAccountActionFlowExecutioner(IApiService apiService,
IMessagingService messagingService,
IPlatformUtilsService platformUtilsService,
IDeviceActionService deviceActionService)
{
_apiService = apiService;
_messagingService = messagingService;
_platformUtilsService = platformUtilsService;
_deviceActionService = deviceActionService;
}
public async Task Execute(IActionFlowParmeters parameters)
{
try
{
await _deviceActionService.ShowLoadingAsync(AppResources.DeletingYourAccount);
var masterPasswordHashKey = await _cryptoService.HashPasswordAsync(password, null);
await _apiService.DeleteAccountAsync(new Core.Models.Request.DeleteAccountRequest
{
MasterPasswordHash = parameters.VerificationType == Core.Enums.VerificationType.MasterPassword ? parameters.Secret : (string)null,
OTP = parameters.VerificationType == Core.Enums.VerificationType.OTP ? parameters.Secret : (string)null
MasterPasswordHash = masterPasswordHashKey
});
await _deviceActionService.HideLoadingAsync();

View File

@@ -10,14 +10,11 @@ namespace Bit.App.Pages
public partial class EnvironmentPage : BaseContentPage
{
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IMessagingService _messagingService;
private readonly EnvironmentPageViewModel _vm;
public EnvironmentPage()
{
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", true);
InitializeComponent();
_vm = BindingContext as EnvironmentPageViewModel;
_vm.Page = this;
@@ -35,7 +32,6 @@ namespace Bit.App.Pages
_vm.SubmitSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SubmitSuccessAsync());
_vm.CloseAction = async () =>
{
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync();
};
}

View File

@@ -1,54 +1,107 @@
<?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.HomePage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
x:DataType="pages:HomeViewModel"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
<pages:HomeViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<controls:ExtendedToolbarItem
x:Name="_accountAvatar"
x:Key="accountAvatar"
IconImageSource="{Binding AvatarImageSource}"
Clicked="AccountSwitch_Clicked"
Order="Primary"
Priority="-1"
UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" />
<ToolbarItem x:Name="_closeItem" x:Key="closeItem" Text="{u:I18n Close}"
Clicked="Close_Clicked" Order="Primary" Priority="-1" />
<ToolbarItem
Icon="cog_environment.png" Clicked="Environment_Clicked" Order="Primary"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
</ContentPage.ToolbarItems>
<StackLayout Spacing="0" Padding="10, 5">
<controls:IconButton Text="{Binding Source={x:Static core:BitwardenIcons.Cog}}"
StyleClass="btn-muted, btn-icon, btn-icon-platform"
HorizontalOptions="Start"
Clicked="Environment_Clicked"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}">
<controls:IconButton.Margin>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 10, 0, 0" />
<On Platform="Android" Value="0" />
</OnPlatform>
</controls:IconButton.Margin>
</controls:IconButton>
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
<Image
x:Name="_logo"
Source="logo.png"
VerticalOptions="Center" />
<Label Text="{u:I18n LoginOrCreateNewAccount}"
StyleClass="text-lg"
HorizontalTextAlignment="Center"></Label>
<StackLayout Spacing="5">
<Button Text="{u:I18n LogIn}"
StyleClass="btn-primary"
Clicked="LogIn_Clicked" />
<Button Text="{u:I18n CreateAccount}"
Clicked="Register_Clicked" />
<Button Text="{u:I18n LogInSso}"
Clicked="LogInSso_Clicked" />
<ContentPage.Resources>
<ResourceDictionary>
<StackLayout x:Name="_mainLayout" x:Key="mainLayout" Spacing="0" Padding="10, 5">
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
<Image
x:Name="_logo"
Source="logo.png"
VerticalOptions="Center" />
<Label Text="{u:I18n LoginOrCreateNewAccount}"
StyleClass="text-lg"
HorizontalTextAlignment="Center">
</Label>
<StackLayout Spacing="5">
<Button Text="{u:I18n LogIn}"
StyleClass="btn-primary"
Clicked="LogIn_Clicked" />
<Button Text="{u:I18n CreateAccount}"
Clicked="Register_Clicked" />
<Button Text="{u:I18n LogInSso}"
Clicked="LogInSso_Clicked" />
</StackLayout>
</StackLayout>
</StackLayout>
</StackLayout>
</StackLayout>
</ResourceDictionary>
</ContentPage.Resources>
<AbsoluteLayout
x:Name="_absLayout"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<ContentView
x:Name="_mainContent"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
</ContentView>
<!-- Account Switching Overlay -->
<ContentView
x:Name="_accountListOverlay"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
AbsoluteLayout.LayoutFlags="All"
IsVisible="False"
BackgroundColor="#22000000"
Padding="0">
<StackLayout
x:Name="_accountListContainer"
VerticalOptions="StartAndExpand"
HorizontalOptions="FillAndExpand"
BackgroundColor="{DynamicResource BackgroundColor}"
xct:ShadowEffect.Color="Black"
xct:ShadowEffect.Radius="10"
xct:ShadowEffect.OffsetY="3">
<ListView
x:Name="_accountListView"
ItemsSource="{Binding AccountViews}"
ItemSelected="AccountRow_Selected"
BackgroundColor="Transparent"
VerticalOptions="FillAndExpand"
RowHeight="60">
<ListView.ItemTemplate>
<DataTemplate x:DataType="view:AccountView">
<controls:AccountViewCell
Account="{Binding .}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentView>
</AbsoluteLayout>
</pages:BaseContentPage>

View File

@@ -12,13 +12,10 @@ namespace Bit.App.Pages
{
private readonly HomeViewModel _vm;
private readonly AppOptions _appOptions;
private IMessagingService _messagingService;
private IBroadcasterService _broadcasterService;
public HomePage(AppOptions appOptions = null)
{
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", false);
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_appOptions = appOptions;
InitializeComponent();
@@ -29,6 +26,11 @@ namespace Bit.App.Pages
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
UpdateLogo();
if (!_appOptions?.IosExtension ?? false)
{
ToolbarItems.Remove(_closeItem);
}
}
public async Task DismissRegisterPageAndLogInAsync(string email)
@@ -37,10 +39,18 @@ namespace Bit.App.Pages
await Navigation.PushModalAsync(new NavigationPage(new LoginPage(email, _appOptions)));
}
protected override void OnAppearing()
protected override async void OnAppearing()
{
base.OnAppearing();
_messagingService.Send("showStatusBar", false);
_mainContent.Content = _mainLayout;
if (await ShowAccountSwitcherAsync())
{
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
}
else
{
ToolbarItems.Remove(_accountAvatar);
}
_broadcasterService.Subscribe(nameof(HomePage), async (message) =>
{
if (message.Command == "updatedTheme")
@@ -127,5 +137,23 @@ namespace Bit.App.Pages
var page = new EnvironmentPage();
await Navigation.PushModalAsync(new NavigationPage(page));
}
private async void AccountSwitch_Clicked(object sender, EventArgs e)
{
if (_accountListOverlay.IsVisible)
{
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay);
}
else
{
await RefreshAccountViewsAsync(_accountListView, false);
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay);
}
}
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
{
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, null, true);
}
}
}

View File

@@ -1,15 +1,27 @@
using System;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
namespace Bit.App.Pages
{
public class HomeViewModel : BaseViewModel
{
private readonly IStateService _stateService;
public HomeViewModel()
{
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
PageTitle = AppResources.Bitwarden;
}
public ExtendedObservableCollection<AccountView> AccountViews
{
get => _stateService.AccountViews;
}
public Action StartLoginAction { get; set; }
public Action StartRegisterAction { get; set; }
public Action StartSsoLoginAction { get; set; }

View File

@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.LockPage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:LockPageViewModel"
Title="{Binding PageTitle}">
@@ -13,6 +15,19 @@
<pages:LockPageViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<controls:ExtendedToolbarItem
x:Name="_accountAvatar"
x:Key="accountAvatar"
IconImageSource="{Binding AvatarImageSource}"
Clicked="AccountSwitch_Clicked"
Order="Primary"
Priority="-1"
UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" />
@@ -25,119 +40,162 @@
x:Name="_logOut"
Clicked="LogOut_Clicked"
Order="Secondary"/>
<ScrollView x:Name="_mainLayout" x:Key="mainLayout">
<StackLayout Spacing="20">
<StackLayout StyleClass="box">
<Grid
StyleClass="box-row"
IsVisible="{Binding PinLock}"
Padding="0, 10, 0, 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Text="{u:I18n PIN}"
StyleClass="box-label"
Grid.Row="0"
Grid.Column="0" />
<controls:MonoEntry
x:Name="_pin"
Text="{Binding Pin}"
StyleClass="box-value"
Keyboard="Numeric"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<Grid
x:Name="_passwordGrid"
StyleClass="box-row"
IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}"
Padding="0, 10, 0, 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Text="{u:I18n MasterPassword}"
StyleClass="box-label"
Grid.Row="0"
Grid.Column="0" />
<controls:MonoEntry
x:Name="_masterPassword"
Text="{Binding MasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<StackLayout
StyleClass="box-row"
Padding="0, 10, 0, 0">
<Label
Text="{Binding LockedVerifyText}"
StyleClass="box-footer-label" />
<Label
Text="{Binding LoggedInAsText}"
StyleClass="box-footer-label"
Margin="0, 10, 0, 0" />
</StackLayout>
</StackLayout>
<StackLayout Padding="10, 0">
<Label
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"
IsVisible="{Binding BiometricButtonVisible}">
</Button>
<Button
x:Name="_unlockButton"
Text="{u:I18n Unlock}"
StyleClass="btn-primary"
Clicked="Unlock_Clicked" />
</StackLayout>
</StackLayout>
</ScrollView>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
</ContentPage.ToolbarItems>
<AbsoluteLayout
x:Name="_absLayout"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<ContentView
x:Name="_mainContent"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
</ContentView>
<ScrollView>
<StackLayout Spacing="20">
<StackLayout StyleClass="box">
<Grid
StyleClass="box-row"
IsVisible="{Binding PinLock}"
Padding="0, 10, 0, 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Text="{u:I18n PIN}"
StyleClass="box-label"
Grid.Row="0"
Grid.Column="0" />
<controls:MonoEntry
x:Name="_pin"
Text="{Binding Pin}"
StyleClass="box-value"
Keyboard="Numeric"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" />
<controls:IconButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<Grid
x:Name="_passwordGrid"
StyleClass="box-row"
IsVisible="{Binding PinLock, Converter={StaticResource inverseBool}}"
Padding="0, 10, 0, 0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Text="{u:I18n MasterPassword}"
StyleClass="box-label"
Grid.Row="0"
Grid.Column="0" />
<controls:MonoEntry
x:Name="_masterPassword"
Text="{Binding MasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding SubmitCommand}" />
<controls:IconButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<StackLayout
StyleClass="box-row"
Padding="0, 10, 0, 0">
<Label
Text="{Binding LockedVerifyText}"
StyleClass="box-footer-label" />
<Label
Text="{Binding LoggedInAsText}"
StyleClass="box-footer-label"
Margin="0, 10, 0, 0" />
</StackLayout>
<!-- Account Switching Overlay -->
<ContentView
x:Name="_accountListOverlay"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
AbsoluteLayout.LayoutFlags="All"
IsVisible="False"
BackgroundColor="#22000000"
Padding="0">
<StackLayout
x:Name="_accountListContainer"
VerticalOptions="StartAndExpand"
HorizontalOptions="FillAndExpand"
BackgroundColor="{DynamicResource BackgroundColor}"
xct:ShadowEffect.Color="Black"
xct:ShadowEffect.Radius="10"
xct:ShadowEffect.OffsetY="3">
<ListView
x:Name="_accountListView"
ItemsSource="{Binding AccountViews}"
ItemSelected="AccountRow_Selected"
BackgroundColor="Transparent"
VerticalOptions="FillAndExpand"
RowHeight="60">
<ListView.ItemTemplate>
<DataTemplate x:DataType="view:AccountView">
<controls:AccountViewCell
Account="{Binding .}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
<StackLayout Padding="10, 0">
<Label
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"
IsVisible="{Binding BiometricButtonVisible}"></Button>
<Button
x:Name="_unlockButton"
Text="{u:I18n Unlock}"
StyleClass="btn-primary"
Clicked="Unlock_Clicked" />
</StackLayout>
</StackLayout>
</ScrollView>
</ContentView>
</AbsoluteLayout>
</pages:BaseContentPage>

View File

@@ -1,7 +1,5 @@
using Bit.App.Models;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Bit.App.Utilities;
@@ -63,6 +61,15 @@ namespace Bit.App.Pages
return;
}
_appeared = true;
_mainContent.Content = _mainLayout;
if (await ShowAccountSwitcherAsync())
{
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
}
else
{
ToolbarItems.Remove(_accountAvatar);
}
await _vm.InitAsync();
if (!_vm.BiometricLock)
{
@@ -147,5 +154,23 @@ namespace Bit.App.Pages
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
}
private async void AccountSwitch_Clicked(object sender, EventArgs e)
{
if (_accountListOverlay.IsVisible)
{
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay);
}
else
{
await RefreshAccountViewsAsync(_accountListView, false);
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay);
}
}
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
{
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, null, true);
}
}
}

View File

@@ -1,18 +1,15 @@
using System;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Request;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Bit.App.Utilities;
using Bit.Core.Models.Request;
using Bit.Core.Models.View;
using Xamarin.Forms;
#if !FDROID
using Microsoft.AppCenter.Crashes;
#endif
namespace Bit.App.Pages
{
@@ -23,10 +20,7 @@ namespace Bit.App.Pages
private readonly IDeviceActionService _deviceActionService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ICryptoService _cryptoService;
private readonly IStorageService _storageService;
private readonly IUserService _userService;
private readonly IMessagingService _messagingService;
private readonly IStorageService _secureStorageService;
private readonly IEnvironmentService _environmentService;
private readonly IStateService _stateService;
private readonly IBiometricService _biometricService;
@@ -51,10 +45,7 @@ namespace Bit.App.Pages
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
@@ -122,9 +113,14 @@ namespace Bit.App.Pages
set => SetProperty(ref _lockedVerifyText, value);
}
public ExtendedObservableCollection<AccountView> AccountViews
{
get => _stateService.AccountViews;
}
public Command SubmitCommand { get; }
public Command TogglePasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string ShowPasswordIcon => ShowPassword ? "" : "";
public string MasterPassword { get; set; }
public string Pin { get; set; }
public Action UnlockedAction { get; set; }
@@ -132,7 +128,7 @@ namespace Bit.App.Pages
public async Task InitAsync()
{
_pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
PinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
PinLock = (_pinSet.Item1 && _stateService.GetPinProtectedAsync() != null) || _pinSet.Item2;
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync();
// Users with key connector and without biometric or pin has no MP to unlock with
@@ -140,17 +136,8 @@ namespace Bit.App.Pages
if (_usingKeyConnector && !(BiometricLock || PinLock))
{
await _vaultTimeoutService.LogOutAsync();
return;
}
_email = await _userService.GetEmailAsync();
if (string.IsNullOrWhiteSpace(_email))
{
await _vaultTimeoutService.LogOutAsync();
#if !FDROID
Crashes.TrackError(new NullReferenceException("Email not found in storage"));
#endif
return;
}
_email = await _stateService.GetEmailAsync();
var webVault = _environmentService.GetWebVaultUrl();
if (string.IsNullOrWhiteSpace(webVault))
{
@@ -215,8 +202,8 @@ namespace Bit.App.Pages
}
ShowPassword = false;
var kdf = await _userService.GetKdfAsync();
var kdfIterations = await _userService.GetKdfIterationsAsync();
var kdf = await _stateService.GetKdfTypeAsync();
var kdfIterations = await _stateService.GetKdfIterationsAsync();
if (PinLock)
{
@@ -227,9 +214,9 @@ namespace Bit.App.Pages
{
var key = await _cryptoService.MakeKeyFromPinAsync(Pin, _email,
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000),
_vaultTimeoutService.PinProtectedKey);
await _stateService.GetPinProtectedCachedAsync());
var encKey = await _cryptoService.GetEncKeyAsync(key);
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
var protectedPin = await _stateService.GetProtectedPinAsync();
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
failed = decPin != Pin;
if (!failed)
@@ -298,12 +285,12 @@ namespace Bit.App.Pages
{
if (_pinSet.Item1)
{
var protectedPin = await _storageService.GetAsync<string>(Constants.ProtectedPin);
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,
kdf.GetValueOrDefault(KdfType.PBKDF2_SHA256), kdfIterations.GetValueOrDefault(5000));
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
await _stateService.SetPinProtectedCachedAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
}
MasterPassword = string.Empty;
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
@@ -369,7 +356,7 @@ namespace Bit.App.Pages
page.MasterPasswordEntry.Focus();
}
});
_vaultTimeoutService.BiometricLocked = !success;
_stateService.BiometricLocked = !success;
if (success)
{
await DoContinueAsync();
@@ -388,9 +375,7 @@ namespace Bit.App.Pages
private async Task DoContinueAsync()
{
_vaultTimeoutService.BiometricLocked = false;
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
_stateService.BiometricLocked = false;
_messagingService.Send("unlocked");
UnlockedAction?.Invoke();
}

View File

@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.LoginPage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:view="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
xmlns:u="clr-namespace:Bit.App.Utilities"
x:DataType="pages:LoginPageViewModel"
Title="{Binding PageTitle}">
@@ -13,6 +15,21 @@
<pages:LoginPageViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<controls:ExtendedToolbarItem
x:Name="_accountAvatar"
x:Key="accountAvatar"
IconImageSource="{Binding AvatarImageSource}"
Clicked="AccountSwitch_Clicked"
Order="Primary"
Priority="-1"
UseOriginalImage="True"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Account}" />
<ToolbarItem x:Name="_closeItem" x:Key="closeItem" Text="{u:I18n Close}"
Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" />
@@ -25,68 +42,109 @@
x:Name="_getPasswordHint"
Clicked="Hint_Clicked"
Order="Secondary"/>
<ScrollView x:Name="_mainLayout" x:Key="mainLayout">
<StackLayout Spacing="20">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row">
<Label
Text="{u:I18n EmailAddress}"
StyleClass="box-label" />
<Entry
x:Name="_email"
Text="{Binding Email}"
Keyboard="Email"
StyleClass="box-value" />
</StackLayout>
<Grid StyleClass="box-row">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Text="{u:I18n MasterPassword}"
StyleClass="box-label"
Grid.Row="0"
Grid.Column="0" />
<controls:MonoEntry
x:Name="_masterPassword"
Text="{Binding MasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding LogInCommand}" />
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
</StackLayout>
<StackLayout Padding="10, 0">
<Button Text="{u:I18n LogIn}"
StyleClass="btn-primary"
Clicked="LogIn_Clicked" />
</StackLayout>
</StackLayout>
</ScrollView>
</ResourceDictionary>
</ContentPage.Resources>
<AbsoluteLayout
x:Name="_absLayout"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<ContentView
x:Name="_mainContent"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1">
</ContentView>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<!-- Account Switching Overlay -->
<ContentView
x:Name="_accountListOverlay"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
AbsoluteLayout.LayoutFlags="All"
IsVisible="False"
BackgroundColor="#22000000"
Padding="0">
<ScrollView>
<StackLayout Spacing="20">
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row">
<Label
Text="{u:I18n EmailAddress}"
StyleClass="box-label" />
<Entry
x:Name="_email"
Text="{Binding Email}"
Keyboard="Email"
StyleClass="box-value" />
</StackLayout>
<Grid StyleClass="box-row">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Text="{u:I18n MasterPassword}"
StyleClass="box-label"
Grid.Row="0"
Grid.Column="0" />
<controls:MonoEntry
x:Name="_masterPassword"
Text="{Binding MasterPassword}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding LogInCommand}" />
<controls:IconButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
</Grid>
<StackLayout
x:Name="_accountListContainer"
VerticalOptions="StartAndExpand"
HorizontalOptions="FillAndExpand"
BackgroundColor="{DynamicResource BackgroundColor}"
xct:ShadowEffect.Color="Black"
xct:ShadowEffect.Radius="10"
xct:ShadowEffect.OffsetY="3">
<ListView
x:Name="_accountListView"
ItemsSource="{Binding AccountViews}"
ItemSelected="AccountRow_Selected"
BackgroundColor="Transparent"
VerticalOptions="FillAndExpand"
RowHeight="60">
<ListView.ItemTemplate>
<DataTemplate x:DataType="view:AccountView">
<controls:AccountViewCell
Account="{Binding .}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
<StackLayout Padding="10, 0">
<Button Text="{u:I18n LogIn}"
StyleClass="btn-primary"
Clicked="LogIn_Clicked" />
</StackLayout>
</StackLayout>
</ScrollView>
</ContentView>
</AbsoluteLayout>
</pages:BaseContentPage>

View File

@@ -1,7 +1,5 @@
using Bit.App.Models;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Bit.App.Utilities;
@@ -11,8 +9,6 @@ namespace Bit.App.Pages
{
public partial class LoginPage : BaseContentPage
{
private readonly IMessagingService _messagingService;
private readonly IStorageService _storageService;
private readonly LoginPageViewModel _vm;
private readonly AppOptions _appOptions;
@@ -20,9 +16,6 @@ namespace Bit.App.Pages
public LoginPage(string email = null, AppOptions appOptions = null)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", true);
_appOptions = appOptions;
InitializeComponent();
_vm = BindingContext as LoginPageViewModel;
@@ -33,7 +26,6 @@ namespace Bit.App.Pages
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
_vm.CloseAction = async () =>
{
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync();
};
_vm.Email = email;
@@ -54,6 +46,11 @@ namespace Bit.App.Pages
{
ToolbarItems.Add(_getPasswordHint);
}
if (!_appOptions?.IosExtension ?? false)
{
ToolbarItems.Remove(_closeItem);
}
}
public Entry MasterPasswordEntry { get; set; }
@@ -61,6 +58,15 @@ namespace Bit.App.Pages
protected override async void OnAppearing()
{
base.OnAppearing();
_mainContent.Content = _mainLayout;
if (await ShowAccountSwitcherAsync())
{
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
}
else
{
ToolbarItems.Remove(_accountAvatar);
}
await _vm.InitAsync();
if (!_inputFocused)
{
@@ -130,5 +136,23 @@ namespace Bit.App.Pages
var page = new UpdateTempPasswordPage();
await Navigation.PushModalAsync(new NavigationPage(page));
}
private async void AccountSwitch_Clicked(object sender, EventArgs e)
{
if (_accountListOverlay.IsVisible)
{
await ShowAccountListAsync(false, _accountListContainer, _accountListOverlay);
}
else
{
await RefreshAccountViewsAsync(_accountListView, false);
await ShowAccountListAsync(true, _accountListContainer, _accountListOverlay);
}
}
private async void AccountRow_Selected(object sender, SelectedItemChangedEventArgs e)
{
await AccountRowSelectedAsync(sender, e, _accountListContainer, _accountListOverlay, null, true);
}
}
}

View File

@@ -1,25 +1,21 @@
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Exceptions;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Bit.App.Utilities;
using Bit.Core.Models.View;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class LoginPageViewModel : CaptchaProtectedViewModel
{
private const string Keys_RememberedEmail = "rememberedEmail";
private const string Keys_RememberEmail = "rememberEmail";
private readonly IDeviceActionService _deviceActionService;
private readonly IAuthService _authService;
private readonly ISyncService _syncService;
private readonly IStorageService _storageService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStateService _stateService;
private readonly IEnvironmentService _environmentService;
@@ -34,7 +30,6 @@ namespace Bit.App.Pages
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_authService = ServiceContainer.Resolve<IAuthService>("authService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
@@ -67,9 +62,14 @@ namespace Bit.App.Pages
set => SetProperty(ref _masterPassword, value);
}
public ExtendedObservableCollection<AccountView> AccountViews
{
get => _stateService.AccountViews;
}
public Command LogInCommand { get; }
public Command TogglePasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string ShowPasswordIcon => ShowPassword ? "" : "";
public bool RememberEmail { get; set; }
public Action StartTwoFactorAction { get; set; }
public Action LogInSuccessAction { get; set; }
@@ -85,9 +85,9 @@ namespace Bit.App.Pages
{
if (string.IsNullOrWhiteSpace(Email))
{
Email = await _storageService.GetAsync<string>(Keys_RememberedEmail);
Email = await _stateService.GetRememberedEmailAsync();
}
var rememberEmail = await _storageService.GetAsync<bool?>(Keys_RememberEmail);
var rememberEmail = await _stateService.GetRememberEmailAsync();
RememberEmail = rememberEmail.GetValueOrDefault(true);
}
@@ -131,11 +131,11 @@ namespace Bit.App.Pages
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
if (RememberEmail)
{
await _storageService.SaveAsync(Keys_RememberedEmail, Email);
await _stateService.SetRememberedEmailAsync(Email);
}
else
{
await _storageService.RemoveAsync(Keys_RememberedEmail);
await _stateService.SetRememberedEmailAsync(null);
}
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
@@ -163,8 +163,6 @@ namespace Bit.App.Pages
}
else
{
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
LogInSuccessAction?.Invoke();
}

View File

@@ -10,8 +10,6 @@ namespace Bit.App.Pages
{
public partial class LoginSsoPage : BaseContentPage
{
private readonly IStorageService _storageService;
private readonly IMessagingService _messagingService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly LoginSsoPageViewModel _vm;
private readonly AppOptions _appOptions;
@@ -20,10 +18,7 @@ namespace Bit.App.Pages
public LoginSsoPage(AppOptions appOptions = null)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_messagingService.Send("showStatusBar", true);
_appOptions = appOptions;
InitializeComponent();
_vm = BindingContext as LoginSsoPageViewModel;
@@ -36,7 +31,6 @@ namespace Bit.App.Pages
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
_vm.CloseAction = async () =>
{
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync();
};
if (Device.RuntimePlatform == Device.Android)

View File

@@ -1,6 +1,5 @@
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System;
@@ -16,16 +15,12 @@ namespace Bit.App.Pages
{
public class LoginSsoPageViewModel : BaseViewModel
{
private const string Keys_RememberedOrgIdentifier = "rememberedOrgIdentifier";
private const string Keys_RememberOrgIdentifier = "rememberOrgIdentifier";
private readonly IDeviceActionService _deviceActionService;
private readonly IAuthService _authService;
private readonly ISyncService _syncService;
private readonly IApiService _apiService;
private readonly IPasswordGenerationService _passwordGenerationService;
private readonly ICryptoFunctionService _cryptoFunctionService;
private readonly IStorageService _storageService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStateService _stateService;
@@ -40,7 +35,6 @@ namespace Bit.App.Pages
_passwordGenerationService =
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
_cryptoFunctionService = ServiceContainer.Resolve<ICryptoFunctionService>("cryptoFunctionService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
@@ -66,9 +60,9 @@ namespace Bit.App.Pages
{
if (string.IsNullOrWhiteSpace(OrgIdentifier))
{
OrgIdentifier = await _storageService.GetAsync<string>(Keys_RememberedOrgIdentifier);
OrgIdentifier = await _stateService.GetRememberedOrgIdentifierAsync();
}
var rememberOrgIdentifier = await _storageService.GetAsync<bool?>(Keys_RememberOrgIdentifier);
var rememberOrgIdentifier = await _stateService.GetRememberOrgIdentifierAsync();
RememberOrgIdentifier = rememberOrgIdentifier.GetValueOrDefault(true);
}
@@ -116,7 +110,7 @@ namespace Bit.App.Pages
var redirectUri = "bitwarden://sso-callback";
var url = _apiService.IdentityBaseUrl + "/connect/authorize?" +
"client_id=" + _platformUtilsService.GetClientType().GetString() + "&" +
"client_id=" + _platformUtilsService.IdentityClientId + "&" +
"redirect_uri=" + Uri.EscapeDataString(redirectUri) + "&" +
"response_type=code&scope=api%20offline_access&" +
"state=" + state + "&code_challenge=" + codeChallenge + "&" +
@@ -172,11 +166,11 @@ namespace Bit.App.Pages
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
if (RememberOrgIdentifier)
{
await _storageService.SaveAsync(Keys_RememberedOrgIdentifier, OrgIdentifier);
await _stateService.SetRememberedOrgIdentifierAsync(OrgIdentifier);
}
else
{
await _storageService.RemoveAsync(Keys_RememberedOrgIdentifier);
await _stateService.SetRememberedOrgIdentifierAsync(null);
}
await _deviceActionService.HideLoadingAsync();
if (response.TwoFactor)
@@ -193,8 +187,6 @@ namespace Bit.App.Pages
}
else
{
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
SsoAuthSuccessAction?.Invoke();
}

View File

@@ -60,7 +60,7 @@
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0" />
<controls:IconButton
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
@@ -98,7 +98,7 @@
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0" />
<controls:IconButton
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding ToggleConfirmPasswordCommand}"

View File

@@ -1,6 +1,4 @@
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System;
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
@@ -8,22 +6,18 @@ namespace Bit.App.Pages
{
public partial class RegisterPage : BaseContentPage
{
private readonly IMessagingService _messagingService;
private readonly RegisterPageViewModel _vm;
private bool _inputFocused;
public RegisterPage(HomePage homePage)
{
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", true);
InitializeComponent();
_vm = BindingContext as RegisterPageViewModel;
_vm.Page = this;
_vm.RegistrationSuccess = () => Device.BeginInvokeOnMainThread(async () => await RegistrationSuccessAsync(homePage));
_vm.CloseAction = async () =>
{
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync();
};
MasterPasswordEntry = _masterPassword;

View File

@@ -8,7 +8,6 @@ using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.Core;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -72,7 +71,7 @@ namespace Bit.App.Pages
public Command SubmitCommand { get; }
public Command TogglePasswordCommand { get; }
public Command ToggleConfirmPasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string ShowPasswordIcon => ShowPassword ? "" : "";
public string Name { get; set; }
public string Email { get; set; }
public string MasterPassword { get; set; }

View File

@@ -99,7 +99,7 @@
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0" />
<controls:IconButton
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
@@ -137,7 +137,7 @@
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0" />
<controls:IconButton
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding ToggleConfirmPasswordCommand}"

View File

@@ -1,6 +1,4 @@
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System;
using System;
using System.Threading.Tasks;
using Bit.App.Models;
using Bit.App.Utilities;
@@ -10,14 +8,11 @@ namespace Bit.App.Pages
{
public partial class SetPasswordPage : BaseContentPage
{
private readonly IMessagingService _messagingService;
private readonly SetPasswordPageViewModel _vm;
private readonly AppOptions _appOptions;
public SetPasswordPage(AppOptions appOptions = null, string orgIdentifier = null)
{
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_messagingService.Send("showStatusBar", true);
_appOptions = appOptions;
InitializeComponent();
_vm = BindingContext as SetPasswordPageViewModel;
@@ -26,7 +21,6 @@ namespace Bit.App.Pages
() => Device.BeginInvokeOnMainThread(async () => await SetPasswordSuccessAsync());
_vm.CloseAction = async () =>
{
_messagingService.Send("showStatusBar", false);
await Navigation.PopModalAsync();
};
_vm.OrgIdentifier = orgIdentifier;

View File

@@ -7,11 +7,9 @@ using Bit.Core.Models.Request;
using Bit.Core.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Bit.Core;
using Bit.Core.Models.Domain;
using Xamarin.Essentials;
using Xamarin.Forms;
@@ -24,7 +22,7 @@ namespace Bit.App.Pages
private readonly IApiService _apiService;
private readonly ICryptoService _cryptoService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IUserService _userService;
private readonly IStateService _stateService;
private readonly IPolicyService _policyService;
private readonly IPasswordGenerationService _passwordGenerationService;
private readonly II18nService _i18nService;
@@ -41,7 +39,7 @@ namespace Bit.App.Pages
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
_passwordGenerationService =
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
@@ -86,7 +84,7 @@ namespace Bit.App.Pages
public Command SubmitCommand { get; }
public Command TogglePasswordCommand { get; }
public Command ToggleConfirmPasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string ShowPasswordIcon => ShowPassword ? "" : "";
public string MasterPassword { get; set; }
public string ConfirmMasterPassword { get; set; }
public string Hint { get; set; }
@@ -160,7 +158,7 @@ namespace Bit.App.Pages
var kdf = KdfType.PBKDF2_SHA256;
var kdfIterations = 100000;
var email = await _userService.GetEmailAsync();
var email = await _stateService.GetEmailAsync();
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.ServerAuthorization);
var localMasterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization);
@@ -197,8 +195,8 @@ namespace Bit.App.Pages
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
// Set Password and relevant information
await _apiService.SetPasswordAsync(request);
await _userService.SetInformationAsync(await _userService.GetUserIdAsync(),
await _userService.GetEmailAsync(), kdf, kdfIterations);
await _stateService.SetKdfTypeAsync(kdf);
await _stateService.SetKdfIterationsAsync(kdfIterations);
await _cryptoService.SetKeyAsync(key);
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
@@ -217,7 +215,7 @@ namespace Bit.App.Pages
{
ResetPasswordKey = encryptedKey.EncryptedString
};
var userId = await _userService.GetUserIdAsync();
var userId = await _stateService.GetActiveUserIdAsync();
// Enroll user
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
}
@@ -290,7 +288,7 @@ namespace Bit.App.Pages
private async Task<List<string>> GetPasswordStrengthUserInput()
{
var email = await _userService.GetEmailAsync();
var email = await _stateService.GetEmailAsync();
List<string> userInput = null;
var atPosition = email.IndexOf('@');
if (atPosition > -1)

View File

@@ -14,8 +14,6 @@ namespace Bit.App.Pages
{
private readonly IBroadcasterService _broadcasterService;
private readonly IMessagingService _messagingService;
private readonly IStorageService _storageService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly AppOptions _appOptions;
private TwoFactorPageViewModel _vm;
@@ -30,10 +28,8 @@ namespace Bit.App.Pages
_authingWithSso = authingWithSso ?? false;
_appOptions = appOptions;
_orgIdentifier = orgIdentifier;
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_vm = BindingContext as TwoFactorPageViewModel;
_vm.Page = this;
_vm.StartSetPasswordAction = () =>

View File

@@ -1,6 +1,5 @@
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
@@ -23,7 +22,6 @@ namespace Bit.App.Pages
private readonly IDeviceActionService _deviceActionService;
private readonly IAuthService _authService;
private readonly ISyncService _syncService;
private readonly IStorageService _storageService;
private readonly IApiService _apiService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IEnvironmentService _environmentService;
@@ -43,7 +41,6 @@ namespace Bit.App.Pages
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_authService = ServiceContainer.Resolve<IAuthService>("authService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
@@ -303,8 +300,6 @@ namespace Bit.App.Pages
}
else
{
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
TwoFactorAuthSuccessAction?.Invoke();
}
}
@@ -328,7 +323,7 @@ namespace Bit.App.Pages
AppResources.Cancel, null, options.ToArray());
if (method == AppResources.RecoveryCodeTitle)
{
_platformUtilsService.LaunchUri("https://bitwarden.com/help/lost-two-step-device/");
_platformUtilsService.LaunchUri("https://help.bitwarden.com/article/lost-two-step-device/");
}
else if (method != AppResources.Cancel && method != null)
{

View File

@@ -97,7 +97,7 @@
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0" />
<controls:IconButton
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
@@ -132,7 +132,7 @@
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0" />
<controls:IconButton
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding ToggleConfirmPasswordCommand}"

View File

@@ -19,9 +19,6 @@ namespace Bit.App.Pages
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
// Service Use
_messagingService.Send("showStatusBar", true);
// Binding
InitializeComponent();
_pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks);

View File

@@ -43,9 +43,9 @@ namespace Bit.App.Pages
}
// Retrieve details for key generation
var kdf = await _userService.GetKdfAsync();
var kdfIterations = await _userService.GetKdfIterationsAsync();
var email = await _userService.GetEmailAsync();
var kdf = await _stateService.GetKdfTypeAsync();
var kdfIterations = await _stateService.GetKdfIterationsAsync();
var email = await _stateService.GetEmailAsync();
// Create new key and hash new password
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);

View File

@@ -1,99 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.Accounts.VerificationCodePage"
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:controls="clr-namespace:Bit.App.Controls"
x:DataType="pages:VerificationCodeViewModel"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
<pages:VerificationCodeViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
</ContentPage.ToolbarItems>
<ContentPage.Resources>
<u:InverseBoolConverter x:Key="inverseBool" />
<Style TargetType="Label" x:Key="lblDescription">
<Setter Property="FontSize" Value="{OnPlatform Android=Large, iOS=Small}" />
</Style>
</ContentPage.Resources>
<ContentPage.Content>
<ScrollView>
<Grid
RowSpacing="10"
ColumnSpacing="10"
RowDefinitions="Auto, Auto, Auto"
ColumnDefinitions="*, *"
Padding="10">
<Label
Grid.Row="0"
Grid.ColumnSpan="2"
Padding="0,10"
Text="{Binding SendCodeStatus}"
StyleClass="box-label"
LineBreakMode="WordWrap"
Margin="0,0,0,10" />
<Grid
StyleClass="box-row"
Grid.Row="1"
Grid.ColumnSpan="2"
RowDefinitions="Auto,Auto,Auto"
ColumnDefinitions="*,Auto"
Padding="0">
<Label
Text="{u:I18n VerificationCode}"
StyleClass="box-label"
Grid.Row="0"
Grid.ColumnSpan="2" />
<controls:MonoEntry
x:Name="_secret"
Text="{Binding Secret}"
StyleClass="box-value"
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
Grid.Row="1"
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding MainActionCommand}" />
<controls:IconButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"
Grid.Row="1"
Grid.Column="1"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n ToggleVisibility}"/>
<Label
Text="{u:I18n ConfirmYourIdentity}"
StyleClass="box-footer-label"
LineBreakMode="WordWrap"
Grid.Row="2"
Margin="0,10,0,0" />
</Grid>
<Button
x:Name="_mainActionButton"
Grid.Row="2"
Padding="10,0"
Text="{Binding MainActionText}"
Command="{Binding MainActionCommand}"
HorizontalOptions="FillAndExpand"
VerticalOptions="Start" />
<Button
Grid.Row="2"
Grid.Column="1"
Text="{u:I18n ResendCode}"
Padding="10,0"
Command="{Binding RequestOTPCommand}"
HorizontalOptions="Fill"
VerticalOptions="Start"/>
</Grid>
</ScrollView>
</ContentPage.Content>
</pages:BaseContentPage>

View File

@@ -1,49 +0,0 @@
using System;
using System.Runtime.CompilerServices;
namespace Bit.App.Pages.Accounts
{
public partial class VerificationCodePage : BaseContentPage
{
VerificationCodeViewModel _vm;
public VerificationCodePage(string mainActionText, bool mainActionStyleDanger)
{
InitializeComponent();
_vm = BindingContext as VerificationCodeViewModel;
_vm.Page = this;
_vm.MainActionText = mainActionText;
_mainActionButton.StyleClass = new[]
{
mainActionStyleDanger ? "btn-danger" : "btn-primary"
};
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == nameof(VerificationCodeViewModel.ShowPassword))
{
RequestFocus(_secret);
}
}
protected async override void OnAppearing()
{
base.OnAppearing();
await _vm.InitAsync();
RequestFocus(_secret);
}
private async void Close_Clicked(object sender, EventArgs e)
{
if (DoOnce())
{
await Navigation.PopModalAsync();
}
}
}
}

View File

@@ -1,181 +0,0 @@
using System;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System.Threading.Tasks;
using Bit.Core.Exceptions;
using Xamarin.Forms;
using Xamarin.CommunityToolkit.ObjectModel;
using System.Windows.Input;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Enums;
#if !FDROID
using Microsoft.AppCenter.Crashes;
#endif
namespace Bit.App.Pages
{
public class VerificationCodeViewModel : BaseViewModel
{
private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IUserVerificationService _userVerificationService;
private readonly IApiService _apiService;
private readonly IVerificationActionsFlowHelper _verificationActionsFlowHelper;
private bool _showPassword;
private string _secret, _mainActionText, _sendCodeStatus;
public VerificationCodeViewModel()
{
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_userVerificationService = ServiceContainer.Resolve<IUserVerificationService>("userVerificationService");
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
_verificationActionsFlowHelper = ServiceContainer.Resolve<IVerificationActionsFlowHelper>("verificationActionsFlowHelper");
PageTitle = AppResources.VerificationCode;
TogglePasswordCommand = new Command(TogglePassword);
MainActionCommand = new AsyncCommand(MainActionAsync, allowsMultipleExecutions: false);
RequestOTPCommand = new AsyncCommand(RequestOTPAsync, allowsMultipleExecutions: false);
}
public bool ShowPassword
{
get => _showPassword;
set => SetProperty(ref _showPassword, value,
additionalPropertyNames: new string[] { nameof(ShowPasswordIcon) });
}
public string Secret
{
get => _secret;
set => SetProperty(ref _secret, value);
}
public string MainActionText
{
get => _mainActionText;
set => SetProperty(ref _mainActionText, value);
}
public string SendCodeStatus
{
get => _sendCodeStatus;
set => SetProperty(ref _sendCodeStatus, value);
}
public ICommand TogglePasswordCommand { get; }
public ICommand MainActionCommand { get; }
public ICommand RequestOTPCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public void TogglePassword() => ShowPassword = !ShowPassword;
public async Task InitAsync()
{
await RequestOTPAsync();
}
public async Task RequestOTPAsync()
{
try
{
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
AppResources.InternetConnectionRequiredTitle, AppResources.Ok);
SendCodeStatus = AppResources.AnErrorOccurredWhileSendingAVerificationCodeToYourEmailPleaseTryAgain;
return;
}
await _deviceActionService.ShowLoadingAsync(AppResources.SendingCode);
await _apiService.PostAccountRequestOTP();
await _deviceActionService.HideLoadingAsync();
SendCodeStatus = AppResources.AVerificationCodeWasSentToYourEmail;
_platformUtilsService.ShowToast(null, null, AppResources.CodeSent);
}
catch (ApiException e)
{
await _deviceActionService.HideLoadingAsync();
SendCodeStatus = AppResources.AnErrorOccurredWhileSendingAVerificationCodeToYourEmailPleaseTryAgain;
if (e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(), AppResources.AnErrorHasOccurred);
}
}
catch (Exception ex)
{
#if !FDROID
Crashes.TrackError(ex);
#endif
await _deviceActionService.HideLoadingAsync();
SendCodeStatus = AppResources.AnErrorOccurredWhileSendingAVerificationCodeToYourEmailPleaseTryAgain;
}
}
private async Task MainActionAsync()
{
try
{
if (string.IsNullOrWhiteSpace(Secret))
{
await _platformUtilsService.ShowDialogAsync(AppResources.EnterTheVerificationCodeThatWasSentToYourEmail, AppResources.AnErrorHasOccurred);
return;
}
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
{
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
AppResources.InternetConnectionRequiredTitle, AppResources.Ok);
return;
}
await _deviceActionService.ShowLoadingAsync(AppResources.Verifying);
if (!await _userVerificationService.VerifyUser(Secret, VerificationType.OTP))
{
await _deviceActionService.HideLoadingAsync();
return;
}
await _deviceActionService.HideLoadingAsync();
var parameters = _verificationActionsFlowHelper.GetParameters();
parameters.Secret = Secret;
parameters.VerificationType = VerificationType.OTP;
await _verificationActionsFlowHelper.ExecuteAsync(parameters);
Secret = string.Empty;
}
catch (ApiException e)
{
await _deviceActionService.HideLoadingAsync();
if (e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
catch (Exception ex)
{
#if !FDROID
Crashes.TrackError(ex);
#endif
await _deviceActionService.HideLoadingAsync();
}
}
}
}

View File

@@ -1,10 +1,11 @@
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using System;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Utilities;
using Bit.Core.Enums;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
@@ -13,8 +14,9 @@ namespace Bit.App.Pages
{
public class BaseContentPage : ContentPage
{
private IStorageService _storageService;
private IStateService _stateService;
private IDeviceActionService _deviceActionService;
private IMessagingService _messagingService;
protected int ShowModalAnimationDelay = 400;
protected int ShowPageAnimationDelay = 100;
@@ -30,18 +32,10 @@ namespace Bit.App.Pages
public DateTime? LastPageAction { get; set; }
public bool IsThemeDirty { get; set; }
protected override void OnAppearing()
protected async override void OnAppearing()
{
base.OnAppearing();
if (IsThemeDirty)
{
UpdateOnThemeChanged();
}
SaveActivity();
await SaveActivity();
}
public bool DoOnce(Action action = null, int milliseconds = 1000)
@@ -114,28 +108,121 @@ namespace Bit.App.Pages
});
}
protected async Task<bool> ShowAccountSwitcherAsync()
{
return await _stateService.HasMultipleAccountsAsync();
}
protected async Task RefreshAccountViewsAsync(Xamarin.Forms.ListView accountListView, bool allowAddAccountRow)
{
await _stateService.RefreshAccountViewsAsync(allowAddAccountRow);
// Property change trigger on account listview is yielding inconsistent results, using a hammer instead
accountListView.ItemsSource = null;
accountListView.ItemsSource = _stateService.AccountViews;
}
protected async Task<AvatarImageSource> GetAvatarImageSourceAsync(bool useCurrentActiveAccount = true)
{
return new AvatarImageSource(useCurrentActiveAccount ? await _stateService.GetEmailAsync() : null);
}
protected async Task ShowAccountListAsync(bool isVisible, View listContainer, View overlay, View fab = null)
{
Device.BeginInvokeOnMainThread(async () =>
{
// Not all animations are awaited. This is intentional to allow multiple simultaneous animations.
if (isVisible)
{
// start listView in default (off-screen) position
await listContainer.TranslateTo(0, listContainer.Height * -1, 0);
// set overlay opacity to zero before making visible and start fade-in
overlay.Opacity = 0;
overlay.IsVisible = true;
overlay.FadeTo(1, 100);
if (Device.RuntimePlatform == Device.Android && fab != null)
{
// start fab fade-out
fab.FadeTo(0, 200);
}
// slide account list into view
await listContainer.TranslateTo(0, 0, 200, Easing.SinOut);
}
else
{
// start overlay fade-out
overlay.FadeTo(0, 200);
if (Device.RuntimePlatform == Device.Android && fab != null)
{
// start fab fade-in
fab.FadeTo(1, 200);
}
// slide account list out of view
await listContainer.TranslateTo(0, listContainer.Height * -1, 200, Easing.SinIn);
// remove overlay
overlay.IsVisible = false;
}
});
}
protected async Task AccountRowSelectedAsync(object sender, SelectedItemChangedEventArgs e, View listContainer,
View overlay, View fab = null, bool? allowActiveAccountSelection = false)
{
if (!DoOnce())
{
return;
}
if (!(e.SelectedItem is AccountViewCellViewModel item))
{
return;
}
((Xamarin.Forms.ListView)sender).SelectedItem = null;
await Task.Delay(100);
await ShowAccountListAsync(false, listContainer, overlay, fab);
if (item.AccountView.IsAccount)
{
if (item.AccountView.AuthStatus != AuthenticationStatus.Active)
{
await _stateService.SetActiveUserAsync(item.AccountView.UserId);
_messagingService.Send("switchedAccount");
}
else if (allowActiveAccountSelection ?? false)
{
_messagingService.Send("switchedAccount");
}
}
else
{
_messagingService.Send("addAccount");
}
}
private void SetServices()
{
if (_storageService == null)
if (_stateService == null)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
}
if (_deviceActionService == null)
{
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
}
if (_messagingService == null)
{
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
}
}
private void SaveActivity()
private async Task SaveActivity()
{
SetServices();
_storageService.SaveAsync(Constants.LastActiveTimeKey, _deviceActionService.GetActiveTime());
}
public virtual Task UpdateOnThemeChanged()
{
IsThemeDirty = false;
return Task.CompletedTask;
await _stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
}
}
}

View File

@@ -1,4 +1,5 @@
using Bit.Core.Utilities;
using Bit.App.Controls;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -6,6 +7,7 @@ namespace Bit.App.Pages
public abstract class BaseViewModel : ExtendedViewModel
{
private string _pageTitle = string.Empty;
private AvatarImageSource _avatar;
public string PageTitle
{
@@ -13,6 +15,12 @@ namespace Bit.App.Pages
set => SetProperty(ref _pageTitle, value);
}
public AvatarImageSource AvatarImageSource
{
get => _avatar ?? new AvatarImageSource();
set => SetProperty(ref _avatar, value);
}
public ContentPage Page { get; set; }
}
}

View File

@@ -7,7 +7,6 @@
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:domain="clr-namespace:Bit.Core.Models.Domain;assembly=BitwardenCore"
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
x:DataType="pages:GeneratorHistoryPageViewModel"
x:Name="_page"
Title="{Binding PageTitle}">
@@ -76,9 +75,9 @@
Grid.Row="1"
StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Date, Mode=OneWay, Converter={StaticResource dateTime}}" />
<controls:IconButton
<controls:FaButton
StyleClass="list-row-button, list-row-button-platform"
Text="{Binding Source={x:Static core:BitwardenIcons.Paste}}"
Text="&#xf0ea;"
Command="{Binding BindingContext.CopyCommand, Source={x:Reference _page}}"
CommandParameter="{Binding .}"
Grid.Row="0"

View File

@@ -1,12 +1,10 @@
using System;
using System.Threading.Tasks;
using Bit.App.Resources;
using Bit.App.Styles;
using Bit.App.Resources;
using System;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public partial class GeneratorHistoryPage : BaseContentPage, IThemeDirtablePage
public partial class GeneratorHistoryPage : BaseContentPage
{
private GeneratorHistoryPageViewModel _vm;
@@ -30,7 +28,6 @@ namespace Bit.App.Pages
protected override async void OnAppearing()
{
base.OnAppearing();
await LoadOnAppearedAsync(_mainLayout, true, async () => {
await _vm.InitAsync();
});
@@ -62,12 +59,5 @@ namespace Bit.App.Pages
await _vm.ClearAsync();
}
}
public override async Task UpdateOnThemeChanged()
{
await base.UpdateOnThemeChanged();
await _vm?.UpdateOnThemeChanged();
}
}
}

View File

@@ -1,12 +1,9 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.App.Resources;
using Bit.App.Resources;
using Bit.Core.Abstractions;
using Bit.Core.Models.Domain;
using Bit.Core.Utilities;
#if !FDROID
using Microsoft.AppCenter.Crashes;
#endif
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -22,7 +19,8 @@ namespace Bit.App.Pages
public GeneratorHistoryPageViewModel()
{
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
"passwordGenerationService");
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
PageTitle = AppResources.PasswordHistory;
@@ -59,21 +57,5 @@ namespace Bit.App.Pages
_platformUtilsService.ShowToast("info", null,
string.Format(AppResources.ValueHasBeenCopied, AppResources.Password));
}
public async Task UpdateOnThemeChanged()
{
try
{
await Device.InvokeOnMainThreadAsync(() => History.ResetWithRange(new List<GeneratedPasswordHistory>()));
await InitAsync();
}
catch (System.Exception ex)
{
#if !FDROID
Crashes.TrackError(ex);
#endif
}
}
}
}

View File

@@ -63,7 +63,6 @@
</Frame>
</Grid>
<controls:MonoLabel
x:Name="lblPassword"
StyleClass="text-lg, text-html"
Text="{Binding ColoredPassword, Mode=OneWay}"
Margin="0, 20"

View File

@@ -1,15 +1,15 @@
using System;
using Bit.App.Resources;
using System;
using System.Threading.Tasks;
using Bit.App.Resources;
using Bit.App.Styles;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace Bit.App.Pages
{
public partial class GeneratorPage : BaseContentPage, IThemeDirtablePage
public partial class GeneratorPage : BaseContentPage
{
private readonly IBroadcasterService _broadcasterService;
@@ -49,7 +49,7 @@ namespace Bit.App.Pages
}
if (isIos)
{
_typePicker.On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_typePicker.On<iOS>().SetUpdateMode(UpdateMode.WhenFinished);
}
}
@@ -61,19 +61,18 @@ namespace Bit.App.Pages
protected async override void OnAppearing()
{
base.OnAppearing();
lblPassword.IsVisible = true;
if (!_fromTabPage)
{
await InitAsync();
}
_broadcasterService.Subscribe(nameof(GeneratorPage), (message) =>
_broadcasterService.Subscribe(nameof(GeneratorPage), async (message) =>
{
if (message.Command == "updatedTheme")
{
Device.BeginInvokeOnMainThread(() => _vm.RedrawPassword());
Device.BeginInvokeOnMainThread(() =>
{
_vm.RedrawPassword();
});
}
});
}
@@ -81,9 +80,6 @@ namespace Bit.App.Pages
protected override void OnDisappearing()
{
base.OnDisappearing();
lblPassword.IsVisible = false;
_broadcasterService.Unsubscribe(nameof(GeneratorPage));
}
@@ -145,12 +141,5 @@ namespace Bit.App.Pages
await Navigation.PopModalAsync();
}
}
public override async Task UpdateOnThemeChanged()
{
await base.UpdateOnThemeChanged();
await Device.InvokeOnMainThreadAsync(() => _vm?.RedrawPassword());
}
}
}

View File

@@ -8,7 +8,6 @@
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:behaviors="clr-namespace:Bit.App.Behaviors"
xmlns:effects="clr-namespace:Bit.App.Effects"
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
x:DataType="pages:SendAddEditPageViewModel"
x:Name="_page"
Title="{Binding PageTitle}">
@@ -235,10 +234,9 @@
Margin="10,0,0,0" />
</StackLayout>
</StackLayout>
<StackLayout
StyleClass="box-row, box-row-switch">
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{Binding ShareOnSaveText}"
Text="{u:I18n ShareOnSave}"
StyleClass="box-label-regular"
VerticalOptions="Center"
HorizontalOptions="StartAndExpand" />
@@ -257,16 +255,16 @@
StyleClass="box-row-button"
TextColor="{DynamicResource PrimaryColor}"
Margin="0" />
<controls:IconButton
<controls:FaButton
x:Name="_btnOptionsUp"
Text="{Binding Source={x:Static core:BitwardenIcons.ChevronUp}}"
Text="&#xf077;"
StyleClass="box-row-button"
TextColor="{DynamicResource PrimaryColor}"
Clicked="ToggleOptions_Clicked"
IsVisible="False" />
<controls:IconButton
<controls:FaButton
x:Name="_btnOptionsDown"
Text="{Binding Source={x:Static core:BitwardenIcons.AngleDown}}"
Text="&#xf078;"
StyleClass="box-row-button"
TextColor="{DynamicResource PrimaryColor}"
Clicked="ToggleOptions_Clicked"
@@ -430,7 +428,7 @@
IsSpellCheckEnabled="False"
IsTextPredictionEnabled="False"
HorizontalOptions="FillAndExpand" />
<controls:IconButton
<controls:FaButton
IsEnabled="{Binding SendEnabled}"
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"

View File

@@ -7,9 +7,6 @@ using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Utilities;
#if !FDROID
using Microsoft.AppCenter.Crashes;
#endif
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
@@ -25,9 +22,6 @@ namespace Bit.App.Pages
private AppOptions _appOptions;
private SendAddEditPageViewModel _vm;
public Action OnClose { get; set; }
public Action AfterSubmit { get; set; }
public SendAddEditPage(
AppOptions appOptions = null,
string sendId = null,
@@ -88,66 +82,42 @@ namespace Bit.App.Pages
protected override async void OnAppearing()
{
base.OnAppearing();
try
if (!await AppHelpers.IsVaultTimeoutImmediateAsync())
{
if (!await AppHelpers.IsVaultTimeoutImmediateAsync())
await _vaultTimeoutService.CheckVaultTimeoutAsync();
}
if (await _vaultTimeoutService.IsLockedAsync())
{
return;
}
await _vm.InitAsync();
_broadcasterService.Subscribe(nameof(SendAddEditPage), message =>
{
if (message.Command == "selectFileResult")
{
await _vaultTimeoutService.CheckVaultTimeoutAsync();
Device.BeginInvokeOnMainThread(() =>
{
var data = message.Data as Tuple<byte[], string>;
_vm.FileData = data.Item1;
_vm.FileName = data.Item2;
});
}
if (await _vaultTimeoutService.IsLockedAsync())
});
await LoadOnAppearedAsync(_scrollView, true, async () =>
{
var success = await _vm.LoadAsync();
if (!success)
{
await Navigation.PopModalAsync();
return;
}
await _vm.InitAsync();
_broadcasterService.Subscribe(nameof(SendAddEditPage), message =>
await HandleCreateRequest();
if (!_vm.EditMode && string.IsNullOrWhiteSpace(_vm.Send?.Name))
{
if (message.Command == "selectFileResult")
{
Device.BeginInvokeOnMainThread(() =>
{
var data = message.Data as Tuple<byte[], string>;
_vm.FileData = data.Item1;
_vm.FileName = data.Item2;
});
}
});
await LoadOnAppearedAsync(_scrollView, true, async () =>
{
var success = await _vm.LoadAsync();
if (!success)
{
await CloseAsync();
return;
}
await HandleCreateRequest();
if (!_vm.EditMode && string.IsNullOrWhiteSpace(_vm.Send?.Name))
{
RequestFocus(_nameEntry);
}
AdjustToolbar();
});
}
catch (Exception ex)
{
#if !FDROID
Crashes.TrackError(ex);
#endif
await CloseAsync();
}
}
private async Task CloseAsync()
{
if (OnClose is null)
{
await Navigation.PopModalAsync();
}
else
{
OnClose();
}
RequestFocus(_nameEntry);
}
AdjustToolbar();
});
}
protected override bool OnBackButtonPressed()
@@ -230,11 +200,7 @@ namespace Bit.App.Pages
{
if (DoOnce())
{
var submitted = await _vm.SubmitAsync();
if (submitted)
{
AfterSubmit?.Invoke();
}
await _vm.SubmitAsync();
}
}
@@ -268,7 +234,7 @@ namespace Bit.App.Pages
{
if (await _vm.DeleteAsync())
{
await CloseAsync();
await Navigation.PopModalAsync();
}
}
}
@@ -308,7 +274,7 @@ namespace Bit.App.Pages
{
if (await _vm.DeleteAsync())
{
await CloseAsync();
await Navigation.PopModalAsync();
}
}
}
@@ -317,7 +283,7 @@ namespace Bit.App.Pages
{
if (DoOnce())
{
await CloseAsync();
await Navigation.PopModalAsync();
}
}
@@ -340,7 +306,6 @@ namespace Bit.App.Pages
}
_vm.IsAddFromShare = true;
_vm.CopyInsteadOfShareAfterSaving = _appOptions.CopyInsteadOfShareAfterSaving;
var name = _appOptions.CreateSend.Item2;
_vm.Send.Name = name;

View File

@@ -4,15 +4,11 @@ using System.Threading.Tasks;
using Bit.App.Abstractions;
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.View;
using Bit.Core.Utilities;
#if !FDROID
using Microsoft.AppCenter.Crashes;
#endif
using Xamarin.Essentials;
using Xamarin.Forms;
@@ -23,7 +19,7 @@ namespace Bit.App.Pages
private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IMessagingService _messagingService;
private readonly IUserService _userService;
private readonly IStateService _stateService;
private readonly ISendService _sendService;
private bool _sendEnabled;
private bool _canAccessPremium;
@@ -49,14 +45,13 @@ namespace Bit.App.Pages
};
private bool _disableHideEmail;
private bool _sendOptionsPolicyInEffect;
private bool _copyInsteadOfShareAfterSaving;
public SendAddEditPageViewModel()
{
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
TogglePasswordCommand = new Command(TogglePassword);
@@ -101,7 +96,6 @@ namespace Bit.App.Pages
public bool ShareOnSave { get; set; }
public bool DisableHideEmailControl { get; set; }
public bool IsAddFromShare { get; set; }
public string ShareOnSaveText => CopyInsteadOfShareAfterSaving ? AppResources.CopySendLinkOnSave : AppResources.ShareOnSave;
public List<KeyValuePair<string, SendType>> TypeOptions { get; }
public List<KeyValuePair<string, string>> DeletionTypeOptions { get; }
public List<KeyValuePair<string, string>> ExpirationTypeOptions { get; }
@@ -180,15 +174,6 @@ namespace Bit.App.Pages
}
}
}
public bool CopyInsteadOfShareAfterSaving
{
get => _copyInsteadOfShareAfterSaving;
set
{
SetProperty(ref _copyInsteadOfShareAfterSaving, value);
TriggerPropertyChanged(nameof(ShareOnSaveText));
}
}
public SendView Send
{
get => _send;
@@ -230,13 +215,13 @@ namespace Bit.App.Pages
public bool IsFile => Send?.Type == SendType.File;
public bool ShowDeletionCustomPickers => EditMode || DeletionDateTypeSelectedIndex == 6;
public bool ShowExpirationCustomPickers => EditMode || ExpirationDateTypeSelectedIndex == 7;
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string ShowPasswordIcon => ShowPassword ? "" : "";
public async Task InitAsync()
{
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
_canAccessPremium = await _userService.CanAccessPremiumAsync();
_emailVerified = await _userService.GetEmailVerifiedAsync();
_canAccessPremium = await _stateService.CanAccessPremiumAsync();
_emailVerified = await _stateService.GetEmailVerifiedAsync();
SendEnabled = ! await AppHelpers.IsSendDisabledByPolicyAsync();
DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync();
SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail;
@@ -411,36 +396,25 @@ namespace Bit.App.Pages
EditMode ? AppResources.SendUpdated : AppResources.NewSendCreated);
}
if (!CopyInsteadOfShareAfterSaving)
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
{
await CloseAsync();
_deviceActionService.CloseMainApp();
}
else
{
await Page.Navigation.PopModalAsync();
}
if (ShareOnSave)
{
var savedSend = await _sendService.GetAsync(sendId);
if (savedSend != null)
{
var savedSendView = await savedSend.DecryptAsync();
if (CopyInsteadOfShareAfterSaving)
{
await AppHelpers.CopySendUrlAsync(savedSendView);
// wait so that the user sees the message before the view gets dismissed
await Task.Delay(1300);
}
else
{
await AppHelpers.ShareSendUrlAsync(savedSendView);
}
await AppHelpers.ShareSendUrlAsync(savedSendView);
}
}
if (CopyInsteadOfShareAfterSaving)
{
await CloseAsync();
}
return true;
}
catch (ApiException e)
@@ -452,37 +426,9 @@ namespace Bit.App.Pages
AppResources.AnErrorHasOccurred);
}
}
catch (Exception ex)
{
await _deviceActionService.HideLoadingAsync();
#if !FDROID
Crashes.TrackError(ex);
#endif
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred);
}
return false;
}
private async Task CloseAsync()
{
if (IsAddFromShare)
{
if (Device.RuntimePlatform == Device.Android)
{
_deviceActionService.CloseMainApp();
return;
}
if (Page is SendAddEditPage sendPage && sendPage.OnClose != null)
{
sendPage.OnClose();
return;
}
}
await Page.Navigation.PopModalAsync();
}
public async Task<bool> RemovePasswordAsync()
{
return await AppHelpers.RemoveSendPasswordAsync(SendId);
@@ -508,7 +454,14 @@ namespace Bit.App.Pages
if (!SendEnabled)
{
await _platformUtilsService.ShowDialogAsync(AppResources.SendDisabledWarning);
await CloseAsync();
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
{
_deviceActionService.CloseMainApp();
}
else
{
await Page.Navigation.PopModalAsync();
}
return;
}
if (Send != null)

View File

@@ -51,14 +51,14 @@
x:DataType="pages:SendGroupingsPageListItem">
<controls:ExtendedStackLayout Orientation="Horizontal"
StyleClass="list-row, list-row-platform">
<controls:IconLabel Text="{Binding Icon, Mode=OneWay}"
<controls:FaLabel Text="{Binding Icon, Mode=OneWay}"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform">
<controls:IconLabel.Effects>
<controls:FaLabel.Effects>
<effects:FixedSizeEffect />
</controls:IconLabel.Effects>
</controls:IconLabel>
</controls:FaLabel.Effects>
</controls:FaLabel>
<Label Text="{Binding Name, Mode=OneWay}"
LineBreakMode="TailTruncation"
HorizontalOptions="FillAndExpand"

View File

@@ -1,5 +1,4 @@
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Enums;
using Bit.Core.Models.View;
@@ -54,10 +53,10 @@ namespace Bit.App.Pages
switch (Type.Value)
{
case SendType.Text:
_icon = BitwardenIcons.FileText;
_icon = "\uf0f6"; // fa-file-text-o
break;
case SendType.File:
_icon = BitwardenIcons.File;
_icon = "\uf016"; // fa-file-o
break;
default:
break;

View File

@@ -32,21 +32,19 @@ namespace Bit.App.Pages
private readonly ISendService _sendService;
private readonly ISyncService _syncService;
private readonly IUserService _userService;
private readonly IStateService _stateService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStorageService _storageService;
public SendGroupingsPageViewModel()
{
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
Loading = true;
PageTitle = AppResources.Send;
@@ -116,7 +114,7 @@ namespace Bit.App.Pages
{
return;
}
var authed = await _userService.IsAuthenticatedAsync();
var authed = await _stateService.IsAuthenticatedAsync();
if (!authed)
{
return;
@@ -125,7 +123,7 @@ namespace Bit.App.Pages
{
return;
}
if (await _storageService.GetAsync<bool>(Constants.SyncOnRefreshKey) && Refreshing && !SyncRefreshing)
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
{
SyncRefreshing = true;
await _syncService.FullSyncAsync(false);

View File

@@ -7,7 +7,6 @@
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:views="clr-namespace:Bit.Core.Models.View;assembly=BitwardenCore"
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
x:DataType="pages:SendsPageViewModel"
x:Name="_page"
Title="{Binding PageTitle}">
@@ -47,8 +46,8 @@
</ContentPage.Resources>
<StackLayout x:Name="_mainLayout" Spacing="0" Padding="0">
<controls:IconLabel IsVisible="{Binding ShowSearchDirection}"
Text="{Binding Source={x:Static core:BitwardenIcons.Search}}"
<controls:FaLabel IsVisible="{Binding ShowSearchDirection}"
Text="&#xf002;"
StyleClass="text-muted"
FontSize="50"
VerticalOptions="CenterAndExpand"

View File

@@ -2,7 +2,6 @@
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.App.Services;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
@@ -11,7 +10,7 @@ namespace Bit.App.Pages
public class AutofillServicesPageViewModel : BaseViewModel
{
private readonly IDeviceActionService _deviceActionService;
private readonly IStorageService _storageService;
private readonly IStateService _stateService;
private readonly MobileI18nService _i18nService;
private bool _autofillServiceToggled;
@@ -23,7 +22,7 @@ namespace Bit.App.Pages
public AutofillServicesPageViewModel()
{
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
PageTitle = AppResources.AutofillServices;
}
@@ -152,7 +151,7 @@ namespace Bit.App.Pages
public async Task InitAsync()
{
InlineAutofillToggled = await _storageService.GetAsync<bool?>(Constants.InlineAutofillEnabledKey) ?? true;
InlineAutofillToggled = await _stateService.GetInlineAutofillEnabledAsync() ?? true;
_inited = true;
}
@@ -202,7 +201,7 @@ namespace Bit.App.Pages
{
if (_inited)
{
await _storageService.SaveAsync(Constants.InlineAutofillEnabledKey, InlineAutofillToggled);
await _stateService.SetInlineAutofillEnabledAsync(InlineAutofillToggled);
}
}
}

View File

@@ -97,7 +97,7 @@
Grid.Column="0"
ReturnType="Go"
ReturnCommand="{Binding ExportVaultCommand}" />
<controls:IconButton
<controls:FaButton
StyleClass="box-row-button, box-row-button-platform"
Text="{Binding ShowPasswordIcon}"
Command="{Binding TogglePasswordCommand}"

View File

@@ -8,7 +8,6 @@ using System.Text;
using System.Threading.Tasks;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core;
#if !FDROID
using Microsoft.AppCenter.Crashes;
#endif
@@ -139,7 +138,7 @@ namespace Bit.App.Pages
public Command TogglePasswordCommand { get; }
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public string ShowPasswordIcon => ShowPassword ? "" : "";
public void TogglePassword()
{

View File

@@ -7,12 +7,8 @@ namespace Bit.App.Pages
{
public class ExtensionPageViewModel : BaseViewModel
{
private const string StartedKey = "appExtensionStarted";
private const string ActivatedKey = "appExtensionActivated";
private readonly IMessagingService _messagingService;
private readonly IStorageService _storageService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IStateService _stateService;
private bool _started;
private bool _activated;
@@ -20,8 +16,7 @@ namespace Bit.App.Pages
public ExtensionPageViewModel()
{
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
PageTitle = AppResources.AppExtension;
}
@@ -52,8 +47,8 @@ namespace Bit.App.Pages
public async Task InitAsync()
{
var started = await _storageService.GetAsync<bool?>(StartedKey);
var activated = await _storageService.GetAsync<bool?>(ActivatedKey);
var started = await _stateService.GetAppExtensionStartedAsync();
var activated = await _stateService.GetAppExtensionActivatedAsync();
Started = started.GetValueOrDefault();
Activated = activated.GetValueOrDefault();
}

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