mirror of
https://github.com/bitwarden/mobile
synced 2025-12-11 22:03:27 +00:00
Compare commits
98 Commits
v2.11.1
...
feature/io
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69cd7d79e7 | ||
|
|
dbf94c1b56 | ||
|
|
4b0fb2840e | ||
|
|
629c696c81 | ||
|
|
bf1aa7c4eb | ||
|
|
318a3e4de9 | ||
|
|
0f992d27b3 | ||
|
|
83fd6736f6 | ||
|
|
397250368a | ||
|
|
5e4365084b | ||
|
|
ea5e4aafa3 | ||
|
|
69d1de47c6 | ||
|
|
0d3f819e93 | ||
|
|
3760e0f9f4 | ||
|
|
5a13cb53ba | ||
|
|
0e9cbe4539 | ||
|
|
b8c1107c94 | ||
|
|
a07ef1a1d6 | ||
|
|
99ccd62bcd | ||
|
|
bfb050a6f9 | ||
|
|
4e0b05571d | ||
|
|
d93d70fd66 | ||
|
|
41098ff05b | ||
|
|
4ed7491116 | ||
|
|
1ebad6bca5 | ||
|
|
48e3986264 | ||
|
|
88a1d8d4e8 | ||
|
|
f3ff991abe | ||
|
|
17b89dc21c | ||
|
|
ff76a3ec15 | ||
|
|
3a2e012c42 | ||
|
|
a0bb16c35f | ||
|
|
62a8d1c017 | ||
|
|
ce4e3ed1cd | ||
|
|
4669275680 | ||
|
|
fc1000acc1 | ||
|
|
c9ce7256e5 | ||
|
|
34aba0e168 | ||
|
|
9e9e2e12d8 | ||
|
|
3eec349038 | ||
|
|
69650a1ab5 | ||
|
|
faac7ebe5e | ||
|
|
d3734c63fc | ||
|
|
4aad34cd75 | ||
|
|
73eb3c2c1e | ||
|
|
6109091ec0 | ||
|
|
c0783cd162 | ||
|
|
a9a4fa56c1 | ||
|
|
271e6b3d92 | ||
|
|
750faf8a83 | ||
|
|
716e52f6ff | ||
|
|
010a4210f4 | ||
|
|
8d23bc89e8 | ||
|
|
f2857397f0 | ||
|
|
6023374fbe | ||
|
|
d3c1b58c2a | ||
|
|
a026af2072 | ||
|
|
51be6e522b | ||
|
|
024d9380c9 | ||
|
|
14b51b1a7f | ||
|
|
4667a9d643 | ||
|
|
d3f00340fb | ||
|
|
8866fc6322 | ||
|
|
68887c5de7 | ||
|
|
99b67b680c | ||
|
|
9b5bf4306f | ||
|
|
be55504b01 | ||
|
|
307a5a5843 | ||
|
|
d050215ebc | ||
|
|
67e26c778b | ||
|
|
efb10d155d | ||
|
|
913c673773 | ||
|
|
339decafe4 | ||
|
|
380c3583fb | ||
|
|
244a6a7f41 | ||
|
|
745b54bf2a | ||
|
|
4f86bb2043 | ||
|
|
ada8a73849 | ||
|
|
1e3204f91d | ||
|
|
b5c6a57fa0 | ||
|
|
6ca5b66aa7 | ||
|
|
24a0396d0f | ||
|
|
7f7b673b0a | ||
|
|
f79ff3fd63 | ||
|
|
2f2fa8a25b | ||
|
|
9042b1009e | ||
|
|
dbc0f490c5 | ||
|
|
6d8f627772 | ||
|
|
b4c016c9d4 | ||
|
|
ae763ebca8 | ||
|
|
880483ac79 | ||
|
|
f44e6ab75f | ||
|
|
10a718b0c7 | ||
|
|
9ec4050e4d | ||
|
|
93e2c0df7c | ||
|
|
8d07397a59 | ||
|
|
15d44b873b | ||
|
|
6a979d0ff5 |
81
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
81
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: File a bug report
|
||||||
|
labels: [bug]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
|
||||||
|
Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests.
|
||||||
|
- type: textarea
|
||||||
|
id: reproduce
|
||||||
|
attributes:
|
||||||
|
label: Steps To Reproduce
|
||||||
|
description: How can we reproduce the behavior.
|
||||||
|
value: |
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. Click on '...'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: Expected Result
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: actual
|
||||||
|
attributes:
|
||||||
|
label: Actual Result
|
||||||
|
description: A clear and concise description of what is happening.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: screenshots
|
||||||
|
attributes:
|
||||||
|
label: Screenshots or Videos
|
||||||
|
description: If applicable, add screenshots and/or a short video to help explain your problem.
|
||||||
|
- type: textarea
|
||||||
|
id: additional-context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Add any other context about the problem here.
|
||||||
|
- type: dropdown
|
||||||
|
id: os
|
||||||
|
attributes:
|
||||||
|
label: Operating System
|
||||||
|
description: What operating system are you seeing the problem on?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- Android
|
||||||
|
- iOS
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: os-version
|
||||||
|
attributes:
|
||||||
|
label: Operating System Version
|
||||||
|
description: What version of the operating system(s) are you seeing the problem on?
|
||||||
|
- type: input
|
||||||
|
id: device
|
||||||
|
attributes:
|
||||||
|
label: Device
|
||||||
|
description: Which device are you seeing the problem on?
|
||||||
|
placeholder: iPhone 12, Samsung Galaxy S10
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Build Version
|
||||||
|
description: What version of our software are you running? (go to "Settings" → "About" in the app)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: beta
|
||||||
|
attributes:
|
||||||
|
label: Beta
|
||||||
|
options:
|
||||||
|
- label: Using a pre-release version of the application.
|
||||||
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Report mobile autofill failure
|
||||||
|
url: https://docs.google.com/forms/d/e/1FAIpQLScMopHyN7KGJs8hW562VTzbIGL4KcFnx0wJcsW0GYE1BnPiGA/viewform
|
||||||
|
about: We are aware of some situations where the Bitwarden mobile app will not autofill information correctly. This is something the Bitwarden team is actively working on but need your help as a community and active Bitwarden users!
|
||||||
|
- name: Feature Requests
|
||||||
|
url: https://community.bitwarden.com/c/feature-requests/
|
||||||
|
about: Request new features using the Community Forums. Please search existing feature requests before making a new one.
|
||||||
|
- name: Bitwarden Community Forums
|
||||||
|
url: https://community.bitwarden.com
|
||||||
|
about: Please visit the community forums for general community discussion, support and the development roadmap.
|
||||||
|
- name: Customer Support
|
||||||
|
url: https://bitwarden.com/contact/
|
||||||
|
about: Please contact our customer support for account issues and general customer support.
|
||||||
|
- name: Security Issues
|
||||||
|
url: https://hackerone.com/bitwarden
|
||||||
|
about: We use HackerOne to manage security disclosures.
|
||||||
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
## Type of change
|
||||||
|
- [ ] Bug fix
|
||||||
|
- [ ] New feature development
|
||||||
|
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
|
||||||
|
- [ ] Build/deploy pipeline (DevOps)
|
||||||
|
- [ ] Other
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
<!--Describe what the purpose of this PR is. For example: what bug you're fixing or what new feature you're adding-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Code changes
|
||||||
|
<!--Explain the changes you've made to each file or major component. This should help the reviewer understand your changes-->
|
||||||
|
<!--Also refer to any related changes or PRs in other repositories-->
|
||||||
|
|
||||||
|
* **file.ext:** Description of what was changed and why
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
<!--Required for any UI changes. Delete if not applicable-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Testing requirements
|
||||||
|
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Before you submit
|
||||||
|
- [ ] I have added **unit tests** where it makes sense to do so (encouraged but not required)
|
||||||
|
- [ ] This change requires a **documentation update** (notify the documentation team)
|
||||||
|
- [ ] This change has particular **deployment requirements** (notify the DevOps team)
|
||||||
13
.github/scripts/android/build.ps1
vendored
13
.github/scripts/android/build.ps1
vendored
@@ -1,13 +0,0 @@
|
|||||||
param (
|
|
||||||
[Parameter(Mandatory=$true)]
|
|
||||||
[string] $configuration
|
|
||||||
)
|
|
||||||
|
|
||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
|
||||||
$androidPath = $($rootPath + "/src/Android/Android.csproj");
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Build $configuration Configuration"
|
|
||||||
Write-Output "########################################"
|
|
||||||
|
|
||||||
msbuild "$($androidPath)" "/p:Configuration=$configuration"
|
|
||||||
69
.github/scripts/android/clean-fdroid.ps1
vendored
69
.github/scripts/android/clean-fdroid.ps1
vendored
@@ -1,69 +0,0 @@
|
|||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
|
||||||
|
|
||||||
$androidPath = $($rootPath + "/src/Android/Android.csproj");
|
|
||||||
$appPath = $($rootPath + "/src/App/App.csproj");
|
|
||||||
|
|
||||||
$androidManifest = $($rootPath + "/src/Android/Properties/AndroidManifest.xml");
|
|
||||||
|
|
||||||
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 $androidPath $($androidPath + ".original");
|
|
||||||
Copy-Item $appPath $($appPath + ".original");
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Cleanup Android Manifest"
|
|
||||||
Write-Output "########################################"
|
|
||||||
|
|
||||||
$xml=New-Object XML;
|
|
||||||
$xml.Load($androidManifest);
|
|
||||||
|
|
||||||
$nsAndroid=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
|
|
||||||
$nsAndroid.AddNamespace("android", "http://schemas.android.com/apk/res/android");
|
|
||||||
|
|
||||||
$xml.Save($androidManifest);
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Uninstall from Android.csproj"
|
|
||||||
Write-Output "########################################"
|
|
||||||
|
|
||||||
$xml=New-Object XML;
|
|
||||||
$xml.Load($androidPath);
|
|
||||||
|
|
||||||
$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($androidPath);
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Uninstall from App.csproj"
|
|
||||||
Write-Output "########################################"
|
|
||||||
|
|
||||||
$xml=New-Object XML;
|
|
||||||
$xml.Load($appPath);
|
|
||||||
|
|
||||||
$appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
|
|
||||||
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
|
|
||||||
|
|
||||||
$xml.Save($appPath);
|
|
||||||
24
.github/scripts/android/compile-fdroid.sh
vendored
24
.github/scripts/android/compile-fdroid.sh
vendored
@@ -1,24 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
cd $GITHUB_WORKSPACE
|
|
||||||
mkdir dist
|
|
||||||
cp CNAME ./dist
|
|
||||||
cd store
|
|
||||||
chmod 600 fdroid/config.py fdroid/keystore.jks
|
|
||||||
mkdir -p temp/fdroid
|
|
||||||
TEMP_DIR="$GITHUB_WORKSPACE/store/temp/fdroid"
|
|
||||||
cd fdroid
|
|
||||||
echo "keypass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
|
|
||||||
echo "keystorepass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
|
|
||||||
echo "local_copy_dir=\"$TEMP_DIR\"" >>config.py
|
|
||||||
mkdir -p repo
|
|
||||||
curl -Lo repo/com.x8bit.bitwarden-fdroid.apk \
|
|
||||||
https://github.com/bitwarden/mobile/releases/download/$RELEASE_TAG_NAME/com.x8bit.bitwarden-fdroid.apk
|
|
||||||
fdroid update
|
|
||||||
fdroid server update
|
|
||||||
cd ..
|
|
||||||
rm -rf temp/fdroid/archive
|
|
||||||
mv -v temp/fdroid ../dist
|
|
||||||
cd fdroid
|
|
||||||
cp index.html btn.png qr.png ../../dist/fdroid
|
|
||||||
cd $GITHUB_WORKSPACE
|
|
||||||
22
.github/scripts/android/decrypt-secrets.ps1
vendored
22
.github/scripts/android/decrypt-secrets.ps1
vendored
@@ -1,22 +0,0 @@
|
|||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
|
||||||
|
|
||||||
$decryptSecretPath = $($rootPath + "/.github/scripts/decrypt-secret.ps1");
|
|
||||||
|
|
||||||
$appKeystorePlayFilename = "app_play-keystore.jks";
|
|
||||||
$appKeystorePlayPath = $($rootPath + "/src/Android/$appKeystorePlayFilename");
|
|
||||||
$appKeystoreUploadFilename = "app_upload-keystore.jks";
|
|
||||||
$appKeystoreUploadPath = $($rootPath + "/src/Android/$appKeystoreUploadFilename");
|
|
||||||
$appKeystoreFdroidFilename = "app_fdroid-keystore.jks";
|
|
||||||
$appKeystoreFdroidPath = $($rootPath + "/src/Android/$appKeystoreFdroidFilename");
|
|
||||||
$googleServicesFilename = "google-services.json";
|
|
||||||
$googleServicesPath = $($rootPath + "/src/Android/$googleServicesFilename");
|
|
||||||
|
|
||||||
Invoke-Expression `
|
|
||||||
"& `"$decryptSecretPath`" -filename $($appKeystorePlayFilename + ".gpg") -output $($appKeystorePlayPath)"
|
|
||||||
Invoke-Expression `
|
|
||||||
"& `"$decryptSecretPath`" -filename $($appKeystoreUploadFilename + ".gpg") -output $($appKeystoreUploadPath)"
|
|
||||||
Invoke-Expression `
|
|
||||||
"& `"$decryptSecretPath`" -filename $($appKeystoreFdroidFilename + ".gpg") -output $($appKeystoreFdroidPath)"
|
|
||||||
Invoke-Expression `
|
|
||||||
"& `"$decryptSecretPath`" -filename $($googleServicesFilename + ".gpg") -output $($googleServicesPath)"
|
|
||||||
Invoke-Expression "& `"$decryptSecretPath`" -filename play_creds.json.gpg"
|
|
||||||
9
.github/scripts/android/deploy-play.ps1
vendored
9
.github/scripts/android/deploy-play.ps1
vendored
@@ -1,9 +0,0 @@
|
|||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
|
||||||
$homePath = Resolve-Path "~" | Select-Object -ExpandProperty Path;
|
|
||||||
|
|
||||||
$publisherPath = $($rootPath + "/store/google/Publisher/bin/Release/netcoreapp2.0/Publisher.dll");
|
|
||||||
$credsPath = $($homePath + "/secrets/play_creds.json");
|
|
||||||
$aabPath = $($rootPath + "/com.x8bit.bitwarden.aab");
|
|
||||||
$track = "internal";
|
|
||||||
|
|
||||||
dotnet $publisherPath $credsPath $aabPath $track
|
|
||||||
16
.github/scripts/android/increment-version.ps1
vendored
16
.github/scripts/android/increment-version.ps1
vendored
@@ -1,16 +0,0 @@
|
|||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
|
||||||
$buildNumber = 3000 + [int]$env:GITHUB_RUN_NUMBER;
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Setting Version Code $buildNumber"
|
|
||||||
Write-Output "########################################"
|
|
||||||
|
|
||||||
$androidManifest = $($rootPath + "/src/Android/Properties/AndroidManifest.xml");
|
|
||||||
|
|
||||||
$xml=New-Object XML;
|
|
||||||
$xml.Load($androidManifest);
|
|
||||||
|
|
||||||
$node=$xml.SelectNodes("/manifest");
|
|
||||||
$node.SetAttribute("android:versionCode", [string]$buildNumber);
|
|
||||||
|
|
||||||
$xml.Save($androidManifest);
|
|
||||||
23
.github/scripts/android/sign-fdroid.ps1
vendored
23
.github/scripts/android/sign-fdroid.ps1
vendored
@@ -1,23 +0,0 @@
|
|||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
|
||||||
|
|
||||||
$androidPath = $($rootPath + "/src/Android/Android.csproj");
|
|
||||||
|
|
||||||
$appKeystoreFdroidFilename = "app_fdroid-keystore.jks";
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Sign FDroid Configuration"
|
|
||||||
Write-Output "########################################"
|
|
||||||
|
|
||||||
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=FDroid" "/p:AndroidKeyStore=true" `
|
|
||||||
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:FDROID_KEYSTORE_PASSWORD)" `
|
|
||||||
"/p:AndroidSigningKeyStore=$($appKeystoreFdroidFilename)" `
|
|
||||||
"/p:AndroidSigningStorePass=$($env:FDROID_KEYSTORE_PASSWORD)" "/v:quiet"
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Copy FDroid apk to project root"
|
|
||||||
Write-Output "########################################"
|
|
||||||
|
|
||||||
$signedApkPath = $($rootPath + "/src/Android/bin/FDroid/com.x8bit.bitwarden-Signed.apk");
|
|
||||||
$signedApkDestPath = $($rootPath + "/com.x8bit.bitwarden-fdroid.apk");
|
|
||||||
|
|
||||||
Copy-Item $signedApkPath $signedApkDestPath
|
|
||||||
42
.github/scripts/android/sign-play.ps1
vendored
42
.github/scripts/android/sign-play.ps1
vendored
@@ -1,42 +0,0 @@
|
|||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
|
||||||
|
|
||||||
$androidPath = $($rootPath + "/src/Android/Android.csproj");
|
|
||||||
|
|
||||||
$appKeystorePlayFilename = "app_play-keystore.jks";
|
|
||||||
$appKeystoreUploadFilename = "app_upload-keystore.jks";
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Sign Google Play Bundle Release Configuration"
|
|
||||||
Write-Output "########################################"
|
|
||||||
|
|
||||||
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
|
|
||||||
"/p:AndroidSigningKeyAlias=upload" "/p:AndroidSigningKeyPass=$($env:UPLOAD_KEYSTORE_PASSWORD)" `
|
|
||||||
"/p:AndroidSigningKeyStore=$($appKeystoreUploadFilename)" `
|
|
||||||
"/p:AndroidSigningStorePass=$($env:UPLOAD_KEYSTORE_PASSWORD)" "/p:AndroidPackageFormat=aab" "/v:quiet"
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Copy Google Play Bundle to project root"
|
|
||||||
Write-Output "########################################"
|
|
||||||
|
|
||||||
$signedAabPath = $($rootPath + "/src/Android/bin/Release/com.x8bit.bitwarden-Signed.aab");
|
|
||||||
$signedAabDestPath = $($rootPath + "/com.x8bit.bitwarden.aab");
|
|
||||||
|
|
||||||
Copy-Item $signedAabPath $signedAabDestPath
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Sign APK Release Configuration"
|
|
||||||
Write-Output "########################################"
|
|
||||||
|
|
||||||
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
|
|
||||||
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:PLAY_KEYSTORE_PASSWORD)" `
|
|
||||||
"/p:AndroidSigningKeyStore=$($appKeystorePlayFilename)" `
|
|
||||||
"/p:AndroidSigningStorePass=$($env:PLAY_KEYSTORE_PASSWORD)" "/v:quiet"
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Copy Release APK to project root"
|
|
||||||
Write-Output "########################################"
|
|
||||||
|
|
||||||
$signedApkPath = $($rootPath + "/src/Android/bin/Release/com.x8bit.bitwarden-Signed.apk");
|
|
||||||
$signedApkDestPath = $($rootPath + "/com.x8bit.bitwarden.apk");
|
|
||||||
|
|
||||||
Copy-Item $signedApkPath $signedApkDestPath
|
|
||||||
29
.github/scripts/decrypt-secret.ps1
vendored
29
.github/scripts/decrypt-secret.ps1
vendored
@@ -1,29 +0,0 @@
|
|||||||
param (
|
|
||||||
[Parameter(Mandatory=$true)]
|
|
||||||
[string] $filename,
|
|
||||||
[string] $output
|
|
||||||
)
|
|
||||||
|
|
||||||
$homePath = Resolve-Path "~" | Select-Object -ExpandProperty Path
|
|
||||||
$rootPath = $env:GITHUB_WORKSPACE
|
|
||||||
|
|
||||||
$secretInputPath = $rootPath + "/.github/secrets"
|
|
||||||
$input = $secretInputPath + "/" + $filename
|
|
||||||
|
|
||||||
$passphrase = $env:DECRYPT_FILE_PASSWORD
|
|
||||||
$secretOutputPath = $homePath + "/secrets"
|
|
||||||
|
|
||||||
if ([string]::IsNullOrEmpty($output)) {
|
|
||||||
if ($filename.EndsWith(".gpg")) {
|
|
||||||
$output = $secretOutputPath + "/" + $filename.TrimEnd(".gpg")
|
|
||||||
} else {
|
|
||||||
$output = $secretOutputPath + "/" + $filename + ".plaintext"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(Test-Path -Path $secretOutputPath))
|
|
||||||
{
|
|
||||||
New-Item -ItemType Directory -Path $secretOutputPath
|
|
||||||
}
|
|
||||||
|
|
||||||
gpg --quiet --batch --yes --decrypt --passphrase="$passphrase" --output $output $input
|
|
||||||
29
.github/scripts/ios/build.ps1
vendored
29
.github/scripts/ios/build.ps1
vendored
@@ -1,29 +0,0 @@
|
|||||||
param (
|
|
||||||
[Parameter(Mandatory=$true)]
|
|
||||||
[string] $configuration,
|
|
||||||
[string] $platform = "iPhone",
|
|
||||||
[switch] $archive
|
|
||||||
)
|
|
||||||
|
|
||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
|
||||||
$iosPath = $($rootPath + "/src/iOS/iOS.csproj");
|
|
||||||
|
|
||||||
if ($archive)
|
|
||||||
{
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Archive $configuration Configuration for $platform Platform"
|
|
||||||
Write-Output "########################################"
|
|
||||||
msbuild "$($iosPath)" "/p:Platform=$platform" "/p:Configuration=$configuration" `
|
|
||||||
"/p:ArchiveOnBuild=true" "/t:`"Build`""
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Done"
|
|
||||||
Write-Output "########################################"
|
|
||||||
ls ~/Library/Developer/Xcode/Archives
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Build $configuration Configuration for $platform Platform"
|
|
||||||
Write-Output "########################################"
|
|
||||||
msbuild "$($iosPath)" "/p:Platform=$platform" "/p:Configuration=$configuration" "/t:`"Build`""
|
|
||||||
}
|
|
||||||
9
.github/scripts/ios/decrypt-secrets.ps1
vendored
9
.github/scripts/ios/decrypt-secrets.ps1
vendored
@@ -1,9 +0,0 @@
|
|||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
|
||||||
|
|
||||||
$decryptSecretPath = $($rootPath + "/.github/scripts/decrypt-secret.ps1");
|
|
||||||
|
|
||||||
Invoke-Expression "& `"$decryptSecretPath`" -filename bitwarden-mobile-key.p12.gpg"
|
|
||||||
Invoke-Expression "& `"$decryptSecretPath`" -filename iphone-distribution-cert.p12.gpg"
|
|
||||||
Invoke-Expression "& `"$decryptSecretPath`" -filename dist_autofill.mobileprovision.gpg"
|
|
||||||
Invoke-Expression "& `"$decryptSecretPath`" -filename dist_bitwarden.mobileprovision.gpg"
|
|
||||||
Invoke-Expression "& `"$decryptSecretPath`" -filename dist_extension.mobileprovision.gpg"
|
|
||||||
5
.github/scripts/ios/deploy-app-store.ps1
vendored
5
.github/scripts/ios/deploy-app-store.ps1
vendored
@@ -1,5 +0,0 @@
|
|||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
|
||||||
$ipaPath = "$rootPath/bitwarden-export/Bitwarden.ipa"
|
|
||||||
|
|
||||||
xcrun altool --upload-app --type ios --file "$ipaPath" `
|
|
||||||
--username "$env:APPLE_ID_USERNAME" --password "$env:APPLE_ID_PASSWORD"
|
|
||||||
13
.github/scripts/ios/export-ipa.ps1
vendored
13
.github/scripts/ios/export-ipa.ps1
vendored
@@ -1,13 +0,0 @@
|
|||||||
param (
|
|
||||||
[Parameter(Mandatory=$true)]
|
|
||||||
[string] $method
|
|
||||||
)
|
|
||||||
|
|
||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
|
||||||
$homePath = Resolve-Path "~" | Select-Object -ExpandProperty Path
|
|
||||||
|
|
||||||
$exportOptionsPath = "$rootPath/.github/resources/export-options-$method.plist";
|
|
||||||
$archivePath = "$homePath/Library/Developer/Xcode/Archives/*/*.xcarchive";
|
|
||||||
$exportPath = "$rootPath/bitwarden-export";
|
|
||||||
|
|
||||||
xcodebuild -exportArchive -archivePath $archivePath -exportPath $exportPath -exportOptionsPlist $exportOptionsPath
|
|
||||||
26
.github/scripts/ios/increment-version.ps1
vendored
26
.github/scripts/ios/increment-version.ps1
vendored
@@ -1,26 +0,0 @@
|
|||||||
$rootPath = $env:GITHUB_WORKSPACE;
|
|
||||||
$buildNumber = 100 + [int]$env:GITHUB_RUN_NUMBER;
|
|
||||||
|
|
||||||
$bitwardenInfo = $($rootPath + "/src/iOS/Info.plist");
|
|
||||||
$extensionInfo = $($rootPath + "/src/iOS.Extension/Info.plist");
|
|
||||||
$autofillInfo = $($rootPath + "/src/iOS.Autofill/Info.plist");
|
|
||||||
|
|
||||||
Write-Output "########################################"
|
|
||||||
Write-Output "##### Setting CFBundleVersion $buildNumber"
|
|
||||||
Write-Output "########################################"
|
|
||||||
|
|
||||||
function Update-Version($file) {
|
|
||||||
$xml=New-Object XML;
|
|
||||||
$xml.Load($file);
|
|
||||||
|
|
||||||
Select-Xml -xml $xml -XPath "//dict/key[. = 'CFBundleVersion']/following-sibling::string[1]" |
|
|
||||||
%{
|
|
||||||
$_.Node.InnerXml = $buildNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
$xml.Save($file);
|
|
||||||
}
|
|
||||||
|
|
||||||
Update-Version $bitwardenInfo
|
|
||||||
Update-Version $extensionInfo
|
|
||||||
Update-Version $autofillInfo
|
|
||||||
13
.github/scripts/ios/setup-keychain.ps1
vendored
13
.github/scripts/ios/setup-keychain.ps1
vendored
@@ -1,13 +0,0 @@
|
|||||||
$homePath = Resolve-Path "~" | Select-Object -ExpandProperty Path;
|
|
||||||
$secretsPath = $homePath + "/secrets"
|
|
||||||
|
|
||||||
$mobileKeyPath = $($secretsPath + "/bitwarden-mobile-key.p12");
|
|
||||||
$distCertPath = $($secretsPath + "/iphone-distribution-cert.p12");
|
|
||||||
|
|
||||||
security create-keychain -p $env:KEYCHAIN_PASSWORD build.keychain
|
|
||||||
security default-keychain -s build.keychain
|
|
||||||
security unlock-keychain -p $env:KEYCHAIN_PASSWORD build.keychain
|
|
||||||
security set-keychain-settings -lut 1200 build.keychain
|
|
||||||
security import $mobileKeyPath -k build.keychain -P $env:MOBILE_KEY_PASSWORD -T /usr/bin/codesign -T /usr/bin/security
|
|
||||||
security import $distCertPath -k build.keychain -P $env:DIST_CERT_PASSWORD -T /usr/bin/codesign -T /usr/bin/security
|
|
||||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $env:KEYCHAIN_PASSWORD build.keychain
|
|
||||||
21
.github/scripts/ios/setup-profiles.ps1
vendored
21
.github/scripts/ios/setup-profiles.ps1
vendored
@@ -1,21 +0,0 @@
|
|||||||
$homePath = Resolve-Path "~" | Select-Object -ExpandProperty Path;
|
|
||||||
$secretsPath = $homePath + "/secrets"
|
|
||||||
|
|
||||||
$autofillProfilePath = $($secretsPath + "/dist_autofill.mobileprovision");
|
|
||||||
$bitwardenProfilePath = $($secretsPath + "/dist_bitwarden.mobileprovision");
|
|
||||||
$extensionProfilePath = $($secretsPath + "/dist_extension.mobileprovision");
|
|
||||||
$profilesDirPath = "~/Library/MobileDevice/Provisioning Profiles"
|
|
||||||
|
|
||||||
if (!(Test-Path -Path $profilesDirPath))
|
|
||||||
{
|
|
||||||
New-Item -ItemType Directory -Path $profilesDirPath
|
|
||||||
}
|
|
||||||
|
|
||||||
$autofill_uuid = grep UUID -A1 -a $autofillProfilePath | grep -io "[-A-F0-9]\{36\}"
|
|
||||||
Copy-Item $autofillProfilePath -destination "$profilesDirPath/$autofill_uuid.mobileprovision"
|
|
||||||
|
|
||||||
$bitwarden_uuid = grep UUID -A1 -a $bitwardenProfilePath | grep -io "[-A-F0-9]\{36\}"
|
|
||||||
Copy-Item $bitwardenProfilePath -destination "$profilesDirPath/$bitwarden_uuid.mobileprovision"
|
|
||||||
|
|
||||||
$extension_uuid = grep UUID -A1 -a $extensionProfilePath | grep -io "[-A-F0-9]\{36\}"
|
|
||||||
Copy-Item $extensionProfilePath -destination "$profilesDirPath/$extension_uuid.mobileprovision"
|
|
||||||
572
.github/workflows/build.yml
vendored
572
.github/workflows/build.yml
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
---
|
||||||
name: Build
|
name: Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -5,20 +6,16 @@ on:
|
|||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'l10n_master'
|
- 'l10n_master'
|
||||||
- 'gh-pages'
|
- 'gh-pages'
|
||||||
release:
|
|
||||||
types:
|
|
||||||
- published
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
cloc:
|
cloc:
|
||||||
runs-on: ubuntu-latest
|
name: CLOC
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
- name: Set up cloc
|
- name: Set up CLOC
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get -y install cloc
|
sudo apt-get -y install cloc
|
||||||
@@ -26,12 +23,41 @@ 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
|
||||||
|
|
||||||
android:
|
|
||||||
runs-on: windows-latest
|
|
||||||
|
|
||||||
|
setup:
|
||||||
|
name: Setup
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
outputs:
|
||||||
|
rc_branch_exists: ${{ steps.branch-check.outputs.rc_branch_exists }}
|
||||||
|
release_branch_exists: ${{ steps.branch-check.outputs.release_branch_exists }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
|
- name: Check if special branches exist
|
||||||
|
id: branch-check
|
||||||
|
run: |
|
||||||
|
if [[ $(git ls-remote --heads origin rc) ]]; then
|
||||||
|
echo "::set-output name=rc_branch_exists::1"
|
||||||
|
else
|
||||||
|
echo "::set-output name=rc_branch_exists::0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $(git ls-remote --heads origin release) ]]; then
|
||||||
|
echo "::set-output name=release_branch_exists::1"
|
||||||
|
else
|
||||||
|
echo "::set-output name=release_branch_exists::0"
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
|
||||||
|
android:
|
||||||
|
name: Android
|
||||||
|
runs-on: windows-2019
|
||||||
|
needs: setup
|
||||||
steps:
|
steps:
|
||||||
- name: Set up MSBuild
|
- name: Set up MSBuild
|
||||||
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d
|
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d # v1
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
@@ -40,177 +66,294 @@ jobs:
|
|||||||
dotnet --info
|
dotnet --info
|
||||||
echo "GitHub ref: $GITHUB_REF"
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
echo "GitHub event: $GITHUB_EVENT"
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
env:
|
|
||||||
GITHUB_REF: ${{ github.ref }}
|
|
||||||
GITHUB_EVENT: ${{ github.event_name }}
|
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
- name: Decrypt secrets
|
- name: Decrypt secrets
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
|
||||||
run: ./.github/scripts/android/decrypt-secrets.ps1
|
|
||||||
shell: pwsh
|
|
||||||
env:
|
env:
|
||||||
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/secrets
|
||||||
|
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output ./src/Android/app_play-keystore.jks ./.github/secrets/app_play-keystore.jks.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output ./src/Android/app_upload-keystore.jks ./.github/secrets/app_upload-keystore.jks.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output ./src/Android/google-services.json ./.github/secrets/google-services.json.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/play_creds.json ./.github/secrets/play_creds.json.gpg
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Increment version
|
- name: Increment version
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
run: |
|
||||||
run: ./.github/scripts/android/increment-version.ps1
|
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
|
||||||
shell: pwsh
|
|
||||||
|
echo "########################################"
|
||||||
|
echo "##### Setting Version Code $BUILD_NUMBER"
|
||||||
|
echo "########################################"
|
||||||
|
|
||||||
|
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
|
||||||
|
./src/Android/Properties/AndroidManifest.xml
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Restore packages
|
- name: Restore packages
|
||||||
run: nuget restore
|
run: nuget restore
|
||||||
|
|
||||||
- name: Run Core Tests
|
- name: Run Core tests
|
||||||
run: dotnet test test/Core.Test/Core.Test.csproj
|
run: dotnet test test/Core.Test/Core.Test.csproj
|
||||||
|
|
||||||
- name: Build Play Store publisher
|
- name: Build Play Store publisher
|
||||||
run: dotnet build ./store/google/Publisher/Publisher.csproj -p:Configuration=Release
|
run: dotnet build ./store/google/Publisher/Publisher.csproj -p:Configuration=Release
|
||||||
|
|
||||||
- name: Build for Play Store
|
- name: Build for Play Store
|
||||||
run: ./.github/scripts/android/build.ps1 -configuration Release
|
run: |
|
||||||
|
$configuration = "Release";
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Build $configuration Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Sign for Play Store
|
- name: Sign for Play Store
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
|
||||||
run: ./.github/scripts/android/sign-play.ps1
|
|
||||||
shell: pwsh
|
|
||||||
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: |
|
||||||
|
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Sign Google Play Bundle Release Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
|
||||||
|
"/p:AndroidSigningKeyAlias=upload" "/p:AndroidSigningKeyPass=$($env:UPLOAD_KEYSTORE_PASSWORD)" `
|
||||||
|
"/p:AndroidSigningKeyStore=$("app_upload-keystore.jks")" `
|
||||||
|
"/p:AndroidSigningStorePass=$($env:UPLOAD_KEYSTORE_PASSWORD)" "/p:AndroidPackageFormat=aab" "/v:quiet"
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Copy Google Play Bundle to project root"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$signedAabPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/com.x8bit.bitwarden-Signed.aab");
|
||||||
|
$signedAabDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden.aab");
|
||||||
|
|
||||||
|
Copy-Item $signedAabPath $signedAabDestPath
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Sign APK Release Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
|
||||||
|
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:PLAY_KEYSTORE_PASSWORD)" `
|
||||||
|
"/p:AndroidSigningKeyStore=$("app_play-keystore.jks")" `
|
||||||
|
"/p:AndroidSigningStorePass=$($env:PLAY_KEYSTORE_PASSWORD)" "/v:quiet"
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Copy Release APK to project root"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/com.x8bit.bitwarden-Signed.apk");
|
||||||
|
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden.apk");
|
||||||
|
|
||||||
|
Copy-Item $signedApkPath $signedApkDestPath
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
- name: Upload Play Store .aab artifact
|
- name: Upload Play Store .aab artifact
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
|
||||||
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden.aab
|
name: com.x8bit.bitwarden.aab
|
||||||
path: ./com.x8bit.bitwarden.aab
|
path: ./com.x8bit.bitwarden.aab
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload Play Store .apk artifact
|
- name: Upload Play Store .apk artifact
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
|
||||||
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
|
||||||
with:
|
with:
|
||||||
name: com.x8bit.bitwarden.apk
|
name: com.x8bit.bitwarden.apk
|
||||||
path: ./com.x8bit.bitwarden.apk
|
path: ./com.x8bit.bitwarden.apk
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
- name: Deploy to Play Store
|
||||||
|
if: |
|
||||||
|
(github.ref == 'refs/heads/master'
|
||||||
|
&& needs.setup.outputs.rc_branch_exists == 0
|
||||||
|
&& needs.setup.outputs.release_branch_exists == 0)
|
||||||
|
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.release_branch_exists == 0)
|
||||||
|
|| github.ref == 'refs/heads/release'
|
||||||
|
run: |
|
||||||
|
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/netcoreapp2.0/Publisher.dll"
|
||||||
|
CREDS_PATH="$HOME/secrets/play_creds.json"
|
||||||
|
AAB_PATH="$GITHUB_WORKSPACE/com.x8bit.bitwarden.aab"
|
||||||
|
TRACK="internal"
|
||||||
|
|
||||||
|
dotnet $PUBLISHER_PATH $CREDS_PATH $AAB_PATH $TRACK
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
|
||||||
|
f-droid:
|
||||||
|
name: F-Droid Build
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- name: Set up MSBuild
|
||||||
|
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d # v1
|
||||||
|
|
||||||
|
- name: Print environment
|
||||||
|
run: |
|
||||||
|
nuget help | grep Version
|
||||||
|
msbuild -version
|
||||||
|
dotnet --info
|
||||||
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
|
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
|
- name: Decrypt secrets
|
||||||
|
env:
|
||||||
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/secrets
|
||||||
|
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output ./src/Android/app_fdroid-keystore.jks ./.github/secrets/app_fdroid-keystore.jks.gpg
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Increment version
|
||||||
|
run: |
|
||||||
|
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
|
||||||
|
|
||||||
|
echo "########################################"
|
||||||
|
echo "##### Setting Version Code $BUILD_NUMBER"
|
||||||
|
echo "########################################"
|
||||||
|
|
||||||
|
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
|
||||||
|
./src/Android/Properties/AndroidManifest.xml
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Clean for F-Droid
|
- name: Clean for F-Droid
|
||||||
run: ./.github/scripts/android/clean-fdroid.ps1
|
run: |
|
||||||
|
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
|
||||||
|
$appPath = $($env:GITHUB_WORKSPACE + "/src/App/App.csproj");
|
||||||
|
|
||||||
|
$androidManifest = $($env:GITHUB_WORKSPACE + "/src/Android/Properties/AndroidManifest.xml");
|
||||||
|
|
||||||
|
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 $androidPath $($androidPath + ".original");
|
||||||
|
Copy-Item $appPath $($appPath + ".original");
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Cleanup Android Manifest"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$xml=New-Object XML;
|
||||||
|
$xml.Load($androidManifest);
|
||||||
|
|
||||||
|
$nsAndroid=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
|
||||||
|
$nsAndroid.AddNamespace("android", "http://schemas.android.com/apk/res/android");
|
||||||
|
|
||||||
|
$xml.Save($androidManifest);
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Uninstall from Android.csproj"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$xml=New-Object XML;
|
||||||
|
$xml.Load($androidPath);
|
||||||
|
|
||||||
|
$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($androidPath);
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Uninstall from App.csproj"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$xml=New-Object XML;
|
||||||
|
$xml.Load($appPath);
|
||||||
|
|
||||||
|
$appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
|
||||||
|
$appCenterNode.ParentNode.RemoveChild($appCenterNode);
|
||||||
|
|
||||||
|
$xml.Save($appPath);
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Restore packages
|
- name: Restore packages
|
||||||
run: nuget restore
|
run: nuget restore
|
||||||
|
|
||||||
- name: Build for F-Droid
|
- name: Build for F-Droid
|
||||||
run: ./.github/scripts/android/build.ps1 -configuration FDroid
|
run: |
|
||||||
|
$configuration = "FDroid";
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Build $configuration Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Sign for F-Droid
|
- name: Sign for F-Droid
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
|
||||||
run: ./.github/scripts/android/sign-fdroid.ps1
|
|
||||||
shell: pwsh
|
|
||||||
env:
|
env:
|
||||||
FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
|
FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Sign FDroid Configuration"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" `
|
||||||
|
"/t:SignAndroidPackage" "/p:Configuration=FDroid" "/p:AndroidKeyStore=true" `
|
||||||
|
"/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:FDROID_KEYSTORE_PASSWORD)" `
|
||||||
|
"/p:AndroidSigningKeyStore=$("app_fdroid-keystore.jks")" `
|
||||||
|
"/p:AndroidSigningStorePass=$($env:FDROID_KEYSTORE_PASSWORD)" "/v:quiet"
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Copy FDroid apk to project root"
|
||||||
|
Write-Output "########################################"
|
||||||
|
|
||||||
|
$signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/FDroid/com.x8bit.bitwarden-Signed.apk");
|
||||||
|
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden-fdroid.apk");
|
||||||
|
|
||||||
|
Copy-Item $signedApkPath $signedApkDestPath
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
- name: Upload F-Droid .apk artifact
|
- name: Upload F-Droid .apk artifact
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
|
||||||
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
|
||||||
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
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Deploy to Play Store
|
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
|
||||||
run: ./.github/scripts/android/deploy-play.ps1
|
|
||||||
shell: pwsh
|
|
||||||
|
|
||||||
- name: Upload release assets
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
run: |
|
|
||||||
hub release edit `
|
|
||||||
-a ./com.x8bit.bitwarden.aab `
|
|
||||||
-a ./com.x8bit.bitwarden.apk `
|
|
||||||
-a ./com.x8bit.bitwarden-fdroid.apk `
|
|
||||||
-m "Version $($env:RELEASE_TAG_NAME.TrimStart('v'))" `
|
|
||||||
$env:RELEASE_TAG_NAME
|
|
||||||
shell: pwsh
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
|
|
||||||
|
|
||||||
android-ubuntu:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: android
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Set up Node
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
|
||||||
with:
|
|
||||||
node-version: '10.x'
|
|
||||||
|
|
||||||
- name: Set up F-Droid server
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
run: |
|
|
||||||
sudo apt-get -qq update
|
|
||||||
sudo apt-get -qqy install --no-install-recommends fdroidserver wget
|
|
||||||
|
|
||||||
- name: Set up git credentials
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
env:
|
|
||||||
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
|
||||||
run: |
|
|
||||||
git config --global credential.helper store
|
|
||||||
echo "https://${ACCESS_TOKEN}:x-oauth-basic@github.com" >> ~/.git-credentials
|
|
||||||
git config --global user.email "ci@bitwarden.com"
|
|
||||||
git config --global user.name "Bitwarden CI"
|
|
||||||
|
|
||||||
- name: Print environment
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
run: |
|
|
||||||
node --version
|
|
||||||
npm --version
|
|
||||||
git --version
|
|
||||||
Write-Output "GitHub ref: $env:GITHUB_REF"
|
|
||||||
Write-Output "GitHub event: $env:GITHUB_EVENT"
|
|
||||||
shell: pwsh
|
|
||||||
env:
|
|
||||||
GITHUB_REF: ${{ github.ref }}
|
|
||||||
GITHUB_EVENT: ${{ github.event_name }}
|
|
||||||
|
|
||||||
- name: Checkout repo
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
|
||||||
|
|
||||||
- name: Install Node dependencies
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
run: npm install
|
|
||||||
|
|
||||||
- name: Decrypt secrets
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
run: |
|
|
||||||
./.github/scripts/decrypt-secret.ps1 -filename store_fdroid-keystore.jks.gpg `
|
|
||||||
-output ./store/fdroid/keystore.jks
|
|
||||||
shell: pwsh
|
|
||||||
env:
|
|
||||||
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Compile for F-Droid Store
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
run: |
|
|
||||||
sudo chmod +x ./.github/scripts/android/compile-fdroid.sh
|
|
||||||
./.github/scripts/android/compile-fdroid.sh
|
|
||||||
env:
|
|
||||||
FDROID_STORE_KEYSTORE_PASSWORD: ${{ secrets.FDROID_STORE_KEYSTORE_PASSWORD }}
|
|
||||||
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
|
|
||||||
|
|
||||||
- name: Deploy to gh-pages
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
run: npm run deploy
|
|
||||||
|
|
||||||
ios:
|
ios:
|
||||||
runs-on: macos-latest
|
name: Apple iOS
|
||||||
|
runs-on: macos-11
|
||||||
|
needs: setup
|
||||||
steps:
|
steps:
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
@@ -219,77 +362,176 @@ jobs:
|
|||||||
dotnet --info
|
dotnet --info
|
||||||
echo "GitHub ref: $GITHUB_REF"
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
echo "GitHub event: $GITHUB_EVENT"
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
env:
|
|
||||||
GITHUB_REF: ${{ github.ref }}
|
|
||||||
GITHUB_EVENT: ${{ github.event_name }}
|
|
||||||
|
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
- name: Decrypt secrets
|
- name: Decrypt secrets
|
||||||
run: ./.github/scripts/ios/decrypt-secrets.ps1
|
|
||||||
shell: pwsh
|
|
||||||
env:
|
env:
|
||||||
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/secrets
|
||||||
|
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/bitwarden-mobile-key.p12 ./.github/secrets/bitwarden-mobile-key.p12.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/iphone-distribution-cert.p12 ./.github/secrets/iphone-distribution-cert.p12.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/dist_autofill.mobileprovision ./.github/secrets/dist_autofill.mobileprovision.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/dist_bitwarden.mobileprovision ./.github/secrets/dist_bitwarden.mobileprovision.gpg
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output $HOME/secrets/dist_extension.mobileprovision ./.github/secrets/dist_extension.mobileprovision.gpg
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Increment version
|
- name: Increment version
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
run: |
|
||||||
run: ./.github/scripts/ios/increment-version.ps1
|
BUILD_NUMBER=$((100 + $GITHUB_RUN_NUMBER))
|
||||||
shell: pwsh
|
|
||||||
|
|
||||||
- name: Set up keychain
|
echo "########################################"
|
||||||
run: ./.github/scripts/ios/setup-keychain.ps1
|
echo "##### Setting CFBundleVersion $BUILD_NUMBER"
|
||||||
shell: pwsh
|
echo "########################################"
|
||||||
|
|
||||||
|
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS/Info.plist
|
||||||
|
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist
|
||||||
|
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- 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 }}
|
MOBILE_KEY_PASSWORD: ${{ secrets.IOS_KEY_PASSWORD }}
|
||||||
DIST_CERT_PASSWORD: ${{ secrets.IOS_DIST_CERT_PASSWORD }}
|
DIST_CERT_PASSWORD: ${{ secrets.IOS_DIST_CERT_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
|
||||||
|
security default-keychain -s build.keychain
|
||||||
|
security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
|
||||||
|
security set-keychain-settings -lut 1200 build.keychain
|
||||||
|
security import ~/secrets/bitwarden-mobile-key.p12 -k build.keychain -P $MOBILE_KEY_PASSWORD \
|
||||||
|
-T /usr/bin/codesign -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
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Set up provisioning profiles
|
- name: Set up provisioning profiles
|
||||||
run: ./.github/scripts/ios/setup-profiles.ps1
|
run: |
|
||||||
shell: pwsh
|
AUTOFILL_PROFILE_PATH=$HOME/secrets/dist_autofill.mobileprovision
|
||||||
|
BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_bitwarden.mobileprovision
|
||||||
|
EXTENSION_PROFILE_PATH=$HOME/secrets/dist_extension.mobileprovision
|
||||||
|
PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles
|
||||||
|
|
||||||
|
mkdir -p "$PROFILES_DIR_PATH"
|
||||||
|
|
||||||
|
AUTOFILL_UUID=$(grep UUID -A1 -a $AUTOFILL_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||||
|
cp $AUTOFILL_PROFILE_PATH "$PROFILES_DIR_PATH/$AUTOFILL_UUID.mobileprovision"
|
||||||
|
|
||||||
|
BITWARDEN_UUID=$(grep UUID -A1 -a $BITWARDEN_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||||
|
cp $BITWARDEN_PROFILE_PATH "$PROFILES_DIR_PATH/$BITWARDEN_UUID.mobileprovision"
|
||||||
|
|
||||||
|
EXTENSION_UUID=$(grep UUID -A1 -a $EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
|
||||||
|
cp $EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$EXTENSION_UUID.mobileprovision"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Restore packages
|
- name: Restore packages
|
||||||
run: nuget restore
|
run: nuget restore
|
||||||
|
|
||||||
- name: Archive Build for App Store
|
- name: Archive Build for App Store
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
run: |
|
||||||
run: ./.github/scripts/ios/build.ps1 -configuration AppStore -platform iPhone -archive
|
$configuration = "AppStore";
|
||||||
shell: pwsh
|
$platform = "iPhone";
|
||||||
|
|
||||||
- name: Build for App Store
|
Write-Output "########################################"
|
||||||
if: github.ref != 'refs/heads/master'
|
Write-Output "##### Archive $configuration Configuration for $platform Platform"
|
||||||
run: ./.github/scripts/ios/build.ps1 -configuration AppStore -platform iPhone
|
Write-Output "########################################"
|
||||||
|
msbuild "$($env:GITHUB_WORKSPACE + "/src/iOS/iOS.csproj")" "/p:Platform=$platform" `
|
||||||
|
"/p:Configuration=$configuration" "/p:ArchiveOnBuild=true" "/t:`"Build`""
|
||||||
|
|
||||||
|
Write-Output "########################################"
|
||||||
|
Write-Output "##### Done"
|
||||||
|
Write-Output "########################################"
|
||||||
|
ls ~/Library/Developer/Xcode/Archives
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Export .ipa for App Store
|
- name: Export .ipa for App Store
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
run: |
|
||||||
run: ./.github/scripts/ios/export-ipa.ps1 -method app-store
|
EXPORT_OPTIONS_PATH="./.github/resources/export-options-app-store.plist"
|
||||||
shell: pwsh
|
ARCHIVE_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive"
|
||||||
|
EXPORT_PATH="./bitwarden-export"
|
||||||
|
|
||||||
|
xcodebuild -exportArchive -archivePath $ARCHIVE_PATH -exportPath $EXPORT_PATH \
|
||||||
|
-exportOptionsPlist $EXPORT_OPTIONS_PATH
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Upload App Store .ipa artifact
|
- name: Upload App Store .ipa artifact
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.4
|
||||||
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
|
||||||
with:
|
with:
|
||||||
name: Bitwarden.ipa
|
name: Bitwarden.ipa
|
||||||
path: ./bitwarden-export/Bitwarden.ipa
|
path: ./bitwarden-export/Bitwarden.ipa
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Deploy to App Store
|
- name: Deploy to App Store
|
||||||
if: github.ref == 'refs/heads/master' || github.event_name == 'release'
|
if: |
|
||||||
run: ./.github/scripts/ios/deploy-app-store.ps1
|
(github.ref == 'refs/heads/master'
|
||||||
shell: pwsh
|
&& needs.setup.outputs.rc_branch_exists == 0
|
||||||
|
&& needs.setup.outputs.release_branch_exists == 0)
|
||||||
|
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.release_branch_exists == 0)
|
||||||
|
|| github.ref == 'refs/heads/release'
|
||||||
env:
|
env:
|
||||||
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
|
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
|
||||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||||
|
|
||||||
- name: Upload release assets
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
run: |
|
run: |
|
||||||
hub release edit `
|
xcrun altool --upload-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
|
||||||
-a ./bitwarden-export/Bitwarden.ipa `
|
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
|
||||||
-m "Version $($env:RELEASE_TAG_NAME.TrimStart('v'))" `
|
shell: bash
|
||||||
$env:RELEASE_TAG_NAME
|
|
||||||
shell: pwsh
|
|
||||||
|
check-failures:
|
||||||
|
name: Check for failures
|
||||||
|
if: always()
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs:
|
||||||
|
- cloc
|
||||||
|
- android
|
||||||
|
- f-droid
|
||||||
|
- ios
|
||||||
|
steps:
|
||||||
|
- name: Check if any job failed
|
||||||
|
if: ${{ (github.ref == 'refs/heads/master') || (github.ref == 'refs/heads/rc') }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
CLOC_STATUS: ${{ needs.cloc.result }}
|
||||||
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
|
ANDROID_STATUS: ${{ needs.android.result }}
|
||||||
|
F_DROID_STATUS: ${{ needs.f-droid.result }}
|
||||||
|
IOS_STATUS: ${{ needs.ios.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
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Login to Azure - Prod Subscription
|
||||||
|
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Retrieve secrets
|
||||||
|
id: retrieve-secrets
|
||||||
|
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
keyvault: "bitwarden-prod-kv"
|
||||||
|
secrets: "devops-alerts-slack-webhook-url"
|
||||||
|
|
||||||
|
- name: Notify Slack on failure
|
||||||
|
uses: act10ns/slack@e4e71685b9b239384b0f676a63c32367f59c2522 # v1.2.2
|
||||||
|
if: failure()
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
|
||||||
|
with:
|
||||||
|
status: ${{ job.status }}
|
||||||
|
|||||||
49
.github/workflows/crowdin-sync.yml
vendored
Normal file
49
.github/workflows/crowdin-sync.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
name: Crowdin Sync
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs: {}
|
||||||
|
# schedule:
|
||||||
|
# - cron: '0 0 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
crowdin-sync:
|
||||||
|
name: Autosync
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
env:
|
||||||
|
_CROWDIN_PROJECT_ID: "269690"
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
|
||||||
|
- name: Login to Azure
|
||||||
|
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||||
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Retrieve secrets
|
||||||
|
id: retrieve-secrets
|
||||||
|
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
||||||
|
with:
|
||||||
|
keyvault: "bitwarden-prod-kv"
|
||||||
|
secrets: "crowdin-api-token"
|
||||||
|
|
||||||
|
- name: Download translations
|
||||||
|
uses: crowdin/github-action@e39093fd75daae7859c68eded4b43d42ec78d8ea
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
|
||||||
|
with:
|
||||||
|
config: crowdin.yml
|
||||||
|
crowdin_branch_name: master
|
||||||
|
upload_sources: false
|
||||||
|
upload_translations: false
|
||||||
|
download_translations: true
|
||||||
|
github_user_name: "github-actions"
|
||||||
|
github_user_email: "<>"
|
||||||
|
commit_message: "Autosync the updated translations"
|
||||||
|
localization_branch_name: crowdin-auto-sync
|
||||||
|
create_pull_request: true
|
||||||
|
pull_request_title: "Autosync Crowdin Translations"
|
||||||
|
pull_request_body: "Autosync the updated translations"
|
||||||
153
.github/workflows/release.yml
vendored
Normal file
153
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
---
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Create Release
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- name: Branch check
|
||||||
|
run: |
|
||||||
|
if [[ "$GITHUB_REF" != "refs/heads/release" ]]; then
|
||||||
|
echo "==================================="
|
||||||
|
echo "[!] Can only release from the 'release' branch"
|
||||||
|
echo "==================================="
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
with:
|
||||||
|
ref: release
|
||||||
|
|
||||||
|
- name: Retrieve Mobile release version
|
||||||
|
id: retrieve-mobile-version
|
||||||
|
run: |
|
||||||
|
ver=$(sed -n -e '/android:versionName/ s/.*\= *//p' ./src/Android/Properties/AndroidManifest.xml | tr -d '"')
|
||||||
|
echo "::set-output name=mobile_version::${ver}"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Check to make sure Mobile release version has been bumped
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
latest_ver=$(hub release -L 1 -f '%T')
|
||||||
|
latest_ver=${latest_ver:1}
|
||||||
|
echo "Latest version: $latest_ver"
|
||||||
|
ver=${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||||
|
echo "Version: $ver"
|
||||||
|
if [ "$latest_ver" = "$ver" ]; then
|
||||||
|
echo "Version has not been bumped!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Download all artifacts
|
||||||
|
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74 # v2.14.0
|
||||||
|
with:
|
||||||
|
workflow: build.yml
|
||||||
|
workflow_conclusion: success
|
||||||
|
branch: release
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 # v2.8.5
|
||||||
|
with:
|
||||||
|
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
|
||||||
|
./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk,
|
||||||
|
./com.x8bit.bitwarden-fdroid.apk/com.x8bit.bitwarden-fdroid.apk,
|
||||||
|
./Bitwarden.ipa/Bitwarden.ipa"
|
||||||
|
commit: ${{ github.sha }}
|
||||||
|
tag: v${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||||
|
name: Version ${{ steps.retrieve-mobile-version.outputs.mobile_version }}
|
||||||
|
body: "<insert release notes here>"
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
draft: true
|
||||||
|
|
||||||
|
|
||||||
|
f-droid:
|
||||||
|
name: F-Droid Release
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
needs: release
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||||
|
with:
|
||||||
|
ref: release
|
||||||
|
|
||||||
|
- name: Download F-Droid .apk artifact
|
||||||
|
uses: dawidd6/action-download-artifact@b9571484721e8187f1fd08147b497129f8972c74 # v2.14.0
|
||||||
|
with:
|
||||||
|
workflow: build.yml
|
||||||
|
workflow_conclusion: success
|
||||||
|
branch: release
|
||||||
|
name: com.x8bit.bitwarden-fdroid.apk
|
||||||
|
|
||||||
|
- name: Set up Node
|
||||||
|
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea # v2.3.0
|
||||||
|
with:
|
||||||
|
node-version: '10.x'
|
||||||
|
|
||||||
|
- name: Set up F-Droid server
|
||||||
|
run: |
|
||||||
|
sudo apt-get -qq update
|
||||||
|
sudo apt-get -qqy install --no-install-recommends fdroidserver wget
|
||||||
|
|
||||||
|
- name: Set up Git credentials
|
||||||
|
env:
|
||||||
|
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||||
|
run: |
|
||||||
|
git config --global credential.helper store
|
||||||
|
echo "https://${ACCESS_TOKEN}:x-oauth-basic@github.com" >> ~/.git-credentials
|
||||||
|
git config --global user.email "ci@bitwarden.com"
|
||||||
|
git config --global user.name "Bitwarden CI"
|
||||||
|
|
||||||
|
- name: Print environment
|
||||||
|
run: |
|
||||||
|
node --version
|
||||||
|
npm --version
|
||||||
|
git --version
|
||||||
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
|
|
||||||
|
- name: Install Node dependencies
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Decrypt secrets
|
||||||
|
env:
|
||||||
|
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/secrets
|
||||||
|
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||||
|
--output ./store/fdroid/keystore.jks ./.github/secrets/store_fdroid-keystore.jks.gpg
|
||||||
|
|
||||||
|
- name: Compile for F-Droid Store
|
||||||
|
env:
|
||||||
|
FDROID_STORE_KEYSTORE_PASSWORD: ${{ secrets.FDROID_STORE_KEYSTORE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
cd $GITHUB_WORKSPACE
|
||||||
|
mkdir dist
|
||||||
|
cp CNAME ./dist
|
||||||
|
cd store
|
||||||
|
chmod 600 fdroid/config.py fdroid/keystore.jks
|
||||||
|
mkdir -p temp/fdroid
|
||||||
|
TEMP_DIR="$GITHUB_WORKSPACE/store/temp/fdroid"
|
||||||
|
cd fdroid
|
||||||
|
echo "keypass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
|
||||||
|
echo "keystorepass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
|
||||||
|
echo "local_copy_dir=\"$TEMP_DIR\"" >>config.py
|
||||||
|
mkdir -p repo
|
||||||
|
mv $GITHUB_WORKSPACE/com.x8bit.bitwarden-fdroid.apk ./repo/
|
||||||
|
fdroid update
|
||||||
|
fdroid server update
|
||||||
|
cd ..
|
||||||
|
rm -rf temp/fdroid/archive
|
||||||
|
mv -v temp/fdroid ../dist
|
||||||
|
cd fdroid
|
||||||
|
cp index.html btn.png qr.png ../../dist/fdroid
|
||||||
|
cd $GITHUB_WORKSPACE
|
||||||
|
|
||||||
|
- name: Deploy to gh-pages
|
||||||
|
run: npm run deploy
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
<!-- Comment:
|
|
||||||
Please do not submit feature requests. The [Community Forums][1] has a
|
|
||||||
section for submitting, voting for, and discussing product feature requests.
|
|
||||||
[1]: https://community.bitwarden.com
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Describe the Bug
|
|
||||||
|
|
||||||
<!-- Comment:
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Steps To Reproduce
|
|
||||||
|
|
||||||
<!-- Comment:
|
|
||||||
How can we reproduce the behavior:
|
|
||||||
-->
|
|
||||||
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. Click on '...'
|
|
||||||
|
|
||||||
## Expected Result
|
|
||||||
|
|
||||||
<!-- Comment:
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Actual Result
|
|
||||||
|
|
||||||
<!-- Comment:
|
|
||||||
A clear and concise description of what is happening.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Screenshots or Videos
|
|
||||||
|
|
||||||
<!-- Comment:
|
|
||||||
If applicable, add screenshots and/or a short video to help explain your problem.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Environment
|
|
||||||
|
|
||||||
- Device: [e.g. iPhone6]
|
|
||||||
- Operating system: [e.g. iOS 8.1]
|
|
||||||
- Build Version (go to "Settings" → "About" in the app): [e.g. 2.3.0 (2221)]
|
|
||||||
- Is this a Beta release? [Y/N]
|
|
||||||
|
|
||||||
## Additional Context
|
|
||||||
|
|
||||||
<!-- Comment:
|
|
||||||
Add any other context about the problem here.
|
|
||||||
-->
|
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin iOS, and Xamarin Forms.
|
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="300" height="533" /> <img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/mobile-ios-myvault.png" alt="" width="300" height="533" />
|
<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" />
|
||||||
|
|
||||||
# Build/Run
|
# Build/Run
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "test\Common\Commo
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Safari", "src\iOS.Safari\iOS.Safari.csproj", "{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
|
||||||
@@ -414,6 +416,36 @@ Global
|
|||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.Build.0 = Release|Any CPU
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.Build.0 = Release|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|Any CPU.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|Any CPU.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|iPhone.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|iPhone.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|Any CPU.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|Any CPU.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|iPhone.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|iPhone.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|iPhoneSimulator.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.AppStore|iPhoneSimulator.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|Any CPU.ActiveCfg = Debug|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|Any CPU.Build.0 = Debug|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|Any CPU.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|Any CPU.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|iPhone.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|iPhone.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|Any CPU.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|Any CPU.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|iPhone.ActiveCfg = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|iPhone.Build.0 = Release|iPhone
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -431,6 +463,7 @@ Global
|
|||||||
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||||
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
{4085B0A5-12A9-4993-B8B8-4ACE72E62E39} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
||||||
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
{8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA}
|
||||||
|
{7CE47211-43D4-4BBB-BB16-82A8A337ECE7} = {D10CA4A9-F866-40E1-B658-F69051236C71}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
project_id_env: _CROWDIN_PROJECT_ID
|
||||||
|
api_token_env: CROWDIN_API_TOKEN
|
||||||
files:
|
files:
|
||||||
- source: /src/App/Resources/AppResources.resx
|
- source: /src/App/Resources/AppResources.resx
|
||||||
translation: /src/App/Resources/AppResources.%two_letters_code%.resx
|
translation: /src/App/Resources/AppResources.%two_letters_code%.resx
|
||||||
|
|||||||
710
package-lock.json
generated
710
package-lock.json
generated
@@ -1,8 +1,468 @@
|
|||||||
{
|
{
|
||||||
"name": "bitwarden-mobile",
|
"name": "bitwarden-mobile",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "bitwarden-mobile",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"gh-pages": "^3.2.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/array-union": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"array-uniq": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/array-uniq": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/async": {
|
||||||
|
"version": "2.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||||
|
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/commondir": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/email-addresses": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/filename-reserved-regex": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/filenamify": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"filename-reserved-regex": "^2.0.0",
|
||||||
|
"strip-outer": "^1.0.1",
|
||||||
|
"trim-repeated": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/find-cache-dir": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"commondir": "^1.0.1",
|
||||||
|
"make-dir": "^3.0.2",
|
||||||
|
"pkg-dir": "^4.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/avajs/find-cache-dir?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/find-up": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"locate-path": "^5.0.0",
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs-extra": {
|
||||||
|
"version": "8.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||||
|
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^4.0.0",
|
||||||
|
"universalify": "^0.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6 <7 || >=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/gh-pages": {
|
||||||
|
"version": "3.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz",
|
||||||
|
"integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"async": "^2.6.1",
|
||||||
|
"commander": "^2.18.0",
|
||||||
|
"email-addresses": "^3.0.1",
|
||||||
|
"filenamify": "^4.3.0",
|
||||||
|
"find-cache-dir": "^3.3.1",
|
||||||
|
"fs-extra": "^8.1.0",
|
||||||
|
"globby": "^6.1.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"gh-pages": "bin/gh-pages.js",
|
||||||
|
"gh-pages-clean": "bin/gh-pages-clean.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/glob": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/globby": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
|
||||||
|
"integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"array-union": "^1.0.1",
|
||||||
|
"glob": "^7.0.3",
|
||||||
|
"object-assign": "^4.0.1",
|
||||||
|
"pify": "^2.0.0",
|
||||||
|
"pinkie-promise": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
|
||||||
|
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/jsonfile": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||||
|
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||||
|
"dev": true,
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/locate-path": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"p-locate": "^4.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/make-dir": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"semver": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minimatch": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-limit": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"p-try": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-locate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"p-limit": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-try": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-exists": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pify": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||||
|
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pinkie": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
|
||||||
|
"integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pinkie-promise": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"pinkie": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pkg-dir": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"find-up": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/semver": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-outer": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"escape-string-regexp": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/trim-repeated": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"escape-string-regexp": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"array-union": {
|
"array-union": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@@ -20,18 +480,18 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"async": {
|
"async": {
|
||||||
"version": "2.6.1",
|
"version": "2.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||||
"integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
|
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "^4.17.10"
|
"lodash": "^4.17.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
@@ -45,9 +505,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "2.15.1",
|
"version": "2.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"commondir": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
@@ -56,6 +522,12 @@
|
|||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"email-addresses": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"escape-string-regexp": {
|
"escape-string-regexp": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
@@ -63,39 +535,50 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"filename-reserved-regex": {
|
"filename-reserved-regex": {
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
|
||||||
"integrity": "sha1-5hz4BfDeHJhFZ9A4bcXfUO5a9+Q=",
|
"integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"filenamify": {
|
"filenamify": {
|
||||||
"version": "1.2.1",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
|
||||||
"integrity": "sha1-qfL/0RxQO+0wABUCknI3jx8TZaU=",
|
"integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"filename-reserved-regex": "^1.0.0",
|
"filename-reserved-regex": "^2.0.0",
|
||||||
"strip-outer": "^1.0.0",
|
"strip-outer": "^1.0.1",
|
||||||
"trim-repeated": "^1.0.0"
|
"trim-repeated": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"filenamify-url": {
|
"find-cache-dir": {
|
||||||
"version": "1.0.0",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/filenamify-url/-/filenamify-url-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
|
||||||
"integrity": "sha1-syvYExnvWGO3MHi+1Q9GpPeXX1A=",
|
"integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"filenamify": "^1.0.0",
|
"commondir": "^1.0.1",
|
||||||
"humanize-url": "^1.0.0"
|
"make-dir": "^3.0.2",
|
||||||
|
"pkg-dir": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"find-up": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"locate-path": "^5.0.0",
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "5.0.0",
|
"version": "8.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||||
"integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
|
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^4.0.0",
|
"jsonfile": "^4.0.0",
|
||||||
"universalify": "^0.1.0"
|
"universalify": "^0.1.0"
|
||||||
}
|
}
|
||||||
@@ -107,24 +590,24 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"gh-pages": {
|
"gh-pages": {
|
||||||
"version": "1.2.0",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz",
|
||||||
"integrity": "sha512-cGLYAvxtlQ1iTwAS4g7FreZPXoE/g62Fsxln2mmR19mgs4zZI+XJ+wVVUhBFCF/0+Nmvbq+abyTWue1m1BSnmg==",
|
"integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"async": "2.6.1",
|
"async": "^2.6.1",
|
||||||
"commander": "2.15.1",
|
"commander": "^2.18.0",
|
||||||
"filenamify-url": "^1.0.0",
|
"email-addresses": "^3.0.1",
|
||||||
"fs-extra": "^5.0.0",
|
"filenamify": "^4.3.0",
|
||||||
"globby": "^6.1.0",
|
"find-cache-dir": "^3.3.1",
|
||||||
"graceful-fs": "4.1.11",
|
"fs-extra": "^8.1.0",
|
||||||
"rimraf": "^2.6.2"
|
"globby": "^6.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "7.1.4",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||||
"integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
|
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
@@ -149,21 +632,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"graceful-fs": {
|
"graceful-fs": {
|
||||||
"version": "4.1.11",
|
"version": "4.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
|
||||||
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
|
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"humanize-url": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/humanize-url/-/humanize-url-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-9KuZ4NKIF0yk4eUEB8VfuuRk7/8=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"normalize-url": "^1.0.0",
|
|
||||||
"strip-url-auth": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"inflight": {
|
"inflight": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
@@ -175,15 +648,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"is-plain-obj": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
|
|
||||||
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
|
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"jsonfile": {
|
"jsonfile": {
|
||||||
@@ -195,12 +662,30 @@
|
|||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"locate-path": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-locate": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.15",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"make-dir": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"semver": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
@@ -210,18 +695,6 @@
|
|||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"normalize-url": {
|
|
||||||
"version": "1.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
|
|
||||||
"integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"object-assign": "^4.0.1",
|
|
||||||
"prepend-http": "^1.0.0",
|
|
||||||
"query-string": "^4.1.0",
|
|
||||||
"sort-keys": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@@ -237,6 +710,36 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"p-limit": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-try": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"p-locate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-limit": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"p-try": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"path-exists": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"path-is-absolute": {
|
"path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
@@ -264,44 +767,19 @@
|
|||||||
"pinkie": "^2.0.0"
|
"pinkie": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"prepend-http": {
|
"pkg-dir": {
|
||||||
"version": "1.0.4",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||||
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
|
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"query-string": {
|
|
||||||
"version": "4.3.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
|
|
||||||
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"object-assign": "^4.1.0",
|
"find-up": "^4.0.0"
|
||||||
"strict-uri-encode": "^1.0.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rimraf": {
|
"semver": {
|
||||||
"version": "2.6.3",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"glob": "^7.1.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sort-keys": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
|
|
||||||
"integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"is-plain-obj": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"strict-uri-encode": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
|
||||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
|
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"strip-outer": {
|
"strip-outer": {
|
||||||
@@ -313,12 +791,6 @@
|
|||||||
"escape-string-regexp": "^1.0.2"
|
"escape-string-regexp": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"strip-url-auth": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-url-auth/-/strip-url-auth-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-IrD6OkE4WzO+PzMVUbu4N/oM164=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"trim-repeated": {
|
"trim-repeated": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
|
||||||
|
|||||||
@@ -6,6 +6,6 @@
|
|||||||
"clean:l10n": "git push origin --delete l10n_master"
|
"clean:l10n": "git push origin --delete l10n_master"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gh-pages": "^1.2.0"
|
"gh-pages": "^3.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ using Bit.Core.Utilities;
|
|||||||
|
|
||||||
namespace Bit.Droid.Accessibility
|
namespace Bit.Droid.Accessibility
|
||||||
{
|
{
|
||||||
[Activity(Theme = "@style/LightTheme.Splash", WindowSoftInputMode = SoftInput.StateHidden)]
|
[Activity(Theme = "@style/BaseTheme", WindowSoftInputMode = SoftInput.StateHidden)]
|
||||||
public class AccessibilityActivity : Activity
|
public class AccessibilityActivity : Activity
|
||||||
{
|
{
|
||||||
private DateTime? _lastLaunch = null;
|
private DateTime? _lastLaunch = null;
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ namespace Bit.Droid.Accessibility
|
|||||||
// So keep them in sync with:
|
// So keep them in sync with:
|
||||||
// - AutofillHelpers.{TrustedBrowsers,CompatBrowsers}
|
// - AutofillHelpers.{TrustedBrowsers,CompatBrowsers}
|
||||||
// - Resources/xml/autofillservice.xml
|
// - Resources/xml/autofillservice.xml
|
||||||
new Browser("alook.browser", "search_fragment_input_view"),
|
|
||||||
new Browser("com.amazon.cloud9", "url"),
|
new Browser("com.amazon.cloud9", "url"),
|
||||||
new Browser("com.android.browser", "url"),
|
new Browser("com.android.browser", "url"),
|
||||||
new Browser("com.android.chrome", "url_bar"),
|
new Browser("com.android.chrome", "url_bar"),
|
||||||
@@ -52,6 +51,7 @@ namespace Bit.Droid.Accessibility
|
|||||||
new Browser("com.ecosia.android", "url_bar"),
|
new Browser("com.ecosia.android", "url_bar"),
|
||||||
new Browser("com.google.android.apps.chrome", "url_bar"),
|
new Browser("com.google.android.apps.chrome", "url_bar"),
|
||||||
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
|
new Browser("com.google.android.apps.chrome_dev", "url_bar"),
|
||||||
|
// Rem. for "com.google.android.captiveportallogin": URL displayed in ActionBar subtitle without viewId.
|
||||||
new Browser("com.jamal2367.styx", "search"),
|
new Browser("com.jamal2367.styx", "search"),
|
||||||
new Browser("com.kiwibrowser.browser", "url_bar"),
|
new Browser("com.kiwibrowser.browser", "url_bar"),
|
||||||
new Browser("com.microsoft.emmx", "url_bar"),
|
new Browser("com.microsoft.emmx", "url_bar"),
|
||||||
@@ -109,6 +109,7 @@ namespace Bit.Droid.Accessibility
|
|||||||
new Browser("org.torproject.torbrowser_alpha", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v10.0a8)
|
new Browser("org.torproject.torbrowser_alpha", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy (before v10.0a8)
|
||||||
new Browser("org.ungoogled.chromium.extensions.stable", "url_bar"),
|
new Browser("org.ungoogled.chromium.extensions.stable", "url_bar"),
|
||||||
new Browser("org.ungoogled.chromium.stable", "url_bar"),
|
new Browser("org.ungoogled.chromium.stable", "url_bar"),
|
||||||
|
new Browser("us.spotco.fennec_dos", "mozac_browser_toolbar_url_view,url_bar_title"), // 2nd = Legacy
|
||||||
|
|
||||||
// [Section B] Entries only present here
|
// [Section B] Entries only present here
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
<DefineConstants>DEBUG;</DefineConstants>
|
<DefineConstants>DEBUG;</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>3</WarningLevel>
|
<WarningLevel>3</WarningLevel>
|
||||||
<AndroidLinkMode>None</AndroidLinkMode>
|
|
||||||
<AndroidSupportedAbis />
|
<AndroidSupportedAbis />
|
||||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -77,22 +76,22 @@
|
|||||||
<PackageReference Include="Portable.BouncyCastle">
|
<PackageReference Include="Portable.BouncyCastle">
|
||||||
<Version>1.8.10</Version>
|
<Version>1.8.10</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.6" />
|
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.9" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.11" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0.10" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.2.5.2" />
|
||||||
|
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.8" />
|
||||||
<PackageReference Include="Xamarin.Essentials">
|
<PackageReference Include="Xamarin.Essentials">
|
||||||
<Version>1.6.1</Version>
|
<Version>1.7.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Firebase.Messaging">
|
<PackageReference Include="Xamarin.Firebase.Messaging">
|
||||||
<Version>121.0.1</Version>
|
<Version>122.0.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.3.0.1" />
|
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.4.0.4" />
|
||||||
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.2.0.7" />
|
<PackageReference Include="Xamarin.Google.Dagger" Version="2.37.0" />
|
||||||
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0.7" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.8" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.2.2.1" />
|
|
||||||
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.8" />
|
|
||||||
<PackageReference Include="Xamarin.Google.Dagger" Version="2.27.0" />
|
|
||||||
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
|
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
|
||||||
<Version>117.0.0</Version>
|
<Version>117.0.1</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -112,7 +111,6 @@
|
|||||||
<Compile Include="Autofill\SavedItem.cs" />
|
<Compile Include="Autofill\SavedItem.cs" />
|
||||||
<Compile Include="Effects\FabShadowEffect.cs" />
|
<Compile Include="Effects\FabShadowEffect.cs" />
|
||||||
<Compile Include="Effects\FixedSizeEffect.cs" />
|
<Compile Include="Effects\FixedSizeEffect.cs" />
|
||||||
<Compile Include="Effects\SelectableLabelEffect.cs" />
|
|
||||||
<Compile Include="Effects\TabBarEffect.cs" />
|
<Compile Include="Effects\TabBarEffect.cs" />
|
||||||
<Compile Include="Push\FirebaseMessagingService.cs" />
|
<Compile Include="Push\FirebaseMessagingService.cs" />
|
||||||
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />
|
<Compile Include="Receivers\ClearClipboardAlarmReceiver.cs" />
|
||||||
@@ -123,6 +121,8 @@
|
|||||||
<Compile Include="Renderers\ExtendedDatePickerRenderer.cs" />
|
<Compile Include="Renderers\ExtendedDatePickerRenderer.cs" />
|
||||||
<Compile Include="Renderers\CustomTabbedRenderer.cs" />
|
<Compile Include="Renderers\CustomTabbedRenderer.cs" />
|
||||||
<Compile Include="Renderers\ExtendedStackLayoutRenderer.cs" />
|
<Compile Include="Renderers\ExtendedStackLayoutRenderer.cs" />
|
||||||
|
<Compile Include="Renderers\ExtendedStepperRenderer.cs" />
|
||||||
|
<Compile Include="Renderers\CustomSwitchRenderer.cs" />
|
||||||
<Compile Include="Renderers\ExtendedTimePickerRenderer.cs" />
|
<Compile Include="Renderers\ExtendedTimePickerRenderer.cs" />
|
||||||
<Compile Include="Renderers\ExtendedSliderRenderer.cs" />
|
<Compile Include="Renderers\ExtendedSliderRenderer.cs" />
|
||||||
<Compile Include="Renderers\CustomEditorRenderer.cs" />
|
<Compile Include="Renderers\CustomEditorRenderer.cs" />
|
||||||
@@ -145,7 +145,9 @@
|
|||||||
<Compile Include="Tiles\MyVaultTileService.cs" />
|
<Compile Include="Tiles\MyVaultTileService.cs" />
|
||||||
<Compile Include="Utilities\AndroidHelpers.cs" />
|
<Compile Include="Utilities\AndroidHelpers.cs" />
|
||||||
<Compile Include="Utilities\AppCenterHelper.cs" />
|
<Compile Include="Utilities\AppCenterHelper.cs" />
|
||||||
|
<Compile Include="Utilities\ThemeHelpers.cs" />
|
||||||
<Compile Include="WebAuthCallbackActivity.cs" />
|
<Compile Include="WebAuthCallbackActivity.cs" />
|
||||||
|
<Compile Include="Renderers\SelectableLabelRenderer.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidAsset Include="Assets\FontAwesome.ttf" />
|
<AndroidAsset Include="Assets\FontAwesome.ttf" />
|
||||||
@@ -172,8 +174,6 @@
|
|||||||
<AndroidResource Include="Resources\drawable\id.xml" />
|
<AndroidResource Include="Resources\drawable\id.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\info.xml" />
|
<AndroidResource Include="Resources\drawable\info.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\list_item_bg.xml" />
|
<AndroidResource Include="Resources\drawable\list_item_bg.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\list_item_bg_dark.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\list_item_bg_nord.xml" />
|
|
||||||
<AndroidResource Include="Resources\drawable\lock.xml" />
|
<AndroidResource Include="Resources\drawable\lock.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\login.xml" />
|
<AndroidResource Include="Resources\drawable\login.xml" />
|
||||||
<AndroidResource Include="Resources\drawable\logo.xml" />
|
<AndroidResource Include="Resources\drawable\logo.xml" />
|
||||||
@@ -186,6 +186,7 @@
|
|||||||
<AndroidResource Include="Resources\drawable\shield.xml" />
|
<AndroidResource Include="Resources\drawable\shield.xml" />
|
||||||
<AndroidResource Include="Resources\drawable-v23\splash_screen.xml" />
|
<AndroidResource Include="Resources\drawable-v23\splash_screen.xml" />
|
||||||
<AndroidResource Include="Resources\drawable-v23\splash_screen_dark.xml" />
|
<AndroidResource Include="Resources\drawable-v23\splash_screen_dark.xml" />
|
||||||
|
<AndroidResource Include="Resources\drawable\switch_thumb.xml" />
|
||||||
<AndroidResource Include="Resources\layout\Tabbar.axml" />
|
<AndroidResource Include="Resources\layout\Tabbar.axml" />
|
||||||
<AndroidResource Include="Resources\layout\Toolbar.axml" />
|
<AndroidResource Include="Resources\layout\Toolbar.axml" />
|
||||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
|
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace Bit.Droid.Autofill
|
|||||||
public static HashSet<string> TrustedBrowsers = new HashSet<string>
|
public static HashSet<string> TrustedBrowsers = new HashSet<string>
|
||||||
{
|
{
|
||||||
"com.duckduckgo.mobile.android",
|
"com.duckduckgo.mobile.android",
|
||||||
|
"com.google.android.googlequicksearchbox",
|
||||||
"org.mozilla.focus",
|
"org.mozilla.focus",
|
||||||
"org.mozilla.klar",
|
"org.mozilla.klar",
|
||||||
};
|
};
|
||||||
@@ -48,7 +49,6 @@ namespace Bit.Droid.Autofill
|
|||||||
// - ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
|
// - ... to keep this list in sync with values in AccessibilityHelpers.SupportedBrowsers [Section A], too.
|
||||||
public static HashSet<string> CompatBrowsers = new HashSet<string>
|
public static HashSet<string> CompatBrowsers = new HashSet<string>
|
||||||
{
|
{
|
||||||
"alook.browser",
|
|
||||||
"com.amazon.cloud9",
|
"com.amazon.cloud9",
|
||||||
"com.android.browser",
|
"com.android.browser",
|
||||||
"com.android.chrome",
|
"com.android.chrome",
|
||||||
@@ -68,6 +68,7 @@ namespace Bit.Droid.Autofill
|
|||||||
"com.ecosia.android",
|
"com.ecosia.android",
|
||||||
"com.google.android.apps.chrome",
|
"com.google.android.apps.chrome",
|
||||||
"com.google.android.apps.chrome_dev",
|
"com.google.android.apps.chrome_dev",
|
||||||
|
"com.google.android.captiveportallogin",
|
||||||
"com.jamal2367.styx",
|
"com.jamal2367.styx",
|
||||||
"com.kiwibrowser.browser",
|
"com.kiwibrowser.browser",
|
||||||
"com.microsoft.emmx",
|
"com.microsoft.emmx",
|
||||||
@@ -122,6 +123,7 @@ namespace Bit.Droid.Autofill
|
|||||||
"org.torproject.torbrowser_alpha",
|
"org.torproject.torbrowser_alpha",
|
||||||
"org.ungoogled.chromium.extensions.stable",
|
"org.ungoogled.chromium.extensions.stable",
|
||||||
"org.ungoogled.chromium.stable",
|
"org.ungoogled.chromium.stable",
|
||||||
|
"us.spotco.fennec_dos",
|
||||||
};
|
};
|
||||||
|
|
||||||
// The URLs are blacklisted from autofilling
|
// The URLs are blacklisted from autofilling
|
||||||
|
|||||||
@@ -94,22 +94,10 @@ namespace Bit.Droid.Autofill
|
|||||||
|
|
||||||
_policyService ??= ServiceContainer.Resolve<IPolicyService>("policyService");
|
_policyService ??= ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
|
|
||||||
var personalOwnershipPolicies = await _policyService.GetAll(PolicyType.PersonalOwnership);
|
var personalOwnershipPolicyApplies = await _policyService.PolicyAppliesToUser(PolicyType.PersonalOwnership);
|
||||||
if (personalOwnershipPolicies != null)
|
if (personalOwnershipPolicyApplies)
|
||||||
{
|
{
|
||||||
_userService ??= ServiceContainer.Resolve<IUserService>("userService");
|
return;
|
||||||
foreach (var policy in personalOwnershipPolicies)
|
|
||||||
{
|
|
||||||
if (policy.Enabled)
|
|
||||||
{
|
|
||||||
var org = await _userService.GetOrganizationAsync(policy.OrganizationId);
|
|
||||||
if (org != null && org.Enabled && org.UsePolicies && !org.canManagePolicies
|
|
||||||
&& org.Status == OrganizationUserStatusType.Confirmed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var parser = new Parser(structure, ApplicationContext);
|
var parser = new Parser(structure, ApplicationContext);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Android.Graphics.Drawables;
|
using Android.Graphics.Drawables;
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Bit.Droid.Effects;
|
using Bit.Droid.Effects;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ namespace Bit.Droid.Effects
|
|||||||
if (Control is Android.Widget.Button button)
|
if (Control is Android.Widget.Button button)
|
||||||
{
|
{
|
||||||
var gd = new GradientDrawable();
|
var gd = new GradientDrawable();
|
||||||
gd.SetColor(ThemeManager.GetResourceColor("FabColor").ToAndroid());
|
gd.SetColor(ThemeHelpers.FabColor);
|
||||||
gd.SetCornerRadius(100);
|
gd.SetCornerRadius(100);
|
||||||
|
|
||||||
button.SetBackground(gd);
|
button.SetBackground(gd);
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
using Android.Widget;
|
|
||||||
using Bit.Droid.Effects;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
using Xamarin.Forms.Platform.Android;
|
|
||||||
|
|
||||||
[assembly: ExportEffect(typeof(SelectableLabelEffect), "SelectableLabelEffect")]
|
|
||||||
namespace Bit.Droid.Effects
|
|
||||||
{
|
|
||||||
public class SelectableLabelEffect : PlatformEffect
|
|
||||||
{
|
|
||||||
protected override void OnAttached()
|
|
||||||
{
|
|
||||||
if (Control is TextView textView)
|
|
||||||
{
|
|
||||||
textView.SetTextIsSelectable(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDetached()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,9 +15,9 @@ using Bit.Droid.Receivers;
|
|||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Android.Nfc;
|
using Android.Nfc;
|
||||||
using Bit.App.Utilities;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AndroidX.Core.Content;
|
using AndroidX.Core.Content;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using ZXing.Net.Mobile.Android;
|
using ZXing.Net.Mobile.Android;
|
||||||
|
|
||||||
namespace Bit.Droid
|
namespace Bit.Droid
|
||||||
@@ -30,7 +30,7 @@ namespace Bit.Droid
|
|||||||
LaunchMode = LaunchMode.SingleTask,
|
LaunchMode = LaunchMode.SingleTask,
|
||||||
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation |
|
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation |
|
||||||
ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden |
|
ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden |
|
||||||
ConfigChanges.Navigation)]
|
ConfigChanges.Navigation | ConfigChanges.UiMode)]
|
||||||
[IntentFilter(
|
[IntentFilter(
|
||||||
new[] { Intent.ActionSend },
|
new[] { Intent.ActionSend },
|
||||||
Categories = new[] { Intent.CategoryDefault },
|
Categories = new[] { Intent.CategoryDefault },
|
||||||
@@ -81,7 +81,6 @@ namespace Bit.Droid
|
|||||||
TabLayoutResource = Resource.Layout.Tabbar;
|
TabLayoutResource = Resource.Layout.Tabbar;
|
||||||
ToolbarResource = Resource.Layout.Toolbar;
|
ToolbarResource = Resource.Layout.Toolbar;
|
||||||
|
|
||||||
UpdateTheme(ThemeManager.GetTheme(true));
|
|
||||||
base.OnCreate(savedInstanceState);
|
base.OnCreate(savedInstanceState);
|
||||||
if (!CoreHelpers.InDebugMode())
|
if (!CoreHelpers.InDebugMode())
|
||||||
{
|
{
|
||||||
@@ -118,7 +117,7 @@ namespace Bit.Droid
|
|||||||
}
|
}
|
||||||
else if (message.Command == "updatedTheme")
|
else if (message.Command == "updatedTheme")
|
||||||
{
|
{
|
||||||
RestartApp();
|
Xamarin.Forms.Device.BeginInvokeOnMainThread(() => AppearanceAdjustments());
|
||||||
}
|
}
|
||||||
else if (message.Command == "exit")
|
else if (message.Command == "exit")
|
||||||
{
|
{
|
||||||
@@ -141,6 +140,7 @@ namespace Bit.Droid
|
|||||||
{
|
{
|
||||||
base.OnResume();
|
base.OnResume();
|
||||||
Xamarin.Essentials.Platform.OnResume();
|
Xamarin.Essentials.Platform.OnResume();
|
||||||
|
AppearanceAdjustments();
|
||||||
if (_deviceActionService.SupportsNfc())
|
if (_deviceActionService.SupportsNfc())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -149,7 +149,9 @@ namespace Bit.Droid
|
|||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
var setRestrictions = AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(this);
|
AndroidHelpers.SetPreconfiguredRestrictionSettingsAsync(this)
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNewIntent(Intent intent)
|
protected override void OnNewIntent(Intent intent)
|
||||||
@@ -380,45 +382,11 @@ namespace Bit.Droid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateTheme(string theme)
|
private void AppearanceAdjustments()
|
||||||
{
|
{
|
||||||
if (theme == "light")
|
Window?.SetStatusBarColor(ThemeHelpers.NavBarBackgroundColor);
|
||||||
{
|
Window?.DecorView.SetBackgroundColor(ThemeHelpers.BackgroundColor);
|
||||||
SetTheme(Resource.Style.LightTheme);
|
ThemeHelpers.SetAppearance(ThemeManager.GetTheme(true), ThemeManager.OsDarkModeEnabled());
|
||||||
}
|
|
||||||
else if (theme == "dark")
|
|
||||||
{
|
|
||||||
SetTheme(Resource.Style.DarkTheme);
|
|
||||||
}
|
|
||||||
else if (theme == "black")
|
|
||||||
{
|
|
||||||
SetTheme(Resource.Style.BlackTheme);
|
|
||||||
}
|
|
||||||
else if (theme == "nord")
|
|
||||||
{
|
|
||||||
SetTheme(Resource.Style.NordTheme);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_deviceActionService.UsingDarkTheme())
|
|
||||||
{
|
|
||||||
SetTheme(Resource.Style.DarkTheme);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetTheme(Resource.Style.LightTheme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RestartApp()
|
|
||||||
{
|
|
||||||
var intent = new Intent(this, typeof(MainActivity));
|
|
||||||
var pendingIntent = PendingIntent.GetActivity(this, 5923650, intent, PendingIntentFlags.CancelCurrent);
|
|
||||||
var alarmManager = GetSystemService(AlarmService) as AlarmManager;
|
|
||||||
var triggerMs = Java.Lang.JavaSystem.CurrentTimeMillis() + 500;
|
|
||||||
alarmManager.Set(AlarmType.Rtc, triggerMs, pendingIntent);
|
|
||||||
Java.Lang.JavaSystem.Exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExitApp()
|
private void ExitApp()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:versionCode="1"
|
android:versionCode="1"
|
||||||
android:versionName="2.11.1"
|
android:versionName="2.14.3"
|
||||||
android:installLocation="internalOnly"
|
android:installLocation="internalOnly"
|
||||||
package="com.x8bit.bitwarden">
|
package="com.x8bit.bitwarden">
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using Android.Content;
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Content.Res;
|
||||||
using Android.Views.InputMethods;
|
using Android.Views.InputMethods;
|
||||||
using Bit.Droid.Renderers;
|
using Bit.Droid.Renderers;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
@@ -25,6 +28,7 @@ namespace Bit.Droid.Renderers
|
|||||||
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
UpdateBorderColor();
|
||||||
if (Control != null && e.NewElement != null)
|
if (Control != null && e.NewElement != null)
|
||||||
{
|
{
|
||||||
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
||||||
@@ -33,5 +37,33 @@ namespace Bit.Droid.Renderers
|
|||||||
(ImeAction)ImeFlags.NoExtractUi;
|
(ImeAction)ImeFlags.NoExtractUi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
|
||||||
|
if (e.PropertyName == Entry.TextColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateBorderColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBorderColor()
|
||||||
|
{
|
||||||
|
if (Control != null)
|
||||||
|
{
|
||||||
|
var states = new[]
|
||||||
|
{
|
||||||
|
new[] { Android.Resource.Attribute.StateFocused }, // focused
|
||||||
|
new[] { -Android.Resource.Attribute.StateFocused }, // unfocused
|
||||||
|
};
|
||||||
|
var colors = new int[]
|
||||||
|
{
|
||||||
|
ThemeHelpers.PrimaryColor,
|
||||||
|
ThemeHelpers.MutedColor
|
||||||
|
};
|
||||||
|
Control.BackgroundTintList = new ColorStateList(states, colors);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
|
using Android.Content.Res;
|
||||||
using Android.Graphics;
|
using Android.Graphics;
|
||||||
using Android.Text;
|
using Android.Text;
|
||||||
using Android.Views.InputMethods;
|
using Android.Views.InputMethods;
|
||||||
using Android.Widget;
|
using Android.Widget;
|
||||||
using Bit.Droid.Renderers;
|
using Bit.Droid.Renderers;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
@@ -20,6 +22,7 @@ namespace Bit.Droid.Renderers
|
|||||||
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
UpdateBorderColor();
|
||||||
if (Control != null && e.NewElement != null)
|
if (Control != null && e.NewElement != null)
|
||||||
{
|
{
|
||||||
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
||||||
@@ -68,6 +71,28 @@ namespace Bit.Droid.Renderers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (e.PropertyName == Entry.TextColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateBorderColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBorderColor()
|
||||||
|
{
|
||||||
|
if (Control != null)
|
||||||
|
{
|
||||||
|
var states = new[]
|
||||||
|
{
|
||||||
|
new[] { Android.Resource.Attribute.StateFocused }, // focused
|
||||||
|
new[] { -Android.Resource.Attribute.StateFocused }, // unfocused
|
||||||
|
};
|
||||||
|
var colors = new int[]
|
||||||
|
{
|
||||||
|
ThemeHelpers.PrimaryColor,
|
||||||
|
ThemeHelpers.MutedColor
|
||||||
|
};
|
||||||
|
Control.BackgroundTintList = new ColorStateList(states, colors);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
using Android.Content;
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Content.Res;
|
||||||
using Bit.Droid.Renderers;
|
using Bit.Droid.Renderers;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
@@ -15,11 +18,40 @@ namespace Bit.Droid.Renderers
|
|||||||
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
UpdateBorderColor();
|
||||||
if (Control != null && e.NewElement != null)
|
if (Control != null && e.NewElement != null)
|
||||||
{
|
{
|
||||||
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight,
|
||||||
Control.PaddingBottom + 20);
|
Control.PaddingBottom + 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
|
||||||
|
if (e.PropertyName == Picker.TextColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateBorderColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBorderColor()
|
||||||
|
{
|
||||||
|
if (Control != null)
|
||||||
|
{
|
||||||
|
var states = new[]
|
||||||
|
{
|
||||||
|
new[] { Android.Resource.Attribute.StateFocused }, // focused
|
||||||
|
new[] { -Android.Resource.Attribute.StateFocused }, // unfocused
|
||||||
|
};
|
||||||
|
var colors = new int[]
|
||||||
|
{
|
||||||
|
ThemeHelpers.PrimaryColor,
|
||||||
|
ThemeHelpers.MutedColor
|
||||||
|
};
|
||||||
|
Control.BackgroundTintList = new ColorStateList(states, colors);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
src/Android/Renderers/CustomSwitchRenderer.cs
Normal file
66
src/Android/Renderers/CustomSwitchRenderer.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Content.Res;
|
||||||
|
using Android.Graphics.Drawables;
|
||||||
|
using Android.OS;
|
||||||
|
using AndroidX.Core.Content.Resources;
|
||||||
|
using Bit.Droid.Renderers;
|
||||||
|
using Bit.Droid.Utilities;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(Switch), typeof(CustomSwitchRenderer))]
|
||||||
|
namespace Bit.Droid.Renderers
|
||||||
|
{
|
||||||
|
public class CustomSwitchRenderer : SwitchRenderer
|
||||||
|
{
|
||||||
|
public CustomSwitchRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
UpdateColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
|
||||||
|
if (e.PropertyName == Switch.OnColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateColors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateColors()
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SdkInt <= BuildVersionCodes.LollipopMr1)
|
||||||
|
{
|
||||||
|
// Android 5.x doesn't support ThumbTintList, and using SwitchCompat on every version after 5.x
|
||||||
|
// doesn't apply tinting the way we want. Let 5.x to do its own thing here.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Control != null)
|
||||||
|
{
|
||||||
|
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.switch_thumb, null);
|
||||||
|
if (t is GradientDrawable thumb)
|
||||||
|
{
|
||||||
|
Control.ThumbDrawable = thumb;
|
||||||
|
}
|
||||||
|
var thumbStates = new[]
|
||||||
|
{
|
||||||
|
new[] { Android.Resource.Attribute.StateChecked }, // checked
|
||||||
|
new[] { -Android.Resource.Attribute.StateChecked }, // unchecked
|
||||||
|
};
|
||||||
|
var thumbColors = new int[]
|
||||||
|
{
|
||||||
|
ThemeHelpers.SwitchOnColor,
|
||||||
|
ThemeHelpers.SwitchThumbColor
|
||||||
|
};
|
||||||
|
Control.ThumbTintList = new ColorStateList(thumbStates, thumbColors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,11 +49,11 @@ namespace Bit.Droid.Renderers
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async void BottomNavigationView.IOnNavigationItemReselectedListener.OnNavigationItemReselected(IMenuItem item)
|
public void OnNavigationItemReselected(IMenuItem item)
|
||||||
{
|
{
|
||||||
if (_page?.CurrentPage?.Navigation != null && _page.CurrentPage.Navigation.NavigationStack.Count > 0)
|
if (_page?.CurrentPage?.Navigation != null && _page.CurrentPage.Navigation.NavigationStack.Count > 0)
|
||||||
{
|
{
|
||||||
await _page.CurrentPage.Navigation.PopToRootAsync();
|
Device.BeginInvokeOnMainThread(async () => await _page.CurrentPage.Navigation.PopToRootAsync());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Bit.Droid.Renderers;
|
using Bit.Droid.Renderers;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
@@ -10,8 +9,6 @@ namespace Bit.Droid.Renderers
|
|||||||
{
|
{
|
||||||
public class ExtendedGridRenderer : ViewRenderer
|
public class ExtendedGridRenderer : ViewRenderer
|
||||||
{
|
{
|
||||||
private static int? _bgResId;
|
|
||||||
|
|
||||||
public ExtendedGridRenderer(Context context) : base(context) { }
|
public ExtendedGridRenderer(Context context) : base(context) { }
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
|
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
|
||||||
@@ -19,25 +16,8 @@ namespace Bit.Droid.Renderers
|
|||||||
base.OnElementChanged(elementChangedEvent);
|
base.OnElementChanged(elementChangedEvent);
|
||||||
if (elementChangedEvent.NewElement != null)
|
if (elementChangedEvent.NewElement != null)
|
||||||
{
|
{
|
||||||
SetBackgroundResource(GetBgResId());
|
SetBackgroundResource(Resource.Drawable.list_item_bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetBgResId()
|
|
||||||
{
|
|
||||||
if (_bgResId == null)
|
|
||||||
{
|
|
||||||
if (ThemeManager.GetTheme(true) == "nord")
|
|
||||||
{
|
|
||||||
_bgResId = Resource.Drawable.list_item_bg_nord;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_bgResId ??= ThemeManager.UsingLightTheme ? Resource.Drawable.list_item_bg :
|
|
||||||
Resource.Drawable.list_item_bg_dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _bgResId.Value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Android.Content;
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
using Android.Graphics.Drawables;
|
using Android.Graphics.Drawables;
|
||||||
using AndroidX.Core.Content.Resources;
|
using AndroidX.Core.Content.Resources;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
@@ -18,6 +19,21 @@ namespace Bit.Droid.Renderers
|
|||||||
protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
|
protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
|
||||||
{
|
{
|
||||||
base.OnElementChanged(e);
|
base.OnElementChanged(e);
|
||||||
|
UpdateColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
|
||||||
|
if (e.PropertyName == ExtendedSlider.ThumbBorderColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateColor()
|
||||||
|
{
|
||||||
if (Control != null && Element is ExtendedSlider view)
|
if (Control != null && Element is ExtendedSlider view)
|
||||||
{
|
{
|
||||||
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null);
|
var t = ResourcesCompat.GetDrawable(Resources, Resource.Drawable.slider_thumb, null);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
using Bit.App.Utilities;
|
|
||||||
using Bit.Droid.Renderers;
|
using Bit.Droid.Renderers;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.Platform.Android;
|
using Xamarin.Forms.Platform.Android;
|
||||||
@@ -10,8 +9,6 @@ namespace Bit.Droid.Renderers
|
|||||||
{
|
{
|
||||||
public class ExtendedStackLayoutRenderer : ViewRenderer
|
public class ExtendedStackLayoutRenderer : ViewRenderer
|
||||||
{
|
{
|
||||||
private static int? _bgResId;
|
|
||||||
|
|
||||||
public ExtendedStackLayoutRenderer(Context context) : base(context) { }
|
public ExtendedStackLayoutRenderer(Context context) : base(context) { }
|
||||||
|
|
||||||
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
|
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
|
||||||
@@ -19,25 +16,8 @@ namespace Bit.Droid.Renderers
|
|||||||
base.OnElementChanged(elementChangedEvent);
|
base.OnElementChanged(elementChangedEvent);
|
||||||
if (elementChangedEvent.NewElement != null)
|
if (elementChangedEvent.NewElement != null)
|
||||||
{
|
{
|
||||||
SetBackgroundResource(GetBgResId());
|
SetBackgroundResource(Resource.Drawable.list_item_bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetBgResId()
|
|
||||||
{
|
|
||||||
if (_bgResId == null)
|
|
||||||
{
|
|
||||||
if (ThemeManager.GetTheme(true) == "nord")
|
|
||||||
{
|
|
||||||
_bgResId = Resource.Drawable.list_item_bg_nord;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_bgResId ??= ThemeManager.UsingLightTheme ? Resource.Drawable.list_item_bg :
|
|
||||||
Resource.Drawable.list_item_bg_dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _bgResId.Value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
72
src/Android/Renderers/ExtendedStepperRenderer.cs
Normal file
72
src/Android/Renderers/ExtendedStepperRenderer.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Graphics;
|
||||||
|
using Android.OS;
|
||||||
|
using Bit.App.Controls;
|
||||||
|
using Bit.Droid.Renderers;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(ExtendedStepper), typeof(ExtendedStepperRenderer))]
|
||||||
|
namespace Bit.Droid.Renderers
|
||||||
|
{
|
||||||
|
public class ExtendedStepperRenderer : StepperRenderer
|
||||||
|
{
|
||||||
|
public ExtendedStepperRenderer(Context context)
|
||||||
|
: base(context)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
UpdateBgColor();
|
||||||
|
UpdateFgColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnElementPropertyChanged(sender, e);
|
||||||
|
|
||||||
|
if (e.PropertyName == ExtendedStepper.StepperBackgroundColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateBgColor();
|
||||||
|
}
|
||||||
|
else if (e.PropertyName == ExtendedStepper.StepperForegroundColorProperty.PropertyName)
|
||||||
|
{
|
||||||
|
UpdateFgColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBgColor()
|
||||||
|
{
|
||||||
|
if (Control != null && Element is ExtendedStepper view)
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
|
||||||
|
{
|
||||||
|
Control.GetChildAt(0)?.Background?.SetColorFilter(
|
||||||
|
new BlendModeColorFilter(view.StepperBackgroundColor.ToAndroid(), BlendMode.Multiply));
|
||||||
|
Control.GetChildAt(1)?.Background?.SetColorFilter(
|
||||||
|
new BlendModeColorFilter(view.StepperBackgroundColor.ToAndroid(), BlendMode.Multiply));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Control.GetChildAt(0)?.Background?.SetColorFilter(
|
||||||
|
view.StepperBackgroundColor.ToAndroid(), PorterDuff.Mode.Multiply);
|
||||||
|
Control.GetChildAt(1)?.Background?.SetColorFilter(
|
||||||
|
view.StepperBackgroundColor.ToAndroid(), PorterDuff.Mode.Multiply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFgColor()
|
||||||
|
{
|
||||||
|
if (Control != null && Element is ExtendedStepper view)
|
||||||
|
{
|
||||||
|
var btn0 = Control.GetChildAt(0) as Android.Widget.Button;
|
||||||
|
btn0?.SetTextColor(view.StepperForegroundColor.ToAndroid());
|
||||||
|
var btn1 = Control.GetChildAt(1) as Android.Widget.Button;
|
||||||
|
btn1?.SetTextColor(view.StepperForegroundColor.ToAndroid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/Android/Renderers/SelectableLabelRenderer.cs
Normal file
25
src/Android/Renderers/SelectableLabelRenderer.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using Android.Content;
|
||||||
|
using Bit.App.Controls;
|
||||||
|
using Bit.Droid.Renderers;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(SelectableLabel), typeof(SelectableLabelRenderer))]
|
||||||
|
namespace Bit.Droid.Renderers
|
||||||
|
{
|
||||||
|
public class SelectableLabelRenderer : LabelRenderer
|
||||||
|
{
|
||||||
|
public SelectableLabelRenderer(Context context) : base(context) { }
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
|
||||||
|
if (Control != null)
|
||||||
|
{
|
||||||
|
Control.SetTextIsSelectable(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ TODO: When API 23 becomes our new minimum, replace 'splash_screen.xml' in 'drawa
|
|||||||
-->
|
-->
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item>
|
<item>
|
||||||
<color android:color="@color/lightgray"/>
|
<color android:color="@color/white"/>
|
||||||
</item>
|
</item>
|
||||||
<item
|
<item
|
||||||
android:drawable="@drawable/logo"
|
android:drawable="@drawable/logo"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:color="@color/itemPressed">
|
android:color="#8E8E93">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@android:id/mask"
|
android:id="@android:id/mask"
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:color="@color/dark_primary">
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@android:id/mask"
|
|
||||||
android:drawable="@android:color/white" />
|
|
||||||
</ripple>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:color="@color/nord_primary">
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@android:id/mask"
|
|
||||||
android:drawable="@android:color/white" />
|
|
||||||
</ripple>
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item>
|
<item>
|
||||||
<color android:color="@color/lightgray"/>
|
<color android:color="@color/white"/>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<bitmap android:src="@drawable/logo_legacy" android:tileMode="disabled" android:gravity="center"/>
|
<bitmap android:src="@drawable/logo_legacy" android:tileMode="disabled" android:gravity="center"/>
|
||||||
|
|||||||
5
src/Android/Resources/drawable/switch_thumb.xml
Normal file
5
src/Android/Resources/drawable/switch_thumb.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||||
|
<solid android:color="@color/white"/>
|
||||||
|
<size android:width="20dp" android:height="20dp"/>
|
||||||
|
</shape>
|
||||||
@@ -1,8 +1,26 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<resources>
|
<resources>
|
||||||
<!-- Launch theme (for auto dark/light based on system) -->
|
<!-- Launch theme (for auto dark/light based on system) -->
|
||||||
<style name="LaunchTheme" parent="DarkTheme.Base">
|
<style name="LaunchTheme" parent="BaseTheme">
|
||||||
<item name="android:windowBackground">@drawable/splash_screen_dark</item>
|
<item name="android:windowBackground">@drawable/splash_screen_dark</item>
|
||||||
<item name="android:windowNoTitle">true</item>
|
<item name="android:windowNoTitle">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="BaseTheme" parent="Theme.AppCompat">
|
||||||
|
<item name="windowNoTitle">true</item>
|
||||||
|
<item name="windowActionBar">false</item>
|
||||||
|
<item name="colorPrimaryDark">@color/dark_notificationBar</item>
|
||||||
|
<item name="colorAccent">@color/dark_primary</item>
|
||||||
|
<item name="colorControlNormal">@color/dark_border</item>
|
||||||
|
<item name="android:navigationBarColor">@color/dark_navigationBarBackground</item>
|
||||||
|
<item name="windowActionModeOverlay">true</item>
|
||||||
|
<item name="android:colorActivatedHighlight">@android:color/transparent</item>
|
||||||
|
<item name="android:textCursorDrawable">@null</item>
|
||||||
|
<item name="popupTheme">@style/ThemeOverlay.AppCompat</item>
|
||||||
|
<item name="buttonStyle">@style/ButtonStyle</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ButtonStyle" parent="Widget.AppCompat.Button">
|
||||||
|
<item name="android:textAllCaps">false</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,38 +1,19 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<!-- Light theme -->
|
<!-- Light System -->
|
||||||
<color name="colorPrimary">#175DDC</color>
|
<color name="colorPrimary">#175DDC</color>
|
||||||
<color name="colorPrimaryDark">#1A3B66</color>
|
<color name="colorPrimaryDark">#1A3B66</color>
|
||||||
<color name="primary">#175DDC</color>
|
<color name="primary">#175DDC</color>
|
||||||
<color name="notificationBar">#1452BC</color>
|
<color name="notificationBar">#1452BC</color>
|
||||||
<color name="border">#dddddd</color>
|
<color name="border">#dddddd</color>
|
||||||
<color name="itemPressed">#bbbbbb</color>
|
|
||||||
|
|
||||||
<!-- Dark theme -->
|
<!-- Dark System -->
|
||||||
<color name="dark_primary">#52bdfb</color>
|
<color name="dark_primary">#52bdfb</color>
|
||||||
<color name="dark_notificationBar">#191919</color>
|
<color name="dark_notificationBar">#191919</color>
|
||||||
<color name="dark_border">#191919</color>
|
<color name="dark_border">#666666</color>
|
||||||
<color name="dark_navigationBarBackground">#191919</color>
|
<color name="dark_navigationBarBackground">#191919</color>
|
||||||
|
|
||||||
<!-- Black theme -->
|
|
||||||
<color name="black_border">#282828</color>
|
|
||||||
|
|
||||||
<!-- Nord theme -->
|
|
||||||
<color name="nord_background">#3b4252</color>
|
|
||||||
<color name="nord_text">#e5e9f0</color>
|
|
||||||
<color name="nord_primary">#81a1c1</color>
|
|
||||||
<color name="nord_actionBar">#2e3440</color>
|
|
||||||
<color name="nord_actionBarText">#e5e9f0</color>
|
|
||||||
<color name="nord_notificationBar">#20242D</color>
|
|
||||||
<color name="nord_dialogBackground">#3b4252</color>
|
|
||||||
<color name="nord_border">#2e3440</color>
|
|
||||||
<color name="nord_popupBackground">#4c566a</color>
|
|
||||||
<color name="nord_popupText">#e5e9f0</color>
|
|
||||||
<color name="nord_buttonBackground">#4c566a</color>
|
|
||||||
<color name="nord_navigationBarBackground">#20242D</color>
|
|
||||||
|
|
||||||
<!-- Other -->
|
<!-- Other -->
|
||||||
<color name="launcher_background">#FFFFFF</color>
|
|
||||||
<color name="white">#FFFFFF</color>
|
<color name="white">#FFFFFF</color>
|
||||||
<color name="black">#000000</color>
|
<color name="black">#000000</color>
|
||||||
<color name="darkgray">#333333</color>
|
<color name="darkgray">#333333</color>
|
||||||
|
|||||||
@@ -1,21 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources>
|
||||||
<!-- Launch theme (for auto dark/light based on system) -->
|
<!-- Launch theme (for auto dark/light based on system) -->
|
||||||
<style name="LaunchTheme" parent="LightTheme.Base">
|
<style name="LaunchTheme" parent="BaseTheme">
|
||||||
<item name="android:windowBackground">@drawable/splash_screen</item>
|
<item name="android:windowBackground">@drawable/splash_screen</item>
|
||||||
<item name="android:windowNoTitle">true</item>
|
<item name="android:windowNoTitle">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Light theme -->
|
<style name="BaseTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||||
<style name="LightTheme" parent="LightTheme.Base">
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="LightTheme.Splash" parent="LightTheme.Base">
|
|
||||||
<item name="android:windowBackground">@drawable/splash_screen</item>
|
|
||||||
<item name="android:windowNoTitle">true</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="LightTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
|
|
||||||
<item name="windowNoTitle">true</item>
|
<item name="windowNoTitle">true</item>
|
||||||
<item name="windowActionBar">false</item>
|
<item name="windowActionBar">false</item>
|
||||||
<item name="colorPrimary">@color/primary</item>
|
<item name="colorPrimary">@color/primary</item>
|
||||||
@@ -27,70 +18,10 @@
|
|||||||
<item name="android:colorActivatedHighlight">@android:color/transparent</item>
|
<item name="android:colorActivatedHighlight">@android:color/transparent</item>
|
||||||
<item name="android:textCursorDrawable">@null</item>
|
<item name="android:textCursorDrawable">@null</item>
|
||||||
<item name="popupTheme">@style/ThemeOverlay.AppCompat.Light</item>
|
<item name="popupTheme">@style/ThemeOverlay.AppCompat.Light</item>
|
||||||
|
<item name="buttonStyle">@style/ButtonStyle</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Dark theme -->
|
<style name="ButtonStyle" parent="Widget.AppCompat.Button">
|
||||||
<style name="DarkTheme" parent="DarkTheme.Base">
|
<item name="android:textAllCaps">false</item>
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="DarkTheme.Splash" parent="DarkTheme.Base">
|
|
||||||
<item name="android:windowBackground">@drawable/splash_screen_dark</item>
|
|
||||||
<item name="android:windowNoTitle">true</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="DarkTheme.Base" parent="Theme.AppCompat">
|
|
||||||
<item name="windowNoTitle">true</item>
|
|
||||||
<item name="windowActionBar">false</item>
|
|
||||||
<item name="colorPrimaryDark">@color/dark_notificationBar</item>
|
|
||||||
<item name="colorAccent">@color/dark_primary</item>
|
|
||||||
<item name="colorControlNormal">@color/dark_border</item>
|
|
||||||
<item name="android:navigationBarColor">@color/dark_navigationBarBackground</item>
|
|
||||||
<item name="windowActionModeOverlay">true</item>
|
|
||||||
<item name="android:colorActivatedHighlight">@android:color/transparent</item>
|
|
||||||
<item name="android:textCursorDrawable">@null</item>
|
|
||||||
<item name="popupTheme">@style/ThemeOverlay.AppCompat</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- Black theme -->
|
|
||||||
<style name="BlackTheme" parent="BlackTheme.Base">
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="BlackTheme.Splash" parent="DarkTheme.Splash">
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="BlackTheme.Base" parent="DarkTheme.Base">
|
|
||||||
<item name="android:windowBackground">@android:color/black</item>
|
|
||||||
<item name="colorPrimary">@android:color/black</item>
|
|
||||||
<item name="colorPrimaryDark">@android:color/black</item>
|
|
||||||
<item name="colorControlNormal">@color/black_border</item>
|
|
||||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- Nord theme -->
|
|
||||||
<style name="NordTheme" parent="NordTheme.Base">
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="NordTheme.Splash" parent="DarkTheme.Splash">
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="NordTheme.Base" parent="DarkTheme.Base">
|
|
||||||
<item name="android:windowBackground">@color/nord_background</item>
|
|
||||||
<item name="android:actionMenuTextColor">@color/nord_actionBarText</item>
|
|
||||||
<item name="android:textColor">@color/nord_text</item>
|
|
||||||
<item name="colorAccent">@color/nord_primary</item>
|
|
||||||
<item name="colorPrimary">@color/nord_actionBar</item>
|
|
||||||
<item name="colorPrimaryDark">@color/nord_notificationBar</item>
|
|
||||||
<item name="colorControlNormal">@color/nord_border</item>
|
|
||||||
<item name="android:navigationBarColor">@color/nord_navigationBarBackground</item>
|
|
||||||
<item name="colorBackgroundFloating">@color/nord_dialogBackground</item>
|
|
||||||
<item name="android:colorBackgroundFloating" tools:targetApi="23">@color/nord_dialogBackground</item>
|
|
||||||
<item name="popupTheme">@style/NordTheme.Popup</item>
|
|
||||||
<item name="colorButtonNormal">@color/nord_buttonBackground</item>
|
|
||||||
<item name="android:colorButtonNormal">@color/nord_buttonBackground</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="NordTheme.Popup" parent="ThemeOverlay.AppCompat">
|
|
||||||
<item name="android:colorBackground">@color/nord_popupBackground</item>
|
|
||||||
<item name="android:textColor">@color/nord_popupText</item>
|
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -11,9 +11,6 @@
|
|||||||
-->
|
-->
|
||||||
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
|
<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:supportsInlineSuggestions="true">
|
android:supportsInlineSuggestions="true">
|
||||||
<compatibility-package
|
|
||||||
android:name="alook.browser"
|
|
||||||
android:maxLongVersionCode="10000000000"/>
|
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.amazon.cloud9"
|
android:name="com.amazon.cloud9"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
@@ -71,6 +68,9 @@
|
|||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.google.android.apps.chrome_dev"
|
android:name="com.google.android.apps.chrome_dev"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="com.google.android.captiveportallogin"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="com.jamal2367.styx"
|
android:name="com.jamal2367.styx"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
@@ -211,10 +211,10 @@
|
|||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="org.mozilla.firefox"
|
android:name="org.mozilla.firefox"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="2015836711"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="org.mozilla.firefox_beta"
|
android:name="org.mozilla.firefox_beta"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="2015849447"/>
|
||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="org.mozilla.reference.browser"
|
android:name="org.mozilla.reference.browser"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
@@ -233,4 +233,7 @@
|
|||||||
<compatibility-package
|
<compatibility-package
|
||||||
android:name="org.ungoogled.chromium.stable"
|
android:name="org.ungoogled.chromium.stable"
|
||||||
android:maxLongVersionCode="10000000000"/>
|
android:maxLongVersionCode="10000000000"/>
|
||||||
|
<compatibility-package
|
||||||
|
android:name="us.spotco.fennec_dos"
|
||||||
|
android:maxLongVersionCode="10000000000"/>
|
||||||
</autofill-service>
|
</autofill-service>
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ using Android.App;
|
|||||||
using Android.App.Assist;
|
using Android.App.Assist;
|
||||||
using Android.Content;
|
using Android.Content;
|
||||||
using Android.Content.PM;
|
using Android.Content.PM;
|
||||||
using Android.Content.Res;
|
|
||||||
using Android.Nfc;
|
using Android.Nfc;
|
||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Provider;
|
using Android.Provider;
|
||||||
@@ -641,18 +640,10 @@ namespace Bit.Droid.Services
|
|||||||
|
|
||||||
public bool AutofillAccessibilityServiceRunning()
|
public bool AutofillAccessibilityServiceRunning()
|
||||||
{
|
{
|
||||||
try
|
var enabledServices = Settings.Secure.GetString(Application.Context.ContentResolver,
|
||||||
{
|
Settings.Secure.EnabledAccessibilityServices);
|
||||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
return Application.Context.PackageName != null &&
|
||||||
var manager = activity.GetSystemService(Context.ActivityService) as ActivityManager;
|
(enabledServices?.Contains(Application.Context.PackageName) ?? false);
|
||||||
var services = manager.GetRunningServices(int.MaxValue);
|
|
||||||
return services.Any(s => s.Process.ToLowerInvariant().Contains("bitwarden") &&
|
|
||||||
s.Service.ClassName.ToLowerInvariant().Contains("accessibilityservice"));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AutofillAccessibilityOverlayPermitted()
|
public bool AutofillAccessibilityOverlayPermitted()
|
||||||
@@ -741,21 +732,6 @@ namespace Bit.Droid.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UsingDarkTheme()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
|
|
||||||
{
|
|
||||||
var app = CrossCurrentActivity.Current.AppContext;
|
|
||||||
var uiModeFlags = app.Resources.Configuration.UiMode & UiMode.NightMask;
|
|
||||||
return uiModeFlags == UiMode.NightYes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetActiveTime()
|
public long GetActiveTime()
|
||||||
{
|
{
|
||||||
// Returns milliseconds since the system was booted, and includes deep sleep. This clock is guaranteed to
|
// Returns milliseconds since the system was booted, and includes deep sleep. This clock is guaranteed to
|
||||||
@@ -776,6 +752,11 @@ namespace Bit.Droid.Services
|
|||||||
_messagingService.Send("finishMainActivity");
|
_messagingService.Send("finishMainActivity");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SupportsFido2()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private bool DeleteDir(Java.IO.File dir)
|
private bool DeleteDir(Java.IO.File dir)
|
||||||
{
|
{
|
||||||
if (dir != null && dir.IsDirectory)
|
if (dir != null && dir.IsDirectory)
|
||||||
|
|||||||
@@ -98,5 +98,15 @@ namespace Bit.Droid.Services
|
|||||||
Console.WriteLine(".NET Fallback Language/Locale:" + netLanguage + " (application-specific)");
|
Console.WriteLine(".NET Fallback Language/Locale:" + netLanguage + " (application-specific)");
|
||||||
return netLanguage;
|
return netLanguage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetLocaleShortDate(DateTime? date)
|
||||||
|
{
|
||||||
|
return date?.ToShortDateString() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetLocaleShortTime(DateTime? time)
|
||||||
|
{
|
||||||
|
return time?.ToShortTimeString() ?? string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
71
src/Android/Utilities/ThemeHelpers.cs
Normal file
71
src/Android/Utilities/ThemeHelpers.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using Android.Graphics;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
namespace Bit.Droid.Utilities
|
||||||
|
{
|
||||||
|
public class ThemeHelpers
|
||||||
|
{
|
||||||
|
public static bool LightTheme = true;
|
||||||
|
|
||||||
|
public static Color PrimaryColor
|
||||||
|
{
|
||||||
|
get => ThemeManager.GetResourceColor("PrimaryColor").ToAndroid();
|
||||||
|
}
|
||||||
|
public static Color MutedColor
|
||||||
|
{
|
||||||
|
get => ThemeManager.GetResourceColor("MutedColor").ToAndroid();
|
||||||
|
}
|
||||||
|
public static Color BackgroundColor
|
||||||
|
{
|
||||||
|
get => ThemeManager.GetResourceColor("BackgroundColor").ToAndroid();
|
||||||
|
}
|
||||||
|
public static Color NavBarBackgroundColor
|
||||||
|
{
|
||||||
|
get => ThemeManager.GetResourceColor("NavigationBarBackgroundColor").ToAndroid();
|
||||||
|
}
|
||||||
|
public static Color FabColor
|
||||||
|
{
|
||||||
|
get => ThemeManager.GetResourceColor("FabColor").ToAndroid();
|
||||||
|
}
|
||||||
|
public static Color SwitchOnColor
|
||||||
|
{
|
||||||
|
get => ThemeManager.GetResourceColor("SwitchOnColor").ToAndroid();
|
||||||
|
}
|
||||||
|
public static Color SwitchThumbColor
|
||||||
|
{
|
||||||
|
get => ThemeManager.GetResourceColor("SwitchThumbColor").ToAndroid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetAppearance(string theme, bool osDarkModeEnabled)
|
||||||
|
{
|
||||||
|
SetThemeVariables(theme, osDarkModeEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetDialogTheme()
|
||||||
|
{
|
||||||
|
if (LightTheme)
|
||||||
|
{
|
||||||
|
return Android.Resource.Style.ThemeMaterialLightDialog;
|
||||||
|
}
|
||||||
|
return Android.Resource.Style.ThemeMaterialDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetThemeVariables(string theme, bool osDarkModeEnabled)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(theme) && osDarkModeEnabled)
|
||||||
|
{
|
||||||
|
theme = "dark";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme == "dark" || theme == "black" || theme == "nord")
|
||||||
|
{
|
||||||
|
LightTheme = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LightTheme = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,8 +42,8 @@ namespace Bit.App.Abstractions
|
|||||||
void OpenAccessibilitySettings();
|
void OpenAccessibilitySettings();
|
||||||
void OpenAccessibilityOverlayPermissionSettings();
|
void OpenAccessibilityOverlayPermissionSettings();
|
||||||
void OpenAutofillSettings();
|
void OpenAutofillSettings();
|
||||||
bool UsingDarkTheme();
|
|
||||||
long GetActiveTime();
|
long GetActiveTime();
|
||||||
void CloseMainApp();
|
void CloseMainApp();
|
||||||
|
bool SupportsFido2();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
using System.Globalization;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
namespace Bit.App.Abstractions
|
namespace Bit.App.Abstractions
|
||||||
{
|
{
|
||||||
public interface ILocalizeService
|
public interface ILocalizeService
|
||||||
{
|
{
|
||||||
CultureInfo GetCurrentCultureInfo();
|
CultureInfo GetCurrentCultureInfo();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Format date using device locale.
|
||||||
|
/// Needed for iOS as it provides locales unsupported in .Net
|
||||||
|
/// </summary>
|
||||||
|
string GetLocaleShortDate(DateTime? date);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Format time using device locale.
|
||||||
|
/// Needed for iOS as it provides locales unsupported in .Net
|
||||||
|
/// </summary>
|
||||||
|
string GetLocaleShortTime(DateTime? time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.2.0" />
|
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
|
||||||
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
|
||||||
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
|
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
|
||||||
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
|
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
|
||||||
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2012" />
|
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2125" />
|
||||||
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
|
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
|
||||||
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
|
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -124,6 +124,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Resources\" />
|
<Folder Include="Resources\" />
|
||||||
|
<Folder Include="Behaviors\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -411,4 +412,7 @@
|
|||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Behaviors\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -17,21 +17,12 @@ namespace Bit.App
|
|||||||
{
|
{
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
private readonly MobileI18nService _i18nService;
|
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||||
private readonly ISyncService _syncService;
|
private readonly ISyncService _syncService;
|
||||||
private readonly ITokenService _tokenService;
|
|
||||||
private readonly ICryptoService _cryptoService;
|
|
||||||
private readonly ICipherService _cipherService;
|
|
||||||
private readonly IFolderService _folderService;
|
|
||||||
private readonly ICollectionService _collectionService;
|
|
||||||
private readonly ISettingsService _settingsService;
|
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
|
||||||
private readonly ISearchService _searchService;
|
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IAuthService _authService;
|
private readonly IAuthService _authService;
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStorageService _storageService;
|
||||||
@@ -54,20 +45,10 @@ namespace Bit.App
|
|||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
_tokenService = ServiceContainer.Resolve<ITokenService>("tokenService");
|
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
|
||||||
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
|
||||||
_folderService = ServiceContainer.Resolve<IFolderService>("folderService");
|
|
||||||
_settingsService = ServiceContainer.Resolve<ISettingsService>("settingsService");
|
|
||||||
_collectionService = ServiceContainer.Resolve<ICollectionService>("collectionService");
|
|
||||||
_searchService = ServiceContainer.Resolve<ISearchService>("searchService");
|
|
||||||
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
_secureStorageService = ServiceContainer.Resolve<IStorageService>("secureStorageService");
|
||||||
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>(
|
|
||||||
"passwordGenerationService");
|
|
||||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService") as MobileI18nService;
|
|
||||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
|
|
||||||
Bootstrap();
|
Bootstrap();
|
||||||
@@ -219,6 +200,7 @@ namespace Bit.App
|
|||||||
|
|
||||||
private async void ResumedAsync()
|
private async void ResumedAsync()
|
||||||
{
|
{
|
||||||
|
UpdateTheme();
|
||||||
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
await _vaultTimeoutService.CheckVaultTimeoutAsync();
|
||||||
_messagingService.Send("startEventTimer");
|
_messagingService.Send("startEventTimer");
|
||||||
await ClearCacheIfNeededAsync();
|
await ClearCacheIfNeededAsync();
|
||||||
@@ -240,22 +222,7 @@ namespace Bit.App
|
|||||||
|
|
||||||
private async Task LogOutAsync(bool expired)
|
private async Task LogOutAsync(bool expired)
|
||||||
{
|
{
|
||||||
var userId = await _userService.GetUserIdAsync();
|
await AppHelpers.LogOutAsync();
|
||||||
await Task.WhenAll(
|
|
||||||
_syncService.SetLastSyncAsync(DateTime.MinValue),
|
|
||||||
_tokenService.ClearTokenAsync(),
|
|
||||||
_cryptoService.ClearKeysAsync(),
|
|
||||||
_userService.ClearAsync(),
|
|
||||||
_settingsService.ClearAsync(userId),
|
|
||||||
_cipherService.ClearAsync(userId),
|
|
||||||
_folderService.ClearAsync(userId),
|
|
||||||
_collectionService.ClearAsync(userId),
|
|
||||||
_passwordGenerationService.ClearAsync(),
|
|
||||||
_vaultTimeoutService.ClearAsync(),
|
|
||||||
_stateService.PurgeAsync(),
|
|
||||||
_deviceActionService.ClearCacheAsync());
|
|
||||||
_vaultTimeoutService.BiometricLocked = true;
|
|
||||||
_searchService.ClearIndex();
|
|
||||||
_authService.LogOut(() =>
|
_authService.LogOut(() =>
|
||||||
{
|
{
|
||||||
Current.MainPage = new HomePage();
|
Current.MainPage = new HomePage();
|
||||||
@@ -309,12 +276,7 @@ namespace Bit.App
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Will only ever be null - look to remove this in the future
|
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||||
var vaultTimeout = _platformUtilsService.LockTimeout();
|
|
||||||
if (vaultTimeout == null)
|
|
||||||
{
|
|
||||||
vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
|
||||||
}
|
|
||||||
vaultTimeout = vaultTimeout.GetValueOrDefault(-1);
|
vaultTimeout = vaultTimeout.GetValueOrDefault(-1);
|
||||||
if (vaultTimeout == 0)
|
if (vaultTimeout == 0)
|
||||||
{
|
{
|
||||||
@@ -376,6 +338,10 @@ namespace Bit.App
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
SetCulture();
|
SetCulture();
|
||||||
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
||||||
|
Current.RequestedThemeChanged += (s, a) =>
|
||||||
|
{
|
||||||
|
UpdateTheme();
|
||||||
|
};
|
||||||
Current.MainPage = new HomePage();
|
Current.MainPage = new HomePage();
|
||||||
var mainPageTask = SetMainPageAsync();
|
var mainPageTask = SetMainPageAsync();
|
||||||
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
||||||
@@ -398,6 +364,15 @@ namespace Bit.App
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateTheme()
|
||||||
|
{
|
||||||
|
Device.BeginInvokeOnMainThread(() =>
|
||||||
|
{
|
||||||
|
ThemeManager.SetTheme(Device.RuntimePlatform == Device.Android, Current.Resources);
|
||||||
|
_messagingService.Send("updatedTheme");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async Task LockedAsync(bool autoPromptBiometric)
|
private async Task LockedAsync(bool autoPromptBiometric)
|
||||||
{
|
{
|
||||||
await _stateService.PurgeAsync();
|
await _stateService.PurgeAsync();
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using Xamarin.Essentials;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Behaviors
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This behavior prevents the Editor to be automatically scrolled to the bottom on focus.
|
||||||
|
/// This is needed due to this Xamarin Forms issue: https://github.com/xamarin/Xamarin.Forms/issues/2233
|
||||||
|
/// </summary>
|
||||||
|
public class EditorPreventAutoBottomScrollingOnFocusedBehavior : Behavior<Editor>
|
||||||
|
{
|
||||||
|
public static readonly BindableProperty ParentScrollViewProperty
|
||||||
|
= BindableProperty.Create(nameof(ParentScrollView), typeof(ScrollView), typeof(EditorPreventAutoBottomScrollingOnFocusedBehavior));
|
||||||
|
|
||||||
|
public ScrollView ParentScrollView
|
||||||
|
{
|
||||||
|
get => (ScrollView)GetValue(ParentScrollViewProperty);
|
||||||
|
set => SetValue(ParentScrollViewProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnAttachedTo(Editor bindable)
|
||||||
|
{
|
||||||
|
base.OnAttachedTo(bindable);
|
||||||
|
|
||||||
|
bindable.Focused += OnFocused;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFocused(object sender, FocusEventArgs e)
|
||||||
|
{
|
||||||
|
if (DeviceInfo.Platform.Equals(DevicePlatform.iOS) && ParentScrollView != null)
|
||||||
|
{
|
||||||
|
ParentScrollView.ScrollToAsync(ParentScrollView.ScrollX, ParentScrollView.ScrollY, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDetachingFrom(Editor bindable)
|
||||||
|
{
|
||||||
|
bindable.Focused -= OnFocused;
|
||||||
|
|
||||||
|
base.OnDetachingFrom(bindable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
<u:IconGlyphConverter x:Key="iconGlyphConverter"/>
|
<u:IconGlyphConverter x:Key="iconGlyphConverter"/>
|
||||||
<u:IconImageConverter x:Key="iconImageConverter"/>
|
<u:IconImageConverter x:Key="iconImageConverter"/>
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
|
<u:StringHasValueConverter x:Key="stringHasValueConverter" />
|
||||||
</Grid.Resources>
|
</Grid.Resources>
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -45,7 +46,7 @@
|
|||||||
WidthRequest="22"
|
WidthRequest="22"
|
||||||
HeightRequest="22"
|
HeightRequest="22"
|
||||||
IsVisible="{Binding ShowIconImage}"
|
IsVisible="{Binding ShowIconImage}"
|
||||||
Source="{Binding Cipher, Converter={StaticResource iconImageConverter}}"
|
Source="{Binding IconImageSource, Mode=OneTime}"
|
||||||
AutomationProperties.IsInAccessibleTree="False" />
|
AutomationProperties.IsInAccessibleTree="False" />
|
||||||
|
|
||||||
<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">
|
||||||
@@ -72,7 +73,9 @@
|
|||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.ColumnSpan="3"
|
Grid.ColumnSpan="3"
|
||||||
StyleClass="list-subtitle, list-subtitle-platform"
|
StyleClass="list-subtitle, list-subtitle-platform"
|
||||||
Text="{Binding Cipher.SubTitle}" />
|
Text="{Binding Cipher.SubTitle}"
|
||||||
|
IsVisible="{Binding Source={RelativeSource Self}, Path=Text,
|
||||||
|
Converter={StaticResource stringHasValueConverter}}"/>
|
||||||
<controls:FaLabel
|
<controls:FaLabel
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
@@ -80,7 +83,7 @@
|
|||||||
VerticalOptions="Center"
|
VerticalOptions="Center"
|
||||||
StyleClass="list-title-icon"
|
StyleClass="list-title-icon"
|
||||||
Margin="5, 0, 0, 0"
|
Margin="5, 0, 0, 0"
|
||||||
Text=""
|
Text=""
|
||||||
IsVisible="{Binding Cipher.Shared, Mode=OneTime}"
|
IsVisible="{Binding Cipher.Shared, Mode=OneTime}"
|
||||||
AutomationProperties.IsInAccessibleTree="True"
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
AutomationProperties.Name="{u:I18n Shared}" />
|
AutomationProperties.Name="{u:I18n Shared}" />
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Models.View;
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.App.Controls
|
namespace Bit.App.Controls
|
||||||
@@ -7,6 +8,7 @@ namespace Bit.App.Controls
|
|||||||
{
|
{
|
||||||
private CipherView _cipher;
|
private CipherView _cipher;
|
||||||
private bool _websiteIconsEnabled;
|
private bool _websiteIconsEnabled;
|
||||||
|
private string _iconImageSource = string.Empty;
|
||||||
|
|
||||||
public CipherViewCellViewModel(CipherView cipherView, bool websiteIconsEnabled)
|
public CipherViewCellViewModel(CipherView cipherView, bool websiteIconsEnabled)
|
||||||
{
|
{
|
||||||
@@ -28,8 +30,22 @@ namespace Bit.App.Controls
|
|||||||
|
|
||||||
public bool ShowIconImage
|
public bool ShowIconImage
|
||||||
{
|
{
|
||||||
get => WebsiteIconsEnabled && !string.IsNullOrWhiteSpace(Cipher.Login?.Uri) &&
|
get => WebsiteIconsEnabled
|
||||||
Cipher.Login.Uri.StartsWith("http");
|
&& !string.IsNullOrWhiteSpace(Cipher.Login?.Uri)
|
||||||
|
&& IconImageSource != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string IconImageSource
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_iconImageSource == string.Empty) // default value since icon source can return null
|
||||||
|
{
|
||||||
|
_iconImageSource = IconImageHelper.GetLoginIconImage(Cipher);
|
||||||
|
}
|
||||||
|
return _iconImageSource;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Bit.App.Controls
|
|||||||
public class ExtendedSlider : Slider
|
public class ExtendedSlider : Slider
|
||||||
{
|
{
|
||||||
public static readonly BindableProperty ThumbBorderColorProperty = BindableProperty.Create(
|
public static readonly BindableProperty ThumbBorderColorProperty = BindableProperty.Create(
|
||||||
nameof(ThumbBorderColor), typeof(Color), typeof(ExtendedSlider), Color.Gray);
|
nameof(ThumbBorderColor), typeof(Color), typeof(ExtendedSlider), Color.Default);
|
||||||
|
|
||||||
public Color ThumbBorderColor
|
public Color ThumbBorderColor
|
||||||
{
|
{
|
||||||
|
|||||||
25
src/App/Controls/ExtendedStepper.cs
Normal file
25
src/App/Controls/ExtendedStepper.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class ExtendedStepper : Stepper
|
||||||
|
{
|
||||||
|
public static readonly BindableProperty StepperBackgroundColorProperty = BindableProperty.Create(
|
||||||
|
nameof(StepperBackgroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default);
|
||||||
|
|
||||||
|
public static readonly BindableProperty StepperForegroundColorProperty = BindableProperty.Create(
|
||||||
|
nameof(StepperForegroundColor), typeof(Color), typeof(ExtendedStepper), Color.Default);
|
||||||
|
|
||||||
|
public Color StepperBackgroundColor
|
||||||
|
{
|
||||||
|
get => (Color)GetValue(StepperBackgroundColorProperty);
|
||||||
|
set => SetValue(StepperBackgroundColorProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color StepperForegroundColor
|
||||||
|
{
|
||||||
|
get => (Color)GetValue(StepperForegroundColorProperty);
|
||||||
|
set => SetValue(StepperForegroundColorProperty, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/App/Controls/SelectableLabel.cs
Normal file
10
src/App/Controls/SelectableLabel.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Controls
|
||||||
|
{
|
||||||
|
public class SelectableLabel : Label
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/App/Effects/ScrollEnabledEffect.cs
Normal file
25
src/App/Effects/ScrollEnabledEffect.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Effects
|
||||||
|
{
|
||||||
|
public class ScrollEnabledEffect : RoutingEffect
|
||||||
|
{
|
||||||
|
public static readonly BindableProperty IsScrollEnabledProperty =
|
||||||
|
BindableProperty.CreateAttached("IsScrollEnabled", typeof(bool), typeof(ScrollEnabledEffect), true);
|
||||||
|
|
||||||
|
public static bool GetIsScrollEnabled(BindableObject view)
|
||||||
|
{
|
||||||
|
return (bool)view.GetValue(IsScrollEnabledProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetIsScrollEnabled(BindableObject view, bool value)
|
||||||
|
{
|
||||||
|
view.SetValue(IsScrollEnabledProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScrollEnabledEffect()
|
||||||
|
: base("Bitwarden.ScrollEnabledEffect")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using Xamarin.Forms;
|
|
||||||
|
|
||||||
namespace Bit.App.Effects
|
|
||||||
{
|
|
||||||
public class SelectableLabelEffect : RoutingEffect
|
|
||||||
{
|
|
||||||
public SelectableLabelEffect()
|
|
||||||
: base("Bitwarden.SelectableLabelEffect")
|
|
||||||
{ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
187
src/App/Pages/Accounts/BaseChangePasswordViewModel.cs
Normal file
187
src/App/Pages/Accounts/BaseChangePasswordViewModel.cs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
|
using Xamarin.Essentials;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class BaseChangePasswordViewModel : BaseViewModel
|
||||||
|
{
|
||||||
|
protected readonly IPlatformUtilsService _platformUtilsService;
|
||||||
|
protected readonly IUserService _userService;
|
||||||
|
protected readonly IPolicyService _policyService;
|
||||||
|
protected readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
|
protected readonly II18nService _i18nService;
|
||||||
|
protected readonly ICryptoService _cryptoService;
|
||||||
|
protected readonly IDeviceActionService _deviceActionService;
|
||||||
|
protected readonly IApiService _apiService;
|
||||||
|
protected readonly ISyncService _syncService;
|
||||||
|
|
||||||
|
private bool _showPassword;
|
||||||
|
private bool _isPolicyInEffect;
|
||||||
|
private string _policySummary;
|
||||||
|
private MasterPasswordPolicyOptions _policy;
|
||||||
|
|
||||||
|
protected BaseChangePasswordViewModel()
|
||||||
|
{
|
||||||
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
|
_userService = ServiceContainer.Resolve<IUserService>("userService");
|
||||||
|
_policyService = ServiceContainer.Resolve<IPolicyService>("policyService");
|
||||||
|
_passwordGenerationService =
|
||||||
|
ServiceContainer.Resolve<IPasswordGenerationService>("passwordGenerationService");
|
||||||
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||||
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
|
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowPassword
|
||||||
|
{
|
||||||
|
get => _showPassword;
|
||||||
|
set => SetProperty(ref _showPassword, value,
|
||||||
|
additionalPropertyNames: new[] { nameof(ShowPasswordIcon) });
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPolicyInEffect
|
||||||
|
{
|
||||||
|
get => _isPolicyInEffect;
|
||||||
|
set => SetProperty(ref _isPolicyInEffect, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PolicySummary
|
||||||
|
{
|
||||||
|
get => _policySummary;
|
||||||
|
set => SetProperty(ref _policySummary, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MasterPasswordPolicyOptions Policy
|
||||||
|
{
|
||||||
|
get => _policy;
|
||||||
|
set => SetProperty(ref _policy, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||||
|
public string MasterPassword { get; set; }
|
||||||
|
public string ConfirmMasterPassword { get; set; }
|
||||||
|
public string Hint { get; set; }
|
||||||
|
|
||||||
|
public async Task InitAsync(bool forceSync = false)
|
||||||
|
{
|
||||||
|
if (forceSync)
|
||||||
|
{
|
||||||
|
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
||||||
|
await task.ContinueWith(async (t) => await CheckPasswordPolicy());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await CheckPasswordPolicy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckPasswordPolicy()
|
||||||
|
{
|
||||||
|
Policy = await _policyService.GetMasterPasswordPolicyOptions();
|
||||||
|
IsPolicyInEffect = Policy?.InEffect() ?? false;
|
||||||
|
if (!IsPolicyInEffect)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bullet = "\n" + "".PadLeft(4) + "\u2022 ";
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.Append(_i18nService.T("MasterPasswordPolicyInEffect"));
|
||||||
|
if (Policy.MinComplexity > 0)
|
||||||
|
{
|
||||||
|
sb.Append(bullet)
|
||||||
|
.Append(string.Format(_i18nService.T("PolicyInEffectMinComplexity"), Policy.MinComplexity));
|
||||||
|
}
|
||||||
|
if (Policy.MinLength > 0)
|
||||||
|
{
|
||||||
|
sb.Append(bullet).Append(string.Format(_i18nService.T("PolicyInEffectMinLength"), Policy.MinLength));
|
||||||
|
}
|
||||||
|
if (Policy.RequireUpper)
|
||||||
|
{
|
||||||
|
sb.Append(bullet).Append(_i18nService.T("PolicyInEffectUppercase"));
|
||||||
|
}
|
||||||
|
if (Policy.RequireLower)
|
||||||
|
{
|
||||||
|
sb.Append(bullet).Append(_i18nService.T("PolicyInEffectLowercase"));
|
||||||
|
}
|
||||||
|
if (Policy.RequireNumbers)
|
||||||
|
{
|
||||||
|
sb.Append(bullet).Append(_i18nService.T("PolicyInEffectNumbers"));
|
||||||
|
}
|
||||||
|
if (Policy.RequireSpecial)
|
||||||
|
{
|
||||||
|
sb.Append(bullet).Append(string.Format(_i18nService.T("PolicyInEffectSpecial"), "!@#$%^&*"));
|
||||||
|
}
|
||||||
|
PolicySummary = sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<bool> ValidateMasterPasswordAsync()
|
||||||
|
{
|
||||||
|
if (Connectivity.NetworkAccess == NetworkAccess.None)
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
||||||
|
AppResources.InternetConnectionRequiredTitle, AppResources.Ok);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(MasterPassword))
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(
|
||||||
|
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
|
||||||
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (IsPolicyInEffect)
|
||||||
|
{
|
||||||
|
var userInput = await GetPasswordStrengthUserInput();
|
||||||
|
var passwordStrength = _passwordGenerationService.PasswordStrength(MasterPassword, userInput);
|
||||||
|
if (!await _policyService.EvaluateMasterPassword(passwordStrength.Score, MasterPassword, Policy))
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordPolicyValidationMessage,
|
||||||
|
AppResources.MasterPasswordPolicyValidationTitle, AppResources.Ok);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (MasterPassword.Length < 8)
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordLengthValMessage,
|
||||||
|
AppResources.MasterPasswordPolicyValidationTitle, AppResources.Ok);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (MasterPassword != ConfirmMasterPassword)
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordConfirmationValMessage,
|
||||||
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<string>> GetPasswordStrengthUserInput()
|
||||||
|
{
|
||||||
|
var email = await _userService.GetEmailAsync();
|
||||||
|
List<string> userInput = null;
|
||||||
|
var atPosition = email.IndexOf('@');
|
||||||
|
if (atPosition > -1)
|
||||||
|
{
|
||||||
|
var rx = new Regex("/[^A-Za-z0-9]/", RegexOptions.Compiled);
|
||||||
|
var data = rx.Split(email.Substring(0, atPosition).Trim().ToLower());
|
||||||
|
userInput = new List<string>(data);
|
||||||
|
}
|
||||||
|
return userInput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n Save}" Clicked="Submit_Clicked" />
|
<ToolbarItem Text="{u:I18n Save}" Clicked="Submit_Clicked" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n Submit}" Clicked="Submit_Clicked" />
|
<ToolbarItem Text="{u:I18n Submit}" Clicked="Submit_Clicked" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
|
|||||||
@@ -40,11 +40,12 @@
|
|||||||
HorizontalTextAlignment="Center"></Label>
|
HorizontalTextAlignment="Center"></Label>
|
||||||
<StackLayout Spacing="5">
|
<StackLayout Spacing="5">
|
||||||
<Button Text="{u:I18n LogIn}"
|
<Button Text="{u:I18n LogIn}"
|
||||||
Clicked="LogIn_Clicked"></Button>
|
StyleClass="btn-primary"
|
||||||
|
Clicked="LogIn_Clicked" />
|
||||||
<Button Text="{u:I18n CreateAccount}"
|
<Button Text="{u:I18n CreateAccount}"
|
||||||
Clicked="Register_Clicked"></Button>
|
Clicked="Register_Clicked" />
|
||||||
<Button Text="{u:I18n LogInSso}"
|
<Button Text="{u:I18n LogInSso}"
|
||||||
Clicked="LogInSso_Clicked"></Button>
|
Clicked="LogInSso_Clicked" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ namespace Bit.App.Pages
|
|||||||
private readonly HomeViewModel _vm;
|
private readonly HomeViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
private IMessagingService _messagingService;
|
private IMessagingService _messagingService;
|
||||||
|
private IBroadcasterService _broadcasterService;
|
||||||
|
|
||||||
public HomePage(AppOptions appOptions = null)
|
public HomePage(AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
_messagingService.Send("showStatusBar", false);
|
_messagingService.Send("showStatusBar", false);
|
||||||
|
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as HomeViewModel;
|
_vm = BindingContext as HomeViewModel;
|
||||||
@@ -26,7 +28,7 @@ namespace Bit.App.Pages
|
|||||||
_vm.StartRegisterAction = () => Device.BeginInvokeOnMainThread(async () => await StartRegisterAsync());
|
_vm.StartRegisterAction = () => Device.BeginInvokeOnMainThread(async () => await StartRegisterAsync());
|
||||||
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
|
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
|
||||||
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
|
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
|
||||||
_logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png";
|
UpdateLogo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DismissRegisterPageAndLogInAsync(string email)
|
public async Task DismissRegisterPageAndLogInAsync(string email)
|
||||||
@@ -39,6 +41,27 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
_messagingService.Send("showStatusBar", false);
|
_messagingService.Send("showStatusBar", false);
|
||||||
|
_broadcasterService.Subscribe(nameof(HomePage), async (message) =>
|
||||||
|
{
|
||||||
|
if (message.Command == "updatedTheme")
|
||||||
|
{
|
||||||
|
Device.BeginInvokeOnMainThread(() =>
|
||||||
|
{
|
||||||
|
UpdateLogo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDisappearing()
|
||||||
|
{
|
||||||
|
base.OnDisappearing();
|
||||||
|
_broadcasterService.Unsubscribe(nameof(HomePage));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateLogo()
|
||||||
|
{
|
||||||
|
_logo.Source = !ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Close_Clicked(object sender, EventArgs e)
|
private void Close_Clicked(object sender, EventArgs e)
|
||||||
|
|||||||
@@ -120,7 +120,9 @@
|
|||||||
IsVisible="{Binding BiometricIntegrityValid, Converter={StaticResource inverseBool}}" />
|
IsVisible="{Binding BiometricIntegrityValid, Converter={StaticResource inverseBool}}" />
|
||||||
<Button Text="{Binding BiometricButtonText}" Clicked="Biometric_Clicked"
|
<Button Text="{Binding BiometricButtonText}" Clicked="Biometric_Clicked"
|
||||||
IsVisible="{Binding BiometricButtonVisible}"></Button>
|
IsVisible="{Binding BiometricButtonVisible}"></Button>
|
||||||
<Button Text="{u:I18n Unlock}" Clicked="Unlock_Clicked"></Button>
|
<Button Text="{u:I18n Unlock}"
|
||||||
|
StyleClass="btn-primary"
|
||||||
|
Clicked="Unlock_Clicked" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -317,7 +317,9 @@ namespace Bit.App.Pages
|
|||||||
ShowPassword = !ShowPassword;
|
ShowPassword = !ShowPassword;
|
||||||
var page = (Page as LockPage);
|
var page = (Page as LockPage);
|
||||||
var entry = PinLock ? page.PinEntry : page.MasterPasswordEntry;
|
var entry = PinLock ? page.PinEntry : page.MasterPasswordEntry;
|
||||||
|
var str = PinLock ? Pin : MasterPassword;
|
||||||
entry.Focus();
|
entry.Focus();
|
||||||
|
entry.CursorPosition = String.IsNullOrEmpty(str) ? 0 : str.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PromptBiometricAsync()
|
public async Task PromptBiometricAsync()
|
||||||
|
|||||||
@@ -82,7 +82,9 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout Padding="10, 0">
|
<StackLayout Padding="10, 0">
|
||||||
<Button Text="{u:I18n LogIn}" Clicked="LogIn_Clicked" />
|
<Button Text="{u:I18n LogIn}"
|
||||||
|
StyleClass="btn-primary"
|
||||||
|
Clicked="LogIn_Clicked" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ namespace Bit.App.Pages
|
|||||||
private readonly LoginPageViewModel _vm;
|
private readonly LoginPageViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
|
|
||||||
|
private bool _inputFocused;
|
||||||
|
|
||||||
public LoginPage(string email = null, AppOptions appOptions = null)
|
public LoginPage(string email = null, AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
@@ -27,6 +29,8 @@ namespace Bit.App.Pages
|
|||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
|
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
|
||||||
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
|
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
|
||||||
|
_vm.UpdateTempPasswordAction =
|
||||||
|
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("showStatusBar", false);
|
_messagingService.Send("showStatusBar", false);
|
||||||
@@ -58,13 +62,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
await _vm.InitAsync();
|
await _vm.InitAsync();
|
||||||
if (string.IsNullOrWhiteSpace(_vm.Email))
|
if (!_inputFocused)
|
||||||
{
|
{
|
||||||
RequestFocus(_email);
|
RequestFocus(string.IsNullOrWhiteSpace(_vm.Email) ? _email : _masterPassword);
|
||||||
}
|
_inputFocused = true;
|
||||||
else
|
|
||||||
{
|
|
||||||
RequestFocus(_masterPassword);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,5 +124,11 @@ namespace Bit.App.Pages
|
|||||||
var previousPage = await AppHelpers.ClearPreviousPage();
|
var previousPage = await AppHelpers.ClearPreviousPage();
|
||||||
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task UpdateTempPasswordAsync()
|
||||||
|
{
|
||||||
|
var page = new UpdateTempPasswordPage();
|
||||||
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ using Xamarin.Forms;
|
|||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class LoginPageViewModel : BaseViewModel
|
public class LoginPageViewModel : CaptchaProtectedViewModel
|
||||||
{
|
{
|
||||||
private const string Keys_RememberedEmail = "rememberedEmail";
|
private const string Keys_RememberedEmail = "rememberedEmail";
|
||||||
private const string Keys_RememberEmail = "rememberEmail";
|
private const string Keys_RememberEmail = "rememberEmail";
|
||||||
@@ -22,6 +22,8 @@ namespace Bit.App.Pages
|
|||||||
private readonly IStorageService _storageService;
|
private readonly IStorageService _storageService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
|
private readonly IEnvironmentService _environmentService;
|
||||||
|
private readonly II18nService _i18nService;
|
||||||
|
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
private string _email;
|
private string _email;
|
||||||
@@ -35,6 +37,8 @@ namespace Bit.App.Pages
|
|||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||||
|
|
||||||
PageTitle = AppResources.Bitwarden;
|
PageTitle = AppResources.Bitwarden;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
@@ -69,8 +73,14 @@ namespace Bit.App.Pages
|
|||||||
public bool RememberEmail { get; set; }
|
public bool RememberEmail { get; set; }
|
||||||
public Action StartTwoFactorAction { get; set; }
|
public Action StartTwoFactorAction { get; set; }
|
||||||
public Action LogInSuccessAction { get; set; }
|
public Action LogInSuccessAction { get; set; }
|
||||||
|
public Action UpdateTempPasswordAction { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
|
|
||||||
|
protected override II18nService i18nService => _i18nService;
|
||||||
|
protected override IEnvironmentService environmentService => _environmentService;
|
||||||
|
protected override IDeviceActionService deviceActionService => _deviceActionService;
|
||||||
|
protected override IPlatformUtilsService platformUtilsService => _platformUtilsService;
|
||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Email))
|
if (string.IsNullOrWhiteSpace(Email))
|
||||||
@@ -81,20 +91,19 @@ namespace Bit.App.Pages
|
|||||||
RememberEmail = rememberEmail.GetValueOrDefault(true);
|
RememberEmail = rememberEmail.GetValueOrDefault(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LogInAsync()
|
public async Task LogInAsync(bool showLoading = true)
|
||||||
{
|
{
|
||||||
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
||||||
AppResources.InternetConnectionRequiredTitle);
|
AppResources.InternetConnectionRequiredTitle, AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(Email))
|
if (string.IsNullOrWhiteSpace(Email))
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(
|
await _platformUtilsService.ShowDialogAsync(
|
||||||
string.Format(AppResources.ValidationFieldRequired, AppResources.EmailAddress),
|
string.Format(AppResources.ValidationFieldRequired, AppResources.EmailAddress),
|
||||||
AppResources.AnErrorHasOccurred,
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
AppResources.Ok);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!Email.Contains("@"))
|
if (!Email.Contains("@"))
|
||||||
@@ -107,17 +116,19 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(
|
await _platformUtilsService.ShowDialogAsync(
|
||||||
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
|
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
|
||||||
AppResources.AnErrorHasOccurred,
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
AppResources.Ok);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ShowPassword = false;
|
ShowPassword = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
if (showLoading)
|
||||||
var response = await _authService.LogInAsync(Email, MasterPassword);
|
{
|
||||||
MasterPassword = string.Empty;
|
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
|
||||||
if (RememberEmail)
|
if (RememberEmail)
|
||||||
{
|
{
|
||||||
await _storageService.SaveAsync(Keys_RememberedEmail, Email);
|
await _storageService.SaveAsync(Keys_RememberedEmail, Email);
|
||||||
@@ -127,11 +138,29 @@ namespace Bit.App.Pages
|
|||||||
await _storageService.RemoveAsync(Keys_RememberedEmail);
|
await _storageService.RemoveAsync(Keys_RememberedEmail);
|
||||||
}
|
}
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
|
|
||||||
|
if (response.CaptchaNeeded)
|
||||||
|
{
|
||||||
|
if (await HandleCaptchaAsync(response.CaptchaSiteKey))
|
||||||
|
{
|
||||||
|
await LogInAsync(false);
|
||||||
|
_captchaToken = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MasterPassword = string.Empty;
|
||||||
|
_captchaToken = null;
|
||||||
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
|
||||||
if (response.TwoFactor)
|
if (response.TwoFactor)
|
||||||
{
|
{
|
||||||
StartTwoFactorAction?.Invoke();
|
StartTwoFactorAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
else if (response.ForcePasswordReset)
|
||||||
|
{
|
||||||
|
UpdateTempPasswordAction?.Invoke();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||||
@@ -142,11 +171,13 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
catch (ApiException e)
|
catch (ApiException e)
|
||||||
{
|
{
|
||||||
|
_captchaToken = null;
|
||||||
|
MasterPassword = string.Empty;
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
if (e?.Error != null)
|
if (e?.Error != null)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
||||||
AppResources.AnErrorHasOccurred);
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +185,9 @@ namespace Bit.App.Pages
|
|||||||
public void TogglePassword()
|
public void TogglePassword()
|
||||||
{
|
{
|
||||||
ShowPassword = !ShowPassword;
|
ShowPassword = !ShowPassword;
|
||||||
(Page as LoginPage).MasterPasswordEntry.Focus();
|
var entry = (Page as LoginPage).MasterPasswordEntry;
|
||||||
|
entry.Focus();
|
||||||
|
entry.CursorPosition = String.IsNullOrEmpty(MasterPassword) ? 0 : MasterPassword.Length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,7 @@
|
|||||||
</ContentPage.BindingContext>
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n LogIn}" Clicked="LogIn_Clicked" />
|
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
@@ -36,6 +35,11 @@
|
|||||||
ReturnCommand="{Binding LogInCommand}" />
|
ReturnCommand="{Binding LogInCommand}" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
<StackLayout Padding="10, 0">
|
||||||
|
<Button Text="{u:I18n LogIn}"
|
||||||
|
StyleClass="btn-primary"
|
||||||
|
Clicked="LogIn_Clicked"></Button>
|
||||||
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ namespace Bit.App.Pages
|
|||||||
_vm.StartSetPasswordAction = () =>
|
_vm.StartSetPasswordAction = () =>
|
||||||
Device.BeginInvokeOnMainThread(async () => await StartSetPasswordAsync());
|
Device.BeginInvokeOnMainThread(async () => await StartSetPasswordAsync());
|
||||||
_vm.SsoAuthSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SsoAuthSuccessAsync());
|
_vm.SsoAuthSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SsoAuthSuccessAsync());
|
||||||
|
_vm.UpdateTempPasswordAction =
|
||||||
|
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
_messagingService.Send("showStatusBar", false);
|
_messagingService.Send("showStatusBar", false);
|
||||||
@@ -104,6 +106,12 @@ namespace Bit.App.Pages
|
|||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task UpdateTempPasswordAsync()
|
||||||
|
{
|
||||||
|
var page = new UpdateTempPasswordPage();
|
||||||
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SsoAuthSuccessAsync()
|
private async Task SsoAuthSuccessAsync()
|
||||||
{
|
{
|
||||||
RestoreAppOptionsFromCopy();
|
RestoreAppOptionsFromCopy();
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ namespace Bit.App.Pages
|
|||||||
public Action StartSetPasswordAction { get; set; }
|
public Action StartSetPasswordAction { get; set; }
|
||||||
public Action SsoAuthSuccessAction { get; set; }
|
public Action SsoAuthSuccessAction { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
|
public Action UpdateTempPasswordAction { get; set; }
|
||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
@@ -123,43 +124,28 @@ namespace Bit.App.Pages
|
|||||||
"domain_hint=" + Uri.EscapeDataString(OrgIdentifier);
|
"domain_hint=" + Uri.EscapeDataString(OrgIdentifier);
|
||||||
|
|
||||||
WebAuthenticatorResult authResult = null;
|
WebAuthenticatorResult authResult = null;
|
||||||
bool cancelled = false;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
authResult = await WebAuthenticator.AuthenticateAsync(new Uri(url),
|
authResult = await WebAuthenticator.AuthenticateAsync(new Uri(url),
|
||||||
new Uri(redirectUri));
|
new Uri(redirectUri));
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException taskCanceledException)
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
// user canceled
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var code = GetResultCode(authResult, state);
|
||||||
|
if (!string.IsNullOrEmpty(code))
|
||||||
|
{
|
||||||
|
await LogIn(code, codeVerifier, redirectUri);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
cancelled = true;
|
await _platformUtilsService.ShowDialogAsync(AppResources.LoginSsoError,
|
||||||
}
|
AppResources.AnErrorHasOccurred);
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// WebAuthenticator throws NSErrorException if iOS flow is cancelled - by setting cancelled to true
|
|
||||||
// here we maintain the appearance of a clean cancellation (we don't want to do this across the board
|
|
||||||
// because we still want to present legitimate errors). If/when this is fixed, we can remove this
|
|
||||||
// particular catch block (catching taskCanceledException above must remain)
|
|
||||||
// https://github.com/xamarin/Essentials/issues/1240
|
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
|
||||||
{
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
|
||||||
cancelled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!cancelled)
|
|
||||||
{
|
|
||||||
var code = GetResultCode(authResult, state);
|
|
||||||
if (!string.IsNullOrEmpty(code))
|
|
||||||
{
|
|
||||||
await LogIn(code, codeVerifier, redirectUri);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.LoginSsoError,
|
|
||||||
AppResources.AnErrorHasOccurred);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +187,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
StartSetPasswordAction?.Invoke();
|
StartSetPasswordAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
else if (response.ForcePasswordReset)
|
||||||
|
{
|
||||||
|
UpdateTempPasswordAction?.Invoke();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1" />
|
||||||
<ToolbarItem Text="{u:I18n Submit}" Clicked="Submit_Clicked" />
|
<ToolbarItem Text="{u:I18n Submit}" Clicked="Submit_Clicked" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
<FormattedString>
|
<FormattedString>
|
||||||
<Span Text="{u:I18n AcceptPolicies}" />
|
<Span Text="{u:I18n AcceptPolicies}" />
|
||||||
<Span Text="{u:I18n TermsOfService}"
|
<Span Text="{u:I18n TermsOfService}"
|
||||||
TextColor="{StaticResource HyperlinkColor}">
|
TextColor="{DynamicResource HyperlinkColor}">
|
||||||
<Span.GestureRecognizers>
|
<Span.GestureRecognizers>
|
||||||
<TapGestureRecognizer Command="{Binding PoliciesClickCommand}"
|
<TapGestureRecognizer Command="{Binding PoliciesClickCommand}"
|
||||||
CommandParameter="https://bitwarden.com/terms/" />
|
CommandParameter="https://bitwarden.com/terms/" />
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
</Span>
|
</Span>
|
||||||
<Span Text=", " />
|
<Span Text=", " />
|
||||||
<Span Text="{u:I18n PrivacyPolicy}"
|
<Span Text="{u:I18n PrivacyPolicy}"
|
||||||
TextColor="{StaticResource HyperlinkColor}">
|
TextColor="{DynamicResource HyperlinkColor}">
|
||||||
<Span.GestureRecognizers>
|
<Span.GestureRecognizers>
|
||||||
<TapGestureRecognizer Command="{Binding PoliciesClickCommand}"
|
<TapGestureRecognizer Command="{Binding PoliciesClickCommand}"
|
||||||
CommandParameter="https://bitwarden.com/privacy/" />
|
CommandParameter="https://bitwarden.com/privacy/" />
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ namespace Bit.App.Pages
|
|||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
private readonly RegisterPageViewModel _vm;
|
private readonly RegisterPageViewModel _vm;
|
||||||
|
|
||||||
|
private bool _inputFocused;
|
||||||
|
|
||||||
public RegisterPage(HomePage homePage)
|
public RegisterPage(HomePage homePage)
|
||||||
{
|
{
|
||||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
@@ -45,7 +47,11 @@ namespace Bit.App.Pages
|
|||||||
protected override void OnAppearing()
|
protected override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
RequestFocus(_email);
|
if (!_inputFocused)
|
||||||
|
{
|
||||||
|
RequestFocus(_email);
|
||||||
|
_inputFocused = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Submit_Clicked(object sender, EventArgs e)
|
private async void Submit_Clicked(object sender, EventArgs e)
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ using Xamarin.Forms;
|
|||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class RegisterPageViewModel : BaseViewModel
|
public class RegisterPageViewModel : CaptchaProtectedViewModel
|
||||||
{
|
{
|
||||||
private readonly IDeviceActionService _deviceActionService;
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
|
private readonly II18nService _i18nService;
|
||||||
|
private readonly IEnvironmentService _environmentService;
|
||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
@@ -27,6 +29,8 @@ namespace Bit.App.Pages
|
|||||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
|
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||||
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
|
|
||||||
PageTitle = AppResources.CreateAccount;
|
PageTitle = AppResources.CreateAccount;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
@@ -76,54 +80,67 @@ namespace Bit.App.Pages
|
|||||||
public Action RegistrationSuccess { get; set; }
|
public Action RegistrationSuccess { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
|
|
||||||
public async Task SubmitAsync()
|
protected override II18nService i18nService => _i18nService;
|
||||||
|
protected override IEnvironmentService environmentService => _environmentService;
|
||||||
|
protected override IDeviceActionService deviceActionService => _deviceActionService;
|
||||||
|
protected override IPlatformUtilsService platformUtilsService => _platformUtilsService;
|
||||||
|
|
||||||
|
public async Task SubmitAsync(bool showLoading = true)
|
||||||
{
|
{
|
||||||
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
||||||
AppResources.InternetConnectionRequiredTitle);
|
AppResources.InternetConnectionRequiredTitle, AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(Email))
|
if (string.IsNullOrWhiteSpace(Email))
|
||||||
{
|
{
|
||||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
await _platformUtilsService.ShowDialogAsync(
|
||||||
string.Format(AppResources.ValidationFieldRequired, AppResources.EmailAddress),
|
string.Format(AppResources.ValidationFieldRequired, AppResources.EmailAddress),
|
||||||
|
AppResources.AnErrorHasOccurred,
|
||||||
AppResources.Ok);
|
AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!Email.Contains("@"))
|
if (!Email.Contains("@"))
|
||||||
{
|
{
|
||||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, AppResources.InvalidEmail, AppResources.Ok);
|
await _platformUtilsService.ShowDialogAsync(AppResources.InvalidEmail, AppResources.AnErrorHasOccurred,
|
||||||
|
AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(MasterPassword))
|
if (string.IsNullOrWhiteSpace(MasterPassword))
|
||||||
{
|
{
|
||||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
await _platformUtilsService.ShowDialogAsync(
|
||||||
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
|
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
|
||||||
|
AppResources.AnErrorHasOccurred,
|
||||||
AppResources.Ok);
|
AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (MasterPassword.Length < 8)
|
if (MasterPassword.Length < 8)
|
||||||
{
|
{
|
||||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordLengthValMessage,
|
||||||
AppResources.MasterPasswordLengthValMessage, AppResources.Ok);
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (MasterPassword != ConfirmMasterPassword)
|
if (MasterPassword != ConfirmMasterPassword)
|
||||||
{
|
{
|
||||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
await _platformUtilsService.ShowDialogAsync(AppResources.MasterPasswordConfirmationValMessage,
|
||||||
AppResources.MasterPasswordConfirmationValMessage, AppResources.Ok);
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ShowTerms && !AcceptPolicies)
|
if (ShowTerms && !AcceptPolicies)
|
||||||
{
|
{
|
||||||
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
await _platformUtilsService.ShowDialogAsync(AppResources.AcceptPoliciesError,
|
||||||
AppResources.AcceptPoliciesError, AppResources.Ok);
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Password strength check?
|
// TODO: Password strength check?
|
||||||
|
|
||||||
|
if (showLoading)
|
||||||
|
{
|
||||||
|
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||||
|
}
|
||||||
|
|
||||||
Name = string.IsNullOrWhiteSpace(Name) ? null : Name;
|
Name = string.IsNullOrWhiteSpace(Name) ? null : Name;
|
||||||
Email = Email.Trim().ToLower();
|
Email = Email.Trim().ToLower();
|
||||||
var kdf = KdfType.PBKDF2_SHA256;
|
var kdf = KdfType.PBKDF2_SHA256;
|
||||||
@@ -145,13 +162,13 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
PublicKey = keys.Item1,
|
PublicKey = keys.Item1,
|
||||||
EncryptedPrivateKey = keys.Item2.EncryptedString
|
EncryptedPrivateKey = keys.Item2.EncryptedString
|
||||||
}
|
},
|
||||||
|
CaptchaResponse = _captchaToken,
|
||||||
};
|
};
|
||||||
// TODO: org invite?
|
// TODO: org invite?
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
|
||||||
await _apiService.PostRegisterAsync(request);
|
await _apiService.PostRegisterAsync(request);
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
_platformUtilsService.ShowToast("success", null, AppResources.AccountCreated,
|
_platformUtilsService.ShowToast("success", null, AppResources.AccountCreated,
|
||||||
@@ -163,11 +180,20 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
catch (ApiException e)
|
catch (ApiException e)
|
||||||
{
|
{
|
||||||
|
if (e?.Error != null && e.Error.CaptchaRequired)
|
||||||
|
{
|
||||||
|
if (await HandleCaptchaAsync(e.Error.CaptchaSiteKey))
|
||||||
|
{
|
||||||
|
await SubmitAsync(false);
|
||||||
|
_captchaToken = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
if (e?.Error != null)
|
if (e?.Error != null)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
||||||
AppResources.AnErrorHasOccurred);
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,13 +201,17 @@ namespace Bit.App.Pages
|
|||||||
public void TogglePassword()
|
public void TogglePassword()
|
||||||
{
|
{
|
||||||
ShowPassword = !ShowPassword;
|
ShowPassword = !ShowPassword;
|
||||||
(Page as RegisterPage).MasterPasswordEntry.Focus();
|
var entry = (Page as RegisterPage).MasterPasswordEntry;
|
||||||
|
entry.Focus();
|
||||||
|
entry.CursorPosition = String.IsNullOrEmpty(MasterPassword) ? 0 : MasterPassword.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ToggleConfirmPassword()
|
public void ToggleConfirmPassword()
|
||||||
{
|
{
|
||||||
ShowPassword = !ShowPassword;
|
ShowPassword = !ShowPassword;
|
||||||
(Page as RegisterPage).ConfirmMasterPasswordEntry.Focus();
|
var entry = (Page as RegisterPage).ConfirmMasterPasswordEntry;
|
||||||
|
entry.Focus();
|
||||||
|
entry.CursorPosition = String.IsNullOrEmpty(ConfirmMasterPassword) ? 0 : ConfirmMasterPassword.Length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,28 @@
|
|||||||
StyleClass="text-md"
|
StyleClass="text-md"
|
||||||
HorizontalTextAlignment="Start"></Label>
|
HorizontalTextAlignment="Start"></Label>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
<Grid IsVisible="{Binding ResetPasswordAutoEnroll}"
|
||||||
|
RowSpacing="0"
|
||||||
|
ColumnSpacing="0">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Frame Padding="10"
|
||||||
|
Margin="0, 12, 0, 0"
|
||||||
|
HasShadow="False"
|
||||||
|
BackgroundColor="Transparent"
|
||||||
|
BorderColor="Accent">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n ResetPasswordAutoEnrollInviteWarning}"
|
||||||
|
StyleClass="text-muted, text-sm, text-bold"
|
||||||
|
HorizontalTextAlignment="Start" />
|
||||||
|
</Frame>
|
||||||
|
</Grid>
|
||||||
<Grid IsVisible="{Binding IsPolicyInEffect}"
|
<Grid IsVisible="{Binding IsPolicyInEffect}"
|
||||||
RowSpacing="0"
|
RowSpacing="0"
|
||||||
ColumnSpacing="0">
|
ColumnSpacing="0">
|
||||||
@@ -44,7 +66,7 @@
|
|||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Frame Padding="10"
|
<Frame Padding="10"
|
||||||
Margin="0"
|
Margin="0, 12, 0, 0"
|
||||||
HasShadow="False"
|
HasShadow="False"
|
||||||
BackgroundColor="Transparent"
|
BackgroundColor="Transparent"
|
||||||
BorderColor="Accent">
|
BorderColor="Accent">
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
private bool _isPolicyInEffect;
|
private bool _isPolicyInEffect;
|
||||||
|
private bool _resetPasswordAutoEnroll;
|
||||||
private string _policySummary;
|
private string _policySummary;
|
||||||
private MasterPasswordPolicyOptions _policy;
|
private MasterPasswordPolicyOptions _policy;
|
||||||
|
|
||||||
@@ -50,7 +51,6 @@ namespace Bit.App.Pages
|
|||||||
ToggleConfirmPasswordCommand = new Command(ToggleConfirmPassword);
|
ToggleConfirmPasswordCommand = new Command(ToggleConfirmPassword);
|
||||||
SubmitCommand = new Command(async () => await SubmitAsync());
|
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowPassword
|
public bool ShowPassword
|
||||||
{
|
{
|
||||||
get => _showPassword;
|
get => _showPassword;
|
||||||
@@ -64,6 +64,12 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _isPolicyInEffect, value);
|
set => SetProperty(ref _isPolicyInEffect, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ResetPasswordAutoEnroll
|
||||||
|
{
|
||||||
|
get => _resetPasswordAutoEnroll;
|
||||||
|
set => SetProperty(ref _resetPasswordAutoEnroll, value);
|
||||||
|
}
|
||||||
|
|
||||||
public string PolicySummary
|
public string PolicySummary
|
||||||
{
|
{
|
||||||
get => _policySummary;
|
get => _policySummary;
|
||||||
@@ -84,12 +90,29 @@ namespace Bit.App.Pages
|
|||||||
public string ConfirmMasterPassword { get; set; }
|
public string ConfirmMasterPassword { get; set; }
|
||||||
public string Hint { get; set; }
|
public string Hint { get; set; }
|
||||||
public Action SetPasswordSuccessAction { get; set; }
|
public Action SetPasswordSuccessAction { get; set; }
|
||||||
|
public Action UpdateTempPasswordAction { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
public string OrgIdentifier { get; set; }
|
public string OrgIdentifier { get; set; }
|
||||||
|
public string OrgId { get; set; }
|
||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
await CheckPasswordPolicy();
|
await CheckPasswordPolicy();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _apiService.GetOrganizationAutoEnrollStatusAsync(OrgIdentifier);
|
||||||
|
OrgId = response.Id;
|
||||||
|
ResetPasswordAutoEnroll = response.ResetPasswordEnabled;
|
||||||
|
}
|
||||||
|
catch (ApiException e)
|
||||||
|
{
|
||||||
|
if (e?.Error != null)
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
||||||
|
AppResources.AnErrorHasOccurred);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SubmitAsync()
|
public async Task SubmitAsync()
|
||||||
@@ -171,6 +194,7 @@ namespace Bit.App.Pages
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
await _deviceActionService.ShowLoadingAsync(AppResources.CreatingAccount);
|
||||||
|
// Set Password and relevant information
|
||||||
await _apiService.SetPasswordAsync(request);
|
await _apiService.SetPasswordAsync(request);
|
||||||
await _userService.SetInformationAsync(await _userService.GetUserIdAsync(),
|
await _userService.SetInformationAsync(await _userService.GetUserIdAsync(),
|
||||||
await _userService.GetEmailAsync(), kdf, kdfIterations);
|
await _userService.GetEmailAsync(), kdf, kdfIterations);
|
||||||
@@ -178,8 +202,26 @@ namespace Bit.App.Pages
|
|||||||
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
await _cryptoService.SetKeyHashAsync(localMasterPasswordHash);
|
||||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||||
await _cryptoService.SetEncPrivateKeyAsync(keys.Item2.EncryptedString);
|
await _cryptoService.SetEncPrivateKeyAsync(keys.Item2.EncryptedString);
|
||||||
await _deviceActionService.HideLoadingAsync();
|
|
||||||
|
|
||||||
|
if (ResetPasswordAutoEnroll)
|
||||||
|
{
|
||||||
|
// Grab Organization Keys
|
||||||
|
var response = await _apiService.GetOrganizationKeysAsync(OrgId);
|
||||||
|
var publicKey = CoreHelpers.Base64UrlDecode(response.PublicKey);
|
||||||
|
// Grab user's Encryption Key and encrypt with Org Public Key
|
||||||
|
var userEncKey = await _cryptoService.GetEncKeyAsync();
|
||||||
|
var encryptedKey = await _cryptoService.RsaEncryptAsync(userEncKey.Key, publicKey);
|
||||||
|
// Request
|
||||||
|
var resetRequest = new OrganizationUserResetPasswordEnrollmentRequest
|
||||||
|
{
|
||||||
|
ResetPasswordKey = encryptedKey.EncryptedString
|
||||||
|
};
|
||||||
|
var userId = await _userService.GetUserIdAsync();
|
||||||
|
// Enroll user
|
||||||
|
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(OrgId, userId, resetRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
SetPasswordSuccessAction?.Invoke();
|
SetPasswordSuccessAction?.Invoke();
|
||||||
}
|
}
|
||||||
catch (ApiException e)
|
catch (ApiException e)
|
||||||
|
|||||||
@@ -16,14 +16,21 @@
|
|||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
|
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
|
||||||
x:Name="_cancelItem" />
|
x:Name="_cancelItem" />
|
||||||
<ToolbarItem Text="{u:I18n Continue}" Clicked="Continue_Clicked" Order="Primary"
|
|
||||||
x:Name="_continueItem" />
|
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
<u:IsNullConverter x:Key="isNull" />
|
<u:IsNullConverter x:Key="isNull" />
|
||||||
|
<ToolbarItem Icon="more_vert.png" Clicked="More_Clicked" Order="Primary"
|
||||||
|
x:Name="_moreItem" x:Key="moreItem"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n Options}" />
|
||||||
|
<ToolbarItem Text="{u:I18n UseAnotherTwoStepMethod}"
|
||||||
|
Clicked="Methods_Clicked"
|
||||||
|
Order="Secondary"
|
||||||
|
x:Name="_useAnotherTwoStepMethod"
|
||||||
|
x:Key="useAnotherTwoStepMethod" />
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
@@ -45,7 +52,8 @@
|
|||||||
Keyboard="Numeric"
|
Keyboard="Numeric"
|
||||||
StyleClass="box-value"
|
StyleClass="box-value"
|
||||||
ReturnType="Go"
|
ReturnType="Go"
|
||||||
ReturnCommand="{Binding SubmitCommand}" />
|
ReturnCommand="{Binding SubmitCommand}"
|
||||||
|
TextChanged="Token_TextChanged"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<StackLayout StyleClass="box-row, box-row-switch">
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
<Label
|
<Label
|
||||||
@@ -94,6 +102,30 @@
|
|||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
<StackLayout Spacing="20" Padding="0" IsVisible="{Binding Fido2Method, Mode=OneWay}">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n Fido2Instruction}"
|
||||||
|
Margin="10, 20, 10, 0"
|
||||||
|
HorizontalTextAlignment="Center" />
|
||||||
|
<Image
|
||||||
|
Source="yubikey.png"
|
||||||
|
Margin="10, 0"
|
||||||
|
WidthRequest="266"
|
||||||
|
HeightRequest="160"
|
||||||
|
HorizontalOptions="Center" />
|
||||||
|
<StackLayout StyleClass="box">
|
||||||
|
<StackLayout StyleClass="box-row, box-row-switch">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n RememberMe}"
|
||||||
|
StyleClass="box-label-regular"
|
||||||
|
HorizontalOptions="StartAndExpand" />
|
||||||
|
<Switch
|
||||||
|
IsToggled="{Binding Remember}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
HorizontalOptions="End" />
|
||||||
|
</StackLayout>
|
||||||
|
</StackLayout>
|
||||||
|
</StackLayout>
|
||||||
<StackLayout Spacing="0" Padding="0" IsVisible="{Binding DuoMethod, Mode=OneWay}"
|
<StackLayout Spacing="0" Padding="0" IsVisible="{Binding DuoMethod, Mode=OneWay}"
|
||||||
VerticalOptions="FillAndExpand">
|
VerticalOptions="FillAndExpand">
|
||||||
<controls:HybridWebView
|
<controls:HybridWebView
|
||||||
@@ -123,6 +155,12 @@
|
|||||||
Margin="10, 20, 10, 10"
|
Margin="10, 20, 10, 10"
|
||||||
HorizontalTextAlignment="Center" />
|
HorizontalTextAlignment="Center" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
<Button Text="{u:I18n Continue}"
|
||||||
|
IsEnabled="{Binding EnableContinue}"
|
||||||
|
IsVisible="{Binding ShowContinue}"
|
||||||
|
Clicked="Continue_Clicked"
|
||||||
|
Margin="10, 0"
|
||||||
|
x:Name="_continue"></Button>
|
||||||
<Button Text="{u:I18n SendVerificationCodeAgain}"
|
<Button Text="{u:I18n SendVerificationCodeAgain}"
|
||||||
IsVisible="{Binding EmailMethod}"
|
IsVisible="{Binding EmailMethod}"
|
||||||
Clicked="ResendEmail_Clicked"
|
Clicked="ResendEmail_Clicked"
|
||||||
@@ -131,9 +169,6 @@
|
|||||||
IsVisible="{Binding ShowTryAgain}"
|
IsVisible="{Binding ShowTryAgain}"
|
||||||
Clicked="TryAgain_Clicked"
|
Clicked="TryAgain_Clicked"
|
||||||
Margin="10, 0"></Button>
|
Margin="10, 0"></Button>
|
||||||
<Button Text="{u:I18n UseAnotherTwoStepMethod}"
|
|
||||||
Clicked="Methods_Clicked"
|
|
||||||
Margin="10, 0"></Button>
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Bit.App.Controls;
|
using Bit.App.Controls;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
@@ -39,32 +40,25 @@ namespace Bit.App.Pages
|
|||||||
Device.BeginInvokeOnMainThread(async () => await StartSetPasswordAsync());
|
Device.BeginInvokeOnMainThread(async () => await StartSetPasswordAsync());
|
||||||
_vm.TwoFactorAuthSuccessAction = () =>
|
_vm.TwoFactorAuthSuccessAction = () =>
|
||||||
Device.BeginInvokeOnMainThread(async () => await TwoFactorAuthSuccessAsync());
|
Device.BeginInvokeOnMainThread(async () => await TwoFactorAuthSuccessAsync());
|
||||||
|
_vm.UpdateTempPasswordAction =
|
||||||
|
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||||
_vm.CloseAction = async () => await Navigation.PopModalAsync();
|
_vm.CloseAction = async () => await Navigation.PopModalAsync();
|
||||||
DuoWebView = _duoWebView;
|
DuoWebView = _duoWebView;
|
||||||
if (Device.RuntimePlatform == Device.Android)
|
if (Device.RuntimePlatform == Device.Android)
|
||||||
{
|
{
|
||||||
ToolbarItems.Remove(_cancelItem);
|
ToolbarItems.Remove(_cancelItem);
|
||||||
}
|
}
|
||||||
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
|
{
|
||||||
|
ToolbarItems.Add(_moreItem);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
ToolbarItems.Add(_useAnotherTwoStepMethod);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HybridWebView DuoWebView { get; set; }
|
public HybridWebView DuoWebView { get; set; }
|
||||||
|
|
||||||
public void AddContinueButton()
|
|
||||||
{
|
|
||||||
if (!ToolbarItems.Contains(_continueItem))
|
|
||||||
{
|
|
||||||
ToolbarItems.Add(_continueItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveContinueButton()
|
|
||||||
{
|
|
||||||
if (ToolbarItems.Contains(_continueItem))
|
|
||||||
{
|
|
||||||
ToolbarItems.Remove(_continueItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async override void OnAppearing()
|
protected async override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
@@ -145,6 +139,21 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void More_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (!DoOnce())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel, null, AppResources.UseAnotherTwoStepMethod);
|
||||||
|
|
||||||
|
if (selection == AppResources.UseAnotherTwoStepMethod)
|
||||||
|
{
|
||||||
|
await _vm.AnotherMethodAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async void ResendEmail_Clicked(object sender, EventArgs e)
|
private async void ResendEmail_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
@@ -161,11 +170,15 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryAgain_Clicked(object sender, EventArgs e)
|
private async void TryAgain_Clicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DoOnce())
|
if (DoOnce())
|
||||||
{
|
{
|
||||||
if (_vm.YubikeyMethod)
|
if (_vm.Fido2Method)
|
||||||
|
{
|
||||||
|
await _vm.Fido2AuthenticateAsync();
|
||||||
|
}
|
||||||
|
else if (_vm.YubikeyMethod)
|
||||||
{
|
{
|
||||||
_messagingService.Send("listenYubiKeyOTP", true);
|
_messagingService.Send("listenYubiKeyOTP", true);
|
||||||
}
|
}
|
||||||
@@ -179,6 +192,12 @@ namespace Bit.App.Pages
|
|||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task UpdateTempPasswordAsync()
|
||||||
|
{
|
||||||
|
var page = new UpdateTempPasswordPage();
|
||||||
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
|
}
|
||||||
|
|
||||||
private async Task TwoFactorAuthSuccessAsync()
|
private async Task TwoFactorAuthSuccessAsync()
|
||||||
{
|
{
|
||||||
if (_authingWithSso)
|
if (_authingWithSso)
|
||||||
@@ -195,5 +214,10 @@ namespace Bit.App.Pages
|
|||||||
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Token_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
|
{
|
||||||
|
_vm.EnableContinue = !string.IsNullOrWhiteSpace(e.NewTextValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,13 @@ using Bit.Core.Exceptions;
|
|||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Xamarin.Essentials;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -27,11 +31,12 @@ namespace Bit.App.Pages
|
|||||||
private readonly IBroadcasterService _broadcasterService;
|
private readonly IBroadcasterService _broadcasterService;
|
||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
|
|
||||||
private bool _u2fSupported = false;
|
|
||||||
private TwoFactorProviderType? _selectedProviderType;
|
private TwoFactorProviderType? _selectedProviderType;
|
||||||
private string _totpInstruction;
|
private string _totpInstruction;
|
||||||
private string _webVaultUrl = "https://vault.bitwarden.com";
|
private string _webVaultUrl = "https://vault.bitwarden.com";
|
||||||
private bool _authingWithSso = false;
|
private bool _authingWithSso = false;
|
||||||
|
private bool _enableContinue = false;
|
||||||
|
private bool _showContinue = true;
|
||||||
|
|
||||||
public TwoFactorPageViewModel()
|
public TwoFactorPageViewModel()
|
||||||
{
|
{
|
||||||
@@ -63,6 +68,8 @@ namespace Bit.App.Pages
|
|||||||
public bool DuoMethod => SelectedProviderType == TwoFactorProviderType.Duo ||
|
public bool DuoMethod => SelectedProviderType == TwoFactorProviderType.Duo ||
|
||||||
SelectedProviderType == TwoFactorProviderType.OrganizationDuo;
|
SelectedProviderType == TwoFactorProviderType.OrganizationDuo;
|
||||||
|
|
||||||
|
public bool Fido2Method => SelectedProviderType == TwoFactorProviderType.Fido2WebAuthn;
|
||||||
|
|
||||||
public bool YubikeyMethod => SelectedProviderType == TwoFactorProviderType.YubiKey;
|
public bool YubikeyMethod => SelectedProviderType == TwoFactorProviderType.YubiKey;
|
||||||
|
|
||||||
public bool AuthenticatorMethod => SelectedProviderType == TwoFactorProviderType.Authenticator;
|
public bool AuthenticatorMethod => SelectedProviderType == TwoFactorProviderType.Authenticator;
|
||||||
@@ -71,7 +78,19 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public bool TotpMethod => AuthenticatorMethod || EmailMethod;
|
public bool TotpMethod => AuthenticatorMethod || EmailMethod;
|
||||||
|
|
||||||
public bool ShowTryAgain => YubikeyMethod && Device.RuntimePlatform == Device.iOS;
|
public bool ShowTryAgain => (YubikeyMethod && Device.RuntimePlatform == Device.iOS) || Fido2Method;
|
||||||
|
|
||||||
|
public bool ShowContinue
|
||||||
|
{
|
||||||
|
get => _showContinue;
|
||||||
|
set => SetProperty(ref _showContinue, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool EnableContinue
|
||||||
|
{
|
||||||
|
get => _enableContinue;
|
||||||
|
set => SetProperty(ref _enableContinue, value);
|
||||||
|
}
|
||||||
|
|
||||||
public string YubikeyInstruction => Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos :
|
public string YubikeyInstruction => Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos :
|
||||||
AppResources.YubiKeyInstruction;
|
AppResources.YubiKeyInstruction;
|
||||||
@@ -83,6 +102,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
nameof(EmailMethod),
|
nameof(EmailMethod),
|
||||||
nameof(DuoMethod),
|
nameof(DuoMethod),
|
||||||
|
nameof(Fido2Method),
|
||||||
nameof(YubikeyMethod),
|
nameof(YubikeyMethod),
|
||||||
nameof(AuthenticatorMethod),
|
nameof(AuthenticatorMethod),
|
||||||
nameof(TotpMethod),
|
nameof(TotpMethod),
|
||||||
@@ -93,6 +113,7 @@ namespace Bit.App.Pages
|
|||||||
public Action TwoFactorAuthSuccessAction { get; set; }
|
public Action TwoFactorAuthSuccessAction { get; set; }
|
||||||
public Action StartSetPasswordAction { get; set; }
|
public Action StartSetPasswordAction { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
|
public Action UpdateTempPasswordAction { get; set; }
|
||||||
|
|
||||||
public void Init()
|
public void Init()
|
||||||
{
|
{
|
||||||
@@ -114,10 +135,7 @@ namespace Bit.App.Pages
|
|||||||
_webVaultUrl = _environmentService.WebVaultUrl;
|
_webVaultUrl = _environmentService.WebVaultUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: init U2F
|
SelectedProviderType = _authService.GetDefaultTwoFactorProvider(_platformUtilsService.SupportsFido2());
|
||||||
_u2fSupported = false;
|
|
||||||
|
|
||||||
SelectedProviderType = _authService.GetDefaultTwoFactorProvider(_u2fSupported);
|
|
||||||
Load();
|
Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,8 +151,8 @@ namespace Bit.App.Pages
|
|||||||
var providerData = _authService.TwoFactorProvidersData[SelectedProviderType.Value];
|
var providerData = _authService.TwoFactorProvidersData[SelectedProviderType.Value];
|
||||||
switch (SelectedProviderType.Value)
|
switch (SelectedProviderType.Value)
|
||||||
{
|
{
|
||||||
case TwoFactorProviderType.U2f:
|
case TwoFactorProviderType.Fido2WebAuthn:
|
||||||
// TODO
|
Fido2AuthenticateAsync(providerData);
|
||||||
break;
|
break;
|
||||||
case TwoFactorProviderType.YubiKey:
|
case TwoFactorProviderType.YubiKey:
|
||||||
_messagingService.Send("listenYubiKeyOTP", true);
|
_messagingService.Send("listenYubiKeyOTP", true);
|
||||||
@@ -169,17 +187,77 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_messagingService.Send("listenYubiKeyOTP", false);
|
_messagingService.Send("listenYubiKeyOTP", false);
|
||||||
}
|
}
|
||||||
if (SelectedProviderType == null || DuoMethod)
|
ShowContinue = !(SelectedProviderType == null || DuoMethod || Fido2Method);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Fido2AuthenticateAsync(Dictionary<string, object> providerData = null)
|
||||||
|
{
|
||||||
|
await _deviceActionService.ShowLoadingAsync(AppResources.Validating);
|
||||||
|
|
||||||
|
if (providerData == null)
|
||||||
{
|
{
|
||||||
page.RemoveContinueButton();
|
providerData = _authService.TwoFactorProvidersData[TwoFactorProviderType.Fido2WebAuthn];
|
||||||
|
}
|
||||||
|
|
||||||
|
var callbackUri = "bitwarden://webauthn-callback";
|
||||||
|
var data = AppHelpers.EncodeDataParameter(new
|
||||||
|
{
|
||||||
|
callbackUri = callbackUri,
|
||||||
|
data = JsonConvert.SerializeObject(providerData),
|
||||||
|
headerText = AppResources.Fido2Title,
|
||||||
|
btnText = AppResources.Fido2AuthenticateWebAuthn,
|
||||||
|
btnReturnText = AppResources.Fido2ReturnToApp,
|
||||||
|
});
|
||||||
|
|
||||||
|
var url = _webVaultUrl + "/webauthn-mobile-connector.html?" + "data=" + data +
|
||||||
|
"&parent=" + Uri.EscapeDataString(callbackUri) + "&v=2";
|
||||||
|
|
||||||
|
WebAuthenticatorResult authResult = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var options = new WebAuthenticatorOptions
|
||||||
|
{
|
||||||
|
Url = new Uri(url),
|
||||||
|
CallbackUrl = new Uri(callbackUri),
|
||||||
|
PrefersEphemeralWebBrowserSession = true,
|
||||||
|
};
|
||||||
|
authResult = await WebAuthenticator.AuthenticateAsync(options);
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
// user canceled
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string response = null;
|
||||||
|
if (authResult != null && authResult.Properties.TryGetValue("data", out var resultData))
|
||||||
|
{
|
||||||
|
response = Uri.UnescapeDataString(resultData);
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(response))
|
||||||
|
{
|
||||||
|
Token = response;
|
||||||
|
await SubmitAsync(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
page.AddContinueButton();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
if (authResult != null && authResult.Properties.TryGetValue("error", out var resultError))
|
||||||
|
{
|
||||||
|
var message = AppResources.Fido2CheckBrowser + "\n\n" + resultError;
|
||||||
|
await _platformUtilsService.ShowDialogAsync(message, AppResources.AnErrorHasOccurred,
|
||||||
|
AppResources.Ok);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.Fido2CheckBrowser,
|
||||||
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SubmitAsync()
|
public async Task SubmitAsync(bool showLoading = true)
|
||||||
{
|
{
|
||||||
if (SelectedProviderType == null)
|
if (SelectedProviderType == null)
|
||||||
{
|
{
|
||||||
@@ -188,14 +266,14 @@ namespace Bit.App.Pages
|
|||||||
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
||||||
AppResources.InternetConnectionRequiredTitle);
|
AppResources.InternetConnectionRequiredTitle, AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(Token))
|
if (string.IsNullOrWhiteSpace(Token))
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(
|
await _platformUtilsService.ShowDialogAsync(
|
||||||
string.Format(AppResources.ValidationFieldRequired, AppResources.VerificationCode),
|
string.Format(AppResources.ValidationFieldRequired, AppResources.VerificationCode),
|
||||||
AppResources.AnErrorHasOccurred);
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (SelectedProviderType == TwoFactorProviderType.Email ||
|
if (SelectedProviderType == TwoFactorProviderType.Email ||
|
||||||
@@ -206,7 +284,10 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.Validating);
|
if (showLoading)
|
||||||
|
{
|
||||||
|
await _deviceActionService.ShowLoadingAsync(AppResources.Validating);
|
||||||
|
}
|
||||||
var result = await _authService.LogInTwoFactorAsync(SelectedProviderType.Value, Token, Remember);
|
var result = await _authService.LogInTwoFactorAsync(SelectedProviderType.Value, Token, Remember);
|
||||||
var task = Task.Run(() => _syncService.FullSyncAsync(true));
|
var task = Task.Run(() => _syncService.FullSyncAsync(true));
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
@@ -216,6 +297,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
StartSetPasswordAction?.Invoke();
|
StartSetPasswordAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
else if (result.ForcePasswordReset)
|
||||||
|
{
|
||||||
|
UpdateTempPasswordAction?.Invoke();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
|
||||||
@@ -229,7 +314,7 @@ namespace Bit.App.Pages
|
|||||||
if (e?.Error != null)
|
if (e?.Error != null)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
||||||
AppResources.AnErrorHasOccurred);
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,7 +330,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
_platformUtilsService.LaunchUri("https://help.bitwarden.com/article/lost-two-step-device/");
|
_platformUtilsService.LaunchUri("https://help.bitwarden.com/article/lost-two-step-device/");
|
||||||
}
|
}
|
||||||
else if (method != AppResources.Cancel)
|
else if (method != AppResources.Cancel && method != null)
|
||||||
{
|
{
|
||||||
var selected = supportedProviders.FirstOrDefault(p => p.Name == method)?.Type;
|
var selected = supportedProviders.FirstOrDefault(p => p.Name == method)?.Type;
|
||||||
if (selected == SelectedProviderType)
|
if (selected == SelectedProviderType)
|
||||||
@@ -267,7 +352,7 @@ namespace Bit.App.Pages
|
|||||||
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
||||||
{
|
{
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
||||||
AppResources.InternetConnectionRequiredTitle);
|
AppResources.InternetConnectionRequiredTitle, AppResources.Ok);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
@@ -298,7 +383,8 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
}
|
}
|
||||||
await _platformUtilsService.ShowDialogAsync(AppResources.VerificationEmailNotSent);
|
await _platformUtilsService.ShowDialogAsync(AppResources.VerificationEmailNotSent,
|
||||||
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
163
src/App/Pages/Accounts/UpdateTempPasswordPage.xaml
Normal file
163
src/App/Pages/Accounts/UpdateTempPasswordPage.xaml
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<pages:BaseContentPage
|
||||||
|
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
x:Class="Bit.App.Pages.UpdateTempPasswordPage"
|
||||||
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
|
x:DataType="pages:UpdateTempPasswordPageViewModel"
|
||||||
|
Title="{Binding PageTitle}">
|
||||||
|
|
||||||
|
<ContentPage.BindingContext>
|
||||||
|
<pages:UpdateTempPasswordPageViewModel />
|
||||||
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
|
<ContentPage.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
|
</ResourceDictionary>
|
||||||
|
</ContentPage.Resources>
|
||||||
|
|
||||||
|
<ContentPage.ToolbarItems>
|
||||||
|
<ToolbarItem Text="{u:I18n LogOut}" Clicked="LogOut_Clicked" Order="Primary" Priority="-1" />
|
||||||
|
<ToolbarItem Text="{u:I18n Submit}" Clicked="Submit_Clicked" />
|
||||||
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
|
<ScrollView>
|
||||||
|
<StackLayout
|
||||||
|
x:Name="_mainLayout"
|
||||||
|
Spacing="20">
|
||||||
|
<StackLayout StyleClass="box">
|
||||||
|
<Grid Margin="0, 12, 0, 0"
|
||||||
|
RowSpacing="0"
|
||||||
|
ColumnSpacing="0">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Frame Padding="10"
|
||||||
|
Margin="0"
|
||||||
|
HasShadow="False"
|
||||||
|
BackgroundColor="Transparent"
|
||||||
|
BorderColor="Accent">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n UpdateMasterPasswordWarning}"
|
||||||
|
StyleClass="text-muted, text-sm, text-bold"
|
||||||
|
HorizontalTextAlignment="Center" />
|
||||||
|
</Frame>
|
||||||
|
</Grid>
|
||||||
|
<Grid IsVisible="{Binding IsPolicyInEffect}"
|
||||||
|
Margin="0, 12, 0, 0"
|
||||||
|
RowSpacing="0"
|
||||||
|
ColumnSpacing="0">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Frame Padding="10"
|
||||||
|
Margin="0"
|
||||||
|
HasShadow="False"
|
||||||
|
BackgroundColor="Transparent"
|
||||||
|
BorderColor="Accent">
|
||||||
|
<Label
|
||||||
|
Text="{Binding PolicySummary}"
|
||||||
|
StyleClass="text-muted, text-sm, text-bold"
|
||||||
|
HorizontalTextAlignment="Start" />
|
||||||
|
</Frame>
|
||||||
|
</Grid>
|
||||||
|
<Grid StyleClass="box-row">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n MasterPassword}"
|
||||||
|
StyleClass="box-label"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0" />
|
||||||
|
<controls:MonoEntry
|
||||||
|
x:Name="_masterPassword"
|
||||||
|
Text="{Binding MasterPassword}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
IsSpellCheckEnabled="False"
|
||||||
|
IsTextPredictionEnabled="False"
|
||||||
|
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0" />
|
||||||
|
<controls:FaButton
|
||||||
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
|
Text="{Binding ShowPasswordIcon}"
|
||||||
|
Command="{Binding TogglePasswordCommand}"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
|
</Grid>
|
||||||
|
</StackLayout>
|
||||||
|
<StackLayout StyleClass="box">
|
||||||
|
<Grid StyleClass="box-row">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n RetypeMasterPassword}"
|
||||||
|
StyleClass="box-label"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0" />
|
||||||
|
<controls:MonoEntry
|
||||||
|
x:Name="_confirmMasterPassword"
|
||||||
|
Text="{Binding ConfirmMasterPassword}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
IsSpellCheckEnabled="False"
|
||||||
|
IsTextPredictionEnabled="False"
|
||||||
|
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0" />
|
||||||
|
<controls:FaButton
|
||||||
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
|
Text="{Binding ShowPasswordIcon}"
|
||||||
|
Command="{Binding ToggleConfirmPasswordCommand}"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
AutomationProperties.IsInAccessibleTree="True"
|
||||||
|
AutomationProperties.Name="{u:I18n ToggleVisibility}" />
|
||||||
|
</Grid>
|
||||||
|
<StackLayout StyleClass="box-row">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n MasterPasswordHint}"
|
||||||
|
StyleClass="box-label" />
|
||||||
|
<Entry
|
||||||
|
x:Name="_hint"
|
||||||
|
Text="{Binding Hint}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
ReturnType="Go"
|
||||||
|
ReturnCommand="{Binding SubmitCommand}" />
|
||||||
|
</StackLayout>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n MasterPasswordHintDescription}"
|
||||||
|
StyleClass="box-footer-label" />
|
||||||
|
</StackLayout>
|
||||||
|
</StackLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</pages:BaseContentPage>
|
||||||
89
src/App/Pages/Accounts/UpdateTempPasswordPage.xaml.cs
Normal file
89
src/App/Pages/Accounts/UpdateTempPasswordPage.xaml.cs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using System;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public partial class UpdateTempPasswordPage : BaseContentPage
|
||||||
|
{
|
||||||
|
private readonly IMessagingService _messagingService;
|
||||||
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
|
private readonly UpdateTempPasswordPageViewModel _vm;
|
||||||
|
private readonly string _pageName;
|
||||||
|
|
||||||
|
public UpdateTempPasswordPage()
|
||||||
|
{
|
||||||
|
// Service Init
|
||||||
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||||
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
|
|
||||||
|
// Service Use
|
||||||
|
_messagingService.Send("showStatusBar", true);
|
||||||
|
|
||||||
|
// Binding
|
||||||
|
InitializeComponent();
|
||||||
|
_pageName = string.Concat(nameof(UpdateTempPasswordPage), "_", DateTime.UtcNow.Ticks);
|
||||||
|
_vm = BindingContext as UpdateTempPasswordPageViewModel;
|
||||||
|
_vm.Page = this;
|
||||||
|
SetActivityIndicator();
|
||||||
|
|
||||||
|
// Actions Declaration
|
||||||
|
_vm.LogOutAction = () =>
|
||||||
|
{
|
||||||
|
_messagingService.Send("logout");
|
||||||
|
};
|
||||||
|
_vm.UpdateTempPasswordSuccessAction = () => Device.BeginInvokeOnMainThread(UpdateTempPasswordSuccess);
|
||||||
|
|
||||||
|
// Link fields that will be referenced in codebehind
|
||||||
|
MasterPasswordEntry = _masterPassword;
|
||||||
|
ConfirmMasterPasswordEntry = _confirmMasterPassword;
|
||||||
|
|
||||||
|
// Return Types and Commands
|
||||||
|
_masterPassword.ReturnType = ReturnType.Next;
|
||||||
|
_masterPassword.ReturnCommand = new Command(() => _confirmMasterPassword.Focus());
|
||||||
|
_confirmMasterPassword.ReturnType = ReturnType.Next;
|
||||||
|
_confirmMasterPassword.ReturnCommand = new Command(() => _hint.Focus());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entry MasterPasswordEntry { get; set; }
|
||||||
|
public Entry ConfirmMasterPasswordEntry { get; set; }
|
||||||
|
|
||||||
|
protected override async void OnAppearing()
|
||||||
|
{
|
||||||
|
base.OnAppearing();
|
||||||
|
await LoadOnAppearedAsync(_mainLayout, true, async () =>
|
||||||
|
{
|
||||||
|
await _vm.InitAsync(true);
|
||||||
|
});
|
||||||
|
RequestFocus(_masterPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Submit_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
await _vm.SubmitAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void LogOut_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.LogoutConfirmation,
|
||||||
|
AppResources.LogOut, AppResources.Yes, AppResources.Cancel);
|
||||||
|
if (confirmed)
|
||||||
|
{
|
||||||
|
_vm.LogOutAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateTempPasswordSuccess()
|
||||||
|
{
|
||||||
|
_messagingService.Send("logout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
src/App/Pages/Accounts/UpdateTempPasswordPageViewModel.cs
Normal file
90
src/App/Pages/Accounts/UpdateTempPasswordPageViewModel.cs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
using Bit.App.Resources;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Request;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class UpdateTempPasswordPageViewModel : BaseChangePasswordViewModel
|
||||||
|
{
|
||||||
|
public UpdateTempPasswordPageViewModel()
|
||||||
|
{
|
||||||
|
PageTitle = AppResources.UpdateMasterPassword;
|
||||||
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
|
ToggleConfirmPasswordCommand = new Command(ToggleConfirmPassword);
|
||||||
|
SubmitCommand = new Command(async () => await SubmitAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Command SubmitCommand { get; }
|
||||||
|
public Command TogglePasswordCommand { get; }
|
||||||
|
public Command ToggleConfirmPasswordCommand { get; }
|
||||||
|
public Action UpdateTempPasswordSuccessAction { get; set; }
|
||||||
|
public Action LogOutAction { get; set; }
|
||||||
|
|
||||||
|
public void TogglePassword()
|
||||||
|
{
|
||||||
|
ShowPassword = !ShowPassword;
|
||||||
|
(Page as UpdateTempPasswordPage).MasterPasswordEntry.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToggleConfirmPassword()
|
||||||
|
{
|
||||||
|
ShowPassword = !ShowPassword;
|
||||||
|
(Page as UpdateTempPasswordPage).ConfirmMasterPasswordEntry.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SubmitAsync()
|
||||||
|
{
|
||||||
|
if (!await ValidateMasterPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve details for key generation
|
||||||
|
var kdf = await _userService.GetKdfAsync();
|
||||||
|
var kdfIterations = await _userService.GetKdfIterationsAsync();
|
||||||
|
var email = await _userService.GetEmailAsync();
|
||||||
|
|
||||||
|
// Create new key and hash new password
|
||||||
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, email, kdf, kdfIterations);
|
||||||
|
var masterPasswordHash = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
||||||
|
|
||||||
|
// Create new encKey for the User
|
||||||
|
var newEncKey = await _cryptoService.RemakeEncKeyAsync(key);
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
var request = new UpdateTempPasswordRequest
|
||||||
|
{
|
||||||
|
Key = newEncKey.Item2.EncryptedString,
|
||||||
|
NewMasterPasswordHash = masterPasswordHash,
|
||||||
|
MasterPasswordHint = Hint
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initiate API action
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _deviceActionService.ShowLoadingAsync(AppResources.UpdatingPassword);
|
||||||
|
await _apiService.PutUpdateTempPasswordAsync(request);
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
|
||||||
|
UpdateTempPasswordSuccessAction?.Invoke();
|
||||||
|
}
|
||||||
|
catch (ApiException e)
|
||||||
|
{
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
if (e?.Error != null)
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
||||||
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.UpdatePasswordError,
|
||||||
|
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ using Bit.Core.Utilities;
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Utilities;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
using Xamarin.Forms.PlatformConfiguration;
|
using Xamarin.Forms.PlatformConfiguration;
|
||||||
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
||||||
@@ -22,6 +23,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if (Device.RuntimePlatform == Device.iOS)
|
if (Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
|
On<iOS>().SetUseSafeArea(true);
|
||||||
On<iOS>().SetModalPresentationStyle(UIModalPresentationStyle.FullScreen);
|
On<iOS>().SetModalPresentationStyle(UIModalPresentationStyle.FullScreen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +54,8 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
IsRunning = true,
|
IsRunning = true,
|
||||||
VerticalOptions = LayoutOptions.CenterAndExpand,
|
VerticalOptions = LayoutOptions.CenterAndExpand,
|
||||||
HorizontalOptions = LayoutOptions.Center
|
HorizontalOptions = LayoutOptions.Center,
|
||||||
|
Color = ThemeManager.GetResourceColor("PrimaryColor"),
|
||||||
};
|
};
|
||||||
if (targetView != null)
|
if (targetView != null)
|
||||||
{
|
{
|
||||||
|
|||||||
69
src/App/Pages/CaptchaProtectedViewModel.cs
Normal file
69
src/App/Pages/CaptchaProtectedViewModel.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Xamarin.Essentials;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public abstract class CaptchaProtectedViewModel : BaseViewModel
|
||||||
|
{
|
||||||
|
protected abstract II18nService i18nService { get; }
|
||||||
|
protected abstract IEnvironmentService environmentService { get; }
|
||||||
|
protected abstract IDeviceActionService deviceActionService { get; }
|
||||||
|
protected abstract IPlatformUtilsService platformUtilsService { get; }
|
||||||
|
protected string _captchaToken = null;
|
||||||
|
|
||||||
|
protected async Task<bool> HandleCaptchaAsync(string CaptchaSiteKey)
|
||||||
|
{
|
||||||
|
var callbackUri = "bitwarden://captcha-callback";
|
||||||
|
var data = AppHelpers.EncodeDataParameter(new
|
||||||
|
{
|
||||||
|
siteKey = CaptchaSiteKey,
|
||||||
|
locale = i18nService.Culture.TwoLetterISOLanguageName,
|
||||||
|
callbackUri = callbackUri,
|
||||||
|
captchaRequiredText = AppResources.CaptchaRequired,
|
||||||
|
});
|
||||||
|
|
||||||
|
var url = environmentService.GetWebVaultUrl();
|
||||||
|
if (url == null)
|
||||||
|
{
|
||||||
|
url = "https://vault.bitwarden.com";
|
||||||
|
}
|
||||||
|
url += "/captcha-mobile-connector.html?" + "data=" + data +
|
||||||
|
"&parent=" + Uri.EscapeDataString(callbackUri) + "&v=1";
|
||||||
|
|
||||||
|
WebAuthenticatorResult authResult = null;
|
||||||
|
bool cancelled = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var options = new WebAuthenticatorOptions
|
||||||
|
{
|
||||||
|
Url = new Uri(url),
|
||||||
|
CallbackUrl = new Uri(callbackUri),
|
||||||
|
PrefersEphemeralWebBrowserSession = true,
|
||||||
|
};
|
||||||
|
authResult = await WebAuthenticator.AuthenticateAsync(options);
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
await deviceActionService.HideLoadingAsync();
|
||||||
|
cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancelled == false && authResult != null &&
|
||||||
|
authResult.Properties.TryGetValue("token", out _captchaToken))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await platformUtilsService.ShowDialogAsync(AppResources.CaptchaFailed,
|
||||||
|
AppResources.CaptchaRequired);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -68,8 +68,7 @@
|
|||||||
<controls:MonoLabel LineBreakMode="CharacterWrap"
|
<controls:MonoLabel LineBreakMode="CharacterWrap"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
StyleClass="list-title, list-title-platform"
|
StyleClass="list-title, list-title-platform, text-html"
|
||||||
TextType="Html"
|
|
||||||
Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" />
|
Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" />
|
||||||
<Label LineBreakMode="TailTruncation"
|
<Label LineBreakMode="TailTruncation"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
|
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
|
||||||
x:Name="_closeItem" x:Key="closeItem" />
|
x:Name="_closeItem" x:Key="closeItem" />
|
||||||
<ToolbarItem Text="{u:I18n Select}"
|
<ToolbarItem Text="{u:I18n Select}"
|
||||||
Clicked="Select_Clicked"
|
Clicked="Select_Clicked"
|
||||||
@@ -63,14 +63,14 @@
|
|||||||
</Frame>
|
</Frame>
|
||||||
</Grid>
|
</Grid>
|
||||||
<controls:MonoLabel
|
<controls:MonoLabel
|
||||||
|
StyleClass="text-lg, text-html"
|
||||||
Text="{Binding ColoredPassword, Mode=OneWay}"
|
Text="{Binding ColoredPassword, Mode=OneWay}"
|
||||||
TextType="Html"
|
|
||||||
Margin="0, 20"
|
Margin="0, 20"
|
||||||
StyleClass="text-lg"
|
|
||||||
HorizontalTextAlignment="Center"
|
HorizontalTextAlignment="Center"
|
||||||
HorizontalOptions="CenterAndExpand"
|
HorizontalOptions="CenterAndExpand"
|
||||||
LineBreakMode="CharacterWrap" />
|
LineBreakMode="CharacterWrap" />
|
||||||
<Button Text="{u:I18n RegeneratePassword}"
|
<Button Text="{u:I18n RegeneratePassword}"
|
||||||
|
StyleClass="btn-primary"
|
||||||
HorizontalOptions="FillAndExpand"
|
HorizontalOptions="FillAndExpand"
|
||||||
Clicked="Regenerate_Clicked"></Button>
|
Clicked="Regenerate_Clicked"></Button>
|
||||||
<Button Text="{u:I18n CopyPassword}"
|
<Button Text="{u:I18n CopyPassword}"
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
HorizontalTextAlignment="End"
|
HorizontalTextAlignment="End"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
VerticalTextAlignment="Center" />
|
VerticalTextAlignment="Center" />
|
||||||
<Stepper
|
<controls:ExtendedStepper
|
||||||
Value="{Binding NumWords}"
|
Value="{Binding NumWords}"
|
||||||
Maximum="20"
|
Maximum="20"
|
||||||
Minimum="3"
|
Minimum="3"
|
||||||
@@ -239,7 +239,7 @@
|
|||||||
HorizontalTextAlignment="End"
|
HorizontalTextAlignment="End"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
VerticalTextAlignment="Center" />
|
VerticalTextAlignment="Center" />
|
||||||
<Stepper
|
<controls:ExtendedStepper
|
||||||
Value="{Binding MinNumber}"
|
Value="{Binding MinNumber}"
|
||||||
Maximum="5"
|
Maximum="5"
|
||||||
Minimum="0"
|
Minimum="0"
|
||||||
@@ -259,7 +259,7 @@
|
|||||||
HorizontalTextAlignment="End"
|
HorizontalTextAlignment="End"
|
||||||
VerticalOptions="FillAndExpand"
|
VerticalOptions="FillAndExpand"
|
||||||
VerticalTextAlignment="Center" />
|
VerticalTextAlignment="Center" />
|
||||||
<Stepper
|
<controls:ExtendedStepper
|
||||||
Value="{Binding MinSpecial}"
|
Value="{Binding MinSpecial}"
|
||||||
Maximum="5"
|
Maximum="5"
|
||||||
Minimum="0"
|
Minimum="0"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user