mirror of
https://github.com/bitwarden/mobile
synced 2025-12-11 05:43:30 +00:00
Compare commits
129 Commits
v2024.3.0
...
feature/ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca95ada8e8 | ||
|
|
fa022a1a4f | ||
|
|
6011b63958 | ||
|
|
7d79b98bf2 | ||
|
|
bf35d1f2dc | ||
|
|
c01a8f8d93 | ||
|
|
8484b4af30 | ||
|
|
6b9faed45f | ||
|
|
770a1c5dfe | ||
|
|
3a40a4cda8 | ||
|
|
9bcd2e51f7 | ||
|
|
741214a1cc | ||
|
|
aad87dfdce | ||
|
|
8fc1e9a3b9 | ||
|
|
2a8e15146e | ||
|
|
8559d5908e | ||
|
|
f60c4d94fe | ||
|
|
4bf695d18c | ||
|
|
9ccd0834ff | ||
|
|
a806f17d3b | ||
|
|
436a162df2 | ||
|
|
b5dbb9ae5e | ||
|
|
7a5f7c0274 | ||
|
|
5803635f44 | ||
|
|
19c393842f | ||
|
|
15a306490d | ||
|
|
a4a3d31c19 | ||
|
|
922dc683af | ||
|
|
bae1b3e891 | ||
|
|
a5888827c9 | ||
|
|
0348940a12 | ||
|
|
4c2998337d | ||
|
|
7ea86380f4 | ||
|
|
406f4425c8 | ||
|
|
95ca911444 | ||
|
|
fa62510e09 | ||
|
|
65dc73495d | ||
|
|
02a2e41118 | ||
|
|
bd6f8295e7 | ||
|
|
0a0cb7093b | ||
|
|
465e5eff76 | ||
|
|
5b756aaf7a | ||
|
|
d168a7b750 | ||
|
|
7f4bbafe3c | ||
|
|
a5804df6a3 | ||
|
|
bfa2a51608 | ||
|
|
32be08daae | ||
|
|
0a628cc8a8 | ||
|
|
80c424ed03 | ||
|
|
99fb5463cf | ||
|
|
c5d941e1df | ||
|
|
3edfef6169 | ||
|
|
1c8742511a | ||
|
|
8e424d6c05 | ||
|
|
390c303b90 | ||
|
|
443f7282b8 | ||
|
|
50109ee70b | ||
|
|
9ffdfd51cc | ||
|
|
04e409f3c6 | ||
|
|
6c143bad57 | ||
|
|
286e18059a | ||
|
|
553bf9ed0a | ||
|
|
ddb27b52d3 | ||
|
|
6c504aa710 | ||
|
|
62254aef8d | ||
|
|
06a0195a6d | ||
|
|
df2b0b21d5 | ||
|
|
e6b1bab860 | ||
|
|
ce41eb0578 | ||
|
|
1a0b52d644 | ||
|
|
16ada4993c | ||
|
|
3795f3aa17 | ||
|
|
eceb506c77 | ||
|
|
2c7870d660 | ||
|
|
f02b3415a3 | ||
|
|
beda4e9ff8 | ||
|
|
df4d89cd52 | ||
|
|
5f12bb9747 | ||
|
|
5712639492 | ||
|
|
e0a3c301fb | ||
|
|
27306fe353 | ||
|
|
a31f15559f | ||
|
|
0e75f3f5c8 | ||
|
|
363da063fa | ||
|
|
974a571455 | ||
|
|
e0c721098c | ||
|
|
a86f6e3034 | ||
|
|
fe17288b99 | ||
|
|
7324da9d47 | ||
|
|
69aa6fc044 | ||
|
|
e840dc2e30 | ||
|
|
eb25ee5d1b | ||
|
|
840f24dbe5 | ||
|
|
c6309173ba | ||
|
|
946c465f0c | ||
|
|
e90409d842 | ||
|
|
484b5a5160 | ||
|
|
2688209752 | ||
|
|
53e0e55915 | ||
|
|
ca57948d9f | ||
|
|
aaf082faba | ||
|
|
e7aeb08cae | ||
|
|
f177968958 | ||
|
|
f1d59210f9 | ||
|
|
62213c0aaf | ||
|
|
8be8abb8fe | ||
|
|
174acbc558 | ||
|
|
4bcc7c0d71 | ||
|
|
14b2960f30 | ||
|
|
455c3a257c | ||
|
|
8c623a2067 | ||
|
|
3cdf1c2f0e | ||
|
|
ce9503fa0c | ||
|
|
2e4da1b87d | ||
|
|
d63a219272 | ||
|
|
c92cd90a97 | ||
|
|
1dcd3a3daa | ||
|
|
efb8763d3c | ||
|
|
90649d1c8b | ||
|
|
828055791f | ||
|
|
87eebda55f | ||
|
|
7542d1ae1c | ||
|
|
990de4ea4e | ||
|
|
0dbc23f734 | ||
|
|
9f6c8601d3 | ||
|
|
8b7f9b9fb3 | ||
|
|
d17789d5ee | ||
|
|
b8f0747dd4 | ||
|
|
8ef9443b1e |
25
.github/CODEOWNERS
vendored
25
.github/CODEOWNERS
vendored
@@ -1,21 +1,12 @@
|
|||||||
# Please sort into logical groups with comment headers. Sort groups in order of specificity.
|
# Please sort lines alphabetically, this will ensure we don't accidentally add duplicates.
|
||||||
# For example, default owners should always be the first group.
|
|
||||||
# Sort lines alphabetically within these groups to avoid accidentally adding duplicates.
|
|
||||||
#
|
#
|
||||||
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
|
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
|
||||||
|
|
||||||
# Default file owners
|
# The following owners will be the default owners for everything in the repo.
|
||||||
* @bitwarden/dept-development-mobile
|
# Unless a later match takes precedence
|
||||||
|
# @bitwarden/tech-leads
|
||||||
|
|
||||||
# DevOps for Actions and other workflow changes
|
@bitwarden/dept-development-mobile
|
||||||
.github/workflows @bitwarden/dept-devops
|
|
||||||
|
|
||||||
# DevOps for Version Bumping
|
|
||||||
src/App/Platforms/Android/AndroidManifest.xml
|
|
||||||
src/iOS.Autofill/Info.plist
|
|
||||||
src/iOS.Extension/Info.plist
|
|
||||||
src/iOS.ShareExtension/Info.plist
|
|
||||||
src/App/Platforms/iOS/Info.plist
|
|
||||||
|
|
||||||
## Auth team files ##
|
## Auth team files ##
|
||||||
|
|
||||||
@@ -30,14 +21,14 @@ src/watchOS @bitwarden/team-vault-dev
|
|||||||
src/Core/Services/EmailForwarders @bitwarden/team-tools-dev
|
src/Core/Services/EmailForwarders @bitwarden/team-tools-dev
|
||||||
|
|
||||||
## Crowdin Sync files ##
|
## Crowdin Sync files ##
|
||||||
src/Core/Resources/Localization @bitwarden/team-tools-dev
|
src/App/Resources @bitwarden/team-tools-dev
|
||||||
src/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization @bitwarden/team-tools-dev
|
src/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization @bitwarden/team-tools-dev
|
||||||
store/apple @bitwarden/team-tools-dev
|
store/apple @bitwarden/team-tools-dev
|
||||||
store/google @bitwarden/team-tools-dev
|
store/google @bitwarden/team-tools-dev
|
||||||
|
|
||||||
## Locales ##
|
## Locales ##
|
||||||
src/Core/Resources/Localization/AppResources.Designer.cs
|
src/App/Resources/AppResources.Designer.cs
|
||||||
src/Core/Resources/Localization/AppResources.resx
|
src/App/Resources/AppResources.resx
|
||||||
src/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization/en.lproj
|
src/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization/en.lproj
|
||||||
store/apple/en
|
store/apple/en
|
||||||
store/google/en
|
store/google/en
|
||||||
|
|||||||
17
.github/renovate.json
vendored
17
.github/renovate.json
vendored
@@ -2,21 +2,22 @@
|
|||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": [
|
"extends": [
|
||||||
"config:base",
|
"config:base",
|
||||||
"github>bitwarden/renovate-config:pin-actions",
|
|
||||||
":combinePatchMinorReleases",
|
":combinePatchMinorReleases",
|
||||||
":dependencyDashboard",
|
":dependencyDashboard",
|
||||||
":maintainLockFilesWeekly",
|
":maintainLockFilesWeekly",
|
||||||
":pinAllExceptPeerDependencies",
|
":pinAllExceptPeerDependencies",
|
||||||
":prConcurrentLimit10",
|
":prConcurrentLimit10",
|
||||||
":rebaseStalePrs",
|
":rebaseStalePrs",
|
||||||
":separateMajorReleases",
|
"schedule:weekends",
|
||||||
"group:monorepos",
|
":separateMajorReleases"
|
||||||
"schedule:weekends"
|
|
||||||
],
|
],
|
||||||
"enabledManagers": ["github-actions", "npm", "nuget"],
|
"enabledManagers": ["cargo", "github-actions", "npm", "nuget"],
|
||||||
"commitMessagePrefix": "[deps]:",
|
|
||||||
"commitMessageTopic": "{{depName}}",
|
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"groupName": "cargo minor",
|
||||||
|
"matchManagers": ["cargo"],
|
||||||
|
"matchUpdateTypes": ["minor", "patch"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"groupName": "gh minor",
|
"groupName": "gh minor",
|
||||||
"matchManagers": ["github-actions"],
|
"matchManagers": ["github-actions"],
|
||||||
@@ -31,6 +32,6 @@
|
|||||||
"groupName": "nuget minor",
|
"groupName": "nuget minor",
|
||||||
"matchManagers": ["nuget"],
|
"matchManagers": ["nuget"],
|
||||||
"matchUpdateTypes": ["minor", "patch"]
|
"matchUpdateTypes": ["minor", "patch"]
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
.github/secrets/GoogleService-Info.plist.gpg
vendored
Normal file
BIN
.github/secrets/GoogleService-Info.plist.gpg
vendored
Normal file
Binary file not shown.
BIN
.github/secrets/app_fdroid-keystore.jks.gpg
vendored
Normal file
BIN
.github/secrets/app_fdroid-keystore.jks.gpg
vendored
Normal file
Binary file not shown.
BIN
.github/secrets/app_play-keystore.jks.gpg
vendored
Normal file
BIN
.github/secrets/app_play-keystore.jks.gpg
vendored
Normal file
Binary file not shown.
BIN
.github/secrets/app_upload-keystore.jks.gpg
vendored
Normal file
BIN
.github/secrets/app_upload-keystore.jks.gpg
vendored
Normal file
Binary file not shown.
BIN
.github/secrets/bitwarden-mobile-key.p12.gpg
vendored
Normal file
BIN
.github/secrets/bitwarden-mobile-key.p12.gpg
vendored
Normal file
Binary file not shown.
BIN
.github/secrets/dist_autofill.mobileprovision.gpg
vendored
Normal file
BIN
.github/secrets/dist_autofill.mobileprovision.gpg
vendored
Normal file
Binary file not shown.
BIN
.github/secrets/dist_bitwarden.mobileprovision.gpg
vendored
Normal file
BIN
.github/secrets/dist_bitwarden.mobileprovision.gpg
vendored
Normal file
Binary file not shown.
BIN
.github/secrets/dist_extension.mobileprovision.gpg
vendored
Normal file
BIN
.github/secrets/dist_extension.mobileprovision.gpg
vendored
Normal file
Binary file not shown.
BIN
.github/secrets/dist_share_extension.mobileprovision.gpg
vendored
Normal file
BIN
.github/secrets/dist_share_extension.mobileprovision.gpg
vendored
Normal file
Binary file not shown.
BIN
.github/secrets/dist_watch_app.mobileprovision.gpg
vendored
Normal file
BIN
.github/secrets/dist_watch_app.mobileprovision.gpg
vendored
Normal file
Binary file not shown.
BIN
.github/secrets/dist_watch_app_extension.mobileprovision.gpg
vendored
Normal file
BIN
.github/secrets/dist_watch_app_extension.mobileprovision.gpg
vendored
Normal file
Binary file not shown.
3
.github/secrets/google-services.json.gpg
vendored
Normal file
3
.github/secrets/google-services.json.gpg
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<EFBFBD>
|
||||||
|
K<>Y#<23>(<28><><EFBFBD><EFBFBD>EI߄T?)l<><6C><EFBFBD><18><><10>"=<3D>|<7C>'e<><0E>m<EFBFBD>/~<7E><>'F<><46>><3E><><EFBFBD><EFBFBD>l<EFBFBD>b<EFBFBD>[<5B>+R<><52>iL<69><4C>"<22><><EFBFBD>~V:<3A><>p<EFBFBD>a<17>ڵel%8t<38><74>튖<EFBFBD>y<<3C>n<EFBFBD><6E><EFBFBD>aU<61>w<16>JD<4A><44><1F><>We<57>9<EFBFBD><39><EFBFBD><EFBFBD><x8d<38>O<EFBFBD>j\<14>ד<EFBFBD><D793><EFBFBD>Vq<56><71>
|
||||||
|
Ǻ<EFBFBD>-<2D>#<23><><11><>]$<24>(<28>l,<2C>Br<42><02><>d<><64><EFBFBD>a-<2D><><EFBFBD>:<3A><>:<3A><04>9b,!Em<02><19><>Qf<>D<EFBFBD>g<EFBFBD><06><0E>x(P<>ȡ~<7E><EFBFBD><CDB9> <09><>[<06><>!:<3A>;f<><66>
|
||||||
BIN
.github/secrets/iphone-distribution-cert.p12.gpg
vendored
Normal file
BIN
.github/secrets/iphone-distribution-cert.p12.gpg
vendored
Normal file
Binary file not shown.
BIN
.github/secrets/play_creds.json.gpg
vendored
Normal file
BIN
.github/secrets/play_creds.json.gpg
vendored
Normal file
Binary file not shown.
BIN
.github/secrets/store_fdroid-keystore.jks.gpg
vendored
Normal file
BIN
.github/secrets/store_fdroid-keystore.jks.gpg
vendored
Normal file
Binary file not shown.
29
.github/workflows/_cut_rc.yml
vendored
Normal file
29
.github/workflows/_cut_rc.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
name: Cut RC Branch
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cut-rc:
|
||||||
|
name: Cut RC branch
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout Branch
|
||||||
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
with:
|
||||||
|
ref: main
|
||||||
|
|
||||||
|
- name: Check if RC branch exists
|
||||||
|
run: |
|
||||||
|
remote_rc_branch_check=$(git ls-remote --heads origin rc | wc -l)
|
||||||
|
if [[ "${remote_rc_branch_check}" -gt 0 ]]; then
|
||||||
|
echo "Remote RC branch exists."
|
||||||
|
echo "Please delete current RC branch before running again."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Cut RC branch
|
||||||
|
run: |
|
||||||
|
git switch --quiet --create rc
|
||||||
|
git push --quiet --set-upstream origin rc
|
||||||
5
.github/workflows/build-beta.yml
vendored
5
.github/workflows/build-beta.yml
vendored
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
name: Build Beta
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
443
.github/workflows/build.yml
vendored
443
.github/workflows/build.yml
vendored
@@ -31,7 +31,6 @@ jobs:
|
|||||||
- name: Print lines of code
|
- name: Print lines of code
|
||||||
run: cloc --vcs git --exclude-dir Resources,store,test,Properties --include-lang C#,XAML
|
run: cloc --vcs git --exclude-dir Resources,store,test,Properties --include-lang C#,XAML
|
||||||
|
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
name: Setup
|
name: Setup
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
@@ -59,7 +58,6 @@ jobs:
|
|||||||
echo "hotfix_branch_exists=0" >> $GITHUB_OUTPUT
|
echo "hotfix_branch_exists=0" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
android:
|
android:
|
||||||
name: Android
|
name: Android
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
@@ -69,8 +67,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
variant: ["prod", "qa"]
|
variant: ["prod", "qa"]
|
||||||
env:
|
env:
|
||||||
android_folder_path: src\App\Platforms\Android
|
android_folder_path: src/App/Platforms/Android
|
||||||
android_folder_path_bash: src/App/Platforms/Android
|
|
||||||
steps:
|
steps:
|
||||||
- name: Setup NuGet
|
- name: Setup NuGet
|
||||||
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
|
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
|
||||||
@@ -78,14 +75,14 @@ jobs:
|
|||||||
nuget-version: 6.4.0
|
nuget-version: 6.4.0
|
||||||
|
|
||||||
- name: Set up .NET
|
- name: Set up .NET
|
||||||
uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
|
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
|
||||||
with:
|
with:
|
||||||
dotnet-version: '8.0.x'
|
dotnet-version: '8.0.x'
|
||||||
|
|
||||||
- name: Set up MSBuild
|
- name: Set up MSBuild
|
||||||
uses: microsoft/setup-msbuild@ede762b26a2de8d110bb5a3db4d7e0e080c0e917 # v1.3.3
|
uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1
|
||||||
|
|
||||||
# This step might be obsolete at some point as .NET MAUI workloads
|
# This step might be obsolete at some point as .NET MAUI workloads
|
||||||
# are starting to come pre-installed on the GH Actions build agents.
|
# are starting to come pre-installed on the GH Actions build agents.
|
||||||
- name: Install MAUI Workload
|
- name: Install MAUI Workload
|
||||||
run: dotnet workload install maui --ignore-failed-sources
|
run: dotnet workload install maui --ignore-failed-sources
|
||||||
@@ -96,8 +93,7 @@ jobs:
|
|||||||
- name: Install Microsoft OpenJDK 11
|
- name: Install Microsoft OpenJDK 11
|
||||||
run: |
|
run: |
|
||||||
choco install microsoft-openjdk11 --no-progress
|
choco install microsoft-openjdk11 --no-progress
|
||||||
Write-Output "JAVA_HOME=$(Get-ChildItem -Path 'C:\Program Files\Microsoft\jdk*' | `
|
Write-Output "JAVA_HOME=$(Get-ChildItem -Path 'C:\Program Files\Microsoft\jdk*' | Select -First 1 -ExpandProperty FullName)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||||
Select -First 1 -ExpandProperty FullName)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
|
||||||
Write-Output "Java Home: $env:JAVA_HOME"
|
Write-Output "Java Home: $env:JAVA_HOME"
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
@@ -113,34 +109,27 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Login to Azure - CI Subscription
|
- name: Decrypt secrets
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
|
||||||
|
|
||||||
- name: Download secrets
|
|
||||||
env:
|
env:
|
||||||
ACCOUNT_NAME: bitwardenci
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
CONTAINER_NAME: mobile
|
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/secrets
|
mkdir -p ~/secrets
|
||||||
|
|
||||||
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
--name app_play-keystore.jks --file ./${{ env.android_folder_path_bash }}/app_play-keystore.jks --output none
|
--output ./${{ env.main_app_folder_path }}/app_play-keystore.jks ./.github/secrets/app_play-keystore.jks.gpg
|
||||||
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
--name app_upload-keystore.jks --file ./${{ env.android_folder_path_bash }}/app_upload-keystore.jks --output none
|
--output ./${{ env.main_app_folder_path }}/app_upload-keystore.jks ./.github/secrets/app_upload-keystore.jks.gpg
|
||||||
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
--name play_creds.json --file $HOME/secrets/play_creds.json --output none
|
--output $HOME/secrets/play_creds.json ./.github/secrets/play_creds.json.gpg
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Download secrets - Google Services
|
- name: Decrypt secrets - Google Services
|
||||||
if: ${{ matrix.variant == 'prod' }}
|
if: ${{ matrix.variant == 'prod' }}
|
||||||
env:
|
env:
|
||||||
ACCOUNT_NAME: bitwardenci
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
CONTAINER_NAME: mobile
|
|
||||||
run: |
|
run: |
|
||||||
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
--name google-services.json --file ./${{ env.android_folder_path_bash }}/google-services.json --output none
|
--output ./${{ env.android_folder_path }}/google-services.json ./.github/secrets/google-services.json.gpg
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Increment version
|
- name: Increment version
|
||||||
@@ -150,9 +139,9 @@ jobs:
|
|||||||
echo "########################################"
|
echo "########################################"
|
||||||
echo "##### Setting Version Code $BUILD_NUMBER"
|
echo "##### Setting Version Code $BUILD_NUMBER"
|
||||||
echo "########################################"
|
echo "########################################"
|
||||||
|
|
||||||
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
|
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
|
||||||
./${{ env.android_folder_path_bash }}/AndroidManifest.xml
|
./${{ env.android_folder_path }}/AndroidManifest.xml
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Restore packages
|
- name: Restore packages
|
||||||
@@ -161,75 +150,83 @@ jobs:
|
|||||||
- name: Restore tools
|
- name: Restore tools
|
||||||
run: dotnet tool restore
|
run: dotnet tool restore
|
||||||
|
|
||||||
# - name: Run Core tests
|
# - name: Verify Format
|
||||||
# run: |
|
# run: dotnet tool run dotnet-format --check
|
||||||
# dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx" `
|
|
||||||
# /p:CustomConstants=UT
|
|
||||||
|
|
||||||
# - name: Report test results
|
# - name: Run Core tests
|
||||||
# uses: dorny/test-reporter@eaa763f6ffc21c7a37837f56cd5f9737f27fc6c8 # v1.8.0
|
# run: dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx"
|
||||||
# if: always()
|
|
||||||
# with:
|
#- name: Report test results
|
||||||
# name: Test Results
|
# uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 # v1.6.0
|
||||||
# path: "**/test-results.trx"
|
# if: always()
|
||||||
# reporter: dotnet-trx
|
# with:
|
||||||
# fail-on-error: true
|
# name: Test Results
|
||||||
|
# path: "**/test-results.trx"
|
||||||
|
# reporter: dotnet-trx
|
||||||
|
# fail-on-error: true
|
||||||
|
|
||||||
- name: Build Play Store publisher
|
- name: Build Play Store publisher
|
||||||
if: ${{ matrix.variant == 'prod' }}
|
if: ${{ matrix.variant == 'prod' }}
|
||||||
run: dotnet build .\store\google\Publisher\Publisher.csproj /p:Configuration=Release
|
run: dotnet build ./store/google/Publisher/Publisher.csproj -p:Configuration=Release
|
||||||
|
|
||||||
- name: Setup Android build (${{ matrix.variant }})
|
- name: Setup Android build (${{ matrix.variant }})
|
||||||
run: dotnet cake build.cake --target Android --variant ${{ matrix.variant }}
|
run: dotnet cake build.cake --target Android --variant ${{ matrix.variant }}
|
||||||
|
|
||||||
- name: Build & Sign Android
|
- name: Build Android
|
||||||
|
run: |
|
||||||
|
$configuration = "Release";
|
||||||
|
$projToBuild = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Build $configuration Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
dotnet build $projToBuild -c $configuration -f ${{ env.target-net-version }}-android
|
||||||
|
|
||||||
|
- name: Sign Android Build
|
||||||
env:
|
env:
|
||||||
PLAY_KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD }}
|
PLAY_KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD }}
|
||||||
UPLOAD_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }}
|
UPLOAD_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }}
|
||||||
run: |
|
run: |
|
||||||
$projToBuild = "$($env:GITHUB_WORKSPACE)/${{ env.main_app_project_path }}";
|
$projToBuild = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
|
||||||
$packageName = "com.x8bit.bitwarden";
|
$packageName = "com.x8bit.bitwarden";
|
||||||
|
|
||||||
if ("${{ matrix.variant }}" -ne "prod")
|
if ("${{ matrix.variant }}" -ne "prod")
|
||||||
{
|
{
|
||||||
$packageName = "com.x8bit.bitwarden.${{ matrix.variant }}";
|
$packageName = "com.x8bit.bitwarden.${{ matrix.variant }}";
|
||||||
}
|
}
|
||||||
|
Write-Output "########################################"
|
||||||
Write-Output "##### Sign Google Play Bundle Release Configuration"
|
Write-Output "##### Sign Google Play Bundle Release Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
$signingUploadKeyStore = "$($env:GITHUB_WORKSPACE)\${{ env.android_folder_path }}\app_upload-keystore.jks"
|
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android /p:AndroidPackageFormats=aab /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=$("app_upload-keystore.jks") /p:AndroidSigningKeyAlias=upload /p:AndroidSigningKeyPass="$($env:UPLOAD_KEYSTORE_PASSWORD)" /p:AndroidSigningStorePass="$($env:UPLOAD_KEYSTORE_PASSWORD)" --no-restore
|
||||||
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android `
|
|
||||||
/p:AndroidPackageFormats=aab `
|
|
||||||
/p:AndroidKeyStore=true `
|
|
||||||
/p:AndroidSigningKeyStore=$signingUploadKeyStore `
|
|
||||||
/p:AndroidSigningKeyAlias=upload `
|
|
||||||
/p:AndroidSigningKeyPass="$($env:UPLOAD_KEYSTORE_PASSWORD)" `
|
|
||||||
/p:AndroidSigningStorePass="$($env:UPLOAD_KEYSTORE_PASSWORD)" --no-restore
|
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
Write-Output "##### Copy Google Play Bundle to project root"
|
Write-Output "##### Copy Google Play Bundle to project root"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
$signedAabPath = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_folder_path }}\bin\Release\${{ env.target-net-version }}-android\publish\$($packageName)-Signed.aab";
|
$signedAabPath = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_folder_path }}/bin/Release/${{ env.target-net-version }}-android/publish/$($packageName)-Signed.aab");
|
||||||
$signedAabDestPath = "$($env:GITHUB_WORKSPACE)\$($packageName).aab";
|
$signedAabDestPath = $($env:GITHUB_WORKSPACE + "/$($packageName).aab");
|
||||||
Copy-Item $signedAabPath $signedAabDestPath
|
Copy-Item $signedAabPath $signedAabDestPath
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
Write-Output "##### Sign APK Release Configuration"
|
Write-Output "##### Sign APK Release Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
$signingPlayKeyStore = "$($env:GITHUB_WORKSPACE)\${{ env.android_folder_path }}\app_play-keystore.jks"
|
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=$("app_play-keystore.jks") /p:AndroidSigningKeyAlias=bitwarden /p:AndroidSigningKeyPass="$($env:PLAY_KEYSTORE_PASSWORD)" /p:AndroidSigningStorePass="$($env:PLAY_KEYSTORE_PASSWORD)" --no-restore
|
||||||
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android `
|
|
||||||
/p:AndroidKeyStore=true `
|
|
||||||
/p:AndroidSigningKeyStore=$signingPlayKeyStore `
|
|
||||||
/p:AndroidSigningKeyAlias=bitwarden `
|
|
||||||
/p:AndroidSigningKeyPass="$($env:PLAY_KEYSTORE_PASSWORD)" `
|
|
||||||
/p:AndroidSigningStorePass="$($env:PLAY_KEYSTORE_PASSWORD)" --no-restore
|
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
Write-Output "##### Copy Release APK to project root"
|
Write-Output "##### Copy Release APK to project root"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$signedApkPath = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_folder_path }}/bin/Release/${{ env.target-net-version }}-android/publish/$($packageName)-Signed.apk");
|
||||||
|
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/$($packageName).apk");
|
||||||
|
|
||||||
$signedApkPath = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_folder_path }}\bin\Release\${{ env.target-net-version }}-android\publish\$($packageName)-Signed.apk";
|
|
||||||
$signedApkDestPath = "$($env:GITHUB_WORKSPACE)\$($packageName).apk";
|
|
||||||
Copy-Item $signedApkPath $signedApkDestPath
|
Copy-Item $signedApkPath $signedApkDestPath
|
||||||
|
|
||||||
- name: Upload Prod .aab artifact
|
- name: Upload Prod .aab artifact
|
||||||
if: ${{ matrix.variant == 'prod' }}
|
if: ${{ matrix.variant == 'prod' }}
|
||||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden.aab
|
name: com.x8bit.bitwarden.aab
|
||||||
path: ./com.x8bit.bitwarden.aab
|
path: ./com.x8bit.bitwarden.aab
|
||||||
@@ -237,7 +234,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Prod .apk artifact
|
- name: Upload Prod .apk artifact
|
||||||
if: ${{ matrix.variant == 'prod' }}
|
if: ${{ matrix.variant == 'prod' }}
|
||||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden.apk
|
name: com.x8bit.bitwarden.apk
|
||||||
path: ./com.x8bit.bitwarden.apk
|
path: ./com.x8bit.bitwarden.apk
|
||||||
@@ -245,7 +242,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Other .apk artifact
|
- name: Upload Other .apk artifact
|
||||||
if: ${{ matrix.variant != 'prod' }}
|
if: ${{ matrix.variant != 'prod' }}
|
||||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden.${{ matrix.variant }}.apk
|
name: com.x8bit.bitwarden.${{ matrix.variant }}.apk
|
||||||
path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk
|
path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk
|
||||||
@@ -265,7 +262,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload .apk sha file for prod
|
- name: Upload .apk sha file for prod
|
||||||
if: ${{ matrix.variant == 'prod' }}
|
if: ${{ matrix.variant == 'prod' }}
|
||||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||||
with:
|
with:
|
||||||
name: bw-android-apk-sha256.txt
|
name: bw-android-apk-sha256.txt
|
||||||
path: ./bw-android-apk-sha256.txt
|
path: ./bw-android-apk-sha256.txt
|
||||||
@@ -273,7 +270,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload .apk sha file for other
|
- name: Upload .apk sha file for other
|
||||||
if: ${{ matrix.variant != 'prod' }}
|
if: ${{ matrix.variant != 'prod' }}
|
||||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||||
with:
|
with:
|
||||||
name: bw-android-${{ matrix.variant }}-apk-sha256.txt
|
name: bw-android-${{ matrix.variant }}-apk-sha256.txt
|
||||||
path: ./bw-android-${{ matrix.variant }}-apk-sha256.txt
|
path: ./bw-android-${{ matrix.variant }}-apk-sha256.txt
|
||||||
@@ -286,20 +283,20 @@ jobs:
|
|||||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| github.ref == 'refs/heads/hotfix-rc' ) }}
|
|| github.ref == 'refs/heads/hotfix-rc' ) }}
|
||||||
run: |
|
run: |
|
||||||
$publisherPath = "$($env:GITHUB_WORKSPACE)\store\google\Publisher\bin\Release\net8.0\Publisher.dll"
|
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/net7.0/Publisher.dll"
|
||||||
$credsPath = "$($HOME)\secrets\play_creds.json"
|
CREDS_PATH="$HOME/secrets/play_creds.json"
|
||||||
$aabPath = "$($env:GITHUB_WORKSPACE)\com.x8bit.bitwarden.aab"
|
AAB_PATH="$GITHUB_WORKSPACE/com.x8bit.bitwarden.aab"
|
||||||
$track = "internal"
|
TRACK="internal"
|
||||||
|
|
||||||
dotnet $publisherPath $credsPath $aabPath $track
|
dotnet $PUBLISHER_PATH $CREDS_PATH $AAB_PATH $TRACK
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
|
||||||
f-droid:
|
f-droid:
|
||||||
name: F-Droid Build
|
name: F-Droid Build
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
env:
|
env:
|
||||||
android_folder_path: src\App\Platforms\Android
|
android_folder_path: src/App/Platforms/Android
|
||||||
android_folder_path_bash: src/App/Platforms/Android
|
|
||||||
android_manifest_path: src/App/Platforms/Android/AndroidManifest.xml
|
android_manifest_path: src/App/Platforms/Android/AndroidManifest.xml
|
||||||
steps:
|
steps:
|
||||||
- name: Setup NuGet
|
- name: Setup NuGet
|
||||||
@@ -313,9 +310,9 @@ jobs:
|
|||||||
dotnet-version: '8.0.x'
|
dotnet-version: '8.0.x'
|
||||||
|
|
||||||
- name: Set up MSBuild
|
- name: Set up MSBuild
|
||||||
uses: microsoft/setup-msbuild@ede762b26a2de8d110bb5a3db4d7e0e080c0e917 # v1.3.3
|
uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1
|
||||||
|
|
||||||
# This step might be obsolete at some point as .NET MAUI workloads
|
# This step might be obsolete at some point as .NET MAUI workloads
|
||||||
# are starting to come pre-installed on the GH Actions build agents.
|
# are starting to come pre-installed on the GH Actions build agents.
|
||||||
- name: Install MAUI Workload
|
- name: Install MAUI Workload
|
||||||
run: dotnet workload install maui --ignore-failed-sources
|
run: dotnet workload install maui --ignore-failed-sources
|
||||||
@@ -340,25 +337,23 @@ jobs:
|
|||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
|
||||||
- name: Login to Azure - CI Subscription
|
- name: Decrypt secrets
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
|
||||||
|
|
||||||
- name: Download secrets
|
|
||||||
env:
|
env:
|
||||||
ACCOUNT_NAME: bitwardenci
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
CONTAINER_NAME: mobile
|
|
||||||
FILE: app_fdroid-keystore.jks
|
|
||||||
run: |
|
run: |
|
||||||
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
|
mkdir -p ~/secrets
|
||||||
--file ${{ env.android_folder_path_bash }}/$FILE --output none
|
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output ./${{ env.main_app_folder_path }}/app_fdroid-keystore.jks ./.github/secrets/app_fdroid-keystore.jks.gpg
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Increment version
|
- name: Increment version
|
||||||
run: |
|
run: |
|
||||||
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
|
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
|
||||||
|
|
||||||
|
echo "########################################"
|
||||||
echo "##### Setting Version Code $BUILD_NUMBER"
|
echo "##### Setting Version Code $BUILD_NUMBER"
|
||||||
|
echo "########################################"
|
||||||
|
|
||||||
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
|
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
|
||||||
./${{ env.android_manifest_path }}
|
./${{ env.android_manifest_path }}
|
||||||
@@ -371,12 +366,23 @@ jobs:
|
|||||||
|
|
||||||
$androidManifest = $($env:GITHUB_WORKSPACE + "/${{ env.android_manifest_path }}");
|
$androidManifest = $($env:GITHUB_WORKSPACE + "/${{ env.android_manifest_path }}");
|
||||||
|
|
||||||
Write-Output "##### Back up project files"
|
# Write-Output "########################################"
|
||||||
|
# Write-Output "##### Clean Android and App"
|
||||||
|
# Write-Output "########################################"
|
||||||
|
|
||||||
|
# msbuild "$($androidPath)" "/t:Clean" "/p:Configuration=FDroid"
|
||||||
|
# msbuild "$($appPath)" "/t:Clean" "/p:Configuration=FDroid"
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Backup project files"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
Copy-Item $androidManifest $($androidManifest + ".original");
|
Copy-Item $androidManifest $($androidManifest + ".original");
|
||||||
Copy-Item $appPath $($appPath + ".original");
|
Copy-Item $appPath $($appPath + ".original");
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
Write-Output "##### Cleanup Android Manifest"
|
Write-Output "##### Cleanup Android Manifest"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
$xml=New-Object XML;
|
$xml=New-Object XML;
|
||||||
$xml.Load($androidManifest);
|
$xml.Load($androidManifest);
|
||||||
@@ -386,36 +392,80 @@ jobs:
|
|||||||
|
|
||||||
$xml.Save($androidManifest);
|
$xml.Save($androidManifest);
|
||||||
|
|
||||||
|
# Write-Output "########################################"
|
||||||
|
# Write-Output "##### Uninstall from App.csproj"
|
||||||
|
# Write-Output "########################################"
|
||||||
|
|
||||||
|
# $xml=New-Object XML;
|
||||||
|
# $xml.Load($appPath);
|
||||||
|
|
||||||
|
# $ns=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
|
||||||
|
# $ns.AddNamespace("ns", $xml.DocumentElement.NamespaceURI);
|
||||||
|
|
||||||
|
# $firebaseNode=$xml.SelectSingleNode(`
|
||||||
|
# "/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Firebase.Messaging']", $ns);
|
||||||
|
# $firebaseNode.ParentNode.RemoveChild($firebaseNode);
|
||||||
|
|
||||||
|
# $daggerNode=$xml.SelectSingleNode(`
|
||||||
|
# "/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Google.Dagger']", $ns);
|
||||||
|
# $daggerNode.ParentNode.RemoveChild($daggerNode);
|
||||||
|
|
||||||
|
# $safetyNetNode=$xml.SelectSingleNode(`
|
||||||
|
# "/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.GooglePlayServices.SafetyNet']", $ns);
|
||||||
|
# $safetyNetNode.ParentNode.RemoveChild($safetyNetNode);
|
||||||
|
|
||||||
|
# $xml.Save($appPath);
|
||||||
|
|
||||||
|
# Write-Output "########################################"
|
||||||
|
# Write-Output "##### Uninstall from Core.csproj"
|
||||||
|
# Write-Output "########################################"
|
||||||
|
|
||||||
|
# $xml=New-Object XML;
|
||||||
|
# $xml.Load($corePath);
|
||||||
|
|
||||||
|
# $appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
|
||||||
|
# $appCenterNode.ParentNode.RemoveChild($appCenterNode);
|
||||||
|
|
||||||
|
# $xml.Save($corePath);
|
||||||
|
|
||||||
- name: Restore packages
|
- name: Restore packages
|
||||||
run: dotnet restore
|
run: dotnet restore
|
||||||
|
|
||||||
- name: Build & Sign F-Droid
|
- name: Build for F-Droid
|
||||||
|
run: |
|
||||||
|
$configuration = "Release";
|
||||||
|
$projToBuild = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Build $configuration FDROID
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
dotnet build $projToBuild -c $configuration -f ${{ env.target-net-version }}-android /p:CustomConstants="FDROID"
|
||||||
|
|
||||||
|
- name: Sign for F-Droid
|
||||||
env:
|
env:
|
||||||
FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
|
FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
|
||||||
run: |
|
run: |
|
||||||
$projToBuild = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_project_path }}";
|
$projToBuild = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
|
||||||
$packageName = "com.x8bit.bitwarden";
|
$packageName = "com.x8bit.bitwarden";
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
Write-Output "##### Sign FDroid"
|
Write-Output "##### Sign FDroid"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
$signingFdroidKeyStore = "$($env:GITHUB_WORKSPACE)\${{ env.android_folder_path }}\app_fdroid-keystore.jks"
|
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=$("app_fdroid-keystore.jks") /p:AndroidSigningKeyAlias=bitwarden /p:AndroidSigningKeyPass="$($env:FDROID_KEYSTORE_PASSWORD)" /p:AndroidSigningStorePass="$($env:FDROID_KEYSTORE_PASSWORD)" /p:CustomConstants="FDROID" --no-restore
|
||||||
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android `
|
|
||||||
/p:AndroidKeyStore=true `
|
|
||||||
/p:AndroidSigningKeyStore=$signingFdroidKeyStore `
|
|
||||||
/p:AndroidSigningKeyAlias=bitwarden `
|
|
||||||
/p:AndroidSigningKeyPass="$($env:FDROID_KEYSTORE_PASSWORD)" `
|
|
||||||
/p:AndroidSigningStorePass="$($env:FDROID_KEYSTORE_PASSWORD)" `
|
|
||||||
/p:CustomConstants="FDROID" --no-restore
|
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
Write-Output "##### Copy FDroid apk to project root"
|
Write-Output "##### Copy FDroid apk to project root"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
$signedApkPath = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_folder_path }}\bin\Release\${{ env.target-net-version }}-android\publish\$($packageName)-Signed.apk";
|
$signedApkPath = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_folder_path }}/bin/Release/${{ env.target-net-version }}-android/publish/$($packageName)-Signed.apk");
|
||||||
$signedApkDestPath = "$($env:GITHUB_WORKSPACE)\com.x8bit.bitwarden-fdroid.apk";
|
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden-fdroid.apk");
|
||||||
|
|
||||||
Copy-Item $signedApkPath $signedApkDestPath
|
Copy-Item $signedApkPath $signedApkDestPath
|
||||||
|
|
||||||
- name: Upload F-Droid .apk artifact
|
- name: Upload F-Droid .apk artifact
|
||||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden-fdroid.apk
|
name: com.x8bit.bitwarden-fdroid.apk
|
||||||
path: ./com.x8bit.bitwarden-fdroid.apk
|
path: ./com.x8bit.bitwarden-fdroid.apk
|
||||||
@@ -427,13 +477,12 @@ jobs:
|
|||||||
-t sha256 | Out-File -Encoding ASCII ./bw-fdroid-apk-sha256.txt
|
-t sha256 | Out-File -Encoding ASCII ./bw-fdroid-apk-sha256.txt
|
||||||
|
|
||||||
- name: Upload F-Droid sha file
|
- name: Upload F-Droid sha file
|
||||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||||
with:
|
with:
|
||||||
name: bw-fdroid-apk-sha256.txt
|
name: bw-fdroid-apk-sha256.txt
|
||||||
path: ./bw-fdroid-apk-sha256.txt
|
path: ./bw-fdroid-apk-sha256.txt
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
ios:
|
ios:
|
||||||
name: Apple iOS
|
name: Apple iOS
|
||||||
runs-on: macos-13
|
runs-on: macos-13
|
||||||
@@ -446,19 +495,19 @@ jobs:
|
|||||||
- name: Set XCode version
|
- name: Set XCode version
|
||||||
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
|
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
|
||||||
with:
|
with:
|
||||||
xcode-version: 15.1
|
xcode-version: 15.0.1
|
||||||
|
|
||||||
- name: Setup NuGet
|
- name: Setup NuGet
|
||||||
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
|
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
|
||||||
with:
|
with:
|
||||||
nuget-version: 6.4.0
|
nuget-version: 6.4.0
|
||||||
|
|
||||||
- name: Set up .NET
|
- name: Set up .NET
|
||||||
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
|
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
|
||||||
with:
|
with:
|
||||||
dotnet-version: '8.0.x'
|
dotnet-version: '8.0.x'
|
||||||
|
|
||||||
# This step might be obsolete at some point as .NET MAUI workloads
|
# This step might be obsolete at some point as .NET MAUI workloads
|
||||||
# are starting to come pre-installed on the GH Actions build agents.
|
# are starting to come pre-installed on the GH Actions build agents.
|
||||||
- name: Install MAUI Workload
|
- name: Install MAUI Workload
|
||||||
run: dotnet workload install maui --ignore-failed-sources
|
run: dotnet workload install maui --ignore-failed-sources
|
||||||
@@ -476,7 +525,7 @@ jobs:
|
|||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
|
|
||||||
- name: Login to Azure - CI Subscription
|
- name: Login to Azure - CI Subscription
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||||
|
|
||||||
@@ -487,73 +536,71 @@ jobs:
|
|||||||
keyvault: "bitwarden-ci"
|
keyvault: "bitwarden-ci"
|
||||||
secrets: "appcenter-ios-token"
|
secrets: "appcenter-ios-token"
|
||||||
|
|
||||||
- name: Download Provisioning Profiles secrets
|
- name: Decrypt secrets
|
||||||
env:
|
env:
|
||||||
ACCOUNT_NAME: bitwardenci
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
CONTAINER_NAME: profiles
|
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/secrets
|
mkdir -p ~/secrets
|
||||||
profiles=(
|
|
||||||
"dist_autofill.mobileprovision"
|
|
||||||
"dist_bitwarden.mobileprovision"
|
|
||||||
"dist_extension.mobileprovision"
|
|
||||||
"dist_share_extension.mobileprovision"
|
|
||||||
"dist_bitwarden_watch_app.mobileprovision"
|
|
||||||
"dist_bitwarden_watch_app_extension.mobileprovision"
|
|
||||||
)
|
|
||||||
|
|
||||||
for FILE in "${profiles[@]}"
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
do
|
--output $HOME/secrets/bitwarden-mobile-key.p12 ./.github/secrets/bitwarden-mobile-key.p12.gpg
|
||||||
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
--file $HOME/secrets/$FILE --output none
|
--output $HOME/secrets/iphone-distribution-cert.p12 ./.github/secrets/iphone-distribution-cert.p12.gpg
|
||||||
done
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/dist_autofill.mobileprovision ./.github/secrets/dist_autofill.mobileprovision.gpg
|
||||||
- name: Download Google Services secret
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
env:
|
--output $HOME/secrets/dist_bitwarden.mobileprovision ./.github/secrets/dist_bitwarden.mobileprovision.gpg
|
||||||
ACCOUNT_NAME: bitwardenci
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
CONTAINER_NAME: mobile
|
--output $HOME/secrets/dist_extension.mobileprovision ./.github/secrets/dist_extension.mobileprovision.gpg
|
||||||
FILE: GoogleService-Info.plist
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
run: |
|
--output $HOME/secrets/dist_share_extension.mobileprovision \
|
||||||
mkdir -p $HOME/secrets
|
./.github/secrets/dist_share_extension.mobileprovision.gpg
|
||||||
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
--file src/watchOS/bitwarden/$FILE --output none
|
--output $HOME/secrets/dist_watch_app.mobileprovision \
|
||||||
|
./.github/secrets/dist_watch_app.mobileprovision.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/dist_watch_app_extension.mobileprovision \
|
||||||
|
./.github/secrets/dist_watch_app_extension.mobileprovision.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output ./src/watchOS/bitwarden/GoogleService-Info.plist ./.github/secrets/GoogleService-Info.plist.gpg
|
||||||
|
|
||||||
- name: Increment version
|
- name: Increment version
|
||||||
run: |
|
run: |
|
||||||
BUILD_NUMBER=$((100 + $GITHUB_RUN_NUMBER))
|
BUILD_NUMBER=$((100 + $GITHUB_RUN_NUMBER))
|
||||||
|
|
||||||
|
echo "########################################"
|
||||||
echo "##### Setting CFBundleVersion $BUILD_NUMBER"
|
echo "##### Setting CFBundleVersion $BUILD_NUMBER"
|
||||||
echo "### CFBundleVersion $BUILD_NUMBER" >> $GITHUB_STEP_SUMMARY
|
echo "########################################"
|
||||||
|
|
||||||
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./${{ env.ios_folder_path }}/Info.plist
|
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./${{ env.ios_folder_path }}/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.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.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
|
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
|
||||||
cd src/watchOS/bitwarden
|
cd src/watchOS/bitwarden
|
||||||
agvtool new-version -all $BUILD_NUMBER
|
agvtool new-version -all $BUILD_NUMBER
|
||||||
|
|
||||||
- name: Update Entitlements
|
- name: Update Entitlements
|
||||||
run: |
|
run: |
|
||||||
|
echo "########################################"
|
||||||
echo "##### Updating Entitlements"
|
echo "##### Updating Entitlements"
|
||||||
|
echo "########################################"
|
||||||
|
|
||||||
perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>production<\/string>/' ./${{ env.ios_folder_path }}/Entitlements.plist
|
perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>production<\/string>/' ./${{ env.ios_folder_path }}/Entitlements.plist
|
||||||
|
|
||||||
- name: Get certificates
|
|
||||||
run: |
|
|
||||||
mkdir -p $HOME/certificates
|
|
||||||
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/ios-distribution |
|
|
||||||
jq -r .value | base64 -d > $HOME/certificates/ios-distribution.p12
|
|
||||||
|
|
||||||
- name: Set up Keychain
|
- name: Set up Keychain
|
||||||
env:
|
env:
|
||||||
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
|
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
|
||||||
|
MOBILE_KEY_PASSWORD: ${{ secrets.IOS_KEY_PASSWORD }}
|
||||||
|
DIST_CERT_PASSWORD: ${{ secrets.IOS_DIST_CERT_PASSWORD }}
|
||||||
run: |
|
run: |
|
||||||
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
|
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
|
||||||
security default-keychain -s build.keychain
|
security default-keychain -s build.keychain
|
||||||
security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
|
security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
|
||||||
security set-keychain-settings -lut 1200 build.keychain
|
security set-keychain-settings -lut 1200 build.keychain
|
||||||
|
security import ~/secrets/bitwarden-mobile-key.p12 -k build.keychain -P $MOBILE_KEY_PASSWORD \
|
||||||
security import $HOME/certificates/ios-distribution.p12 -k build.keychain -P "" -T /usr/bin/codesign \
|
-T /usr/bin/codesign -T /usr/bin/security
|
||||||
-T /usr/bin/security
|
security import ~/secrets/iphone-distribution-cert.p12 -k build.keychain -P $DIST_CERT_PASSWORD \
|
||||||
|
-T /usr/bin/codesign -T /usr/bin/security
|
||||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
|
||||||
|
|
||||||
- name: Set up provisioning profiles
|
- name: Set up provisioning profiles
|
||||||
@@ -562,8 +609,8 @@ jobs:
|
|||||||
BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_bitwarden.mobileprovision
|
BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_bitwarden.mobileprovision
|
||||||
EXTENSION_PROFILE_PATH=$HOME/secrets/dist_extension.mobileprovision
|
EXTENSION_PROFILE_PATH=$HOME/secrets/dist_extension.mobileprovision
|
||||||
SHARE_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_share_extension.mobileprovision
|
SHARE_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_share_extension.mobileprovision
|
||||||
WATCH_APP_PROFILE_PATH=$HOME/secrets/dist_bitwarden_watch_app.mobileprovision
|
WATCH_APP_PROFILE_PATH=$HOME/secrets/dist_watch_app.mobileprovision
|
||||||
WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_bitwarden_watch_app_extension.mobileprovision
|
WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_watch_app_extension.mobileprovision
|
||||||
PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles
|
PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles
|
||||||
|
|
||||||
mkdir -p "$PROFILES_DIR_PATH"
|
mkdir -p "$PROFILES_DIR_PATH"
|
||||||
@@ -591,50 +638,74 @@ jobs:
|
|||||||
|
|
||||||
- name: Bulid WatchApp
|
- name: Bulid WatchApp
|
||||||
run: |
|
run: |
|
||||||
|
echo "########################################"
|
||||||
echo "##### Build WatchApp with Release Configuration"
|
echo "##### Build WatchApp with Release Configuration"
|
||||||
|
echo "########################################"
|
||||||
|
|
||||||
xcodebuild archive -workspace ./src/watchOS/bitwarden/bitwarden.xcodeproj/project.xcworkspace -configuration Release -scheme bitwarden\ WatchKit\ App -archivePath ./src/watchOS/bitwarden
|
xcodebuild archive -workspace ./src/watchOS/bitwarden/bitwarden.xcodeproj/project.xcworkspace -configuration Release -scheme bitwarden\ WatchKit\ App -archivePath ./src/watchOS/bitwarden
|
||||||
|
|
||||||
|
echo "########################################"
|
||||||
|
echo "##### Done"
|
||||||
|
echo "########################################"
|
||||||
|
|
||||||
- name: Archive Build for App Store
|
- name: Archive Build for App Store
|
||||||
run: |
|
run: |
|
||||||
echo "##### Archive for Release ios-arm64"
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Archive for Release ios-arm64
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
dotnet publish ${{ env.main_app_project_path }} -c Release -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=ios-arm64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
|
dotnet publish ${{ env.main_app_project_path }} -c Release -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=ios-arm64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Done"
|
||||||
|
Write-Output "########################################"
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
- name: Archive Build for Mobile Automation
|
- name: Archive Build for Mobile Automation
|
||||||
run: |
|
run: |
|
||||||
echo "##### Archive Debug for iossimulator-x64"
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Archive Debug for iossimulator-x64
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
dotnet build ${{ env.main_app_project_path }} -c Debug -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=iossimulator-x64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
|
dotnet build ${{ env.main_app_project_path }} -c Debug -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=iossimulator-x64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
|
||||||
ls $HOME/Library/Developer/Xcode/Archives
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Done"
|
||||||
|
Write-Output "########################################"
|
||||||
|
ls ~/Library/Developer/Xcode/Archives
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
- name: Export .ipa for App Store
|
- name: Export .ipa for App Store
|
||||||
env:
|
|
||||||
EXPORT_OPTIONS_PATH: ./.github/resources/export-options-app-store.plist
|
|
||||||
EXPORT_PATH: ./bitwarden-export
|
|
||||||
run: |
|
run: |
|
||||||
|
EXPORT_OPTIONS_PATH="./.github/resources/export-options-app-store.plist"
|
||||||
ARCHIVE_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive"
|
ARCHIVE_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive"
|
||||||
|
EXPORT_PATH="./bitwarden-export"
|
||||||
|
|
||||||
xcodebuild -exportArchive -archivePath $ARCHIVE_PATH -exportPath $EXPORT_PATH \
|
xcodebuild -exportArchive -archivePath $ARCHIVE_PATH -exportPath $EXPORT_PATH \
|
||||||
-exportOptionsPlist $EXPORT_OPTIONS_PATH
|
-exportOptionsPlist $EXPORT_OPTIONS_PATH
|
||||||
|
|
||||||
- name: Export .app for Automation CI
|
- name: Export .app for Automation CI
|
||||||
env:
|
|
||||||
ARCHIVE_PATH: ./${{ env.main_app_folder_path }}/bin/Debug/${{ env.target-net-version }}-ios/iossimulator-x64
|
|
||||||
EXPORT_PATH: ./bitwarden-export
|
|
||||||
run: |
|
run: |
|
||||||
|
ARCHIVE_PATH="./${{ env.main_app_folder_path }}/bin/Debug/${{ env.target-net-version }}-ios/iossimulator-x64"
|
||||||
|
EXPORT_PATH="./bitwarden-export"
|
||||||
|
|
||||||
zip -r -q ${{ env.app_ci_output_filename }}.app.zip $ARCHIVE_PATH
|
zip -r -q ${{ env.app_ci_output_filename }}.app.zip $ARCHIVE_PATH
|
||||||
mv ${{ env.app_ci_output_filename }}.app.zip $EXPORT_PATH
|
mv ${{ env.app_ci_output_filename }}.app.zip $EXPORT_PATH
|
||||||
|
|
||||||
- name: Copy all dSYMs files to upload
|
- name: Copy all dSYMs files to upload
|
||||||
env:
|
|
||||||
EXPORT_PATH: ./bitwarden-export
|
|
||||||
WATCH_ARCHIVE_DSYMS_PATH: ./src/watchOS/bitwarden.xcarchive/dSYMs/
|
|
||||||
WATCH_DSYMS_EXPORT_PATH: ./bitwarden-export/Watch_dSYMs
|
|
||||||
run: |
|
run: |
|
||||||
ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
|
ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
|
||||||
|
EXPORT_PATH="./bitwarden-export"
|
||||||
|
|
||||||
|
WATCH_ARCHIVE_DSYMS_PATH="./src/watchOS/bitwarden.xcarchive/dSYMs/"
|
||||||
|
WATCH_DSYMS_EXPORT_PATH="$EXPORT_PATH/Watch_dSYMs"
|
||||||
|
|
||||||
cp -r -v $ARCHIVE_DSYMS_PATH $EXPORT_PATH
|
cp -r -v $ARCHIVE_DSYMS_PATH $EXPORT_PATH
|
||||||
mkdir $WATCH_DSYMS_EXPORT_PATH
|
mkdir $WATCH_DSYMS_EXPORT_PATH
|
||||||
cp -r -v $WATCH_ARCHIVE_DSYMS_PATH $WATCH_DSYMS_EXPORT_PATH
|
cp -r -v $WATCH_ARCHIVE_DSYMS_PATH $WATCH_DSYMS_EXPORT_PATH
|
||||||
|
|
||||||
- name: Upload App Store .ipa & dSYMs artifacts
|
- name: Upload App Store .ipa & dSYMs artifacts
|
||||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||||
with:
|
with:
|
||||||
name: Bitwarden iOS
|
name: Bitwarden iOS
|
||||||
path: |
|
path: |
|
||||||
@@ -643,7 +714,7 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload .app file for Automation CI
|
- name: Upload .app file for Automation CI
|
||||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||||
with:
|
with:
|
||||||
name: ${{ env.app_ci_output_filename }}.app.zip
|
name: ${{ env.app_ci_output_filename }}.app.zip
|
||||||
path: ./bitwarden-export/${{ env.app_ci_output_filename }}.app.zip
|
path: ./bitwarden-export/${{ env.app_ci_output_filename }}.app.zip
|
||||||
@@ -677,7 +748,10 @@ jobs:
|
|||||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||||
|| github.ref == 'refs/heads/hotfix-rc'
|
|| github.ref == 'refs/heads/hotfix-rc'
|
||||||
run: |
|
run: |
|
||||||
|
echo "########################################"
|
||||||
echo "##### Uploading Watch dSYMs to Firebase"
|
echo "##### Uploading Watch dSYMs to Firebase"
|
||||||
|
echo "########################################"
|
||||||
|
|
||||||
find "$HOME/Library/Developer/XCode/DerivedData" -name "upload-symbols" -exec chmod +x {} \; -exec {} -gsp "./src/watchOS/bitwarden/GoogleService-Info.plist" -p ios "./bitwarden-export/Watch_dSYMs" \;
|
find "$HOME/Library/Developer/XCode/DerivedData" -name "upload-symbols" -exec chmod +x {} \; -exec {} -gsp "./src/watchOS/bitwarden/GoogleService-Info.plist" -p ios "./bitwarden-export/Watch_dSYMs" \;
|
||||||
|
|
||||||
- name: Validate app in App Store
|
- name: Validate app in App Store
|
||||||
@@ -693,6 +767,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
xcrun altool --validate-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
|
xcrun altool --validate-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
|
||||||
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
|
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Deploy to App Store
|
- name: Deploy to App Store
|
||||||
if: |
|
if: |
|
||||||
@@ -724,7 +799,7 @@ jobs:
|
|||||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
|
||||||
- name: Login to Azure - CI Subscription
|
- name: Login to Azure - CI Subscription
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||||
|
|
||||||
@@ -736,13 +811,13 @@ jobs:
|
|||||||
secrets: "crowdin-api-token"
|
secrets: "crowdin-api-token"
|
||||||
|
|
||||||
- name: Upload Sources
|
- name: Upload Sources
|
||||||
uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0
|
uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
||||||
with:
|
with:
|
||||||
config: crowdin.yml
|
config: crowdin.yml
|
||||||
crowdin_branch_name: main
|
crowdin_branch_name: main
|
||||||
upload_sources: true
|
upload_sources: true
|
||||||
upload_translations: false
|
upload_translations: false
|
||||||
|
|
||||||
@@ -760,14 +835,30 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Check if any job failed
|
- name: Check if any job failed
|
||||||
if: |
|
if: |
|
||||||
(github.ref == 'refs/heads/main'
|
(github.ref == 'refs/heads/main')
|
||||||
|| github.ref == 'refs/heads/rc'
|
|| (github.ref == 'refs/heads/rc')
|
||||||
|| github.ref == 'refs/heads/hotfix-rc')
|
|| (github.ref == 'refs/heads/hotfix-rc')
|
||||||
&& contains(needs.*.result, 'failure')
|
env:
|
||||||
run: exit 1
|
CLOC_STATUS: ${{ needs.cloc.result }}
|
||||||
|
ANDROID_STATUS: ${{ needs.android.result }}
|
||||||
|
F_DROID_STATUS: ${{ needs.f-droid.result }}
|
||||||
|
IOS_STATUS: ${{ needs.ios.result }}
|
||||||
|
CROWDIN_PUSH_STATUS: ${{ needs.crowdin-push.result }}
|
||||||
|
run: |
|
||||||
|
if [ "$CLOC_STATUS" = "failure" ]; then
|
||||||
|
exit 1
|
||||||
|
elif [ "$ANDROID_STATUS" = "failure" ]; then
|
||||||
|
exit 1
|
||||||
|
elif [ "$F_DROID_STATUS" = "failure" ]; then
|
||||||
|
exit 1
|
||||||
|
elif [ "$IOS_STATUS" = "failure" ]; then
|
||||||
|
exit 1
|
||||||
|
elif [ "$CROWDIN_PUSH_STATUS" = "failure" ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Login to Azure - CI Subscription
|
- name: Login to Azure - CI Subscription
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||||
|
|||||||
6
.github/workflows/crowdin-pull.yml
vendored
6
.github/workflows/crowdin-pull.yml
vendored
@@ -15,10 +15,10 @@ jobs:
|
|||||||
_CROWDIN_PROJECT_ID: "269690"
|
_CROWDIN_PROJECT_ID: "269690"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||||
|
|
||||||
- name: Login to Azure - CI Subscription
|
- name: Login to Azure - CI Subscription
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ jobs:
|
|||||||
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
|
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
|
||||||
|
|
||||||
- name: Download translations
|
- name: Download translations
|
||||||
uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0
|
uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
||||||
|
|||||||
62
.github/workflows/release.yml
vendored
62
.github/workflows/release.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
branch-name: ${{ steps.branch.outputs.branch-name }}
|
branch-name: ${{ steps.branch.outputs.branch-name }}
|
||||||
steps:
|
steps:
|
||||||
- name: Branch check
|
- name: Branch check
|
||||||
if: inputs.release_type != 'Dry Run'
|
if: github.event.inputs.release_type != 'Dry Run'
|
||||||
run: |
|
run: |
|
||||||
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
|
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
|
||||||
echo "==================================="
|
echo "==================================="
|
||||||
@@ -38,15 +38,15 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||||
|
|
||||||
- name: Check Release Version
|
- name: Check Release Version
|
||||||
id: version
|
id: version
|
||||||
uses: bitwarden/gh-actions/release-version-check@main
|
uses: bitwarden/gh-actions/release-version-check@main
|
||||||
with:
|
with:
|
||||||
release-type: ${{ inputs.release_type }}
|
release-type: ${{ github.event.inputs.release_type }}
|
||||||
project-type: xamarin
|
project-type: xamarin
|
||||||
file: src/App/Platforms/Android/AndroidManifest.xml
|
file: src/Android/Properties/AndroidManifest.xml
|
||||||
|
|
||||||
- name: Get branch name
|
- name: Get branch name
|
||||||
id: branch
|
id: branch
|
||||||
@@ -55,8 +55,8 @@ jobs:
|
|||||||
echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Create GitHub deployment
|
- name: Create GitHub deployment
|
||||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||||
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
|
uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5
|
||||||
id: deployment
|
id: deployment
|
||||||
with:
|
with:
|
||||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
@@ -67,16 +67,16 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||||
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
|
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
|
||||||
with:
|
with:
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
branch: ${{ steps.branch.outputs.branch-name }}
|
branch: ${{ steps.branch.outputs.branch-name }}
|
||||||
|
|
||||||
- name: Dry Run - Download all artifacts
|
- name: Dry Run - Download all artifacts
|
||||||
if: ${{ inputs.release_type == 'Dry Run' }}
|
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
|
||||||
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
|
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
|
||||||
with:
|
with:
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
@@ -86,8 +86,8 @@ jobs:
|
|||||||
run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS
|
run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||||
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0
|
uses: ncipollo/release-action@6c75be85e571768fa31b40abf38de58ba0397db5 # v1.13.0
|
||||||
with:
|
with:
|
||||||
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
|
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
|
||||||
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
|
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
|
||||||
@@ -103,16 +103,16 @@ jobs:
|
|||||||
draft: true
|
draft: true
|
||||||
|
|
||||||
- name: Update deployment status to Success
|
- name: Update deployment status to Success
|
||||||
if: ${{ inputs.release_type != 'Dry Run' && success() }}
|
if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }}
|
||||||
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
|
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1
|
||||||
with:
|
with:
|
||||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
state: 'success'
|
state: 'success'
|
||||||
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
|
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
|
||||||
|
|
||||||
- name: Update deployment status to Failure
|
- name: Update deployment status to Failure
|
||||||
if: ${{ inputs.release_type != 'Dry Run' && failure() }}
|
if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
|
||||||
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
|
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1
|
||||||
with:
|
with:
|
||||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
state: 'failure'
|
state: 'failure'
|
||||||
@@ -126,11 +126,11 @@ jobs:
|
|||||||
if: inputs.fdroid_publish
|
if: inputs.fdroid_publish
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||||
|
|
||||||
- name: Download F-Droid .apk artifact
|
- name: Download F-Droid .apk artifact
|
||||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||||
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
|
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
|
||||||
with:
|
with:
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
@@ -138,8 +138,8 @@ jobs:
|
|||||||
name: com.x8bit.bitwarden-fdroid.apk
|
name: com.x8bit.bitwarden-fdroid.apk
|
||||||
|
|
||||||
- name: Dry Run - Download F-Droid .apk artifact
|
- name: Dry Run - Download F-Droid .apk artifact
|
||||||
if: ${{ inputs.release_type == 'Dry Run' }}
|
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
|
||||||
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
|
uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0
|
||||||
with:
|
with:
|
||||||
workflow: build.yml
|
workflow: build.yml
|
||||||
workflow_conclusion: success
|
workflow_conclusion: success
|
||||||
@@ -147,7 +147,7 @@ jobs:
|
|||||||
name: com.x8bit.bitwarden-fdroid.apk
|
name: com.x8bit.bitwarden-fdroid.apk
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||||
with:
|
with:
|
||||||
node-version: '16.x'
|
node-version: '16.x'
|
||||||
|
|
||||||
@@ -176,19 +176,13 @@ jobs:
|
|||||||
- name: Install Node dependencies
|
- name: Install Node dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
- name: Login to Azure - CI Subscription
|
- name: Decrypt secrets
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
|
||||||
|
|
||||||
- name: Download secrets
|
|
||||||
env:
|
env:
|
||||||
ACCOUNT_NAME: bitwardenci
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
CONTAINER_NAME: mobile
|
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $HOME/secrets
|
mkdir -p ~/secrets
|
||||||
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
--name store_fdroid-keystore.jks --file ./store/fdroid/keystore.jks --output none
|
--output ./store/fdroid/keystore.jks ./.github/secrets/store_fdroid-keystore.jks.gpg
|
||||||
|
|
||||||
- name: Compile for F-Droid Store
|
- name: Compile for F-Droid Store
|
||||||
env:
|
env:
|
||||||
@@ -217,5 +211,5 @@ jobs:
|
|||||||
cd $GITHUB_WORKSPACE
|
cd $GITHUB_WORKSPACE
|
||||||
|
|
||||||
- name: Deploy to gh-pages
|
- name: Deploy to gh-pages
|
||||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||||
run: npm run deploy
|
run: npm run deploy
|
||||||
|
|||||||
2
.github/workflows/stale-bot.yml
vendored
2
.github/workflows/stale-bot.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
|||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- name: 'Run stale action'
|
- name: 'Run stale action'
|
||||||
uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
|
uses: actions/stale@f7176fd3007623b69d27091f9b9d4ab7995f0a06 # v5.2.1
|
||||||
with:
|
with:
|
||||||
stale-issue-label: 'needs-reply'
|
stale-issue-label: 'needs-reply'
|
||||||
stale-pr-label: 'needs-changes'
|
stale-pr-label: 'needs-changes'
|
||||||
|
|||||||
37
.github/workflows/version-auto-bump.yml
vendored
37
.github/workflows/version-auto-bump.yml
vendored
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
name: Auto Bump Mobile Version
|
name: Version Auto Bump
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -7,12 +7,14 @@ on:
|
|||||||
- v**
|
- v**
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
bump-version:
|
setup:
|
||||||
name: Bump Mobile Version
|
name: "Setup"
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
outputs:
|
||||||
|
version_number: ${{ steps.version.outputs.new-version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Branch
|
- name: Checkout Branch
|
||||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||||
|
|
||||||
- name: Calculate bumped version
|
- name: Calculate bumped version
|
||||||
id: version
|
id: version
|
||||||
@@ -27,23 +29,12 @@ jobs:
|
|||||||
NEW_PATCH=$((CURR_PATCH+1))
|
NEW_PATCH=$((CURR_PATCH+1))
|
||||||
NEW_VER=$CURR_MAJOR.$NEW_PATCH
|
NEW_VER=$CURR_MAJOR.$NEW_PATCH
|
||||||
echo "New Version: $NEW_VER"
|
echo "New Version: $NEW_VER"
|
||||||
echo "new_version=$NEW_VER" >> $GITHUB_OUTPUT
|
echo "new-version=$NEW_VER" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Login to Azure - CI Subscription
|
trigger_version_bump:
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
name: Bump version to ${{ needs.setup.outputs.version_number }}
|
||||||
with:
|
needs: setup
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
uses: ./.github/workflows/version-bump.yml
|
||||||
|
with:
|
||||||
- name: Retrieve bot secrets
|
version_number: ${{ needs.setup.outputs.version_number }}
|
||||||
id: retrieve-bot-secrets
|
secrets: inherit
|
||||||
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
|
||||||
with:
|
|
||||||
keyvault: bitwarden-ci
|
|
||||||
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
|
||||||
|
|
||||||
- name: "Bump version to ${{ steps.version.outputs.new_version }}"
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
|
|
||||||
run: |
|
|
||||||
echo '{"cut_rc_branch": "false", "version_number": "${{ steps.version.outputs.new_version }}"}' | \
|
|
||||||
gh workflow run version-bump.yml --json --repo bitwarden/mobile
|
|
||||||
61
.github/workflows/version-bump.yml
vendored
61
.github/workflows/version-bump.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Login to Azure - CI Subscription
|
- name: Login to Azure - CI Subscription
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||||
|
|
||||||
@@ -33,22 +33,13 @@ jobs:
|
|||||||
github-pat-bitwarden-devops-bot-repo-scope"
|
github-pat-bitwarden-devops-bot-repo-scope"
|
||||||
|
|
||||||
- name: Checkout Branch
|
- name: Checkout Branch
|
||||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
repository: bitwarden/mobile
|
||||||
- name: Check if RC branch exists
|
|
||||||
if: ${{ inputs.cut_rc_branch == true }}
|
|
||||||
run: |
|
|
||||||
remote_rc_branch_check=$(git ls-remote --heads origin rc | wc -l)
|
|
||||||
if [[ "${remote_rc_branch_check}" -gt 0 ]]; then
|
|
||||||
echo "Remote RC branch exists."
|
|
||||||
echo "Please delete current RC branch before running again."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Import GPG key
|
- name: Import GPG key
|
||||||
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
|
uses: crazy-max/ghaction-import-gpg@d6f3f49f3345e29369fe57596a3ca8f94c4d2ca7 # v5.4.0
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
|
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
|
||||||
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
|
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
|
||||||
@@ -72,7 +63,7 @@ jobs:
|
|||||||
CURRENT_VERSION=$(xmllint --xpath '
|
CURRENT_VERSION=$(xmllint --xpath '
|
||||||
string(/manifest/@*[local-name()="versionName"
|
string(/manifest/@*[local-name()="versionName"
|
||||||
and namespace-uri()="http://schemas.android.com/apk/res/android"])
|
and namespace-uri()="http://schemas.android.com/apk/res/android"])
|
||||||
' src/App/Platforms/Android/AndroidManifest.xml)
|
' src/Android/Properties/AndroidManifest.xml)
|
||||||
|
|
||||||
# Error if version has not changed.
|
# Error if version has not changed.
|
||||||
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
|
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
|
||||||
@@ -93,7 +84,7 @@ jobs:
|
|||||||
uses: bitwarden/gh-actions/version-bump@main
|
uses: bitwarden/gh-actions/version-bump@main
|
||||||
with:
|
with:
|
||||||
version: ${{ inputs.version_number }}
|
version: ${{ inputs.version_number }}
|
||||||
file_path: "src/App/Platforms/Android/AndroidManifest.xml"
|
file_path: "src/Android/Properties/AndroidManifest.xml"
|
||||||
|
|
||||||
- name: Bump Version - iOS.Autofill
|
- name: Bump Version - iOS.Autofill
|
||||||
uses: bitwarden/gh-actions/version-bump@main
|
uses: bitwarden/gh-actions/version-bump@main
|
||||||
@@ -117,7 +108,7 @@ jobs:
|
|||||||
uses: bitwarden/gh-actions/version-bump@main
|
uses: bitwarden/gh-actions/version-bump@main
|
||||||
with:
|
with:
|
||||||
version: ${{ inputs.version_number }}
|
version: ${{ inputs.version_number }}
|
||||||
file_path: "src/App/Platforms/iOS/Info.plist"
|
file_path: "src/iOS/Info.plist"
|
||||||
|
|
||||||
- name: Setup git
|
- name: Setup git
|
||||||
run: |
|
run: |
|
||||||
@@ -182,38 +173,8 @@ jobs:
|
|||||||
run: gh pr merge $PR_NUMBER --squash --auto --delete-branch
|
run: gh pr merge $PR_NUMBER --squash --auto --delete-branch
|
||||||
|
|
||||||
cut_rc:
|
cut_rc:
|
||||||
name: Cut RC branch
|
name: Cut RC Branch
|
||||||
needs: bump_version
|
|
||||||
if: ${{ inputs.cut_rc_branch == true }}
|
if: ${{ inputs.cut_rc_branch == true }}
|
||||||
runs-on: ubuntu-22.04
|
needs: bump_version
|
||||||
steps:
|
uses: ./.github/workflows/_cut_rc.yml
|
||||||
- name: Checkout Branch
|
secrets: inherit
|
||||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
|
||||||
with:
|
|
||||||
ref: main
|
|
||||||
|
|
||||||
- name: Install xmllint
|
|
||||||
run: sudo apt install -y libxml2-utils
|
|
||||||
|
|
||||||
- name: Verify version has been updated
|
|
||||||
env:
|
|
||||||
NEW_VERSION: ${{ inputs.version_number }}
|
|
||||||
run: |
|
|
||||||
# Wait for version to change.
|
|
||||||
while : ; do
|
|
||||||
echo "Waiting for version to be updated..."
|
|
||||||
git pull --force
|
|
||||||
CURRENT_VERSION=$(xmllint --xpath '
|
|
||||||
string(/manifest/@*[local-name()="versionName"
|
|
||||||
and namespace-uri()="http://schemas.android.com/apk/res/android"])
|
|
||||||
' src/App/Platforms/Android/AndroidManifest.xml)
|
|
||||||
|
|
||||||
# If the versions don't match we continue the loop, otherwise we break out of the loop.
|
|
||||||
[[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] || break
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Cut RC branch
|
|
||||||
run: |
|
|
||||||
git switch --quiet --create rc
|
|
||||||
git push --quiet --set-upstream origin rc
|
|
||||||
|
|||||||
11
.github/workflows/workflow-linter.yml
vendored
Normal file
11
.github/workflows/workflow-linter.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
name: Workflow Linter
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- .github/workflows/**
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-workflow:
|
||||||
|
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@main
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<PropertyGroup>
|
|
||||||
<MauiVersion>8.0.7</MauiVersion>
|
|
||||||
<ReleaseCodesignProvision>Automatic:AppStore</ReleaseCodesignProvision>
|
|
||||||
<ReleaseCodesignKey>iPhone Distribution</ReleaseCodesignKey>
|
|
||||||
<IncludeBitwardeniOSExtensions>True</IncludeBitwardeniOSExtensions>
|
|
||||||
<IncludeBitwardenWatchOSApp>True</IncludeBitwardenWatchOSApp>
|
|
||||||
<Argon2IdLoadMtouchExtraArgs>-gcc_flags "-L$(ProjectDir)../../lib/ios -largon2 -force_load $(ProjectDir)../../lib/ios/libargon2.a"</Argon2IdLoadMtouchExtraArgs>
|
|
||||||
|
|
||||||
<!-- Uncomment this when Unit Testing-->
|
|
||||||
<!-- <CustomConstants>UT</CustomConstants> -->
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
[](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:main)
|
[](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:master)
|
||||||
[](https://crowdin.com/project/bitwarden-mobile)
|
[](https://crowdin.com/project/bitwarden-mobile)
|
||||||
[](https://gitter.im/bitwarden/Lobby)
|
[](https://gitter.im/bitwarden/Lobby)
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<a href="https://play.google.com/store/apps/details?id=com.x8bit.bitwarden" target="_blank"><img alt="Get it on Google Play" src="https://imgur.com/YQzmZi9.png" width="153" height="46"></a> <a href="https://mobileapp.bitwarden.com/fdroid/" target="_blank"><img alt="Get it on F-Droid" src="https://i.imgur.com/HDicnzz.png" width="154" height="46"></a> <a href="https://itunes.apple.com/us/app/bitwarden-free-password-manager/id1137397744?mt=8" target="_blank"><img src="https://imgur.com/GdGqPMY.png" width="135" height="40"></a>
|
<a href="https://play.google.com/store/apps/details?id=com.x8bit.bitwarden" target="_blank"><img alt="Get it on Google Play" src="https://imgur.com/YQzmZi9.png" width="153" height="46"></a> <a href="https://mobileapp.bitwarden.com/fdroid/" target="_blank"><img alt="Get it on F-Droid" src="https://i.imgur.com/HDicnzz.png" width="154" height="46"></a> <a href="https://itunes.apple.com/us/app/bitwarden-free-password-manager/id1137397744?mt=8" target="_blank"><img src="https://imgur.com/GdGqPMY.png" width="135" height="40"></a>
|
||||||
|
|
||||||
The Bitwarden mobile application is written in C# using .NET MAUI.
|
The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin iOS, and Xamarin Forms.
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-android-myvault.png" alt="" width="325" height="650" /> <img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-ios-myvault.png" alt="" width="300" height="650" />
|
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-android-myvault.png" alt="" width="325" height="650" /> <img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-ios-myvault.png" alt="" width="300" height="650" />
|
||||||
|
|
||||||
@@ -20,6 +20,6 @@ Interested in contributing in a big way? Consider joining our team! We're hiring
|
|||||||
|
|
||||||
# Contribute
|
# Contribute
|
||||||
|
|
||||||
Code contributions are welcome! Please commit any pull requests against the `main` branch. Learn more about how to contribute by reading the [Contributing Guidelines](https://contributing.bitwarden.com/contributing/). Check out the [Contributing Documentation](https://contributing.bitwarden.com/) for how to get started with your first contribution.
|
Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [Contributing Guidelines](https://contributing.bitwarden.com/contributing/). Check out the [Contributing Documentation](https://contributing.bitwarden.com/) for how to get started with your first contribution.
|
||||||
|
|
||||||
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file.
|
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ VisualStudioVersion = 17.8.34112.27
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "src\App\App.csproj", "{971FDF07-E288-4239-B47A-E9E7E912193B}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "src\App\App.csproj", "{971FDF07-E288-4239-B47A-E9E7E912193B}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "src\Core\Core.csproj", "{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "iOS.Core", "src\iOS.Core\iOS.Core.csproj", "{E71F3053-056C-4381-9638-048ED73BDFF6}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "iOS.Core", "src\iOS.Core\iOS.Core.csproj", "{E71F3053-056C-4381-9638-048ED73BDFF6}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -15,14 +15,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\i
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{83449CC4-1F76-4CFE-92B1-D2E13A62506F}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{83449CC4-1F76-4CFE-92B1-D2E13A62506F}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{BB702EBD-3B79-4ECA-A2A6-1237B07F0AF0}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "test\Common\Common.csproj", "{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -35,7 +27,6 @@ Global
|
|||||||
AppStore|iPhone = AppStore|iPhone
|
AppStore|iPhone = AppStore|iPhone
|
||||||
Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator
|
Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator
|
||||||
Ad-Hoc|iPhone = Ad-Hoc|iPhone
|
Ad-Hoc|iPhone = Ad-Hoc|iPhone
|
||||||
FDroid|Any CPU = FDroid|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
@@ -60,8 +51,6 @@ Global
|
|||||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
{971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{971FDF07-E288-4239-B47A-E9E7E912193B}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -82,8 +71,6 @@ Global
|
|||||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -104,8 +91,6 @@ Global
|
|||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
{E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -126,8 +111,6 @@ Global
|
|||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -148,8 +131,6 @@ Global
|
|||||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
|
||||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
{F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
|
||||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -162,52 +143,6 @@ Global
|
|||||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhone.Build.0 = Release|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhone.Build.0 = Release|Any CPU
|
||||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhone.ActiveCfg = Release|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhone.Build.0 = Release|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.FDroid|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -220,14 +155,4 @@ Global
|
|||||||
$0.DotNetNamingPolicy = $1
|
$0.DotNetNamingPolicy = $1
|
||||||
$1.DirectoryNamespaceAssociation = PrefixedHierarchical
|
$1.DirectoryNamespaceAssociation = PrefixedHierarchical
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
|
||||||
{971FDF07-E288-4239-B47A-E9E7E912193B} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
|
|
||||||
{11DBC05E-F8B4-49ED-AAC9-96D92336D21C} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
|
|
||||||
{83449CC4-1F76-4CFE-92B1-D2E13A62506F} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
|
|
||||||
{E71F3053-056C-4381-9638-048ED73BDFF6} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
|
|
||||||
{324BE76C-38FA-4F11-8BB1-95C7B3B1B545} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
|
|
||||||
{F8C3F648-EA5A-4719-8005-85D1690B1655} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}
|
|
||||||
{137959BD-073B-4EC7-8ED5-31D73FA7DBC6} = {BB702EBD-3B79-4ECA-A2A6-1237B07F0AF0}
|
|
||||||
{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44} = {BB702EBD-3B79-4ECA-A2A6-1237B07F0AF0}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ project_id_env: _CROWDIN_PROJECT_ID
|
|||||||
api_token_env: CROWDIN_API_TOKEN
|
api_token_env: CROWDIN_API_TOKEN
|
||||||
preserve_hierarchy: true
|
preserve_hierarchy: true
|
||||||
files:
|
files:
|
||||||
- source: /src/Core/Resources/Localization/AppResources.resx
|
- source: /src/App/Resources/AppResources.resx
|
||||||
dest: /src/Core/Resources/Localization/%original_file_name%
|
dest: /src/App/Resources/%original_file_name%
|
||||||
translation: /src/Core/Resources/Localization/AppResources.%two_letters_code%.resx
|
translation: /src/App/Resources/AppResources.%two_letters_code%.resx
|
||||||
update_option: update_as_unapproved
|
update_option: update_as_unapproved
|
||||||
languages_mapping:
|
languages_mapping:
|
||||||
two_letters_code:
|
two_letters_code:
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<configuration>
|
|
||||||
<packageSources>
|
|
||||||
<add key="MAUI Nightly builds" value="https://pkgs.dev.azure.com/xamarin/public/_packaging/maui-nightly/nuget/v3/index.json" />
|
|
||||||
</packageSources>
|
|
||||||
</configuration>
|
|
||||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -8,7 +8,7 @@
|
|||||||
"name": "bitwarden-mobile",
|
"name": "bitwarden-mobile",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gh-pages": "3.2.3"
|
"gh-pages": "^3.2.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/array-union": {
|
"node_modules/array-union": {
|
||||||
|
|||||||
@@ -6,6 +6,6 @@
|
|||||||
"clean:l10n": "git push origin --delete l10n_master"
|
"clean:l10n": "git push origin --delete l10n_master"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gh-pages": "3.2.3"
|
"gh-pages": "^3.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,20 +56,18 @@
|
|||||||
<CodesignKey>iPhone Developer</CodesignKey>
|
<CodesignKey>iPhone Developer</CodesignKey>
|
||||||
<CodesignEntitlements>Platforms\iOS\Entitlements.plist</CodesignEntitlements>
|
<CodesignEntitlements>Platforms\iOS\Entitlements.plist</CodesignEntitlements>
|
||||||
<UseInterpreter>true</UseInterpreter>
|
<UseInterpreter>true</UseInterpreter>
|
||||||
</PropertyGroup>
|
<!--TODO: add argon2id load when library is built with the corresponding architecture for iOS Simulator-->
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(RuntimeIdentifier)'=='Debug|net8.0-ios|iossimulator-x64'">
|
|
||||||
<MtouchExtraArgs>$(Argon2IdLoadMtouchExtraArgs)</MtouchExtraArgs>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(RuntimeIdentifier)'=='Debug|net8.0-ios|ios-arm64'">
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(RuntimeIdentifier)'=='Debug|net8.0-ios|ios-arm64'">
|
||||||
<MtouchExtraArgs>$(Argon2IdLoadMtouchExtraArgs)</MtouchExtraArgs>
|
<MtouchExtraArgs>-gcc_flags "-L$(ProjectDir)../../lib/ios -largon2 -force_load $(ProjectDir)../../lib/ios/libargon2.a"</MtouchExtraArgs>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-ios|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-ios|AnyCPU'">
|
||||||
<CreatePackage>false</CreatePackage>
|
<CreatePackage>false</CreatePackage>
|
||||||
<CodesignProvision>$(ReleaseCodesignProvision)</CodesignProvision>
|
<CodesignProvision>Automatic:AppStore</CodesignProvision>
|
||||||
<CodesignKey>$(ReleaseCodesignKey)</CodesignKey>
|
<CodesignKey>iPhone Distribution</CodesignKey>
|
||||||
<CodesignEntitlements>Platforms\iOS\Entitlements.plist</CodesignEntitlements>
|
<CodesignEntitlements>Platforms\iOS\Entitlements.plist</CodesignEntitlements>
|
||||||
<UseInterpreter>true</UseInterpreter>
|
<UseInterpreter>true</UseInterpreter>
|
||||||
<MtouchExtraArgs>$(Argon2IdLoadMtouchExtraArgs)</MtouchExtraArgs>
|
<MtouchExtraArgs>-gcc_flags "-L$(ProjectDir)../../lib/ios -largon2 -force_load $(ProjectDir)../../lib/ios/libargon2.a"</MtouchExtraArgs>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
|
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
|
||||||
<!--This is needed for PCLCrypto to work correctly-->
|
<!--This is needed for PCLCrypto to work correctly-->
|
||||||
@@ -157,15 +155,6 @@
|
|||||||
<BundleResource Include="Platforms\Android\Resources\drawable-hdpi\logo_white_legacy.png" />
|
<BundleResource Include="Platforms\Android\Resources\drawable-hdpi\logo_white_legacy.png" />
|
||||||
<BundleResource Include="Platforms\Android\Resources\mipmap-xhdpi\ic_launcher.png" />
|
<BundleResource Include="Platforms\Android\Resources\mipmap-xhdpi\ic_launcher.png" />
|
||||||
<BundleResource Include="Platforms\Android\Resources\mipmap-xhdpi\ic_launcher_round.png" />
|
<BundleResource Include="Platforms\Android\Resources\mipmap-xhdpi\ic_launcher_round.png" />
|
||||||
<BundleResource Include="Platforms\iOS\Resources\logo.png" />
|
|
||||||
<BundleResource Include="Platforms\iOS\Resources\logo_white%402x.png" />
|
|
||||||
<BundleResource Include="Platforms\iOS\Resources\more_vert%402x.png" />
|
|
||||||
<BundleResource Include="Platforms\iOS\Resources\logo_white%403x.png" />
|
|
||||||
<BundleResource Include="Platforms\iOS\Resources\logo%403x.png" />
|
|
||||||
<BundleResource Include="Platforms\iOS\Resources\more_vert%403x.png" />
|
|
||||||
<BundleResource Include="Platforms\iOS\Resources\more_vert.png" />
|
|
||||||
<BundleResource Include="Platforms\iOS\Resources\logo_white.png" />
|
|
||||||
<BundleResource Include="Platforms\iOS\Resources\logo%402x.png" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\iOS.Core\iOS.Core.csproj" Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'" />
|
<ProjectReference Include="..\iOS.Core\iOS.Core.csproj" Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'" />
|
||||||
@@ -205,12 +194,15 @@
|
|||||||
<MauiImage Include="Resources\plus.svg" TintColor="#FFFFFFFF">
|
<MauiImage Include="Resources\plus.svg" TintColor="#FFFFFFFF">
|
||||||
<BaseSize>24,24</BaseSize>
|
<BaseSize>24,24</BaseSize>
|
||||||
</MauiImage>
|
</MauiImage>
|
||||||
|
<MauiImage Include="Resources\search.svg" TintColor="#FFFFFFFF">
|
||||||
|
<BaseSize>24,24</BaseSize>
|
||||||
|
</MauiImage>
|
||||||
<MauiImage Include="Resources\send.svg">
|
<MauiImage Include="Resources\send.svg">
|
||||||
<BaseSize>24,24</BaseSize>
|
<BaseSize>24,24</BaseSize>
|
||||||
</MauiImage>
|
</MauiImage>
|
||||||
<MauiImage Include="Resources\yubikey.png" />
|
<MauiImage Include="Resources\yubikey.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios' AND '$(IncludeBitwardeniOSExtensions)' == 'True'">
|
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
|
||||||
<ProjectReference Include="..\iOS.Autofill\iOS.Autofill.csproj">
|
<ProjectReference Include="..\iOS.Autofill\iOS.Autofill.csproj">
|
||||||
<IsAppExtension>true</IsAppExtension>
|
<IsAppExtension>true</IsAppExtension>
|
||||||
<IsWatchApp>false</IsWatchApp>
|
<IsWatchApp>false</IsWatchApp>
|
||||||
@@ -224,15 +216,15 @@
|
|||||||
<IsWatchApp>false</IsWatchApp>
|
<IsWatchApp>false</IsWatchApp>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Condition="'$(TargetFramework)'=='net8.0-ios' AND '$(IncludeBitwardenWatchOSApp)' == 'True'">
|
<PropertyGroup Condition="'$(TargetFramework)'=='net8.0-ios'">
|
||||||
<WatchAppBuildPath Condition=" '$(Configuration)' == 'Debug' ">$(Home)/Library/Developer/Xcode/DerivedData/bitwarden-acgkbpwvmebfiofokotvoerzkqcl/Build/Products</WatchAppBuildPath>
|
<WatchAppBuildPath Condition=" '$(Configuration)' == 'Debug' ">$(Home)/Library/Developer/Xcode/DerivedData/bitwarden-acgkbpwvmebfiofokotvoerzkqcl/Build/Products</WatchAppBuildPath>
|
||||||
<WatchAppBuildPath Condition=" '$(Configuration)' != 'Debug' ">$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\..'))/watchOS/bitwarden.xcarchive/Products/Applications/bitwarden.app/Watch</WatchAppBuildPath>
|
<WatchAppBuildPath Condition=" '$(Configuration)' != 'Debug' ">$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\..'))/watchOS/bitwarden.xcarchive/Products/Applications/bitwarden.app/Watch</WatchAppBuildPath>
|
||||||
<WatchAppBundle>Bitwarden.app</WatchAppBundle>
|
<WatchAppBundle>Bitwarden.app</WatchAppBundle>
|
||||||
<WatchAppConfiguration Condition="'$(RuntimeIdentifier)'!='ios-arm64'">watchsimulator</WatchAppConfiguration>
|
<WatchAppConfiguration Condition="'$(RuntimeIdentifier)'!='ios-arm64'">watchsimulator</WatchAppConfiguration>
|
||||||
<WatchAppConfiguration Condition="'$(RuntimeIdentifier)'=='ios-arm64'">watchos</WatchAppConfiguration>
|
<WatchAppConfiguration Condition="'$(RuntimeIdentifier)'=='ios-arm64'">watchos</WatchAppConfiguration>
|
||||||
<WatchAppBundleFullPath Condition=" '$(Configuration)' == 'Debug' ">$(WatchAppBuildPath)/$(Configuration)-$(WatchAppConfiguration)/$(WatchAppBundle)</WatchAppBundleFullPath>
|
<WatchAppBundleFullPath Condition=" '$(Configuration)' == 'Debug' ">$(WatchAppBuildPath)/$(Configuration)-$(WatchAppConfiguration)/$(WatchAppBundle)</WatchAppBundleFullPath>
|
||||||
<WatchAppBundleFullPath Condition=" '$(Configuration)' != 'Debug' ">$(WatchAppBuildPath)/$(WatchAppBundle)</WatchAppBundleFullPath>
|
<WatchAppBundleFullPath Condition=" '$(Configuration)' != 'Debug' ">$(WatchAppBuildPath)/$(WatchAppBundle)</WatchAppBundleFullPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='net8.0-ios' AND Exists('$(WatchAppBundleFullPath)') ">
|
<ItemGroup Condition="'$(TargetFramework)'=='net8.0-ios' AND Exists('$(WatchAppBundleFullPath)') ">
|
||||||
<_ResolvedWatchAppReferences Include="$(WatchAppBundleFullPath)" />
|
<_ResolvedWatchAppReferences Include="$(WatchAppBundleFullPath)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -246,15 +238,4 @@
|
|||||||
<GoogleServicesJson Include="Platforms\Android\google-services.json" />
|
<GoogleServicesJson Include="Platforms\Android\google-services.json" />
|
||||||
<GoogleServicesJson Include="Platforms\Android\google-services.json.enc" />
|
<GoogleServicesJson Include="Platforms\Android\google-services.json.enc" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="Platforms\iOS\Resources\logo.png" />
|
|
||||||
<None Remove="Platforms\iOS\Resources\logo_white%402x.png" />
|
|
||||||
<None Remove="Platforms\iOS\Resources\more_vert%402x.png" />
|
|
||||||
<None Remove="Platforms\iOS\Resources\logo_white%403x.png" />
|
|
||||||
<None Remove="Platforms\iOS\Resources\logo%403x.png" />
|
|
||||||
<None Remove="Platforms\iOS\Resources\more_vert%403x.png" />
|
|
||||||
<None Remove="Platforms\iOS\Resources\more_vert.png" />
|
|
||||||
<None Remove="Platforms\iOS\Resources\logo_white.png" />
|
|
||||||
<None Remove="Platforms\iOS\Resources\logo%402x.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
25
src/App/Handlers/HybridWebViewHandler.cs
Normal file
25
src/App/Handlers/HybridWebViewHandler.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#if IOS || MACCATALYST
|
||||||
|
using PlatformView = WebKit.WKWebView;
|
||||||
|
#elif ANDROID
|
||||||
|
using PlatformView = Android.Webkit.WebView;
|
||||||
|
#elif (NETSTANDARD || !PLATFORM) || (NET6_0_OR_GREATER && !IOS && !ANDROID)
|
||||||
|
using PlatformView = System.Object;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using Bit.App.Controls;
|
||||||
|
using Microsoft.Maui.Handlers;
|
||||||
|
|
||||||
|
namespace Bit.App.Handlers
|
||||||
|
{
|
||||||
|
public partial class HybridWebViewHandler
|
||||||
|
{
|
||||||
|
public static PropertyMapper<HybridWebView, HybridWebViewHandler> PropertyMapper = new PropertyMapper<HybridWebView, HybridWebViewHandler>(ViewHandler.ViewMapper)
|
||||||
|
{
|
||||||
|
[nameof(HybridWebView.Uri)] = MapUri
|
||||||
|
};
|
||||||
|
|
||||||
|
public HybridWebViewHandler() : base(PropertyMapper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
},
|
},
|
||||||
handlers =>
|
handlers =>
|
||||||
{
|
{
|
||||||
|
handlers.AddHandler(typeof(Bit.App.Controls.HybridWebView), typeof(Bit.App.Handlers.HybridWebViewHandler));
|
||||||
#if ANDROID
|
#if ANDROID
|
||||||
Bit.App.Handlers.EntryHandlerMappings.Setup();
|
Bit.App.Handlers.EntryHandlerMappings.Setup();
|
||||||
Bit.App.Handlers.EditorHandlerMappings.Setup();
|
Bit.App.Handlers.EditorHandlerMappings.Setup();
|
||||||
@@ -27,7 +28,6 @@
|
|||||||
Bit.App.Handlers.ButtonHandlerMappings.Setup();
|
Bit.App.Handlers.ButtonHandlerMappings.Setup();
|
||||||
Bit.App.Handlers.ToolbarHandlerMappings.Setup();
|
Bit.App.Handlers.ToolbarHandlerMappings.Setup();
|
||||||
|
|
||||||
handlers.AddHandler(typeof(Bit.App.Controls.HybridWebView), typeof(Bit.App.Handlers.HybridWebViewHandler));
|
|
||||||
handlers.AddHandler(typeof(Bit.App.Pages.TabsPage), typeof(Bit.App.Handlers.CustomTabbedPageHandler));
|
handlers.AddHandler(typeof(Bit.App.Pages.TabsPage), typeof(Bit.App.Handlers.CustomTabbedPageHandler));
|
||||||
handlers.AddHandler(typeof(Bit.App.Controls.ExtendedDatePicker), typeof(Bit.App.Handlers.ExtendedDatePickerHandler));
|
handlers.AddHandler(typeof(Bit.App.Controls.ExtendedDatePicker), typeof(Bit.App.Handlers.ExtendedDatePickerHandler));
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2024.3.0" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.12.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
@@ -43,9 +43,6 @@
|
|||||||
<!-- Support for Xamarin.Essentials.Browser.OpenAsync (for Android > 11) -->
|
<!-- Support for Xamarin.Essentials.Browser.OpenAsync (for Android > 11) -->
|
||||||
<!-- Related docs: https://learn.microsoft.com/en-us/xamarin/essentials/open-browser?tabs=android -->
|
<!-- Related docs: https://learn.microsoft.com/en-us/xamarin/essentials/open-browser?tabs=android -->
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
|
||||||
<action android:name="android.support.customtabs.action.CustomTabsService" />
|
|
||||||
</intent>
|
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<data android:scheme="http" />
|
<data android:scheme="http" />
|
||||||
|
|||||||
@@ -347,7 +347,7 @@ namespace Bit.Droid.Autofill
|
|||||||
// InlinePresentation requires nonNull pending intent (even though we only utilize one for the
|
// InlinePresentation requires nonNull pending intent (even though we only utilize one for the
|
||||||
// "my vault" presentation) so we're including an empty one here
|
// "my vault" presentation) so we're including an empty one here
|
||||||
pendingIntent = PendingIntent.GetService(context, 0, new Intent(),
|
pendingIntent = PendingIntent.GetService(context, 0, new Intent(),
|
||||||
AndroidHelpers.AddPendingIntentMutabilityFlag(PendingIntentFlags.OneShot | PendingIntentFlags.UpdateCurrent, false));
|
AndroidHelpers.AddPendingIntentMutabilityFlag(PendingIntentFlags.OneShot | PendingIntentFlags.UpdateCurrent, true));
|
||||||
}
|
}
|
||||||
var slice = CreateInlinePresentationSlice(
|
var slice = CreateInlinePresentationSlice(
|
||||||
inlinePresentationSpec,
|
inlinePresentationSpec,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using AndroidX.AppCompat.View.Menu;
|
using AndroidX.AppCompat.View.Menu;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Google.Android.Material.BottomNavigation;
|
using Google.Android.Material.BottomNavigation;
|
||||||
using Microsoft.Maui.Handlers;
|
using Microsoft.Maui.Handlers;
|
||||||
@@ -91,17 +90,7 @@ namespace Bit.App.Handlers
|
|||||||
if(e.Item is MenuItemImpl item)
|
if(e.Item is MenuItemImpl item)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine($"Tab '{item.Title}' was reselected so we'll PopToRoot.");
|
System.Diagnostics.Debug.WriteLine($"Tab '{item.Title}' was reselected so we'll PopToRoot.");
|
||||||
MainThread.BeginInvokeOnMainThread(async () =>
|
MainThread.BeginInvokeOnMainThread(async () => await _tabbedPage.CurrentPage.Navigation.PopToRootAsync());
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _tabbedPage.CurrentPage.Navigation.PopToRootAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,19 +6,10 @@ using AWebkit = Android.Webkit;
|
|||||||
|
|
||||||
namespace Bit.App.Handlers
|
namespace Bit.App.Handlers
|
||||||
{
|
{
|
||||||
public class HybridWebViewHandler : ViewHandler<HybridWebView, AWebkit.WebView>
|
public partial class HybridWebViewHandler : ViewHandler<HybridWebView, AWebkit.WebView>
|
||||||
{
|
{
|
||||||
private const string JSFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
|
private const string JSFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
|
||||||
|
|
||||||
public static PropertyMapper<HybridWebView, HybridWebViewHandler> PropertyMapper = new PropertyMapper<HybridWebView, HybridWebViewHandler>(ViewHandler.ViewMapper)
|
|
||||||
{
|
|
||||||
[nameof(HybridWebView.Uri)] = MapUri
|
|
||||||
};
|
|
||||||
|
|
||||||
public HybridWebViewHandler() : base(PropertyMapper)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public HybridWebViewHandler([NotNull] IPropertyMapper mapper, CommandMapper commandMapper = null) : base(mapper, commandMapper)
|
public HybridWebViewHandler([NotNull] IPropertyMapper mapper, CommandMapper commandMapper = null) : base(mapper, commandMapper)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,7 @@
|
|||||||
using Android.Graphics.Drawables;
|
using Android.Graphics.Drawables;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using AndroidX.Core.Content.Resources;
|
using AndroidX.Core.Content.Resources;
|
||||||
using AndroidX.Core.Graphics;
|
|
||||||
using Bit.App.Droid.Utilities;
|
using Bit.App.Droid.Utilities;
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Microsoft.Maui.Platform;
|
|
||||||
|
|
||||||
namespace Bit.App.Handlers
|
namespace Bit.App.Handlers
|
||||||
{
|
{
|
||||||
@@ -40,31 +37,6 @@ namespace Bit.App.Handlers
|
|||||||
};
|
};
|
||||||
handler.PlatformView.ThumbTintList = new ColorStateList(thumbStates, thumbColors);
|
handler.PlatformView.ThumbTintList = new ColorStateList(thumbStates, thumbColors);
|
||||||
});
|
});
|
||||||
|
|
||||||
Microsoft.Maui.Handlers.SwitchHandler.Mapper.AppendToMapping(nameof(ISwitch.TrackColor), (handler, mauiSwitch) =>
|
|
||||||
{
|
|
||||||
var trackStates = new[]
|
|
||||||
{
|
|
||||||
new[] { Android.Resource.Attribute.StateChecked }, // checked
|
|
||||||
new[] { -Android.Resource.Attribute.StateChecked }, // unchecked
|
|
||||||
};
|
|
||||||
|
|
||||||
var selectedColor = ColorUtils.BlendARGB(ThemeHelpers.SwitchOnColor.ToArgb(), Colors.Black.ToPlatform().ToArgb(), 0.5f);
|
|
||||||
var unselectedColor = ColorUtils.BlendARGB(ThemeHelpers.SwitchThumbColor.ToArgb(), Colors.Black.ToPlatform().ToArgb(), 0.7f);
|
|
||||||
if (ThemeManager.UsingLightTheme)
|
|
||||||
{
|
|
||||||
selectedColor = ColorUtils.BlendARGB(ThemeHelpers.SwitchOnColor.ToArgb(), Colors.White.ToPlatform().ToArgb(), 0.7f);
|
|
||||||
unselectedColor = ColorUtils.BlendARGB(ThemeHelpers.SwitchThumbColor.ToArgb(), Colors.Black.ToPlatform().ToArgb(), 0.3f);
|
|
||||||
}
|
|
||||||
|
|
||||||
var trackColors = new int[]
|
|
||||||
{
|
|
||||||
selectedColor,
|
|
||||||
unselectedColor
|
|
||||||
};
|
|
||||||
|
|
||||||
handler.PlatformView.TrackTintList = new ColorStateList(trackStates, trackColors);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,11 +74,6 @@ namespace Bit.Droid
|
|||||||
// this needs to be called here before base.OnCreate(...)
|
// this needs to be called here before base.OnCreate(...)
|
||||||
Intent?.Validate();
|
Intent?.Validate();
|
||||||
|
|
||||||
//We need to get and set the Options before calling OnCreate as that will "trigger" CreateWindow on App.xaml.cs
|
|
||||||
_appOptions = GetOptions();
|
|
||||||
//This does not replace existing Options in App.xaml.cs if it exists already. It only updates properties in Options related with Autofill/CreateSend/etc..
|
|
||||||
((Bit.App.App)Microsoft.Maui.Controls.Application.Current).SetAndroidOptions(_appOptions);
|
|
||||||
|
|
||||||
base.OnCreate(savedInstanceState);
|
base.OnCreate(savedInstanceState);
|
||||||
|
|
||||||
_deviceActionService.SetScreenCaptureAllowedAsync().FireAndForget(_ =>
|
_deviceActionService.SetScreenCaptureAllowedAsync().FireAndForget(_ =>
|
||||||
@@ -94,6 +89,7 @@ namespace Bit.Droid
|
|||||||
toplayout.FilterTouchesWhenObscured = true;
|
toplayout.FilterTouchesWhenObscured = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_appOptions = GetOptions();
|
||||||
CreateNotificationChannel();
|
CreateNotificationChannel();
|
||||||
DisableAndroidFontScale();
|
DisableAndroidFontScale();
|
||||||
|
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ namespace Bit.Droid
|
|||||||
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService,
|
var autofillHandler = new AutofillHandler(stateService, messagingService, clipboardService,
|
||||||
platformUtilsService, new LazyResolve<IEventService>());
|
platformUtilsService, new LazyResolve<IEventService>());
|
||||||
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService);
|
||||||
var cryptoService = new CryptoService(stateService, cryptoFunctionService, logger);
|
var cryptoService = new CryptoService(stateService, cryptoFunctionService);
|
||||||
var biometricService = new BiometricService(stateService, cryptoService);
|
var biometricService = new BiometricService(stateService, cryptoService);
|
||||||
var userPinService = new UserPinService(stateService, cryptoService);
|
var userPinService = new UserPinService(stateService, cryptoService);
|
||||||
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService, stateService);
|
var passwordRepromptService = new MobilePasswordRepromptService(platformUtilsService, cryptoService, stateService);
|
||||||
|
|||||||
@@ -79,29 +79,24 @@ namespace Bit.Droid.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
var context = Android.App.Application.Context;
|
var context = Android.App.Application.Context;
|
||||||
var intent = context.PackageManager?.GetLaunchIntentForPackage(context.PackageName ?? string.Empty);
|
var intent = new Intent(context, typeof(MainActivity));
|
||||||
|
intent.PutExtra(Bit.Core.Constants.NotificationData, JsonConvert.SerializeObject(data));
|
||||||
|
var pendingIntentFlags = AndroidHelpers.AddPendingIntentMutabilityFlag(PendingIntentFlags.UpdateCurrent, true);
|
||||||
|
var pendingIntent = PendingIntent.GetActivity(context, 20220801, intent, pendingIntentFlags);
|
||||||
|
|
||||||
var builder = new NotificationCompat.Builder(context, Bit.Core.Constants.AndroidNotificationChannelId);
|
var deleteIntent = new Intent(context, typeof(NotificationDismissReceiver));
|
||||||
if(intent != null && context.PackageManager != null && !string.IsNullOrEmpty(context.PackageName))
|
deleteIntent.PutExtra(Bit.Core.Constants.NotificationData, JsonConvert.SerializeObject(data));
|
||||||
{
|
var deletePendingIntent = PendingIntent.GetBroadcast(context, 20220802, deleteIntent, pendingIntentFlags);
|
||||||
intent.PutExtra(Bit.Core.Constants.NotificationData, JsonConvert.SerializeObject(data));
|
|
||||||
var pendingIntentFlags = AndroidHelpers.AddPendingIntentMutabilityFlag(PendingIntentFlags.UpdateCurrent, true);
|
|
||||||
var pendingIntent = PendingIntent.GetActivity(context, 20220801, intent, pendingIntentFlags);
|
|
||||||
|
|
||||||
var deleteIntent = new Intent(context, typeof(NotificationDismissReceiver));
|
var builder = new NotificationCompat.Builder(context, Bit.Core.Constants.AndroidNotificationChannelId)
|
||||||
deleteIntent.PutExtra(Bit.Core.Constants.NotificationData, JsonConvert.SerializeObject(data));
|
.SetContentIntent(pendingIntent)
|
||||||
var deletePendingIntent = PendingIntent.GetBroadcast(context, 20220802, deleteIntent, pendingIntentFlags);
|
.SetContentTitle(title)
|
||||||
|
|
||||||
builder.SetContentIntent(pendingIntent)
|
|
||||||
.SetDeleteIntent(deletePendingIntent);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.SetContentTitle(title)
|
|
||||||
.SetContentText(message)
|
.SetContentText(message)
|
||||||
.SetSmallIcon(Bit.Core.Resource.Drawable.ic_notification)
|
.SetSmallIcon(Bit.Core.Resource.Drawable.ic_notification)
|
||||||
.SetColor((int)Android.Graphics.Color.White)
|
.SetColor((int)Android.Graphics.Color.White)
|
||||||
|
.SetDeleteIntent(deletePendingIntent)
|
||||||
.SetAutoCancel(true);
|
.SetAutoCancel(true);
|
||||||
|
|
||||||
if (data is PasswordlessNotificationData passwordlessNotificationData && passwordlessNotificationData.TimeoutInMinutes > 0)
|
if (data is PasswordlessNotificationData passwordlessNotificationData && passwordlessNotificationData.TimeoutInMinutes > 0)
|
||||||
{
|
{
|
||||||
builder.SetTimeoutAfter(passwordlessNotificationData.TimeoutInMinutes * 60000);
|
builder.SetTimeoutAfter(passwordlessNotificationData.TimeoutInMinutes * 60000);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ using Bit.Core.Abstractions;
|
|||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Droid.Accessibility;
|
using Bit.Droid.Accessibility;
|
||||||
using Java.Lang;
|
using Java.Lang;
|
||||||
using Bit.App.Droid.Utilities;
|
|
||||||
|
|
||||||
namespace Bit.Droid.Tile
|
namespace Bit.Droid.Tile
|
||||||
{
|
{
|
||||||
@@ -77,7 +76,7 @@ namespace Bit.Droid.Tile
|
|||||||
var intent = new Intent(this, typeof(AccessibilityActivity));
|
var intent = new Intent(this, typeof(AccessibilityActivity));
|
||||||
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
||||||
intent.PutExtra("autofillTileClicked", true);
|
intent.PutExtra("autofillTileClicked", true);
|
||||||
this.StartActivityAndCollapseWithIntent(intent, isMutable: true);
|
StartActivityAndCollapse(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowConfigErrorDialog()
|
private void ShowConfigErrorDialog()
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
using Android.App;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
|
using Android.OS;
|
||||||
using Android.Runtime;
|
using Android.Runtime;
|
||||||
using Android.Service.QuickSettings;
|
using Android.Service.QuickSettings;
|
||||||
using Bit.App.Droid.Utilities;
|
using Android.Views;
|
||||||
|
using Android.Widget;
|
||||||
using Java.Lang;
|
using Java.Lang;
|
||||||
|
|
||||||
namespace Bit.Droid.Tile
|
namespace Bit.Droid.Tile
|
||||||
@@ -55,7 +62,7 @@ namespace Bit.Droid.Tile
|
|||||||
var intent = new Intent(this, typeof(MainActivity));
|
var intent = new Intent(this, typeof(MainActivity));
|
||||||
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
||||||
intent.PutExtra("generatorTile", true);
|
intent.PutExtra("generatorTile", true);
|
||||||
this.StartActivityAndCollapseWithIntent(intent, isMutable: false);
|
StartActivityAndCollapse(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
using Android.App;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using Android.App;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
|
using Android.OS;
|
||||||
using Android.Runtime;
|
using Android.Runtime;
|
||||||
using Android.Service.QuickSettings;
|
using Android.Service.QuickSettings;
|
||||||
using Bit.App.Droid.Utilities;
|
using Android.Views;
|
||||||
|
using Android.Widget;
|
||||||
using Java.Lang;
|
using Java.Lang;
|
||||||
|
|
||||||
namespace Bit.Droid.Tile
|
namespace Bit.Droid.Tile
|
||||||
@@ -56,7 +63,7 @@ namespace Bit.Droid.Tile
|
|||||||
var intent = new Intent(this, typeof(MainActivity));
|
var intent = new Intent(this, typeof(MainActivity));
|
||||||
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
|
||||||
intent.PutExtra("myVaultTile", true);
|
intent.PutExtra("myVaultTile", true);
|
||||||
this.StartActivityAndCollapseWithIntent(intent, isMutable: false);
|
StartActivityAndCollapse(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Provider;
|
using Android.Provider;
|
||||||
using Android.Service.QuickSettings;
|
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
|
|
||||||
namespace Bit.App.Droid.Utilities
|
namespace Bit.App.Droid.Utilities
|
||||||
@@ -65,26 +64,5 @@ namespace Bit.App.Droid.Utilities
|
|||||||
|
|
||||||
return pendingIntentFlags;
|
return pendingIntentFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void StartActivityAndCollapseWithIntent(this TileService service, Intent intent, bool isMutable)
|
|
||||||
{
|
|
||||||
//For Android 14+ We need to use PendingIntent instead of Intent directly. Older versions still need to use Intent.
|
|
||||||
if (Build.VERSION.SdkInt < BuildVersionCodes.UpsideDownCake)
|
|
||||||
{
|
|
||||||
service.StartActivityAndCollapse(intent);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var pendingIntent = PendingIntent.GetActivity(
|
|
||||||
service.ApplicationContext,
|
|
||||||
0,
|
|
||||||
intent,
|
|
||||||
AddPendingIntentMutabilityFlag(PendingIntentFlags.UpdateCurrent, isMutable)
|
|
||||||
);
|
|
||||||
if (pendingIntent == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
service.StartActivityAndCollapse(pendingIntent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ using CoreNFC;
|
|||||||
using Foundation;
|
using Foundation;
|
||||||
using Microsoft.Maui.Platform;
|
using Microsoft.Maui.Platform;
|
||||||
using UIKit;
|
using UIKit;
|
||||||
using UserNotifications;
|
|
||||||
using WatchConnectivity;
|
using WatchConnectivity;
|
||||||
|
|
||||||
namespace Bit.iOS
|
namespace Bit.iOS
|
||||||
@@ -42,78 +41,77 @@ namespace Bit.iOS
|
|||||||
private IStateService _stateService;
|
private IStateService _stateService;
|
||||||
private IEventService _eventService;
|
private IEventService _eventService;
|
||||||
|
|
||||||
private readonly LazyResolve<IDeepLinkContext> _deepLinkContext = new LazyResolve<IDeepLinkContext>();
|
private LazyResolve<IDeepLinkContext> _deepLinkContext = new LazyResolve<IDeepLinkContext>();
|
||||||
|
|
||||||
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
|
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
|
||||||
{
|
{
|
||||||
try
|
InitApp();
|
||||||
|
|
||||||
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
|
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
||||||
|
|
||||||
|
//LoadApplication(new App.App(null));
|
||||||
|
//iOSCoreHelpers.AppearanceAdjustments();
|
||||||
|
//ZXing.Net.Mobile.Forms.iOS.Platform.Init();
|
||||||
|
|
||||||
|
ConnectToWatchIfNeededAsync().FireAndForget();
|
||||||
|
|
||||||
|
_broadcasterService.Subscribe(nameof(AppDelegate), async (message) =>
|
||||||
{
|
{
|
||||||
InitApp();
|
try
|
||||||
|
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
|
||||||
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
|
||||||
|
|
||||||
ConnectToWatchIfNeededAsync().FireAndForget();
|
|
||||||
|
|
||||||
_broadcasterService.Subscribe(nameof(AppDelegate), async (message) =>
|
|
||||||
{
|
{
|
||||||
try
|
if (message.Command == "startEventTimer")
|
||||||
{
|
{
|
||||||
if (message.Command == "startEventTimer")
|
StartEventTimer();
|
||||||
|
}
|
||||||
|
else if (message.Command == "stopEventTimer")
|
||||||
|
{
|
||||||
|
var task = StopEventTimerAsync();
|
||||||
|
}
|
||||||
|
else if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY)
|
||||||
|
{
|
||||||
|
MainThread.BeginInvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
StartEventTimer();
|
iOSCoreHelpers.AppearanceAdjustments();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (message.Command == "listenYubiKeyOTP")
|
||||||
|
{
|
||||||
|
iOSCoreHelpers.ListenYubiKey((bool)message.Data, _deviceActionService, _nfcSession, _nfcDelegate);
|
||||||
|
}
|
||||||
|
else if (message.Command == "unlocked")
|
||||||
|
{
|
||||||
|
var needsAutofillReplacement = await _storageService.GetAsync<bool?>(
|
||||||
|
Core.Constants.AutofillNeedsIdentityReplacementKey);
|
||||||
|
if (needsAutofillReplacement.GetValueOrDefault())
|
||||||
|
{
|
||||||
|
await ASHelpers.ReplaceAllIdentities();
|
||||||
}
|
}
|
||||||
else if (message.Command == "stopEventTimer")
|
}
|
||||||
|
else if (message.Command == "showAppExtension")
|
||||||
|
{
|
||||||
|
MainThread.BeginInvokeOnMainThread(() => ShowAppExtension((ExtensionPageViewModel)message.Data));
|
||||||
|
}
|
||||||
|
else if (message.Command == "syncCompleted")
|
||||||
|
{
|
||||||
|
if (message.Data is Dictionary<string, object> data && data.ContainsKey("successfully"))
|
||||||
{
|
{
|
||||||
var task = StopEventTimerAsync();
|
var success = data["successfully"] as bool?;
|
||||||
}
|
if (success.GetValueOrDefault() && _deviceActionService.SystemMajorVersion() >= 12)
|
||||||
else if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY)
|
|
||||||
{
|
|
||||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
|
||||||
{
|
|
||||||
iOSCoreHelpers.AppearanceAdjustments();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (message.Command == "listenYubiKeyOTP" && message.Data is bool listen)
|
|
||||||
{
|
|
||||||
iOSCoreHelpers.ListenYubiKey(listen, _deviceActionService, _nfcSession, _nfcDelegate);
|
|
||||||
}
|
|
||||||
else if (message.Command == "unlocked")
|
|
||||||
{
|
|
||||||
var needsAutofillReplacement = await _storageService.GetAsync<bool?>(
|
|
||||||
Core.Constants.AutofillNeedsIdentityReplacementKey);
|
|
||||||
if (needsAutofillReplacement.GetValueOrDefault())
|
|
||||||
{
|
{
|
||||||
await ASHelpers.ReplaceAllIdentities();
|
await ASHelpers.ReplaceAllIdentities();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (message.Command == "showAppExtension")
|
}
|
||||||
|
else if (message.Command == "addedCipher" || message.Command == "editedCipher" ||
|
||||||
|
message.Command == "restoredCipher")
|
||||||
|
{
|
||||||
|
if (_deviceActionService.SystemMajorVersion() >= 12)
|
||||||
{
|
{
|
||||||
await MainThread.InvokeOnMainThreadAsync(() => ShowAppExtension((ExtensionPageViewModel)message.Data));
|
|
||||||
}
|
|
||||||
else if (message.Command == "syncCompleted")
|
|
||||||
{
|
|
||||||
if (message.Data is Dictionary<string, object> data && data.TryGetValue("successfully", out var value))
|
|
||||||
{
|
|
||||||
var success = value as bool?;
|
|
||||||
if (success.GetValueOrDefault() && _deviceActionService.SystemMajorVersion() >= 12)
|
|
||||||
{
|
|
||||||
await ASHelpers.ReplaceAllIdentities();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (message.Command == "addedCipher" || message.Command == "editedCipher" ||
|
|
||||||
message.Command == "restoredCipher")
|
|
||||||
{
|
|
||||||
if (!UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await ASHelpers.IdentitiesCanIncremental())
|
if (await ASHelpers.IdentitiesCanIncremental())
|
||||||
{
|
{
|
||||||
var cipherId = message.Data as string;
|
var cipherId = message.Data as string;
|
||||||
@@ -131,13 +129,11 @@ namespace Bit.iOS
|
|||||||
}
|
}
|
||||||
await ASHelpers.ReplaceAllIdentities();
|
await ASHelpers.ReplaceAllIdentities();
|
||||||
}
|
}
|
||||||
else if (message.Command == "deletedCipher" || message.Command == "softDeletedCipher")
|
}
|
||||||
|
else if (message.Command == "deletedCipher" || message.Command == "softDeletedCipher")
|
||||||
|
{
|
||||||
|
if (_deviceActionService.SystemMajorVersion() >= 12)
|
||||||
{
|
{
|
||||||
if (!UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await ASHelpers.IdentitiesCanIncremental())
|
if (await ASHelpers.IdentitiesCanIncremental())
|
||||||
{
|
{
|
||||||
var identity = ASHelpers.ToCredentialIdentity(
|
var identity = ASHelpers.ToCredentialIdentity(
|
||||||
@@ -152,145 +148,97 @@ namespace Bit.iOS
|
|||||||
}
|
}
|
||||||
await ASHelpers.ReplaceAllIdentities();
|
await ASHelpers.ReplaceAllIdentities();
|
||||||
}
|
}
|
||||||
else if (message.Command == "logout" && UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
}
|
||||||
|
else if (message.Command == "logout")
|
||||||
|
{
|
||||||
|
if (_deviceActionService.SystemMajorVersion() >= 12)
|
||||||
{
|
{
|
||||||
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
|
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
|
||||||
}
|
}
|
||||||
else if ((message.Command == "softDeletedCipher" || message.Command == "restoredCipher")
|
}
|
||||||
&& UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
else if ((message.Command == "softDeletedCipher" || message.Command == "restoredCipher")
|
||||||
|
&& _deviceActionService.SystemMajorVersion() >= 12)
|
||||||
|
{
|
||||||
|
await ASHelpers.ReplaceAllIdentities();
|
||||||
|
}
|
||||||
|
else if (message.Command == AppHelpers.VAULT_TIMEOUT_ACTION_CHANGED_MESSAGE_COMMAND)
|
||||||
|
{
|
||||||
|
var timeoutAction = await _stateService.GetVaultTimeoutActionAsync();
|
||||||
|
if (timeoutAction == VaultTimeoutAction.Logout)
|
||||||
|
{
|
||||||
|
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
await ASHelpers.ReplaceAllIdentities();
|
await ASHelpers.ReplaceAllIdentities();
|
||||||
}
|
}
|
||||||
else if (message.Command == AppHelpers.VAULT_TIMEOUT_ACTION_CHANGED_MESSAGE_COMMAND)
|
|
||||||
{
|
|
||||||
var timeoutAction = await _stateService.GetVaultTimeoutActionAsync();
|
|
||||||
if (timeoutAction == VaultTimeoutAction.Logout)
|
|
||||||
{
|
|
||||||
if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
|
||||||
{
|
|
||||||
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await ASHelpers.ReplaceAllIdentities();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
}
|
||||||
{
|
catch (Exception ex)
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
{
|
||||||
}
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var finishedLaunching = base.FinishedLaunching(app, options);
|
var finishedLaunching = base.FinishedLaunching(app, options);
|
||||||
|
|
||||||
ThemeManager.SetTheme(Microsoft.Maui.Controls.Application.Current.Resources);
|
ThemeManager.SetTheme(Microsoft.Maui.Controls.Application.Current.Resources);
|
||||||
iOSCoreHelpers.AppearanceAdjustments();
|
iOSCoreHelpers.AppearanceAdjustments();
|
||||||
|
|
||||||
return finishedLaunching;
|
return finishedLaunching;
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnResignActivation(UIApplication uiApplication)
|
public override void OnResignActivation(UIApplication uiApplication)
|
||||||
{
|
{
|
||||||
try
|
if (UIApplication.SharedApplication.KeyWindow != null)
|
||||||
{
|
{
|
||||||
if (UIApplication.SharedApplication.KeyWindow != null)
|
var view = new UIView(UIApplication.SharedApplication.KeyWindow.Frame)
|
||||||
{
|
{
|
||||||
var view = new UIView(UIApplication.SharedApplication.KeyWindow.Frame)
|
Tag = SPLASH_VIEW_TAG
|
||||||
{
|
};
|
||||||
Tag = SPLASH_VIEW_TAG
|
var backgroundView = new UIView(UIApplication.SharedApplication.KeyWindow.Frame)
|
||||||
};
|
{
|
||||||
var backgroundView = new UIView(UIApplication.SharedApplication.KeyWindow.Frame)
|
BackgroundColor = ThemeManager.GetResourceColor("SplashBackgroundColor").ToPlatform()
|
||||||
{
|
};
|
||||||
BackgroundColor = ThemeManager.GetResourceColor("SplashBackgroundColor").ToPlatform()
|
var logo = new UIImage(!ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png");
|
||||||
};
|
var frame = new CGRect(0, 0, 280, 100); //Setting image width to avoid it being larger and getting cropped on smaller devices. This harcoded size should be good even for very small devices.
|
||||||
var logo = new UIImage(!ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png");
|
var imageView = new UIImageView(frame)
|
||||||
var frame = new CGRect(0, 0, 280, 100); //Setting image width to avoid it being larger and getting cropped on smaller devices. This harcoded size should be good even for very small devices.
|
{
|
||||||
var imageView = new UIImageView(frame)
|
Image = logo,
|
||||||
{
|
Center = new CGPoint(view.Center.X, view.Center.Y - 30),
|
||||||
Image = logo,
|
ContentMode = UIViewContentMode.ScaleAspectFit
|
||||||
Center = new CGPoint(view.Center.X, view.Center.Y - 30),
|
};
|
||||||
ContentMode = UIViewContentMode.ScaleAspectFit
|
view.AddSubview(backgroundView);
|
||||||
};
|
view.AddSubview(imageView);
|
||||||
view.AddSubview(backgroundView);
|
UIApplication.SharedApplication.KeyWindow.AddSubview(view);
|
||||||
view.AddSubview(imageView);
|
UIApplication.SharedApplication.KeyWindow.BringSubviewToFront(view);
|
||||||
UIApplication.SharedApplication.KeyWindow.AddSubview(view);
|
UIApplication.SharedApplication.KeyWindow.EndEditing(true);
|
||||||
UIApplication.SharedApplication.KeyWindow.BringSubviewToFront(view);
|
|
||||||
UIApplication.SharedApplication.KeyWindow.EndEditing(true);
|
|
||||||
}
|
|
||||||
base.OnResignActivation(uiApplication);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
|
base.OnResignActivation(uiApplication);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DidEnterBackground(UIApplication uiApplication)
|
public override void DidEnterBackground(UIApplication uiApplication)
|
||||||
{
|
{
|
||||||
try
|
_stateService?.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
||||||
{
|
_messagingService?.Send("slept");
|
||||||
if (_stateService != null && _deviceActionService != null)
|
base.DidEnterBackground(uiApplication);
|
||||||
{
|
|
||||||
_stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
_messagingService?.Send("slept");
|
|
||||||
base.DidEnterBackground(uiApplication);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async void OnActivated(UIApplication uiApplication)
|
public override void OnActivated(UIApplication uiApplication)
|
||||||
{
|
{
|
||||||
try
|
base.OnActivated(uiApplication);
|
||||||
{
|
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
|
||||||
base.OnActivated(uiApplication);
|
UIApplication.SharedApplication.KeyWindow?
|
||||||
|
.ViewWithTag(SPLASH_VIEW_TAG)?
|
||||||
|
.RemoveFromSuperview();
|
||||||
|
|
||||||
if (UIDevice.CurrentDevice.CheckSystemVersion(17, 0))
|
ThemeManager.UpdateThemeOnPagesAsync();
|
||||||
{
|
|
||||||
await UNUserNotificationCenter.Current.SetBadgeCountAsync(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
UIApplication.SharedApplication.KeyWindow?
|
|
||||||
.ViewWithTag(SPLASH_VIEW_TAG)?
|
|
||||||
.RemoveFromSuperview();
|
|
||||||
|
|
||||||
ThemeManager.UpdateThemeOnPagesAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void WillEnterForeground(UIApplication uiApplication)
|
public override void WillEnterForeground(UIApplication uiApplication)
|
||||||
{
|
{
|
||||||
try
|
_messagingService?.Send(AppHelpers.RESUMED_MESSAGE_COMMAND);
|
||||||
{
|
base.WillEnterForeground(uiApplication);
|
||||||
_messagingService?.Send(AppHelpers.RESUMED_MESSAGE_COMMAND);
|
|
||||||
base.WillEnterForeground(uiApplication);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Export("application:openURL:sourceApplication:annotation:")]
|
[Export("application:openURL:sourceApplication:annotation:")]
|
||||||
@@ -301,30 +249,15 @@ namespace Bit.iOS
|
|||||||
|
|
||||||
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
|
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
|
||||||
{
|
{
|
||||||
try
|
return _deepLinkContext.Value.OnNewUri(url) || base.OpenUrl(app, url, options);
|
||||||
{
|
|
||||||
return _deepLinkContext.Value.OnNewUri(url) || base.OpenUrl(app, url, options);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity,
|
public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity,
|
||||||
UIApplicationRestorationHandler completionHandler)
|
UIApplicationRestorationHandler completionHandler)
|
||||||
{
|
{
|
||||||
try
|
if (Microsoft.Maui.ApplicationModel.Platform.ContinueUserActivity(application, userActivity, completionHandler))
|
||||||
{
|
{
|
||||||
if (Microsoft.Maui.ApplicationModel.Platform.ContinueUserActivity(application, userActivity, completionHandler))
|
return true;
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
}
|
||||||
return base.ContinueUserActivity(application, userActivity, completionHandler);
|
return base.ContinueUserActivity(application, userActivity, completionHandler);
|
||||||
}
|
}
|
||||||
@@ -332,68 +265,33 @@ namespace Bit.iOS
|
|||||||
[Export("application:didFailToRegisterForRemoteNotificationsWithError:")]
|
[Export("application:didFailToRegisterForRemoteNotificationsWithError:")]
|
||||||
public void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
|
public void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
|
||||||
{
|
{
|
||||||
try
|
_pushHandler?.OnErrorReceived(error);
|
||||||
{
|
|
||||||
_pushHandler?.OnErrorReceived(error);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")]
|
[Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")]
|
||||||
public void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
|
public void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
|
||||||
{
|
{
|
||||||
try
|
_pushHandler?.OnRegisteredSuccess(deviceToken);
|
||||||
{
|
|
||||||
_pushHandler?.OnRegisteredSuccess(deviceToken);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Export("application:didRegisterUserNotificationSettings:")]
|
[Export("application:didRegisterUserNotificationSettings:")]
|
||||||
public void DidRegisterUserNotificationSettings(UIApplication application,
|
public void DidRegisterUserNotificationSettings(UIApplication application,
|
||||||
UIUserNotificationSettings notificationSettings)
|
UIUserNotificationSettings notificationSettings)
|
||||||
{
|
{
|
||||||
try
|
application.RegisterForRemoteNotifications();
|
||||||
{
|
|
||||||
application.RegisterForRemoteNotifications();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Export("application:didReceiveRemoteNotification:fetchCompletionHandler:")]
|
[Export("application:didReceiveRemoteNotification:fetchCompletionHandler:")]
|
||||||
public void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo,
|
public void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo,
|
||||||
Action<UIBackgroundFetchResult> completionHandler)
|
Action<UIBackgroundFetchResult> completionHandler)
|
||||||
{
|
{
|
||||||
try
|
_pushHandler?.OnMessageReceived(userInfo);
|
||||||
{
|
|
||||||
_pushHandler?.OnMessageReceived(userInfo);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Export("application:didReceiveRemoteNotification:")]
|
[Export("application:didReceiveRemoteNotification:")]
|
||||||
public void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
|
public void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
|
||||||
{
|
{
|
||||||
try
|
_pushHandler?.OnMessageReceived(userInfo);
|
||||||
{
|
|
||||||
_pushHandler?.OnMessageReceived(userInfo);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitApp()
|
public void InitApp()
|
||||||
@@ -406,6 +304,17 @@ namespace Bit.iOS
|
|||||||
// Migration services
|
// Migration services
|
||||||
ServiceContainer.Register<INativeLogService>("nativeLogService", new ConsoleLogService());
|
ServiceContainer.Register<INativeLogService>("nativeLogService", new ConsoleLogService());
|
||||||
|
|
||||||
|
// Note: This might cause a race condition. Investigate more.
|
||||||
|
//Task.Run(() =>
|
||||||
|
//{
|
||||||
|
// FFImageLoading.Forms.Platform.CachedImageRenderer.Init();
|
||||||
|
// FFImageLoading.ImageService.Instance.Initialize(new FFImageLoading.Config.Configuration
|
||||||
|
// {
|
||||||
|
// FadeAnimationEnabled = false,
|
||||||
|
// FadeAnimationForCachedImages = false
|
||||||
|
// });
|
||||||
|
//});
|
||||||
|
|
||||||
iOSCoreHelpers.RegisterLocalServices();
|
iOSCoreHelpers.RegisterLocalServices();
|
||||||
RegisterPush();
|
RegisterPush();
|
||||||
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
@@ -419,7 +328,7 @@ namespace Bit.iOS
|
|||||||
_nfcDelegate = new Core.NFCReaderDelegate((success, message) =>
|
_nfcDelegate = new Core.NFCReaderDelegate((success, message) =>
|
||||||
_messagingService.Send("gotYubiKeyOTP", message));
|
_messagingService.Send("gotYubiKeyOTP", message));
|
||||||
|
|
||||||
iOSCoreHelpers.Bootstrap(ApplyManagedSettingsAsync);
|
iOSCoreHelpers.Bootstrap(async () => await ApplyManagedSettingsAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterPush()
|
private void RegisterPush()
|
||||||
@@ -464,45 +373,31 @@ namespace Bit.iOS
|
|||||||
_eventTimer = null;
|
_eventTimer = null;
|
||||||
MainThread.BeginInvokeOnMainThread(() =>
|
MainThread.BeginInvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
try
|
_eventTimer = NSTimer.CreateScheduledTimer(60, true, timer =>
|
||||||
{
|
{
|
||||||
_eventTimer = NSTimer.CreateScheduledTimer(60, true, timer =>
|
var task = Task.Run(() => _eventService.UploadEventsAsync());
|
||||||
{
|
});
|
||||||
_eventService?.UploadEventsAsync().FireAndForget();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StopEventTimerAsync()
|
private async Task StopEventTimerAsync()
|
||||||
{
|
{
|
||||||
try
|
_eventTimer?.Invalidate();
|
||||||
|
_eventTimer?.Dispose();
|
||||||
|
_eventTimer = null;
|
||||||
|
if (_eventBackgroundTaskId > 0)
|
||||||
{
|
{
|
||||||
_eventTimer?.Invalidate();
|
|
||||||
_eventTimer?.Dispose();
|
|
||||||
_eventTimer = null;
|
|
||||||
if (_eventBackgroundTaskId > 0)
|
|
||||||
{
|
|
||||||
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
|
||||||
_eventBackgroundTaskId = 0;
|
|
||||||
}
|
|
||||||
_eventBackgroundTaskId = UIApplication.SharedApplication.BeginBackgroundTask(() =>
|
|
||||||
{
|
|
||||||
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
|
||||||
_eventBackgroundTaskId = 0;
|
|
||||||
});
|
|
||||||
await _eventService.UploadEventsAsync();
|
|
||||||
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||||
_eventBackgroundTaskId = 0;
|
_eventBackgroundTaskId = 0;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
_eventBackgroundTaskId = UIApplication.SharedApplication.BeginBackgroundTask(() =>
|
||||||
{
|
{
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||||
}
|
_eventBackgroundTaskId = 0;
|
||||||
|
});
|
||||||
|
await _eventService.UploadEventsAsync();
|
||||||
|
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
|
||||||
|
_eventBackgroundTaskId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ApplyManagedSettingsAsync()
|
private async Task ApplyManagedSettingsAsync()
|
||||||
|
|||||||
@@ -5,24 +5,15 @@ using Foundation;
|
|||||||
using Microsoft.Maui.Handlers;
|
using Microsoft.Maui.Handlers;
|
||||||
using WebKit;
|
using WebKit;
|
||||||
|
|
||||||
namespace Bit.iOS.Core.Handlers
|
namespace Bit.App.Handlers
|
||||||
{
|
{
|
||||||
public class HybridWebViewHandler : ViewHandler<HybridWebView, WebKit.WKWebView>
|
public partial class HybridWebViewHandler : ViewHandler<HybridWebView, WebKit.WKWebView>
|
||||||
{
|
{
|
||||||
private const string JSFunction =
|
private const string JSFunction =
|
||||||
"function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";
|
"function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";
|
||||||
|
|
||||||
private WKUserContentController _userController;
|
private WKUserContentController _userController;
|
||||||
|
|
||||||
public static PropertyMapper<HybridWebView, HybridWebViewHandler> PropertyMapper = new PropertyMapper<HybridWebView, HybridWebViewHandler>(ViewHandler.ViewMapper)
|
|
||||||
{
|
|
||||||
[nameof(HybridWebView.Uri)] = MapUri
|
|
||||||
};
|
|
||||||
|
|
||||||
public HybridWebViewHandler() : base(PropertyMapper)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public HybridWebViewHandler([NotNull] IPropertyMapper mapper, CommandMapper commandMapper = null) : base(mapper, commandMapper)
|
public HybridWebViewHandler([NotNull] IPropertyMapper mapper, CommandMapper commandMapper = null) : base(mapper, commandMapper)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.8bit.bitwarden</string>
|
<string>com.8bit.bitwarden</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2024.3.0</string>
|
<string>2023.12.1</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>CFBundleIconName</key>
|
<key>CFBundleIconName</key>
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"images": [
|
|
||||||
{
|
|
||||||
"appearances": [],
|
|
||||||
"scale": "1x",
|
|
||||||
"idiom": "universal",
|
|
||||||
"filename": "search.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances": [],
|
|
||||||
"scale": "2x",
|
|
||||||
"idiom": "universal",
|
|
||||||
"filename": "search@2x.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances": [],
|
|
||||||
"scale": "3x",
|
|
||||||
"idiom": "universal",
|
|
||||||
"filename": "search@3x.png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"properties": {},
|
|
||||||
"info": {
|
|
||||||
"version": 1,
|
|
||||||
"author": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,8 +5,7 @@ namespace Bit.Core.Abstractions
|
|||||||
{
|
{
|
||||||
public enum AwaiterPrecondition
|
public enum AwaiterPrecondition
|
||||||
{
|
{
|
||||||
EnvironmentUrlsInited,
|
EnvironmentUrlsInited
|
||||||
AndroidWindowCreated
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IConditionedAwaiterManager
|
public interface IConditionedAwaiterManager
|
||||||
|
|||||||
@@ -63,7 +63,5 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<UserKey> DecryptAndMigrateOldPinKeyAsync(bool masterPasswordOnRestart, string pin, string email, KdfConfig kdfConfig, EncString oldPinKey);
|
Task<UserKey> DecryptAndMigrateOldPinKeyAsync(bool masterPasswordOnRestart, string pin, string email, KdfConfig kdfConfig, EncString oldPinKey);
|
||||||
Task<MasterKey> GetOrDeriveMasterKeyAsync(string password, string userId = null);
|
Task<MasterKey> GetOrDeriveMasterKeyAsync(string password, string userId = null);
|
||||||
Task UpdateMasterKeyAndUserKeyAsync(MasterKey masterKey);
|
Task UpdateMasterKeyAndUserKeyAsync(MasterKey masterKey);
|
||||||
Task<string> HashAsync(string value, CryptoHashAlgorithm hashAlgorithm);
|
|
||||||
Task<bool> ValidateUriChecksumAsync(EncString remoteUriChecksum, string rawUri, string orgId, SymmetricCryptoKey key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using Bit.Core.Abstractions;
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Models.Response;
|
using Bit.Core.Models.Response;
|
||||||
using Bit.Core.Pages;
|
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
@@ -35,8 +34,6 @@ namespace Bit.App
|
|||||||
private readonly IAccountsManager _accountsManager;
|
private readonly IAccountsManager _accountsManager;
|
||||||
private readonly IPushNotificationService _pushNotificationService;
|
private readonly IPushNotificationService _pushNotificationService;
|
||||||
private readonly IConfigService _configService;
|
private readonly IConfigService _configService;
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
private static bool _isResumed;
|
private static bool _isResumed;
|
||||||
// these variables are static because the app is launching new activities on notification click, creating new instances of App.
|
// these variables are static because the app is launching new activities on notification click, creating new instances of App.
|
||||||
private static bool _pendingCheckPasswordlessLoginRequests;
|
private static bool _pendingCheckPasswordlessLoginRequests;
|
||||||
@@ -47,99 +44,6 @@ namespace Bit.App
|
|||||||
// Links: https://github.com/dotnet/maui/issues/11501 and https://bitwarden.atlassian.net/wiki/spaces/NMME/pages/664862722/MainPage+Assignments+not+working+on+Android+on+Background+or+App+resume
|
// Links: https://github.com/dotnet/maui/issues/11501 and https://bitwarden.atlassian.net/wiki/spaces/NMME/pages/664862722/MainPage+Assignments+not+working+on+Android+on+Background+or+App+resume
|
||||||
private readonly Queue<Action> _onResumeActions = new Queue<Action>();
|
private readonly Queue<Action> _onResumeActions = new Queue<Action>();
|
||||||
|
|
||||||
#if ANDROID
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ** Workaround for our Android crashes when trying to use Autofill **
|
|
||||||
*
|
|
||||||
* This workaround works by managing the "Window Creation" ourselves.
|
|
||||||
* - If we get an AutofillExternalActivity we just create a "dummy" window/navigation page so that the activity can run without crashing. (no visible UI is needed)
|
|
||||||
* - If we get an FromAutofillFramework/Uri/Otp/CreateSend special Option request we create an Autofill Window
|
|
||||||
* - For everything else we use the default "mainWindow"
|
|
||||||
*/
|
|
||||||
|
|
||||||
public new static Page MainPage
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return CurrentWindow?.Page;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (CurrentWindow != null)
|
|
||||||
{
|
|
||||||
CurrentWindow.Page = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Find the Current Active Window. There should only be one at any point in Android
|
|
||||||
/// </summary>
|
|
||||||
public static ResumeWindow CurrentWindow
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Application.Current?.Windows.OfType<ResumeWindow>().FirstOrDefault(w => w.IsActive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Allows setting Options from MainActivity before base.OnCreate
|
|
||||||
/// Note 1: This is only be used by Android due to way it's Lifecycle works
|
|
||||||
/// Note 2: This method does not replace existing Options in App.xaml.cs if it exists already.
|
|
||||||
/// It only updates properties in Options related with Autofill/CreateSend/etc..
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="appOptions">Options created in Android MainActivity.cs</param>
|
|
||||||
public void SetAndroidOptions(AppOptions appOptions)
|
|
||||||
{
|
|
||||||
if (Options == null)
|
|
||||||
{
|
|
||||||
Options = appOptions ?? new AppOptions();
|
|
||||||
}
|
|
||||||
else if(appOptions != null)
|
|
||||||
{
|
|
||||||
Options.Uri = appOptions.Uri;
|
|
||||||
Options.MyVaultTile = appOptions.MyVaultTile;
|
|
||||||
Options.GeneratorTile = appOptions.GeneratorTile;
|
|
||||||
Options.FromAutofillFramework = appOptions.FromAutofillFramework;
|
|
||||||
Options.CreateSend = appOptions.CreateSend;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Window CreateWindow(IActivationState activationState)
|
|
||||||
{
|
|
||||||
//When executing from AutofillExternalActivity we don't have "Options" so we need to filter "manually"
|
|
||||||
//In the AutofillExternalActivity we don't need to show any Page, so we just create a "dummy" Window with a NavigationPage to avoid crashing.
|
|
||||||
if (activationState != null
|
|
||||||
&& activationState.State.TryGetValue("autofillFramework", out string autofillFramework)
|
|
||||||
&& autofillFramework == "true"
|
|
||||||
&& activationState.State.ContainsKey("autofillFrameworkCipherId"))
|
|
||||||
{
|
|
||||||
return new Window(new NavigationPage()); //No actual page needed. Only used for auto-filling the fields directly (externally)
|
|
||||||
}
|
|
||||||
|
|
||||||
_isResumed = true;
|
|
||||||
return new ResumeWindow(new NavigationPage(new AndroidNavigationRedirectPage(Options)));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
//iOS doesn't use the CreateWindow override used in Android so we just set the Application.Current.MainPage directly
|
|
||||||
public new static Page MainPage
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Application.Current?.MainPage;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (Application.Current != null)
|
|
||||||
{
|
|
||||||
Application.Current.MainPage = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public App() : this(null)
|
public App() : this(null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -163,7 +67,6 @@ namespace Bit.App
|
|||||||
_accountsManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
|
_accountsManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
|
||||||
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>();
|
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>();
|
||||||
_configService = ServiceContainer.Resolve<IConfigService>();
|
_configService = ServiceContainer.Resolve<IConfigService>();
|
||||||
_logger = ServiceContainer.Resolve<ILogger>();
|
|
||||||
|
|
||||||
_accountsManager.Init(() => Options, this);
|
_accountsManager.Init(() => Options, this);
|
||||||
|
|
||||||
@@ -178,7 +81,7 @@ namespace Bit.App
|
|||||||
var confirmed = true;
|
var confirmed = true;
|
||||||
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ?
|
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ?
|
||||||
AppResources.Ok : details.ConfirmText;
|
AppResources.Ok : details.ConfirmText;
|
||||||
await MainThread.InvokeOnMainThreadAsync(async () =>
|
MainThread.BeginInvokeOnMainThread(async () =>
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(details.CancelText))
|
if (!string.IsNullOrWhiteSpace(details.CancelText))
|
||||||
{
|
{
|
||||||
@@ -192,16 +95,20 @@ namespace Bit.App
|
|||||||
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
|
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#if IOS
|
|
||||||
else if (message.Command == AppHelpers.RESUMED_MESSAGE_COMMAND)
|
else if (message.Command == AppHelpers.RESUMED_MESSAGE_COMMAND)
|
||||||
{
|
{
|
||||||
ResumedAsync().FireAndForget();
|
if (DeviceInfo.Platform == DevicePlatform.iOS)
|
||||||
|
{
|
||||||
|
ResumedAsync().FireAndForget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (message.Command == "slept")
|
else if (message.Command == "slept")
|
||||||
{
|
{
|
||||||
await SleptAsync();
|
if (DeviceInfo.Platform == DevicePlatform.iOS)
|
||||||
|
{
|
||||||
|
await SleptAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
else if (message.Command == "migrated")
|
else if (message.Command == "migrated")
|
||||||
{
|
{
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
@@ -218,7 +125,7 @@ namespace Bit.App
|
|||||||
Options.OtpData = new OtpData((string)message.Data);
|
Options.OtpData = new OtpData((string)message.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
await MainThread.InvokeOnMainThreadAsync(async () =>
|
MainThread.InvokeOnMainThreadAsync(async () =>
|
||||||
{
|
{
|
||||||
if (MainPage is TabsPage tabsPage)
|
if (MainPage is TabsPage tabsPage)
|
||||||
{
|
{
|
||||||
@@ -254,7 +161,7 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
else if (message.Command == "convertAccountToKeyConnector")
|
else if (message.Command == "convertAccountToKeyConnector")
|
||||||
{
|
{
|
||||||
await MainThread.InvokeOnMainThreadAsync(async () =>
|
MainThread.BeginInvokeOnMainThread(async () =>
|
||||||
{
|
{
|
||||||
await MainPage.Navigation.PushModalAsync(
|
await MainPage.Navigation.PushModalAsync(
|
||||||
new NavigationPage(new RemoveMasterPasswordPage()));
|
new NavigationPage(new RemoveMasterPasswordPage()));
|
||||||
@@ -262,7 +169,7 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
else if (message.Command == Constants.ForceUpdatePassword)
|
else if (message.Command == Constants.ForceUpdatePassword)
|
||||||
{
|
{
|
||||||
await MainThread.InvokeOnMainThreadAsync(async () =>
|
MainThread.BeginInvokeOnMainThread(async () =>
|
||||||
{
|
{
|
||||||
await MainPage.Navigation.PushModalAsync(
|
await MainPage.Navigation.PushModalAsync(
|
||||||
new NavigationPage(new UpdateTempPasswordPage()));
|
new NavigationPage(new UpdateTempPasswordPage()));
|
||||||
@@ -378,52 +285,40 @@ namespace Bit.App
|
|||||||
|
|
||||||
protected override async void OnStart()
|
protected override async void OnStart()
|
||||||
{
|
{
|
||||||
try
|
System.Diagnostics.Debug.WriteLine("XF App: OnStart");
|
||||||
|
_isResumed = true;
|
||||||
|
await ClearCacheIfNeededAsync();
|
||||||
|
Prime();
|
||||||
|
if (string.IsNullOrWhiteSpace(Options.Uri))
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine("XF App: OnStart");
|
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
|
||||||
_isResumed = true;
|
_stateService);
|
||||||
await ClearCacheIfNeededAsync();
|
if (!updated)
|
||||||
Prime();
|
|
||||||
if (string.IsNullOrWhiteSpace(Options.Uri))
|
|
||||||
{
|
{
|
||||||
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
|
SyncIfNeeded();
|
||||||
_stateService);
|
|
||||||
if (!updated)
|
|
||||||
{
|
|
||||||
SyncIfNeeded();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (_pendingCheckPasswordlessLoginRequests)
|
}
|
||||||
{
|
if (_pendingCheckPasswordlessLoginRequests)
|
||||||
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
|
{
|
||||||
}
|
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
|
||||||
#if ANDROID
|
}
|
||||||
|
if (DeviceInfo.Platform == DevicePlatform.Android)
|
||||||
|
{
|
||||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
// Reset delay on every start
|
// Reset delay on every start
|
||||||
_vaultTimeoutService.DelayLockAndLogoutMs = null;
|
_vaultTimeoutService.DelayLockAndLogoutMs = null;
|
||||||
#endif
|
}
|
||||||
|
|
||||||
await _configService.GetAsync();
|
await _configService.GetAsync();
|
||||||
_messagingService.Send("startEventTimer");
|
_messagingService.Send("startEventTimer");
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger?.Exception(ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ANDROID
|
|
||||||
protected override async void OnSleep()
|
protected override async void OnSleep()
|
||||||
#else
|
|
||||||
protected override void OnSleep()
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
try
|
System.Diagnostics.Debug.WriteLine("XF App: OnSleep");
|
||||||
|
_isResumed = false;
|
||||||
|
if (DeviceInfo.Platform == DevicePlatform.Android)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine("XF App: OnSleep");
|
|
||||||
_isResumed = false;
|
|
||||||
#if ANDROID
|
|
||||||
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
var isLocked = await _vaultTimeoutService.IsLockedAsync();
|
||||||
if (!isLocked)
|
if (!isLocked)
|
||||||
{
|
{
|
||||||
@@ -434,34 +329,20 @@ namespace Bit.App
|
|||||||
ClearAutofillUri();
|
ClearAutofillUri();
|
||||||
}
|
}
|
||||||
await SleptAsync();
|
await SleptAsync();
|
||||||
#endif
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger?.Exception(ex);
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnResume()
|
protected override void OnResume()
|
||||||
{
|
{
|
||||||
try
|
System.Diagnostics.Debug.WriteLine("XF App: OnResume");
|
||||||
|
_isResumed = true;
|
||||||
|
if (_pendingCheckPasswordlessLoginRequests)
|
||||||
{
|
{
|
||||||
System.Diagnostics.Debug.WriteLine("XF App: OnResume");
|
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
|
||||||
_isResumed = true;
|
|
||||||
if (_pendingCheckPasswordlessLoginRequests)
|
|
||||||
{
|
|
||||||
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
|
|
||||||
}
|
|
||||||
#if ANDROID
|
|
||||||
ResumedAsync().FireAndForget();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
if (DeviceInfo.Platform == DevicePlatform.Android)
|
||||||
{
|
{
|
||||||
_logger?.Exception(ex);
|
ResumedAsync().FireAndForget();
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,22 +429,14 @@ namespace Bit.App
|
|||||||
{
|
{
|
||||||
MainThread.BeginInvokeOnMainThread(() =>
|
MainThread.BeginInvokeOnMainThread(() =>
|
||||||
{
|
{
|
||||||
try
|
Options.Uri = null;
|
||||||
|
if (isLocked)
|
||||||
{
|
{
|
||||||
Options.Uri = null;
|
MainPage = new NavigationPage(new LockPage());
|
||||||
if (isLocked)
|
|
||||||
{
|
|
||||||
App.MainPage = new NavigationPage(new LockPage());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
App.MainPage = new TabsPage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
else
|
||||||
{
|
{
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
MainPage = new TabsPage();
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -588,13 +461,10 @@ namespace Bit.App
|
|||||||
ThemeManager.SetTheme(Resources);
|
ThemeManager.SetTheme(Resources);
|
||||||
RequestedThemeChanged += (s, a) =>
|
RequestedThemeChanged += (s, a) =>
|
||||||
{
|
{
|
||||||
UpdateThemeAsync().FireAndForget();
|
UpdateThemeAsync();
|
||||||
};
|
};
|
||||||
_isResumed = true;
|
_isResumed = true;
|
||||||
#if IOS
|
MainPage = new NavigationPage(new HomePage(Options));
|
||||||
//We only set the MainPage here for iOS. Android is using the CreateWindow override for the initial page.
|
|
||||||
App.MainPage = new NavigationPage(new HomePage(Options));
|
|
||||||
#endif
|
|
||||||
_accountsManager.NavigateOnAccountChangeAsync().FireAndForget();
|
_accountsManager.NavigateOnAccountChangeAsync().FireAndForget();
|
||||||
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
||||||
}
|
}
|
||||||
@@ -607,18 +477,11 @@ namespace Bit.App
|
|||||||
}
|
}
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
try
|
var lastSync = await _syncService.GetLastSyncAsync();
|
||||||
|
if (lastSync == null || ((DateTime.UtcNow - lastSync) > TimeSpan.FromMinutes(30)))
|
||||||
{
|
{
|
||||||
var lastSync = await _syncService.GetLastSyncAsync();
|
await Task.Delay(1000);
|
||||||
if (lastSync == null || ((DateTime.UtcNow - lastSync) > TimeSpan.FromMinutes(30)))
|
await _syncService.FullSyncAsync(false);
|
||||||
{
|
|
||||||
await Task.Delay(1000);
|
|
||||||
await _syncService.FullSyncAsync(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.Exception(ex);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -673,36 +536,36 @@ namespace Bit.App
|
|||||||
switch (navTarget)
|
switch (navTarget)
|
||||||
{
|
{
|
||||||
case NavigationTarget.HomeLogin:
|
case NavigationTarget.HomeLogin:
|
||||||
App.MainPage = new NavigationPage(new HomePage(Options));
|
MainPage = new NavigationPage(new HomePage(Options));
|
||||||
break;
|
break;
|
||||||
case NavigationTarget.Login:
|
case NavigationTarget.Login:
|
||||||
if (navParams is LoginNavigationParams loginParams)
|
if (navParams is LoginNavigationParams loginParams)
|
||||||
{
|
{
|
||||||
App.MainPage = new NavigationPage(new LoginPage(loginParams.Email, Options));
|
MainPage = new NavigationPage(new LoginPage(loginParams.Email, Options));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NavigationTarget.Lock:
|
case NavigationTarget.Lock:
|
||||||
if (navParams is LockNavigationParams lockParams)
|
if (navParams is LockNavigationParams lockParams)
|
||||||
{
|
{
|
||||||
App.MainPage = new NavigationPage(new LockPage(Options, lockParams.AutoPromptBiometric));
|
MainPage = new NavigationPage(new LockPage(Options, lockParams.AutoPromptBiometric));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
App.MainPage = new NavigationPage(new LockPage(Options));
|
MainPage = new NavigationPage(new LockPage(Options));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NavigationTarget.Home:
|
case NavigationTarget.Home:
|
||||||
App.MainPage = new TabsPage(Options);
|
MainPage = new TabsPage(Options);
|
||||||
break;
|
break;
|
||||||
case NavigationTarget.AddEditCipher:
|
case NavigationTarget.AddEditCipher:
|
||||||
App.MainPage = new NavigationPage(new CipherAddEditPage(appOptions: Options));
|
MainPage = new NavigationPage(new CipherAddEditPage(appOptions: Options));
|
||||||
break;
|
break;
|
||||||
case NavigationTarget.AutofillCiphers:
|
case NavigationTarget.AutofillCiphers:
|
||||||
case NavigationTarget.OtpCipherSelection:
|
case NavigationTarget.OtpCipherSelection:
|
||||||
App.MainPage = new NavigationPage(new CipherSelectionPage(Options));
|
MainPage = new NavigationPage(new CipherSelectionPage(Options));
|
||||||
break;
|
break;
|
||||||
case NavigationTarget.SendAddEdit:
|
case NavigationTarget.SendAddEdit:
|
||||||
App.MainPage = new NavigationPage(new SendAddEditPage(Options));
|
MainPage = new NavigationPage(new SendAddEditPage(Options));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ namespace Bit.Core
|
|||||||
public const string ConfigsKey = "configsKey";
|
public const string ConfigsKey = "configsKey";
|
||||||
public const string DisplayEuEnvironmentFlag = "display-eu-environment";
|
public const string DisplayEuEnvironmentFlag = "display-eu-environment";
|
||||||
public const string RegionEnvironment = "regionEnvironment";
|
public const string RegionEnvironment = "regionEnvironment";
|
||||||
public const string DuoCallback = "bitwarden://duo-callback";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This key is used to store the value of "ShouldConnectToWatch" of the last user that had logged in
|
/// This key is used to store the value of "ShouldConnectToWatch" of the last user that had logged in
|
||||||
@@ -71,11 +70,10 @@ namespace Bit.Core
|
|||||||
public const int Argon2Parallelism = 4;
|
public const int Argon2Parallelism = 4;
|
||||||
public const int MasterPasswordMinimumChars = 12;
|
public const int MasterPasswordMinimumChars = 12;
|
||||||
public const int CipherKeyRandomBytesLength = 64;
|
public const int CipherKeyRandomBytesLength = 64;
|
||||||
public const string CipherKeyEncryptionMinServerVersion = "2024.2.0";
|
public const string CipherKeyEncryptionMinServerVersion = "2023.9.1";
|
||||||
public const string DefaultFido2CredentialType = "public-key";
|
public const string DefaultFido2CredentialType = "public-key";
|
||||||
public const string DefaultFido2CredentialAlgorithm = "ECDSA";
|
public const string DefaultFido2CredentialAlgorithm = "ECDSA";
|
||||||
public const string DefaultFido2CredentialCurve = "P-256";
|
public const string DefaultFido2CredentialCurve = "P-256";
|
||||||
public const int LatestStateVersion = 7;
|
|
||||||
|
|
||||||
public static readonly string[] AndroidAllClearCipherCacheKeys =
|
public static readonly string[] AndroidAllClearCipherCacheKeys =
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,14 +12,12 @@
|
|||||||
BackgroundColor="#22000000"
|
BackgroundColor="#22000000"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
IsVisible="False">
|
IsVisible="False">
|
||||||
<Grid
|
<VerticalStackLayout
|
||||||
x:Name="_accountListContainer"
|
x:Name="_accountListContainer"
|
||||||
VerticalOptions="Fill"
|
VerticalOptions="Fill"
|
||||||
HorizontalOptions="Fill"
|
HorizontalOptions="FillAndExpand"
|
||||||
BackgroundColor="Transparent"
|
BackgroundColor="Transparent">
|
||||||
RowDefinitions="Auto, *">
|
|
||||||
<Frame
|
<Frame
|
||||||
Grid.Row="0"
|
|
||||||
Padding="0"
|
Padding="0"
|
||||||
HorizontalOptions="Fill"
|
HorizontalOptions="Fill"
|
||||||
VerticalOptions="Start">
|
VerticalOptions="Start">
|
||||||
@@ -51,13 +49,12 @@
|
|||||||
</ListView>
|
</ListView>
|
||||||
</Frame>
|
</Frame>
|
||||||
<BoxView
|
<BoxView
|
||||||
Grid.Row="1"
|
|
||||||
BackgroundColor="Transparent"
|
BackgroundColor="Transparent"
|
||||||
HorizontalOptions="Fill"
|
HorizontalOptions="Fill"
|
||||||
VerticalOptions="Fill">
|
VerticalOptions="FillAndExpand">
|
||||||
<BoxView.GestureRecognizers>
|
<BoxView.GestureRecognizers>
|
||||||
<TapGestureRecognizer Tapped="FreeSpaceOverlay_Tapped" />
|
<TapGestureRecognizer Tapped="FreeSpaceOverlay_Tapped" />
|
||||||
</BoxView.GestureRecognizers>
|
</BoxView.GestureRecognizers>
|
||||||
</BoxView>
|
</BoxView>
|
||||||
</Grid>
|
</VerticalStackLayout>
|
||||||
</ContentView>
|
</ContentView>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<controls:BaseCipherViewCell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
<controls:ExtendedGrid xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
x:Class="Bit.App.Controls.AuthenticatorViewCell"
|
x:Class="Bit.App.Controls.AuthenticatorViewCell"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
|
xmlns:ff="clr-namespace:FFImageLoading.Maui;assembly=FFImageLoading.Compat.Maui"
|
||||||
xmlns:core="clr-namespace:Bit.Core"
|
xmlns:core="clr-namespace:Bit.Core"
|
||||||
StyleClass="list-row, list-row-platform"
|
StyleClass="list-row, list-row-platform"
|
||||||
HorizontalOptions="FillAndExpand"
|
HorizontalOptions="FillAndExpand"
|
||||||
@@ -13,32 +14,34 @@
|
|||||||
RowSpacing="0"
|
RowSpacing="0"
|
||||||
Padding="0,10,0,0"
|
Padding="0,10,0,0"
|
||||||
RowDefinitions="*,*">
|
RowDefinitions="*,*">
|
||||||
<controls:BaseCipherViewCell.Resources>
|
|
||||||
|
<Grid.Resources>
|
||||||
<u:IconGlyphConverter x:Key="iconGlyphConverter" />
|
<u:IconGlyphConverter x:Key="iconGlyphConverter" />
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
</controls:BaseCipherViewCell.Resources>
|
</Grid.Resources>
|
||||||
|
|
||||||
<controls:CachedImage
|
<controls:IconLabel
|
||||||
x:Name="_iconImage"
|
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-icon, list-icon-platform"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
|
IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}"
|
||||||
|
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
|
||||||
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
|
|
||||||
|
<ff:CachedImage
|
||||||
|
Grid.Column="0"
|
||||||
BitmapOptimizations="True"
|
BitmapOptimizations="True"
|
||||||
|
ErrorPlaceholder="login.png"
|
||||||
|
LoadingPlaceholder="login.png"
|
||||||
HorizontalOptions="Center"
|
HorizontalOptions="Center"
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
WidthRequest="22"
|
WidthRequest="22"
|
||||||
HeightRequest="22"
|
HeightRequest="22"
|
||||||
Success="Icon_Success"
|
|
||||||
Error="Icon_Error"
|
|
||||||
AutomationProperties.IsInAccessibleTree="False" />
|
|
||||||
|
|
||||||
<controls:IconLabel
|
|
||||||
x:Name="_iconPlaceholderImage"
|
|
||||||
Grid.Column="0"
|
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
HorizontalOptions="Center"
|
IsVisible="{Binding ShowIconImage}"
|
||||||
VerticalOptions="Center"
|
Source="{Binding IconImageSource, Mode=OneTime}"
|
||||||
StyleClass="list-icon, list-icon-platform"
|
|
||||||
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
|
|
||||||
AutomationProperties.IsInAccessibleTree="False" />
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
|
|
||||||
<Label
|
<Label
|
||||||
@@ -46,7 +49,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
VerticalTextAlignment="Center"
|
VerticalTextAlignment="Center"
|
||||||
VerticalOptions="End"
|
VerticalOptions="Fill"
|
||||||
StyleClass="list-title, list-title-platform"
|
StyleClass="list-title, list-title-platform"
|
||||||
Text="{Binding Cipher.Name}" />
|
Text="{Binding Cipher.Name}" />
|
||||||
|
|
||||||
@@ -55,7 +58,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
VerticalTextAlignment="Center"
|
VerticalTextAlignment="Center"
|
||||||
VerticalOptions="Start"
|
VerticalOptions="Fill"
|
||||||
StyleClass="list-subtitle, list-subtitle-platform"
|
StyleClass="list-subtitle, list-subtitle-platform"
|
||||||
Text="{Binding Cipher.SubTitle}" />
|
Text="{Binding Cipher.SubTitle}" />
|
||||||
|
|
||||||
@@ -65,14 +68,11 @@
|
|||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
HorizontalOptions="Fill"
|
HorizontalOptions="Fill"
|
||||||
WidthRequest="50"
|
|
||||||
HeightRequest="50"
|
|
||||||
VerticalOptions="CenterAndExpand" />
|
VerticalOptions="CenterAndExpand" />
|
||||||
|
|
||||||
<Label
|
<Label
|
||||||
Text="{Binding TotpSec, Mode=OneWay}"
|
Text="{Binding TotpSec, Mode=OneWay}"
|
||||||
Style="{DynamicResource textTotp}"
|
Style="{DynamicResource textTotp}"
|
||||||
BackgroundColor="Transparent"
|
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
@@ -123,4 +123,4 @@
|
|||||||
HorizontalOptions="Center"
|
HorizontalOptions="Center"
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
SemanticProperties.Description="{u:I18n CopyTotp}" />
|
SemanticProperties.Description="{u:I18n CopyTotp}" />
|
||||||
</controls:BaseCipherViewCell>
|
</controls:ExtendedGrid>
|
||||||
@@ -1,14 +1,10 @@
|
|||||||
namespace Bit.App.Controls
|
namespace Bit.App.Controls
|
||||||
{
|
{
|
||||||
public partial class AuthenticatorViewCell : BaseCipherViewCell
|
public partial class AuthenticatorViewCell : ExtendedGrid
|
||||||
{
|
{
|
||||||
public AuthenticatorViewCell()
|
public AuthenticatorViewCell()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override CachedImage Icon => _iconImage;
|
|
||||||
|
|
||||||
protected override IconLabel IconPlaceholder => _iconPlaceholderImage;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
|
||||||
{
|
|
||||||
public class AvatarImageSource : StreamImageSource
|
|
||||||
{
|
|
||||||
private readonly string _text;
|
|
||||||
private readonly string _id;
|
|
||||||
private readonly string _color;
|
|
||||||
private readonly AvatarInfo _avatarInfo;
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj is AvatarImageSource avatar)
|
|
||||||
{
|
|
||||||
return avatar._id == _id && avatar._text == _text && avatar._color == _color;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.Equals(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode() => _id?.GetHashCode() ?? _text?.GetHashCode() ?? -1;
|
|
||||||
|
|
||||||
public AvatarImageSource(string userId = null, string name = null, string email = null, string color = null)
|
|
||||||
{
|
|
||||||
_id = userId;
|
|
||||||
_text = name;
|
|
||||||
if (string.IsNullOrWhiteSpace(_text))
|
|
||||||
{
|
|
||||||
_text = email;
|
|
||||||
}
|
|
||||||
_color = color;
|
|
||||||
|
|
||||||
//Workaround: [MAUI-Migration] There is currently a bug in MAUI where the actual size of the image is used instead of the size it should occupy in the Toolbar.
|
|
||||||
//This causes some issues with the position of the icon. As a workaround we make the icon smaller until this is fixed.
|
|
||||||
//Github issues: https://github.com/dotnet/maui/issues/12359 and https://github.com/dotnet/maui/pull/17120
|
|
||||||
_avatarInfo = new AvatarInfo(userId, name, email, color, DeviceInfo.Platform == DevicePlatform.iOS ? 20 : 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Func<CancellationToken, Task<Stream>> Stream => GetStreamAsync;
|
|
||||||
|
|
||||||
private Task<Stream> GetStreamAsync(CancellationToken userToken = new CancellationToken())
|
|
||||||
{
|
|
||||||
var result = Draw();
|
|
||||||
return Task.FromResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Stream Draw()
|
|
||||||
{
|
|
||||||
using (var img = SKAvatarImageHelper.Draw(_avatarInfo))
|
|
||||||
{
|
|
||||||
var data = img.Encode(SKEncodedImageFormat.Png, 100);
|
|
||||||
return data?.AsStream(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
using Bit.Core.Utilities;
|
|
||||||
|
|
||||||
#nullable enable
|
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
|
||||||
{
|
|
||||||
public struct AvatarInfo
|
|
||||||
{
|
|
||||||
private const string DEFAULT_BACKGROUND_COLOR = "#33ffffff";
|
|
||||||
|
|
||||||
public AvatarInfo(string? userId = null, string? name = null, string? email = null, string? color = null, int size = 50)
|
|
||||||
{
|
|
||||||
Size = size;
|
|
||||||
var text = string.IsNullOrWhiteSpace(name) ? email : name;
|
|
||||||
|
|
||||||
string? upperCaseText = null;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(text))
|
|
||||||
{
|
|
||||||
CharsToDraw = "..";
|
|
||||||
}
|
|
||||||
else if (text.Length > 1)
|
|
||||||
{
|
|
||||||
upperCaseText = text.ToUpper();
|
|
||||||
CharsToDraw = GetFirstLetters(upperCaseText, 2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CharsToDraw = upperCaseText = text.ToUpper();
|
|
||||||
}
|
|
||||||
|
|
||||||
BackgroundColor = color ?? CoreHelpers.StringToColor(userId ?? upperCaseText, DEFAULT_BACKGROUND_COLOR);
|
|
||||||
TextColor = CoreHelpers.TextColorFromBgColor(BackgroundColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string CharsToDraw { get; }
|
|
||||||
public string BackgroundColor { get; }
|
|
||||||
public string TextColor { get; }
|
|
||||||
public int Size { get; }
|
|
||||||
|
|
||||||
private static string GetFirstLetters(string data, int charCount)
|
|
||||||
{
|
|
||||||
var sanitizedData = data.Trim();
|
|
||||||
var parts = sanitizedData.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
if (parts.Length > 1 && charCount <= 2)
|
|
||||||
{
|
|
||||||
var text = string.Empty;
|
|
||||||
for (var i = 0; i < charCount; i++)
|
|
||||||
{
|
|
||||||
text += parts[i][0];
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
if (sanitizedData.Length > 2)
|
|
||||||
{
|
|
||||||
return sanitizedData.Substring(0, 2);
|
|
||||||
}
|
|
||||||
return sanitizedData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
|
||||||
{
|
|
||||||
public static class SKAvatarImageHelper
|
|
||||||
{
|
|
||||||
public static SKImage Draw(AvatarInfo avatarInfo)
|
|
||||||
{
|
|
||||||
using (var bitmap = new SKBitmap(avatarInfo.Size * 2,
|
|
||||||
avatarInfo.Size * 2,
|
|
||||||
SKImageInfo.PlatformColorType,
|
|
||||||
SKAlphaType.Premul))
|
|
||||||
{
|
|
||||||
using (var canvas = new SKCanvas(bitmap))
|
|
||||||
{
|
|
||||||
canvas.Clear(SKColors.Transparent);
|
|
||||||
using (var paint = new SKPaint
|
|
||||||
{
|
|
||||||
IsAntialias = true,
|
|
||||||
Style = SKPaintStyle.Fill,
|
|
||||||
StrokeJoin = SKStrokeJoin.Miter,
|
|
||||||
Color = SKColor.Parse(avatarInfo.BackgroundColor)
|
|
||||||
})
|
|
||||||
{
|
|
||||||
var midX = canvas.LocalClipBounds.Size.ToSizeI().Width / 2;
|
|
||||||
var midY = canvas.LocalClipBounds.Size.ToSizeI().Height / 2;
|
|
||||||
var radius = midX - midX / 5;
|
|
||||||
|
|
||||||
using (var circlePaint = new SKPaint
|
|
||||||
{
|
|
||||||
IsAntialias = true,
|
|
||||||
Style = SKPaintStyle.Fill,
|
|
||||||
StrokeJoin = SKStrokeJoin.Miter,
|
|
||||||
Color = SKColor.Parse(avatarInfo.BackgroundColor)
|
|
||||||
})
|
|
||||||
{
|
|
||||||
canvas.DrawCircle(midX, midY, radius, circlePaint);
|
|
||||||
|
|
||||||
var typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal);
|
|
||||||
var textSize = midX / 1.3f;
|
|
||||||
using (var textPaint = new SKPaint
|
|
||||||
{
|
|
||||||
IsAntialias = true,
|
|
||||||
Style = SKPaintStyle.Fill,
|
|
||||||
Color = SKColor.Parse(avatarInfo.TextColor),
|
|
||||||
TextSize = textSize,
|
|
||||||
TextAlign = SKTextAlign.Center,
|
|
||||||
Typeface = typeface
|
|
||||||
})
|
|
||||||
{
|
|
||||||
var rect = new SKRect();
|
|
||||||
textPaint.MeasureText(avatarInfo.CharsToDraw, ref rect);
|
|
||||||
canvas.DrawText(avatarInfo.CharsToDraw, midX, midY + rect.Height / 2, textPaint);
|
|
||||||
|
|
||||||
return SKImage.FromBitmap(bitmap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
179
src/Core/Controls/AvatarImageSource.cs
Normal file
179
src/Core/Controls/AvatarImageSource.cs
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
using Bit.Core.Utilities;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class AvatarImageSource : StreamImageSource
|
||||||
|
{
|
||||||
|
private readonly string _text;
|
||||||
|
private readonly string _id;
|
||||||
|
private readonly string _color;
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is AvatarImageSource avatar)
|
||||||
|
{
|
||||||
|
return avatar._id == _id && avatar._text == _text && avatar._color == _color;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.Equals(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode() => _id?.GetHashCode() ?? _text?.GetHashCode() ?? -1;
|
||||||
|
|
||||||
|
public AvatarImageSource(string userId = null, string name = null, string email = null, string color = null)
|
||||||
|
{
|
||||||
|
_id = userId;
|
||||||
|
_text = name;
|
||||||
|
if (string.IsNullOrWhiteSpace(_text))
|
||||||
|
{
|
||||||
|
_text = email;
|
||||||
|
}
|
||||||
|
_color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Func<CancellationToken, Task<Stream>> Stream => GetStreamAsync;
|
||||||
|
|
||||||
|
private Task<Stream> GetStreamAsync(CancellationToken userToken = new CancellationToken())
|
||||||
|
{
|
||||||
|
var result = Draw();
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream Draw()
|
||||||
|
{
|
||||||
|
string chars;
|
||||||
|
string upperCaseText = null;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(_text))
|
||||||
|
{
|
||||||
|
chars = "..";
|
||||||
|
}
|
||||||
|
else if (_text?.Length > 1)
|
||||||
|
{
|
||||||
|
upperCaseText = _text.ToUpper();
|
||||||
|
chars = GetFirstLetters(upperCaseText, 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chars = upperCaseText = _text.ToUpper();
|
||||||
|
}
|
||||||
|
|
||||||
|
var bgColor = _color ?? CoreHelpers.StringToColor(_id ?? upperCaseText, "#33ffffff");
|
||||||
|
var textColor = CoreHelpers.TextColorFromBgColor(bgColor);
|
||||||
|
var size = 50;
|
||||||
|
|
||||||
|
//Workaround: [MAUI-Migration] There is currently a bug in MAUI where the actual size of the image is used instead of the size it should occupy in the Toolbar.
|
||||||
|
//This causes some issues with the position of the icon. As a workaround we make the icon smaller until this is fixed.
|
||||||
|
//Github issues: https://github.com/dotnet/maui/issues/12359 and https://github.com/dotnet/maui/pull/17120
|
||||||
|
if (DeviceInfo.Platform == DevicePlatform.iOS)
|
||||||
|
{
|
||||||
|
size = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var bitmap = new SKBitmap(size * 2,
|
||||||
|
size * 2,
|
||||||
|
SKImageInfo.PlatformColorType,
|
||||||
|
SKAlphaType.Premul))
|
||||||
|
{
|
||||||
|
using (var canvas = new SKCanvas(bitmap))
|
||||||
|
{
|
||||||
|
canvas.Clear(SKColors.Transparent);
|
||||||
|
using (var paint = new SKPaint
|
||||||
|
{
|
||||||
|
IsAntialias = true,
|
||||||
|
Style = SKPaintStyle.Fill,
|
||||||
|
StrokeJoin = SKStrokeJoin.Miter,
|
||||||
|
Color = SKColor.Parse(bgColor)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
var midX = canvas.LocalClipBounds.Size.ToSizeI().Width / 2;
|
||||||
|
var midY = canvas.LocalClipBounds.Size.ToSizeI().Height / 2;
|
||||||
|
var radius = midX - midX / 5;
|
||||||
|
|
||||||
|
using (var circlePaint = new SKPaint
|
||||||
|
{
|
||||||
|
IsAntialias = true,
|
||||||
|
Style = SKPaintStyle.Fill,
|
||||||
|
StrokeJoin = SKStrokeJoin.Miter,
|
||||||
|
Color = SKColor.Parse(bgColor)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
canvas.DrawCircle(midX, midY, radius, circlePaint);
|
||||||
|
|
||||||
|
var typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal);
|
||||||
|
var textSize = midX / 1.3f;
|
||||||
|
using (var textPaint = new SKPaint
|
||||||
|
{
|
||||||
|
IsAntialias = true,
|
||||||
|
Style = SKPaintStyle.Fill,
|
||||||
|
Color = SKColor.Parse(textColor),
|
||||||
|
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);
|
||||||
|
|
||||||
|
using (var img = SKImage.FromBitmap(bitmap))
|
||||||
|
{
|
||||||
|
var data = img.Encode(SKEncodedImageFormat.Png, 100);
|
||||||
|
return data?.AsStream(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFirstLetters(string data, int charCount)
|
||||||
|
{
|
||||||
|
var sanitizedData = data.Trim();
|
||||||
|
var parts = sanitizedData.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if (parts.Length > 1 && charCount <= 2)
|
||||||
|
{
|
||||||
|
var text = string.Empty;
|
||||||
|
for (var i = 0; i < charCount; i++)
|
||||||
|
{
|
||||||
|
text += parts[i][0];
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
if (sanitizedData.Length > 2)
|
||||||
|
{
|
||||||
|
return sanitizedData.Substring(0, 2);
|
||||||
|
}
|
||||||
|
return sanitizedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color StringToColor(string str)
|
||||||
|
{
|
||||||
|
if (str == null)
|
||||||
|
{
|
||||||
|
return Color.FromArgb("#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);
|
||||||
|
}
|
||||||
|
return Color.FromArgb(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
namespace Bit.App.Controls
|
|
||||||
{
|
|
||||||
#if !UT
|
|
||||||
public class CachedImage : FFImageLoading.Maui.CachedImage
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/// <summary>
|
|
||||||
/// Given that FFImageLoading package doesn't support net8.0 then for Unit tests projects to build and run correctly
|
|
||||||
/// we need to not include the reference to FFImageLoading and therefore wrap this class
|
|
||||||
/// to provide a stub one that does nothing so this project doesn't break and we can run the tests.
|
|
||||||
/// </summary>
|
|
||||||
public class CachedImage : View
|
|
||||||
{
|
|
||||||
public static readonly BindableProperty SourceProperty = BindableProperty.Create(
|
|
||||||
nameof(Source), typeof(ImageSource), typeof(CachedImage));
|
|
||||||
|
|
||||||
public static readonly BindableProperty AspectProperty = BindableProperty.Create(
|
|
||||||
nameof(Aspect), typeof(Aspect), typeof(CachedImage));
|
|
||||||
|
|
||||||
public bool BitmapOptimizations { get; set; }
|
|
||||||
public string ErrorPlaceholder { get; set; }
|
|
||||||
public string LoadingPlaceholder { get; set; }
|
|
||||||
|
|
||||||
public ImageSource Source
|
|
||||||
{
|
|
||||||
get { return (ImageSource)GetValue(SourceProperty); }
|
|
||||||
set { SetValue(SourceProperty, value); }
|
|
||||||
}
|
|
||||||
public Aspect Aspect
|
|
||||||
{
|
|
||||||
get { return (Aspect)GetValue(AspectProperty); }
|
|
||||||
set { SetValue(AspectProperty, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsLoading { get; set; }
|
|
||||||
|
|
||||||
public event EventHandler Success;
|
|
||||||
public event EventHandler Error;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
using Bit.App.Pages;
|
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
|
||||||
{
|
|
||||||
public abstract class BaseCipherViewCell : ExtendedGrid
|
|
||||||
{
|
|
||||||
protected virtual CachedImage Icon { get; }
|
|
||||||
|
|
||||||
protected virtual IconLabel IconPlaceholder { get; }
|
|
||||||
|
|
||||||
// HACK: PM-5896 Fix for Background Crash on iOS
|
|
||||||
// While loading the cipher icon and the user sent the app to background
|
|
||||||
// the app was crashing sometimes when the "LoadingPlaceholder" or "ErrorPlaceholder"
|
|
||||||
// were being accessed, thus locked, and as soon the app got suspended by the OS
|
|
||||||
// the app would crash because there can't be any lock files by the app when it gets suspended.
|
|
||||||
// So, the approach has changed to reuse the IconLabel default icon to use it for these placeholders
|
|
||||||
// as well. In order to do that both icon controls change their visibility dynamically here reacting to
|
|
||||||
// CachedImage events and binding context changes.
|
|
||||||
|
|
||||||
protected override void OnBindingContextChanged()
|
|
||||||
{
|
|
||||||
Icon.Source = null;
|
|
||||||
if (BindingContext is CipherItemViewModel cipherItemVM)
|
|
||||||
{
|
|
||||||
Icon.Source = cipherItemVM.IconImageSource;
|
|
||||||
if (!cipherItemVM.IconImageSuccesfullyLoaded)
|
|
||||||
{
|
|
||||||
UpdateIconImages(cipherItemVM.ShowIconImage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
base.OnBindingContextChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateIconImages(bool showIcon)
|
|
||||||
{
|
|
||||||
MainThread.BeginInvokeOnMainThread(() =>
|
|
||||||
{
|
|
||||||
if (!showIcon)
|
|
||||||
{
|
|
||||||
Icon.IsVisible = false;
|
|
||||||
IconPlaceholder.IsVisible = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IconPlaceholder.IsVisible = Icon.IsLoading;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !UT
|
|
||||||
public void Icon_Success(object sender, FFImageLoading.Maui.CachedImageEvents.SuccessEventArgs e)
|
|
||||||
{
|
|
||||||
if (BindingContext is CipherItemViewModel cipherItemVM)
|
|
||||||
{
|
|
||||||
cipherItemVM.IconImageSuccesfullyLoaded = true;
|
|
||||||
|
|
||||||
MainThread.BeginInvokeOnMainThread(() =>
|
|
||||||
{
|
|
||||||
Icon.IsVisible = cipherItemVM.ShowIconImage;
|
|
||||||
IconPlaceholder.IsVisible = !cipherItemVM.ShowIconImage;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Icon_Error(object sender, FFImageLoading.Maui.CachedImageEvents.ErrorEventArgs e)
|
|
||||||
{
|
|
||||||
if (BindingContext is CipherItemViewModel cipherItemVM)
|
|
||||||
{
|
|
||||||
cipherItemVM.IconImageSuccesfullyLoaded = false;
|
|
||||||
}
|
|
||||||
MainThread.BeginInvokeOnMainThread(() =>
|
|
||||||
{
|
|
||||||
Icon.IsVisible = false;
|
|
||||||
IconPlaceholder.IsVisible = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
private void Icon_Success(object sender, EventArgs e) {}
|
|
||||||
private void Icon_Error(object sender, EventArgs e) {}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public class StubBaseCipherViewCellSoLinkerDoesntRemoveMethods : BaseCipherViewCell
|
|
||||||
{
|
|
||||||
protected override CachedImage Icon => new CachedImage();
|
|
||||||
protected override IconLabel IconPlaceholder => new IconLabel();
|
|
||||||
|
|
||||||
public static void CallThisSoLinkerDoesntRemoveMethods()
|
|
||||||
{
|
|
||||||
#if !UT
|
|
||||||
var stub = new StubBaseCipherViewCellSoLinkerDoesntRemoveMethods();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
stub.Icon_Success(stub, new FFImageLoading.Maui.CachedImageEvents.SuccessEventArgs(new FFImageLoading.Work.ImageInformation(), FFImageLoading.Work.LoadingResult.Disk));
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
stub.Icon_Error(stub, new FFImageLoading.Maui.CachedImageEvents.ErrorEventArgs(new InvalidOperationException("stub")));
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<controls:BaseCipherViewCell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
<controls:ExtendedGrid xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
x:Class="Bit.App.Controls.CipherViewCell"
|
x:Class="Bit.App.Controls.CipherViewCell"
|
||||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
|
xmlns:ff="clr-namespace:FFImageLoading.Maui;assembly=FFImageLoading.Compat.Maui"
|
||||||
xmlns:core="clr-namespace:Bit.Core"
|
xmlns:core="clr-namespace:Bit.Core"
|
||||||
StyleClass="list-row, list-row-platform"
|
StyleClass="list-row, list-row-platform"
|
||||||
RowSpacing="0"
|
RowSpacing="0"
|
||||||
@@ -29,32 +30,34 @@
|
|||||||
<ColumnDefinition Width="60" />
|
<ColumnDefinition Width="60" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<controls:CachedImage
|
<controls:IconLabel
|
||||||
|
Grid.Column="0"
|
||||||
|
HorizontalOptions="Center"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
StyleClass="list-icon, list-icon-platform"
|
||||||
|
IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}"
|
||||||
|
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
|
||||||
|
ShouldUpdateFontSizeDynamicallyForAccesibility="True"
|
||||||
|
AutomationProperties.IsInAccessibleTree="False"
|
||||||
|
AutomationId="CipherTypeIcon" />
|
||||||
|
|
||||||
|
<ff:CachedImage
|
||||||
x:Name="_iconImage"
|
x:Name="_iconImage"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
BitmapOptimizations="True"
|
BitmapOptimizations="True"
|
||||||
|
ErrorPlaceholder="login.png"
|
||||||
|
LoadingPlaceholder="login.png"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
VerticalOptions="CenterAndExpand"
|
VerticalOptions="CenterAndExpand"
|
||||||
Margin="9"
|
Margin="9"
|
||||||
WidthRequest="22"
|
WidthRequest="22"
|
||||||
HeightRequest="22"
|
HeightRequest="22"
|
||||||
Aspect="AspectFit"
|
Aspect="AspectFit"
|
||||||
Success="Icon_Success"
|
IsVisible="{Binding ShowIconImage}"
|
||||||
Error="Icon_Error"
|
Source="{Binding IconImageSource, Mode=OneTime}"
|
||||||
AutomationProperties.IsInAccessibleTree="False"
|
AutomationProperties.IsInAccessibleTree="False"
|
||||||
AutomationId="CipherWebsiteIcon" />
|
AutomationId="CipherWebsiteIcon" />
|
||||||
|
|
||||||
<controls:IconLabel
|
|
||||||
x:Name="_iconPlaceholderImage"
|
|
||||||
Grid.Column="0"
|
|
||||||
HorizontalOptions="Center"
|
|
||||||
VerticalOptions="Center"
|
|
||||||
StyleClass="list-icon, list-icon-platform"
|
|
||||||
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
|
|
||||||
ShouldUpdateFontSizeDynamicallyForAccesibility="True"
|
|
||||||
AutomationProperties.IsInAccessibleTree="False"
|
|
||||||
AutomationId="CipherTypeIcon" />
|
|
||||||
|
|
||||||
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
|
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
@@ -119,4 +122,4 @@
|
|||||||
SemanticProperties.Description="{u:I18n Options}"
|
SemanticProperties.Description="{u:I18n Options}"
|
||||||
AutomationId="CipherOptionsButton" />
|
AutomationId="CipherOptionsButton" />
|
||||||
|
|
||||||
</controls:BaseCipherViewCell>
|
</controls:ExtendedGrid>
|
||||||
@@ -5,7 +5,7 @@ using Bit.Core.Utilities;
|
|||||||
|
|
||||||
namespace Bit.App.Controls
|
namespace Bit.App.Controls
|
||||||
{
|
{
|
||||||
public partial class CipherViewCell : BaseCipherViewCell
|
public partial class CipherViewCell : ExtendedGrid
|
||||||
{
|
{
|
||||||
private const int ICON_COLUMN_DEFAULT_WIDTH = 40;
|
private const int ICON_COLUMN_DEFAULT_WIDTH = 40;
|
||||||
private const int ICON_IMAGE_DEFAULT_WIDTH = 22;
|
private const int ICON_IMAGE_DEFAULT_WIDTH = 22;
|
||||||
@@ -23,10 +23,6 @@ namespace Bit.App.Controls
|
|||||||
_iconImage.HeightRequest = ICON_IMAGE_DEFAULT_WIDTH * fontScale;
|
_iconImage.HeightRequest = ICON_IMAGE_DEFAULT_WIDTH * fontScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override CachedImage Icon => _iconImage;
|
|
||||||
|
|
||||||
protected override IconLabel IconPlaceholder => _iconPlaceholderImage;
|
|
||||||
|
|
||||||
public ICommand ButtonCommand
|
public ICommand ButtonCommand
|
||||||
{
|
{
|
||||||
get => GetValue(ButtonCommandProperty) as ICommand;
|
get => GetValue(ButtonCommandProperty) as ICommand;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<ContentView
|
<ContentView
|
||||||
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
LineBreakMode="TailTruncation" />
|
LineBreakMode="TailTruncation" />
|
||||||
|
|
||||||
<controls:IconLabel
|
<controls:IconLabel
|
||||||
Text="{Binding Source={x:Static core:BitwardenIcons.ExternalLink}}"
|
Text="{Binding Source={x:Static core:BitwardenIcons.ShareSquare}}"
|
||||||
TextColor="{DynamicResource TextColor}"
|
TextColor="{DynamicResource TextColor}"
|
||||||
HorizontalOptions="End"
|
HorizontalOptions="End"
|
||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
|
|||||||
@@ -1,299 +0,0 @@
|
|||||||
#if ANDROID
|
|
||||||
using System;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using Android.App;
|
|
||||||
using Android.Content.PM;
|
|
||||||
using Android.Content.Res;
|
|
||||||
using Android.Graphics.Drawables;
|
|
||||||
using Android.Text;
|
|
||||||
using Android.Text.Style;
|
|
||||||
using Android.Widget;
|
|
||||||
using Microsoft.Maui;
|
|
||||||
using Microsoft.Maui.Handlers;
|
|
||||||
using Microsoft.Maui.Platform;
|
|
||||||
using AGravityFlags = Android.Views.GravityFlags;
|
|
||||||
using ALayoutDirection = Android.Views.LayoutDirection;
|
|
||||||
using AppCompatAlertDialog = AndroidX.AppCompat.App.AlertDialog;
|
|
||||||
using AResource = Android.Resource;
|
|
||||||
using ATextAlignment = Android.Views.TextAlignment;
|
|
||||||
using ATextDirection = Android.Views.TextDirection;
|
|
||||||
|
|
||||||
namespace Bit.Core.Controls.Picker
|
|
||||||
{
|
|
||||||
// HACK: Due to https://github.com/dotnet/maui/issues/19681 and not willing to use reflection to access
|
|
||||||
// the alert dialog, we need to redefine the PickerHandler implementation for a custom one of ours
|
|
||||||
// which handles showing the current selected item. Remove this workaround when MAUI releases a fix for this.
|
|
||||||
// This is an adapted copy from https://github.com/dotnet/maui/blob/main/src/Core/src/Handlers/Picker/PickerHandler.Android.cs
|
|
||||||
public partial class PickerHandler : ViewHandler<IPicker, MauiPicker>
|
|
||||||
{
|
|
||||||
AppCompatAlertDialog? _dialog;
|
|
||||||
|
|
||||||
protected override MauiPicker CreatePlatformView() =>
|
|
||||||
new MauiPicker(Context);
|
|
||||||
|
|
||||||
protected override void ConnectHandler(MauiPicker platformView)
|
|
||||||
{
|
|
||||||
platformView.FocusChange += OnFocusChange;
|
|
||||||
platformView.Click += OnClick;
|
|
||||||
|
|
||||||
base.ConnectHandler(platformView);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DisconnectHandler(MauiPicker platformView)
|
|
||||||
{
|
|
||||||
platformView.FocusChange -= OnFocusChange;
|
|
||||||
platformView.Click -= OnClick;
|
|
||||||
|
|
||||||
base.DisconnectHandler(platformView);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a Android-specific mapping
|
|
||||||
public static void MapBackground(IPickerHandler handler, IPicker picker)
|
|
||||||
{
|
|
||||||
handler.PlatformView?.UpdateBackground(picker);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Uncomment me on NET8 [Obsolete]
|
|
||||||
public static void MapReload(IPickerHandler handler, IPicker picker, object? args) => Reload(handler);
|
|
||||||
|
|
||||||
internal static void MapItems(IPickerHandler handler, IPicker picker) => Reload(handler);
|
|
||||||
|
|
||||||
public static void MapTitle(IPickerHandler handler, IPicker picker)
|
|
||||||
{
|
|
||||||
handler.PlatformView?.UpdateTitle(picker);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void MapTitleColor(IPickerHandler handler, IPicker picker)
|
|
||||||
{
|
|
||||||
handler.PlatformView?.UpdateTitleColor(picker);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void MapSelectedIndex(IPickerHandler handler, IPicker picker)
|
|
||||||
{
|
|
||||||
handler.PlatformView?.UpdateSelectedIndex(picker);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void MapCharacterSpacing(IPickerHandler handler, IPicker picker)
|
|
||||||
{
|
|
||||||
handler.PlatformView?.UpdateCharacterSpacing(picker);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void MapFont(IPickerHandler handler, IPicker picker)
|
|
||||||
{
|
|
||||||
var fontManager = handler.GetRequiredService<IFontManager>();
|
|
||||||
|
|
||||||
handler.PlatformView?.UpdateFont(picker, fontManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void MapHorizontalTextAlignment(IPickerHandler handler, IPicker picker)
|
|
||||||
{
|
|
||||||
handler.PlatformView?.UpdateHorizontalAlignment(picker.HorizontalTextAlignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void MapTextColor(IPickerHandler handler, IPicker picker)
|
|
||||||
{
|
|
||||||
handler.PlatformView.UpdateTextColor(picker);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void MapVerticalTextAlignment(IPickerHandler handler, IPicker picker)
|
|
||||||
{
|
|
||||||
handler.PlatformView?.UpdateVerticalAlignment(picker.VerticalTextAlignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnFocusChange(object? sender, global::Android.Views.View.FocusChangeEventArgs e)
|
|
||||||
{
|
|
||||||
if (PlatformView == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (e.HasFocus)
|
|
||||||
{
|
|
||||||
if (PlatformView.Clickable)
|
|
||||||
PlatformView.CallOnClick();
|
|
||||||
else
|
|
||||||
OnClick(PlatformView, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
else if (_dialog != null)
|
|
||||||
{
|
|
||||||
_dialog.Hide();
|
|
||||||
_dialog = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnClick(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
if (_dialog == null && VirtualView != null)
|
|
||||||
{
|
|
||||||
using (var builder = new AppCompatAlertDialog.Builder(Context))
|
|
||||||
{
|
|
||||||
if (VirtualView.TitleColor == null)
|
|
||||||
{
|
|
||||||
builder.SetTitle(VirtualView.Title ?? string.Empty);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var title = new SpannableString(VirtualView.Title ?? string.Empty);
|
|
||||||
#pragma warning disable CA1416 // https://github.com/xamarin/xamarin-android/issues/6962
|
|
||||||
title.SetSpan(new ForegroundColorSpan(VirtualView.TitleColor.ToPlatform()), 0, title.Length(), SpanTypes.ExclusiveExclusive);
|
|
||||||
#pragma warning restore CA1416
|
|
||||||
builder.SetTitle(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] items = VirtualView.GetItemsAsArray();
|
|
||||||
|
|
||||||
for (var i = 0; i < items.Length; i++)
|
|
||||||
{
|
|
||||||
var item = items[i];
|
|
||||||
if (item == null)
|
|
||||||
items[i] = String.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.SetSingleChoiceItems(items, VirtualView.SelectedIndex, (s, e) =>
|
|
||||||
{
|
|
||||||
var selectedIndex = e.Which;
|
|
||||||
VirtualView.SelectedIndex = selectedIndex;
|
|
||||||
base.PlatformView?.UpdatePicker(VirtualView);
|
|
||||||
_dialog.Dismiss();
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.SetNegativeButton(AResource.String.Cancel, (o, args) => { });
|
|
||||||
|
|
||||||
_dialog = builder.Create();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_dialog == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_dialog.UpdateFlowDirection(PlatformView);
|
|
||||||
|
|
||||||
_dialog.SetCanceledOnTouchOutside(true);
|
|
||||||
|
|
||||||
_dialog.DismissEvent += (sender, args) =>
|
|
||||||
{
|
|
||||||
_dialog = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
_dialog.Show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Reload(IPickerHandler handler)
|
|
||||||
{
|
|
||||||
handler.PlatformView.UpdatePicker(handler.VirtualView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PickerExtensions
|
|
||||||
{
|
|
||||||
const AGravityFlags HorizontalGravityMask = AGravityFlags.CenterHorizontal | AGravityFlags.End | AGravityFlags.Start;
|
|
||||||
|
|
||||||
internal static void UpdatePicker(this MauiPicker platformPicker, IPicker picker)
|
|
||||||
{
|
|
||||||
platformPicker.Hint = picker.Title;
|
|
||||||
|
|
||||||
if (picker.SelectedIndex == -1 || picker.SelectedIndex >= picker.GetCount())
|
|
||||||
platformPicker.Text = null;
|
|
||||||
else
|
|
||||||
platformPicker.Text = picker.GetItem(picker.SelectedIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void UpdateHorizontalAlignment(this EditText view, TextAlignment alignment, AGravityFlags orMask = AGravityFlags.NoGravity)
|
|
||||||
{
|
|
||||||
if (!Rtl.IsSupported)
|
|
||||||
{
|
|
||||||
view.Gravity = (view.Gravity & ~HorizontalGravityMask) | alignment.ToHorizontalGravityFlags() | orMask;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
view.TextAlignment = alignment.ToTextAlignment();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static AGravityFlags ToHorizontalGravityFlags(this TextAlignment alignment)
|
|
||||||
{
|
|
||||||
switch (alignment)
|
|
||||||
{
|
|
||||||
case TextAlignment.Center:
|
|
||||||
return AGravityFlags.CenterHorizontal;
|
|
||||||
case TextAlignment.End:
|
|
||||||
return AGravityFlags.End;
|
|
||||||
default:
|
|
||||||
return AGravityFlags.Start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static ATextAlignment ToTextAlignment(this TextAlignment alignment)
|
|
||||||
{
|
|
||||||
switch (alignment)
|
|
||||||
{
|
|
||||||
case TextAlignment.Center:
|
|
||||||
return ATextAlignment.Center;
|
|
||||||
case TextAlignment.End:
|
|
||||||
return ATextAlignment.ViewEnd;
|
|
||||||
default:
|
|
||||||
return ATextAlignment.ViewStart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void UpdateFlowDirection(this AndroidX.AppCompat.App.AlertDialog alertDialog, MauiPicker platformPicker)
|
|
||||||
{
|
|
||||||
var platformLayoutDirection = platformPicker.LayoutDirection;
|
|
||||||
|
|
||||||
// Propagate the MauiPicker LayoutDirection to the AlertDialog
|
|
||||||
var dv = alertDialog.Window?.DecorView;
|
|
||||||
|
|
||||||
if (dv is not null)
|
|
||||||
dv.LayoutDirection = platformLayoutDirection;
|
|
||||||
|
|
||||||
var lv = alertDialog?.ListView;
|
|
||||||
|
|
||||||
if (lv is not null)
|
|
||||||
{
|
|
||||||
lv.LayoutDirection = platformLayoutDirection;
|
|
||||||
lv.TextDirection = platformLayoutDirection.ToTextDirection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static ATextDirection ToTextDirection(this ALayoutDirection direction)
|
|
||||||
{
|
|
||||||
switch (direction)
|
|
||||||
{
|
|
||||||
case ALayoutDirection.Ltr:
|
|
||||||
return ATextDirection.Ltr;
|
|
||||||
case ALayoutDirection.Rtl:
|
|
||||||
return ATextDirection.Rtl;
|
|
||||||
default:
|
|
||||||
return ATextDirection.Inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T GetRequiredService<T>(this IElementHandler handler)
|
|
||||||
where T : notnull
|
|
||||||
{
|
|
||||||
var services = handler.GetServiceProvider();
|
|
||||||
|
|
||||||
var service = services.GetRequiredService<T>();
|
|
||||||
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceProvider GetServiceProvider(this IElementHandler handler)
|
|
||||||
{
|
|
||||||
var context = handler.MauiContext ??
|
|
||||||
throw new InvalidOperationException($"Unable to find the context. The {nameof(ElementHandler.MauiContext)} property should have been set by the host.");
|
|
||||||
|
|
||||||
var services = context?.Services ??
|
|
||||||
throw new InvalidOperationException($"Unable to find the service provider. The {nameof(ElementHandler.MauiContext)} property should have been set by the host.");
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Rtl
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// True if /manifest/application@android:supportsRtl="true"
|
|
||||||
/// </summary>
|
|
||||||
public static readonly bool IsSupported =
|
|
||||||
(Android.App.Application.Context?.ApplicationInfo?.Flags & ApplicationInfoFlags.SupportsRtl) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#if ANDROID
|
|
||||||
using Microsoft.Maui.Handlers;
|
|
||||||
using Microsoft.Maui.Platform;
|
|
||||||
|
|
||||||
namespace Bit.Core.Controls.Picker
|
|
||||||
{
|
|
||||||
// HACK: Due to https://github.com/dotnet/maui/issues/19681 and not willing to use reflection to access
|
|
||||||
// the alert dialog, we need to redefine the PickerHandler implementation for a custom one of ours
|
|
||||||
// which handles showing the current selected item. Remove this workaround when MAUI releases a fix for this.
|
|
||||||
// This is a copy from https://github.com/dotnet/maui/blob/main/src/Core/src/Handlers/Picker/PickerHandler.cs
|
|
||||||
public partial class PickerHandler : ViewHandler<IPicker, MauiPicker>, IPickerHandler
|
|
||||||
{
|
|
||||||
public static IPropertyMapper<IPicker, IPickerHandler> Mapper = new PropertyMapper<IPicker, PickerHandler>(ViewMapper)
|
|
||||||
{
|
|
||||||
#if __ANDROID__ || WINDOWS
|
|
||||||
[nameof(IPicker.Background)] = MapBackground,
|
|
||||||
#endif
|
|
||||||
[nameof(IPicker.CharacterSpacing)] = MapCharacterSpacing,
|
|
||||||
[nameof(IPicker.Font)] = MapFont,
|
|
||||||
[nameof(IPicker.SelectedIndex)] = MapSelectedIndex,
|
|
||||||
[nameof(IPicker.TextColor)] = MapTextColor,
|
|
||||||
[nameof(IPicker.Title)] = MapTitle,
|
|
||||||
[nameof(IPicker.TitleColor)] = MapTitleColor,
|
|
||||||
[nameof(ITextAlignment.HorizontalTextAlignment)] = MapHorizontalTextAlignment,
|
|
||||||
[nameof(ITextAlignment.VerticalTextAlignment)] = MapVerticalTextAlignment,
|
|
||||||
[nameof(IPicker.Items)] = MapItems,
|
|
||||||
};
|
|
||||||
|
|
||||||
public static CommandMapper<IPicker, IPickerHandler> CommandMapper = new(ViewCommandMapper)
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
public PickerHandler() : base(Mapper, CommandMapper)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public PickerHandler(IPropertyMapper? mapper)
|
|
||||||
: base(mapper ?? Mapper, CommandMapper)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public PickerHandler(IPropertyMapper? mapper, CommandMapper? commandMapper)
|
|
||||||
: base(mapper ?? Mapper, commandMapper ?? CommandMapper)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IPicker IPickerHandler.VirtualView => VirtualView;
|
|
||||||
|
|
||||||
Microsoft.Maui.Platform.MauiPicker IPickerHandler.PlatformView => PlatformView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!--When running Unit tests we'll have the custom constant "UT" added, so in this manner we can add the net8.0 target we need for UT -->
|
<TargetFrameworks>net8.0-android;net8.0-ios</TargetFrameworks>
|
||||||
<TargetFrameworks Condition="$(CustomConstants.Contains(UT))">net8.0;net8.0-android;net8.0-ios</TargetFrameworks>
|
|
||||||
<TargetFrameworks Condition="!$(CustomConstants.Contains(UT))">net8.0-android;net8.0-ios</TargetFrameworks>
|
|
||||||
<RootNamespace>Bit.Core</RootNamespace>
|
<RootNamespace>Bit.Core</RootNamespace>
|
||||||
|
|
||||||
<UseMaui>true</UseMaui>
|
<UseMaui>true</UseMaui>
|
||||||
@@ -43,11 +41,10 @@
|
|||||||
<PackageReference Include="Plugin.Fingerprint" Version="3.0.0-beta.1" />
|
<PackageReference Include="Plugin.Fingerprint" Version="3.0.0-beta.1" />
|
||||||
<PackageReference Include="SkiaSharp.Views.Maui.Controls" Version="2.88.4-preview.84" />
|
<PackageReference Include="SkiaSharp.Views.Maui.Controls" Version="2.88.4-preview.84" />
|
||||||
<PackageReference Include="SkiaSharp.Views.Maui.Controls.Compatibility" Version="2.88.4-preview.84" />
|
<PackageReference Include="SkiaSharp.Views.Maui.Controls.Compatibility" Version="2.88.4-preview.84" />
|
||||||
|
<PackageReference Include="FFImageLoadingCompat.Maui" Version="0.1.1" />
|
||||||
<PackageReference Include="AsyncAwaitBestPractices.MVVM" Version="6.0.6" />
|
<PackageReference Include="AsyncAwaitBestPractices.MVVM" Version="6.0.6" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
|
||||||
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
|
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
|
||||||
<!-- HACK: When running Unit Tests we cannot load FFImageLoading because it doesn't support "raw" net8.0 -->
|
|
||||||
<PackageReference Condition="!$(CustomConstants.Contains(UT))" Include="FFImageLoadingCompat.Maui" Version="0.1.1" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
|
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
|
||||||
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.18" />
|
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.18" />
|
||||||
@@ -67,6 +64,7 @@
|
|||||||
<Folder Include="Resources\Fonts\" />
|
<Folder Include="Resources\Fonts\" />
|
||||||
<Folder Include="Effects\" />
|
<Folder Include="Effects\" />
|
||||||
<Folder Include="Resources\Raw\" />
|
<Folder Include="Resources\Raw\" />
|
||||||
|
<Folder Include="Pages\" />
|
||||||
<Folder Include="Behaviors\" />
|
<Folder Include="Behaviors\" />
|
||||||
<Folder Include="Controls\" />
|
<Folder Include="Controls\" />
|
||||||
<Folder Include="Lists\" />
|
<Folder Include="Lists\" />
|
||||||
@@ -75,9 +73,6 @@
|
|||||||
<Folder Include="Utilities\Automation\" />
|
<Folder Include="Utilities\Automation\" />
|
||||||
<Folder Include="Utilities\Prompts\" />
|
<Folder Include="Utilities\Prompts\" />
|
||||||
<Folder Include="Resources\Localization\" />
|
<Folder Include="Resources\Localization\" />
|
||||||
<Folder Include="Controls\Picker\" />
|
|
||||||
<Folder Include="Controls\Avatar\" />
|
|
||||||
<Folder Include="Utilities\WebAuthenticatorMAUI\" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<MauiImage Include="Resources\Images\dotnet_bot.svg">
|
<MauiImage Include="Resources\Images\dotnet_bot.svg">
|
||||||
@@ -91,23 +86,10 @@
|
|||||||
<LastGenOutput>AppResources.Designer.cs</LastGenOutput>
|
<LastGenOutput>AppResources.Designer.cs</LastGenOutput>
|
||||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<Compile Update="Pages\AndroidNavigationRedirectPage.xaml.cs">
|
|
||||||
<DependentUpon>AndroidNavigationRedirectPage.xaml</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Resources\Localization\AppResources.Designer.cs">
|
<Compile Update="Resources\Localization\AppResources.Designer.cs">
|
||||||
<DependentUpon>AppResources.resx</DependentUpon>
|
<DependentUpon>AppResources.resx</DependentUpon>
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
</Compile>
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<MauiXaml Update="Pages\AndroidNavigationRedirectPage.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</MauiXaml>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="Controls\Picker\" />
|
|
||||||
<None Remove="Controls\Avatar\" />
|
|
||||||
<None Remove="Utilities\WebAuthenticatorMAUI\" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -6,18 +6,13 @@
|
|||||||
public const string HELP_ABOUT_ORGANIZATIONS = "https://bitwarden.com/help/about-organizations/";
|
public const string HELP_ABOUT_ORGANIZATIONS = "https://bitwarden.com/help/about-organizations/";
|
||||||
public const string HELP_FINGERPRINT_PHRASE = "https://bitwarden.com/help/fingerprint-phrase/";
|
public const string HELP_FINGERPRINT_PHRASE = "https://bitwarden.com/help/fingerprint-phrase/";
|
||||||
|
|
||||||
public const string PRIVACY_POLICY = "https://bitwarden.com/privacy/";
|
public const string CONTACT_SUPPORT = "https://bitwarden.com/contact/";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Link to go to settings website. Requires to pass website URL as parameter.
|
/// Link to go to settings website. Requires to pass website URL as parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string WEB_VAULT_SETTINGS_FORMAT = "{0}/#/settings";
|
public const string WEB_VAULT_SETTINGS_FORMAT = "{0}/#/settings";
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Link to go to individual vault import page. Requires to pass vault URL as parameter.
|
|
||||||
/// </summary>
|
|
||||||
public const string WEB_VAULT_TOOLS_IMPORT_FORMAT = "{0}/#/tools/import";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// General website, not in the full format of a URL given that this is used as parameter of string resources to be shown to the user.
|
/// General website, not in the full format of a URL given that this is used as parameter of string resources to be shown to the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
using Bit.App.Controls;
|
using Camera.MAUI;
|
||||||
using Camera.MAUI;
|
|
||||||
using CommunityToolkit.Maui;
|
using CommunityToolkit.Maui;
|
||||||
#if !UT
|
|
||||||
using FFImageLoading.Maui;
|
using FFImageLoading.Maui;
|
||||||
#endif
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Maui.Controls.Compatibility.Hosting;
|
using Microsoft.Maui.Controls.Compatibility.Hosting;
|
||||||
using Microsoft.Maui.Handlers;
|
|
||||||
using SkiaSharp.Views.Maui.Controls.Hosting;
|
using SkiaSharp.Views.Maui.Controls.Hosting;
|
||||||
using AppEffects = Bit.App.Effects;
|
using AppEffects = Bit.App.Effects;
|
||||||
|
|
||||||
@@ -26,9 +22,7 @@ public static class MauiProgram
|
|||||||
.UseMauiCompatibility()
|
.UseMauiCompatibility()
|
||||||
.UseMauiCameraView()
|
.UseMauiCameraView()
|
||||||
.UseSkiaSharp()
|
.UseSkiaSharp()
|
||||||
#if !UT
|
|
||||||
.UseFFImageLoading()
|
.UseFFImageLoading()
|
||||||
#endif
|
|
||||||
.ConfigureEffects(effects =>
|
.ConfigureEffects(effects =>
|
||||||
{
|
{
|
||||||
#if ANDROID
|
#if ANDROID
|
||||||
@@ -46,16 +40,6 @@ public static class MauiProgram
|
|||||||
})
|
})
|
||||||
.ConfigureMauiHandlers(handlers =>
|
.ConfigureMauiHandlers(handlers =>
|
||||||
{
|
{
|
||||||
#if ANDROID
|
|
||||||
// HACK: Due to https://github.com/dotnet/maui/issues/19681 and not willing to use reflection to access
|
|
||||||
// the alert dialog, we need to redefine the PickerHandler implementation for a custom one of ours
|
|
||||||
// which handles showing the current selected item. Remove this workaround when MAUI releases a fix for this.
|
|
||||||
if (handlers.FirstOrDefault(h => h.ServiceType == typeof(Picker)) is ServiceDescriptor sd)
|
|
||||||
{
|
|
||||||
handlers.Remove(sd);
|
|
||||||
handlers.AddHandler(typeof(IPicker), typeof(Controls.Picker.PickerHandler));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
customHandlers?.Invoke(handlers);
|
customHandlers?.Invoke(handlers);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -63,13 +47,6 @@ public static class MauiProgram
|
|||||||
builder.Logging.AddDebug();
|
builder.Logging.AddDebug();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ExplicitlyPreventThingsGetRemovedBecauseOfLinker();
|
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ExplicitlyPreventThingsGetRemovedBecauseOfLinker()
|
|
||||||
{
|
|
||||||
StubBaseCipherViewCellSoLinkerDoesntRemoveMethods.CallThisSoLinkerDoesntRemoveMethods();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,5 @@ namespace Bit.Core.Models.Api
|
|||||||
{
|
{
|
||||||
public string Uri { get; set; }
|
public string Uri { get; set; }
|
||||||
public UriMatchType? Match { get; set; }
|
public UriMatchType? Match { get; set; }
|
||||||
public string UriChecksum { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,9 @@ namespace Bit.Core.Models.Data
|
|||||||
{
|
{
|
||||||
Uri = data.Uri;
|
Uri = data.Uri;
|
||||||
Match = data.Match;
|
Match = data.Match;
|
||||||
UriChecksum = data.UriChecksum;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Uri { get; set; }
|
public string Uri { get; set; }
|
||||||
public UriMatchType? Match { get; set; }
|
public UriMatchType? Match { get; set; }
|
||||||
public string UriChecksum { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ namespace Bit.Core.Models.Domain
|
|||||||
switch (Type)
|
switch (Type)
|
||||||
{
|
{
|
||||||
case Enums.CipherType.Login:
|
case Enums.CipherType.Login:
|
||||||
model.Login = await Login.DecryptAsync(OrganizationId, Key == null, model.Key);
|
model.Login = await Login.DecryptAsync(OrganizationId, model.Key);
|
||||||
break;
|
break;
|
||||||
case Enums.CipherType.SecureNote:
|
case Enums.CipherType.SecureNote:
|
||||||
model.SecureNote = await SecureNote.DecryptAsync(OrganizationId, model.Key);
|
model.SecureNote = await SecureNote.DecryptAsync(OrganizationId, model.Key);
|
||||||
|
|||||||
@@ -2,10 +2,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Abstractions;
|
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
|
|
||||||
namespace Bit.Core.Models.Domain
|
namespace Bit.Core.Models.Domain
|
||||||
{
|
{
|
||||||
@@ -33,7 +31,7 @@ namespace Bit.Core.Models.Domain
|
|||||||
public EncString Totp { get; set; }
|
public EncString Totp { get; set; }
|
||||||
public List<Fido2Credential> Fido2Credentials { get; set; }
|
public List<Fido2Credential> Fido2Credentials { get; set; }
|
||||||
|
|
||||||
public async Task<LoginView> DecryptAsync(string orgId, bool bypassUriChecksumValidation, SymmetricCryptoKey key = null)
|
public async Task<LoginView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
|
||||||
{
|
{
|
||||||
var view = await DecryptObjAsync(new LoginView(this), this, new HashSet<string>
|
var view = await DecryptObjAsync(new LoginView(this), this, new HashSet<string>
|
||||||
{
|
{
|
||||||
@@ -43,15 +41,10 @@ namespace Bit.Core.Models.Domain
|
|||||||
}, orgId, key);
|
}, orgId, key);
|
||||||
if (Uris != null)
|
if (Uris != null)
|
||||||
{
|
{
|
||||||
var cryptoService = ServiceContainer.Resolve<ICryptoService>();
|
|
||||||
view.Uris = new List<LoginUriView>();
|
view.Uris = new List<LoginUriView>();
|
||||||
foreach (var uri in Uris)
|
foreach (var uri in Uris)
|
||||||
{
|
{
|
||||||
var loginUriView = await uri.DecryptAsync(orgId, key);
|
view.Uris.Add(await uri.DecryptAsync(orgId, key));
|
||||||
if (bypassUriChecksumValidation || await cryptoService.ValidateUriChecksumAsync(uri.UriChecksum, loginUriView.Uri, orgId, key))
|
|
||||||
{
|
|
||||||
view.Uris.Add(loginUriView);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Fido2Credentials != null)
|
if (Fido2Credentials != null)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
@@ -11,8 +10,7 @@ namespace Bit.Core.Models.Domain
|
|||||||
{
|
{
|
||||||
private HashSet<string> _map = new HashSet<string>
|
private HashSet<string> _map = new HashSet<string>
|
||||||
{
|
{
|
||||||
nameof(Uri),
|
"Uri"
|
||||||
nameof(UriChecksum)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public LoginUri() { }
|
public LoginUri() { }
|
||||||
@@ -25,11 +23,10 @@ namespace Bit.Core.Models.Domain
|
|||||||
|
|
||||||
public EncString Uri { get; set; }
|
public EncString Uri { get; set; }
|
||||||
public UriMatchType? Match { get; set; }
|
public UriMatchType? Match { get; set; }
|
||||||
public EncString UriChecksum { get; set; }
|
|
||||||
|
|
||||||
public Task<LoginUriView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
|
public Task<LoginUriView> DecryptAsync(string orgId, SymmetricCryptoKey key = null)
|
||||||
{
|
{
|
||||||
return DecryptObjAsync(new LoginUriView(this), this, _map.Where(m => m != nameof(UriChecksum)).ToHashSet<string>(), orgId, key);
|
return DecryptObjAsync(new LoginUriView(this), this, _map, orgId, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoginUriData ToLoginUriData()
|
public LoginUriData ToLoginUriData()
|
||||||
|
|||||||
@@ -17,12 +17,10 @@ namespace Bit.Core.Models.Export
|
|||||||
{
|
{
|
||||||
Match = obj.Match;
|
Match = obj.Match;
|
||||||
Uri = obj.Uri?.EncryptedString;
|
Uri = obj.Uri?.EncryptedString;
|
||||||
UriChecksum = obj.UriChecksum?.EncryptedString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public UriMatchType? Match { get; set; }
|
public UriMatchType? Match { get; set; }
|
||||||
public string Uri { get; set; }
|
public string Uri { get; set; }
|
||||||
public string UriChecksum { get; set; }
|
|
||||||
|
|
||||||
public static LoginUriView ToView(LoginUri req, LoginUriView view = null)
|
public static LoginUriView ToView(LoginUri req, LoginUriView view = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Bit.Core.Models.Request
|
|||||||
Login = new LoginApi
|
Login = new LoginApi
|
||||||
{
|
{
|
||||||
Uris = cipher.Login.Uris?.Select(
|
Uris = cipher.Login.Uris?.Select(
|
||||||
u => new LoginUriApi { Match = u.Match, Uri = u.Uri?.EncryptedString, UriChecksum = u.UriChecksum?.EncryptedString }).ToList(),
|
u => new LoginUriApi { Match = u.Match, Uri = u.Uri?.EncryptedString }).ToList(),
|
||||||
Username = cipher.Login.Username?.EncryptedString,
|
Username = cipher.Login.Username?.EncryptedString,
|
||||||
Password = cipher.Login.Password?.EncryptedString,
|
Password = cipher.Login.Password?.EncryptedString,
|
||||||
PasswordRevisionDate = cipher.Login.PasswordRevisionDate,
|
PasswordRevisionDate = cipher.Login.PasswordRevisionDate,
|
||||||
|
|||||||
@@ -61,7 +61,6 @@
|
|||||||
VerticalOptions="Start"
|
VerticalOptions="Start"
|
||||||
Margin="0,20,6,0"
|
Margin="0,20,6,0"
|
||||||
Padding="16,0"
|
Padding="16,0"
|
||||||
MinimumHeightRequest="45"
|
|
||||||
CornerRadius="2"
|
CornerRadius="2"
|
||||||
TextTransform="Uppercase"
|
TextTransform="Uppercase"
|
||||||
Clicked="DeleteAccount_Clicked"/>
|
Clicked="DeleteAccount_Clicked"/>
|
||||||
@@ -74,7 +73,6 @@
|
|||||||
VerticalOptions="Start"
|
VerticalOptions="Start"
|
||||||
Margin="0,20,0,0"
|
Margin="0,20,0,0"
|
||||||
Padding="16,0"
|
Padding="16,0"
|
||||||
MinimumHeightRequest="45"
|
|
||||||
CornerRadius="2"
|
CornerRadius="2"
|
||||||
TextTransform="Uppercase"
|
TextTransform="Uppercase"
|
||||||
Clicked="Close_Clicked" />
|
Clicked="Close_Clicked" />
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
<ScrollView>
|
<ScrollView>
|
||||||
<StackLayout Spacing="20">
|
<StackLayout Spacing="20">
|
||||||
<StackLayout StyleClass="box">
|
<StackLayout StyleClass="box">
|
||||||
|
<StackLayout StyleClass="box-row-header">
|
||||||
|
<Label Text="MAUI APP"
|
||||||
|
StyleClass="box-header, box-header-platform" />
|
||||||
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row-header">
|
<StackLayout StyleClass="box-row-header">
|
||||||
<Label Text="{u:I18n SelfHostedEnvironment, Header=True}"
|
<Label Text="{u:I18n SelfHostedEnvironment, Header=True}"
|
||||||
StyleClass="box-header, box-header-platform" />
|
StyleClass="box-header, box-header-platform" />
|
||||||
@@ -33,7 +37,7 @@
|
|||||||
Text="{Binding BaseUrl}"
|
Text="{Binding BaseUrl}"
|
||||||
Keyboard="Url"
|
Keyboard="Url"
|
||||||
Placeholder="ex. https://bitwarden.company.com"
|
Placeholder="ex. https://bitwarden.company.com"
|
||||||
StyleClass="box-value, no-keyboard-auto-help"
|
StyleClass="box-value"
|
||||||
ReturnType="Go"
|
ReturnType="Go"
|
||||||
ReturnCommand="{Binding SubmitCommand}"
|
ReturnCommand="{Binding SubmitCommand}"
|
||||||
AutomationId="ServerUrlEntry"/>
|
AutomationId="ServerUrlEntry"/>
|
||||||
@@ -55,7 +59,7 @@
|
|||||||
x:Name="_webVaultEntry"
|
x:Name="_webVaultEntry"
|
||||||
Text="{Binding WebVaultUrl}"
|
Text="{Binding WebVaultUrl}"
|
||||||
Keyboard="Url"
|
Keyboard="Url"
|
||||||
StyleClass="box-value, no-keyboard-auto-help"
|
StyleClass="box-value"
|
||||||
AutomationId="WebVaultUrlEntry"/>
|
AutomationId="WebVaultUrlEntry"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row">
|
<StackLayout StyleClass="box-row">
|
||||||
@@ -66,7 +70,7 @@
|
|||||||
x:Name="_apiEntry"
|
x:Name="_apiEntry"
|
||||||
Text="{Binding ApiUrl}"
|
Text="{Binding ApiUrl}"
|
||||||
Keyboard="Url"
|
Keyboard="Url"
|
||||||
StyleClass="box-value, no-keyboard-auto-help"
|
StyleClass="box-value"
|
||||||
AutomationId="ApiUrlEntry"/>
|
AutomationId="ApiUrlEntry"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row">
|
<StackLayout StyleClass="box-row">
|
||||||
@@ -77,7 +81,7 @@
|
|||||||
x:Name="_identityEntry"
|
x:Name="_identityEntry"
|
||||||
Text="{Binding IdentityUrl}"
|
Text="{Binding IdentityUrl}"
|
||||||
Keyboard="Url"
|
Keyboard="Url"
|
||||||
StyleClass="box-value, no-keyboard-auto-help"
|
StyleClass="box-value"
|
||||||
AutomationId="IdentityUrlEntry"/>
|
AutomationId="IdentityUrlEntry"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row">
|
<StackLayout StyleClass="box-row">
|
||||||
@@ -88,7 +92,7 @@
|
|||||||
x:Name="_iconsEntry"
|
x:Name="_iconsEntry"
|
||||||
Text="{Binding IconsUrl}"
|
Text="{Binding IconsUrl}"
|
||||||
Keyboard="Url"
|
Keyboard="Url"
|
||||||
StyleClass="box-value, no-keyboard-auto-help"
|
StyleClass="box-value"
|
||||||
ReturnType="Go"
|
ReturnType="Go"
|
||||||
ReturnCommand="{Binding SubmitCommand}"
|
ReturnCommand="{Binding SubmitCommand}"
|
||||||
AutomationId="IconsUrlEntry"/>
|
AutomationId="IconsUrlEntry"/>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Resources.Localization;
|
using Bit.Core.Resources.Localization;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.Maui.Platform;
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -27,7 +26,7 @@ namespace Bit.App.Pages
|
|||||||
_apiEntry.ReturnCommand = new Command(() => _identityEntry.Focus());
|
_apiEntry.ReturnCommand = new Command(() => _identityEntry.Focus());
|
||||||
_identityEntry.ReturnType = ReturnType.Next;
|
_identityEntry.ReturnType = ReturnType.Next;
|
||||||
_identityEntry.ReturnCommand = new Command(() => _iconsEntry.Focus());
|
_identityEntry.ReturnCommand = new Command(() => _iconsEntry.Focus());
|
||||||
_vm.SubmitSuccessTask = () => MainThread.InvokeOnMainThreadAsync(SubmitSuccessAsync);
|
_vm.SubmitSuccessAction = () => MainThread.BeginInvokeOnMainThread(async () => await SubmitSuccessAsync());
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
@@ -38,12 +37,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_platformUtilsService.ShowToast("success", null, AppResources.EnvironmentSaved);
|
_platformUtilsService.ShowToast("success", null, AppResources.EnvironmentSaved);
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
#if ANDROID
|
|
||||||
if (Platform.CurrentActivity.CurrentFocus != null)
|
|
||||||
{
|
|
||||||
Platform.CurrentActivity.HideKeyboard(Platform.CurrentActivity.CurrentFocus);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Close_Clicked(object sender, EventArgs e)
|
private void Close_Clicked(object sender, EventArgs e)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace Bit.App.Pages
|
|||||||
public string WebVaultUrl { get; set; }
|
public string WebVaultUrl { get; set; }
|
||||||
public string IconsUrl { get; set; }
|
public string IconsUrl { get; set; }
|
||||||
public string NotificationsUrls { get; set; }
|
public string NotificationsUrls { get; set; }
|
||||||
public Func<Task> SubmitSuccessTask { get; set; }
|
public Action SubmitSuccessAction { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
|
|
||||||
public async Task SubmitAsync()
|
public async Task SubmitAsync()
|
||||||
@@ -73,10 +73,7 @@ namespace Bit.App.Pages
|
|||||||
IconsUrl = resUrls.Icons;
|
IconsUrl = resUrls.Icons;
|
||||||
NotificationsUrls = resUrls.Notifications;
|
NotificationsUrls = resUrls.Notifications;
|
||||||
|
|
||||||
if (SubmitSuccessTask != null)
|
SubmitSuccessAction?.Invoke();
|
||||||
{
|
|
||||||
await SubmitSuccessTask();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ValidateUrls()
|
public bool ValidateUrls()
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
x:DataType="pages:HomeViewModel"
|
x:DataType="pages:HomeViewModel"
|
||||||
HideSoftInputOnTapped="True"
|
HideSoftInputOnTapped="True"
|
||||||
x:Name="_page"
|
x:Name="_page"
|
||||||
Loaded="HomePage_Loaded"
|
|
||||||
Title="{Binding PageTitle}">
|
Title="{Binding PageTitle}">
|
||||||
<ContentPage.BindingContext>
|
<ContentPage.BindingContext>
|
||||||
<pages:HomeViewModel />
|
<pages:HomeViewModel />
|
||||||
@@ -50,10 +49,11 @@
|
|||||||
x:Name="_email"
|
x:Name="_email"
|
||||||
Text="{Binding Email}"
|
Text="{Binding Email}"
|
||||||
Keyboard="Email"
|
Keyboard="Email"
|
||||||
StyleClass="box-value, no-keyboard-auto-help"
|
StyleClass="box-value"
|
||||||
ReturnType="Go"
|
ReturnType="Go"
|
||||||
ReturnCommand="{Binding ContinueCommand}"
|
ReturnCommand="{Binding ContinueCommand}"
|
||||||
AutomationId="EmailAddressEntry">
|
AutomationId="EmailAddressEntry"
|
||||||
|
>
|
||||||
<VisualStateManager.VisualStateGroups>
|
<VisualStateManager.VisualStateGroups>
|
||||||
<VisualStateGroup x:Name="CommonStates">
|
<VisualStateGroup x:Name="CommonStates">
|
||||||
<VisualState x:Name="Disabled">
|
<VisualState x:Name="Disabled">
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
Command="{Binding ShowEnvironmentPickerCommand}" />
|
Command="{Binding ShowEnvironmentPickerCommand}" />
|
||||||
</StackLayout.GestureRecognizers>
|
</StackLayout.GestureRecognizers>
|
||||||
<Label
|
<Label
|
||||||
Margin="{OnPlatform Android='0,0,6,1', iOS='0,0,6,0'}"
|
Margin="0,0,6,0"
|
||||||
Text="{Binding RegionText}"
|
Text="{Binding RegionText}"
|
||||||
FontSize="13"
|
FontSize="13"
|
||||||
TextColor="{DynamicResource MutedColor}"
|
TextColor="{DynamicResource MutedColor}"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Models;
|
||||||
using Bit.App.Models;
|
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
using Microsoft.Maui.Platform;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -12,14 +12,12 @@ namespace Bit.App.Pages
|
|||||||
private readonly HomeViewModel _vm;
|
private readonly HomeViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
private IBroadcasterService _broadcasterService;
|
private IBroadcasterService _broadcasterService;
|
||||||
private IConditionedAwaiterManager _conditionedAwaiterManager;
|
|
||||||
|
|
||||||
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
|
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
|
||||||
|
|
||||||
public HomePage(AppOptions appOptions = null)
|
public HomePage(AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>();
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>();
|
||||||
_conditionedAwaiterManager = ServiceContainer.Resolve<IConditionedAwaiterManager>();
|
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as HomeViewModel;
|
_vm = BindingContext as HomeViewModel;
|
||||||
@@ -46,23 +44,6 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool PerformNavigationOnAccountChangedOnLoad { get; internal set; }
|
|
||||||
|
|
||||||
void HomePage_Loaded(System.Object sender, System.EventArgs e)
|
|
||||||
{
|
|
||||||
#if ANDROID
|
|
||||||
// WORKAROUND: This is needed to fix the navigation when coming back from autofill when Accessibility Services is enabled
|
|
||||||
// See App.xaml.cs -> CreateWindow(...) for more info.
|
|
||||||
if (PerformNavigationOnAccountChangedOnLoad && ServiceContainer.TryResolve<IAccountsManager>(out var accountsManager))
|
|
||||||
{
|
|
||||||
PerformNavigationOnAccountChangedOnLoad = false;
|
|
||||||
accountsManager.NavigateOnAccountChangeAsync().FireAndForget();
|
|
||||||
}
|
|
||||||
|
|
||||||
_conditionedAwaiterManager.SetAsCompleted(AwaiterPrecondition.AndroidWindowCreated);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DismissRegisterPageAndLogInAsync(string email)
|
public async Task DismissRegisterPageAndLogInAsync(string email)
|
||||||
{
|
{
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
@@ -157,7 +138,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task StartEnvironmentAsync()
|
private async Task StartEnvironmentAsync()
|
||||||
{
|
{
|
||||||
await _accountListOverlay.HideAsync();
|
await _accountListOverlay.HideAsync();
|
||||||
var page = new EnvironmentPage();
|
var page = new EnvironmentPage();
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
await MainThread.InvokeOnMainThreadAsync(async () =>
|
await MainThread.InvokeOnMainThreadAsync(async () =>
|
||||||
{
|
{
|
||||||
var result = await _deviceActionService.Value.DisplayActionSheetAsync(AppResources.LoggingInOn, AppResources.Cancel, null, options);
|
var result = await Page.DisplayActionSheet(AppResources.LoggingInOn, AppResources.Cancel, null, options);
|
||||||
|
|
||||||
if (result is null || result == AppResources.Cancel)
|
if (result is null || result == AppResources.Cancel)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using Bit.App.Utilities;
|
|||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Core.Services;
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -27,24 +26,16 @@ namespace Bit.App.Pages
|
|||||||
_vm = BindingContext as LockPageViewModel;
|
_vm = BindingContext as LockPageViewModel;
|
||||||
_vm.CheckPendingAuthRequests = checkPendingAuthRequests;
|
_vm.CheckPendingAuthRequests = checkPendingAuthRequests;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_vm.UnlockedAction = () => MainThread.BeginInvokeOnMainThread(async () =>
|
_vm.UnlockedAction = () => MainThread.BeginInvokeOnMainThread(async () => await UnlockedAsync());
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await UnlockedAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
#if IOS
|
if (DeviceInfo.Platform == DevicePlatform.iOS)
|
||||||
ToolbarItems.Add(_moreItem);
|
{
|
||||||
#else
|
ToolbarItems.Add(_moreItem);
|
||||||
ToolbarItems.Add(_logOut);
|
}
|
||||||
#endif
|
else
|
||||||
|
{
|
||||||
|
ToolbarItems.Add(_logOut);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry SecretEntry
|
public Entry SecretEntry
|
||||||
@@ -74,60 +65,52 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
protected override async void OnAppearing()
|
protected override async void OnAppearing()
|
||||||
{
|
{
|
||||||
try
|
base.OnAppearing();
|
||||||
|
_broadcasterService.Subscribe(nameof(LockPage), message =>
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
if (message.Command == Constants.ClearSensitiveFields)
|
||||||
_broadcasterService.Subscribe(nameof(LockPage), message =>
|
|
||||||
{
|
{
|
||||||
if (message.Command == Constants.ClearSensitiveFields)
|
MainThread.BeginInvokeOnMainThread(_vm.ResetPinPasswordFields);
|
||||||
{
|
|
||||||
MainThread.BeginInvokeOnMainThread(() => _vm?.ResetPinPasswordFields());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (_appeared)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_appeared = true;
|
|
||||||
_mainContent.Content = _mainLayout;
|
|
||||||
|
|
||||||
//Workaround: This delay allows the Avatar to correctly load on iOS. The cause of this issue is also likely connected with the race conditions issue when using loading modals in iOS
|
|
||||||
await Task.Delay(50);
|
|
||||||
|
|
||||||
_accountAvatar?.OnAppearing();
|
|
||||||
|
|
||||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
|
||||||
|
|
||||||
await _vm.InitAsync();
|
|
||||||
|
|
||||||
_vm.FocusSecretEntry += PerformFocusSecretEntry;
|
|
||||||
|
|
||||||
if (!_vm.BiometricEnabled)
|
|
||||||
{
|
|
||||||
RequestFocus(SecretEntry);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!_vm.HasMasterPassword && !_vm.PinEnabled)
|
|
||||||
{
|
|
||||||
_passwordGrid.IsVisible = false;
|
|
||||||
_unlockButton.IsVisible = false;
|
|
||||||
}
|
|
||||||
if (_autoPromptBiometric)
|
|
||||||
{
|
|
||||||
var tasks = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await Task.Delay(500);
|
|
||||||
await MainThread.InvokeOnMainThreadAsync(async () => await _vm.PromptBiometricAsync());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
if (_appeared)
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
|
_appeared = true;
|
||||||
|
_mainContent.Content = _mainLayout;
|
||||||
|
|
||||||
|
//Workaround: This delay allows the Avatar to correctly load on iOS. The cause of this issue is also likely connected with the race conditions issue when using loading modals in iOS
|
||||||
|
await Task.Delay(50);
|
||||||
|
|
||||||
|
_accountAvatar?.OnAppearing();
|
||||||
|
|
||||||
|
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||||
|
|
||||||
|
await _vm.InitAsync();
|
||||||
|
|
||||||
|
_vm.FocusSecretEntry += PerformFocusSecretEntry;
|
||||||
|
|
||||||
|
if (!_vm.BiometricEnabled)
|
||||||
{
|
{
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
RequestFocus(SecretEntry);
|
||||||
throw;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!_vm.HasMasterPassword && !_vm.PinEnabled)
|
||||||
|
{
|
||||||
|
_passwordGrid.IsVisible = false;
|
||||||
|
_unlockButton.IsVisible = false;
|
||||||
|
}
|
||||||
|
if (_autoPromptBiometric)
|
||||||
|
{
|
||||||
|
var tasks = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(500);
|
||||||
|
MainThread.BeginInvokeOnMainThread(async () => await _vm.PromptBiometricAsync());
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,44 +167,27 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async void Biometric_Clicked(object sender, EventArgs e)
|
private async void Biometric_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
try
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
await _vm.PromptBiometricAsync();
|
||||||
{
|
|
||||||
await _vm.PromptBiometricAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void More_Clicked(object sender, System.EventArgs e)
|
private async void More_Clicked(object sender, System.EventArgs e)
|
||||||
{
|
{
|
||||||
try
|
await _accountListOverlay.HideAsync();
|
||||||
|
|
||||||
|
if (!DoOnce())
|
||||||
{
|
{
|
||||||
await _accountListOverlay.HideAsync();
|
return;
|
||||||
|
|
||||||
if (!DoOnce())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection = await DisplayActionSheet(AppResources.Options,
|
|
||||||
AppResources.Cancel, null, AppResources.LogOut);
|
|
||||||
|
|
||||||
if (selection == AppResources.LogOut)
|
|
||||||
{
|
|
||||||
await _vm.LogOutAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
|
var selection = await DisplayActionSheet(AppResources.Options,
|
||||||
|
AppResources.Cancel, null, AppResources.LogOut);
|
||||||
|
|
||||||
|
if (selection == AppResources.LogOut)
|
||||||
{
|
{
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
await _vm.LogOutAsync();
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +199,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||||
|
|
||||||
App.MainPage = new TabsPage(_appOptions, previousPage);
|
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,9 +245,9 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task SubmitAsync()
|
public async Task SubmitAsync()
|
||||||
{
|
{
|
||||||
|
ShowPassword = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ShowPassword = false;
|
|
||||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||||
if (PinEnabled)
|
if (PinEnabled)
|
||||||
{
|
{
|
||||||
@@ -257,15 +257,12 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await UnlockWithMasterPasswordAsync(kdfConfig);
|
await UnlockWithMasterPasswordAsync(kdfConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (LegacyUserException)
|
catch (LegacyUserException)
|
||||||
{
|
{
|
||||||
await HandleLegacyUserAsync();
|
await HandleLegacyUserAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
HandleException(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UnlockWithPinAsync(KdfConfig kdfConfig)
|
private async Task UnlockWithPinAsync(KdfConfig kdfConfig)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||||
App.MainPage = new TabsPage(_appOptions, previousPage);
|
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartLogInWithMasterPasswordAsync()
|
private async Task StartLogInWithMasterPasswordAsync()
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using Bit.App.Utilities;
|
|||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -75,7 +74,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (message.Command == Constants.ClearSensitiveFields)
|
if (message.Command == Constants.ClearSensitiveFields)
|
||||||
{
|
{
|
||||||
MainThread.BeginInvokeOnMainThread(() => _vm?.ResetPasswordField());
|
MainThread.BeginInvokeOnMainThread(_vm.ResetPasswordField);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
_mainContent.Content = _mainLayout;
|
_mainContent.Content = _mainLayout;
|
||||||
@@ -189,20 +188,12 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task LogInSuccessAsync()
|
private async Task LogInSuccessAsync()
|
||||||
{
|
{
|
||||||
try
|
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
||||||
{
|
{
|
||||||
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
|
||||||
App.MainPage = new TabsPage(_appOptions, previousPage);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
|
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||||
|
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateTempPasswordAsync()
|
private async Task UpdateTempPasswordAsync()
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
FontSize="Small"
|
FontSize="Small"
|
||||||
FontAttributes="Bold"/>
|
FontAttributes="Bold"/>
|
||||||
<controls:MonoLabel
|
<controls:MonoLabel
|
||||||
Text="{Binding LoginRequest.FingerprintPhrase}"
|
FormattedText="{Binding LoginRequest.FingerprintPhrase}"
|
||||||
FontSize="Medium"
|
FontSize="Medium"
|
||||||
TextColor="{DynamicResource FingerprintPhrase}"
|
TextColor="{DynamicResource FingerprintPhrase}"
|
||||||
Margin="0,0,0,27"
|
Margin="0,0,0,27"
|
||||||
@@ -85,6 +85,7 @@
|
|||||||
<Button
|
<Button
|
||||||
Text="{u:I18n DenyLogIn}"
|
Text="{u:I18n DenyLogIn}"
|
||||||
Command="{Binding RejectRequestCommand}"
|
Command="{Binding RejectRequestCommand}"
|
||||||
|
StyleClass="btn-secundary"
|
||||||
AutomationId="DenyLoginButton" />
|
AutomationId="DenyLoginButton" />
|
||||||
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
FontSize="Small"
|
FontSize="Small"
|
||||||
FontAttributes="Bold" />
|
FontAttributes="Bold" />
|
||||||
<controls:MonoLabel
|
<controls:MonoLabel
|
||||||
Text="{Binding FingerprintPhrase}"
|
FormattedText="{Binding FingerprintPhrase}"
|
||||||
FontSize="Small"
|
FontSize="Small"
|
||||||
TextColor="{DynamicResource FingerprintPhrase}"
|
TextColor="{DynamicResource FingerprintPhrase}"
|
||||||
AutomationId="FingerprintPhraseValue" />
|
AutomationId="FingerprintPhraseValue" />
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Services;
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -49,20 +48,12 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task LogInSuccessAsync()
|
private async Task LogInSuccessAsync()
|
||||||
{
|
{
|
||||||
try
|
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
||||||
{
|
{
|
||||||
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
|
||||||
App.MainPage = new TabsPage(_appOptions, previousPage);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
|
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||||
|
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateTempPasswordAsync()
|
private async Task UpdateTempPasswordAsync()
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_requestTimeCts?.Cancel();
|
_requestTimeCts?.Cancel();
|
||||||
_requestTimeCts?.Dispose();
|
_requestTimeCts?.Dispose();
|
||||||
_requestTimeCts = null;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -21,7 +20,6 @@ namespace Bit.App.Pages
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as LoginSsoPageViewModel;
|
_vm = BindingContext as LoginSsoPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_vm.FromIosExtension = _appOptions?.IosExtension ?? false;
|
|
||||||
_vm.StartTwoFactorAction = () => MainThread.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
|
_vm.StartTwoFactorAction = () => MainThread.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
|
||||||
_vm.StartSetPasswordAction = () =>
|
_vm.StartSetPasswordAction = () =>
|
||||||
MainThread.BeginInvokeOnMainThread(async () => await StartSetPasswordAsync());
|
MainThread.BeginInvokeOnMainThread(async () => await StartSetPasswordAsync());
|
||||||
@@ -91,30 +89,16 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task StartTwoFactorAsync()
|
private async Task StartTwoFactorAsync()
|
||||||
{
|
{
|
||||||
try
|
RestoreAppOptionsFromCopy();
|
||||||
{
|
var page = new TwoFactorPage(true, _appOptions, _vm.OrgIdentifier);
|
||||||
RestoreAppOptionsFromCopy();
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
var page = new TwoFactorPage(true, _appOptions, _vm.OrgIdentifier);
|
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartSetPasswordAsync()
|
private async Task StartSetPasswordAsync()
|
||||||
{
|
{
|
||||||
try
|
RestoreAppOptionsFromCopy();
|
||||||
{
|
var page = new SetPasswordPage(_appOptions, _vm.OrgIdentifier);
|
||||||
RestoreAppOptionsFromCopy();
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
var page = new SetPasswordPage(_appOptions, _vm.OrgIdentifier);
|
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateTempPasswordAsync()
|
private async Task UpdateTempPasswordAsync()
|
||||||
@@ -131,23 +115,16 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task SsoAuthSuccessAsync()
|
private async Task SsoAuthSuccessAsync()
|
||||||
{
|
{
|
||||||
try
|
RestoreAppOptionsFromCopy();
|
||||||
{
|
await AppHelpers.ClearPreviousPage();
|
||||||
RestoreAppOptionsFromCopy();
|
|
||||||
await AppHelpers.ClearPreviousPage();
|
|
||||||
|
|
||||||
if (await _vaultTimeoutService.IsLockedAsync())
|
if (await _vaultTimeoutService.IsLockedAsync())
|
||||||
{
|
|
||||||
App.MainPage = new NavigationPage(new LockPage(_appOptions));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
App.MainPage = new TabsPage(_appOptions, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
{
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Application.Current.MainPage = new TabsPage(_appOptions, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,17 +14,6 @@ using Bit.Core.Utilities;
|
|||||||
|
|
||||||
using Microsoft.Maui.Authentication;
|
using Microsoft.Maui.Authentication;
|
||||||
using Microsoft.Maui.Networking;
|
using Microsoft.Maui.Networking;
|
||||||
using NetworkAccess = Microsoft.Maui.Networking.NetworkAccess;
|
|
||||||
using Org.BouncyCastle.Asn1.Ocsp;
|
|
||||||
|
|
||||||
#if IOS
|
|
||||||
using AuthenticationServices;
|
|
||||||
using Foundation;
|
|
||||||
using UIKit;
|
|
||||||
using WebAuthenticator = Bit.Core.Utilities.MAUI.WebAuthenticator;
|
|
||||||
using WebAuthenticatorResult = Bit.Core.Utilities.MAUI.WebAuthenticatorResult;
|
|
||||||
using WebAuthenticatorOptions = Bit.Core.Utilities.MAUI.WebAuthenticatorOptions;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -74,8 +63,6 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _orgIdentifier, value);
|
set => SetProperty(ref _orgIdentifier, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool FromIosExtension { get; set; }
|
|
||||||
|
|
||||||
public ICommand LogInCommand { get; }
|
public ICommand LogInCommand { get; }
|
||||||
public Action StartTwoFactorAction { get; set; }
|
public Action StartTwoFactorAction { get; set; }
|
||||||
public Action StartSetPasswordAction { get; set; }
|
public Action StartSetPasswordAction { get; set; }
|
||||||
@@ -165,9 +152,6 @@ namespace Bit.App.Pages
|
|||||||
CallbackUrl = new Uri(REDIRECT_URI),
|
CallbackUrl = new Uri(REDIRECT_URI),
|
||||||
Url = new Uri(url),
|
Url = new Uri(url),
|
||||||
PrefersEphemeralWebBrowserSession = _useEphemeralWebBrowserSession,
|
PrefersEphemeralWebBrowserSession = _useEphemeralWebBrowserSession,
|
||||||
#if IOS
|
|
||||||
ShouldUseSharedApplicationKeyWindow = FromIosExtension
|
|
||||||
#endif
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var code = GetResultCode(authResult, state);
|
var code = GetResultCode(authResult, state);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core.Services;
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
@@ -65,19 +64,12 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task SetPasswordSuccessAsync()
|
private async Task SetPasswordSuccessAsync()
|
||||||
{
|
{
|
||||||
try
|
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
||||||
{
|
{
|
||||||
if (AppHelpers.SetAlternateMainPage(_appOptions))
|
return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
|
||||||
App.MainPage = new TabsPage(_appOptions, previousPage);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
|
||||||
}
|
}
|
||||||
|
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||||
|
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,26 +132,14 @@
|
|||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout
|
<StackLayout Spacing="0" Padding="0" IsVisible="{Binding DuoMethod, Mode=OneWay}"
|
||||||
Spacing="0"
|
VerticalOptions="FillAndExpand">
|
||||||
Padding="0"
|
|
||||||
IsVisible="{Binding DuoMethod, Mode=OneWay}"
|
|
||||||
VerticalOptions="FillAndExpand">
|
|
||||||
<Label
|
|
||||||
StyleClass="box"
|
|
||||||
Text="{Binding DuoFramelessLabel}"
|
|
||||||
HorizontalOptions="StartAndExpand"
|
|
||||||
Margin="10,21"
|
|
||||||
IsVisible="{Binding IsDuoFrameless}"/>
|
|
||||||
<controls:HybridWebView
|
<controls:HybridWebView
|
||||||
x:Name="_duoWebView"
|
x:Name="_duoWebView"
|
||||||
HorizontalOptions="FillAndExpand"
|
HorizontalOptions="FillAndExpand"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
HeightRequest="{Binding DuoWebViewHeight, Mode=OneWay}"
|
MinimumHeightRequest="400" />
|
||||||
IsVisible="{Binding IsDuoFrameless, Converter={StaticResource inverseBool}}"/>
|
<StackLayout StyleClass="box" VerticalOptions="End">
|
||||||
<StackLayout
|
|
||||||
StyleClass="box"
|
|
||||||
VerticalOptions="End">
|
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n RememberMe}"
|
Text="{u:I18n RememberMe}"
|
||||||
@@ -163,12 +151,6 @@
|
|||||||
HorizontalOptions="End" />
|
HorizontalOptions="End" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Button Text="{u:I18n LaunchDuo}"
|
|
||||||
Margin="10,21"
|
|
||||||
StyleClass="btn-primary"
|
|
||||||
Command="{Binding AuthenticateWithDuoFramelessCommand}"
|
|
||||||
AutomationId="DuoFramelessButton"
|
|
||||||
IsVisible="{Binding IsDuoFrameless}"/>
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
Spacing="0"
|
Spacing="0"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user