1
0
mirror of https://github.com/bitwarden/mobile synced 2026-02-25 00:52:49 +00:00

Compare commits

..

13 Commits

Author SHA1 Message Date
Federico Maccaroni
d3c44c5c5e Enabled verbosity log in build cake for GitVersion and also added .NET 6 and 7 to see if that's what is requiring GitVersion. 2024-02-26 15:33:52 -03:00
Opeyemi Alao
1a23e358f4 Add tool restore 2024-02-22 19:31:44 +01:00
Opeyemi Alao
cdb0672390 move cake step up 2024-02-22 19:12:21 +01:00
Opeyemi Alao
8a3e658e69 pull ios distribution, edit paths 2024-02-22 16:12:24 +01:00
Opeyemi
78ee7ef919 Merge branch 'main' into DEVOPS-1822-upload-latest-ios-native-mobile-build-to-beta-app 2024-02-22 15:29:47 +01:00
Opeyemi Alao
121adb18bc update profile provisioning pattern 2024-02-22 15:28:05 +01:00
Opeyemi Alao
4ac3662271 Remove references from Provisioning profile 2024-02-21 19:01:56 +01:00
Opeyemi Alao
aadaf45084 Edit gpg file paths 2024-02-21 17:02:49 +01:00
Opeyemi Alao
bf4cfbca21 Fix typo 2024-02-21 16:50:19 +01:00
Opeyemi Alao
e9400e497a Mute Crowdin and failure-check for testing 2024-02-21 16:47:07 +01:00
Opeyemi Alao
80225915b8 Update the jobs dependencies, remove CLOC 2024-02-21 16:37:47 +01:00
Opeyemi Alao
951fc3d470 Update beta workflow name 2024-02-21 16:23:31 +01:00
Opeyemi Alao
a19bc95bea Upload mobile beta native build 2024-02-21 16:01:19 +01:00
119 changed files with 1238 additions and 2791 deletions

6
.github/CODEOWNERS vendored
View File

@@ -30,14 +30,14 @@ src/watchOS @bitwarden/team-vault-dev
src/Core/Services/EmailForwarders @bitwarden/team-tools-dev
## Crowdin Sync files ##
src/Core/Resources/Localization @bitwarden/team-tools-dev
src/App/Resources @bitwarden/team-tools-dev
src/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization @bitwarden/team-tools-dev
store/apple @bitwarden/team-tools-dev
store/google @bitwarden/team-tools-dev
## Locales ##
src/Core/Resources/Localization/AppResources.Designer.cs
src/Core/Resources/Localization/AppResources.resx
src/App/Resources/AppResources.Designer.cs
src/App/Resources/AppResources.resx
src/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization/en.lproj
store/apple/en
store/google/en

View File

@@ -3,3 +3,408 @@ name: Build Beta
on:
workflow_dispatch:
inputs:
ref:
description: 'Branch or tag to build'
required: true
default: 'main'
type: string
env:
main_app_folder_path: src/App
main_app_project_path: src/App/App.csproj
target-net-version: net8.0
jobs:
setup:
name: Setup
runs-on: ubuntu-22.04
outputs:
rc_branch_exists: ${{ steps.branch-check.outputs.rc_branch_exists }}
hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
submodules: 'true'
- name: Check if special branches exist
id: branch-check
run: |
if [[ $(git ls-remote --heads origin rc) ]]; then
echo "rc_branch_exists=1" >> $GITHUB_OUTPUT
else
echo "rc_branch_exists=0" >> $GITHUB_OUTPUT
fi
if [[ $(git ls-remote --heads origin hotfix-rc) ]]; then
echo "hotfix_branch_exists=1" >> $GITHUB_OUTPUT
else
echo "hotfix_branch_exists=0" >> $GITHUB_OUTPUT
fi
ios:
name: Apple iOS
runs-on: macos-13
needs: setup
env:
ios_folder_path: src/App/Platforms/iOS
app_output_name: App
app_ci_output_filename: App_x64_Debug
steps:
- name: Set XCode version
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
with:
xcode-version: 15.1
- name: Setup NuGet
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
with:
nuget-version: 6.4.0
- name: Set up .NET
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
with:
dotnet-version: |
3.1.x
6.0.x
7.0.x
8.0.x
# This step might be obsolete at some point as .NET MAUI workloads
# are starting to come pre-installed on the GH Actions build agents.
- name: Install MAUI Workload
run: dotnet workload install maui --ignore-failed-sources
- name: Print environment
run: |
nuget help | grep Version
dotnet --info
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
submodules: 'true'
ref: ${{ inputs.ref }}
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "appcenter-ios-token"
- name: Download Provisioning Profiles secrets
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: profiles
run: |
mkdir -p $HOME/secrets
profiles=(
"dist_beta_autofill.mobileprovision"
"dist_beta_bitwarden.mobileprovision"
"dist_beta_extension.mobileprovision"
"dist_beta_share_extension.mobileprovision"
"dist_beta_bitwarden_watch_app.mobileprovision"
"dist_beta_bitwarden_watch_app_extension.mobileprovision"
)
for FILE in "${profiles[@]}"
do
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file $HOME/secrets/$FILE --output none
done
- name: Download Google Services secret
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
FILE: GoogleService-Info.plist
run: |
mkdir -p $HOME/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file $HOME/secrets/$FILE --output none
- name: Increment version
run: |
BUILD_NUMBER=$((100 + $GITHUB_RUN_NUMBER))
echo "##### Setting CFBundleVersion $BUILD_NUMBER"
echo "### CFBundleVersion $BUILD_NUMBER" >> $GITHUB_STEP_SUMMARY
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./${{ env.ios_folder_path }}/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.ShareExtension/Info.plist
cd src/watchOS/bitwarden
agvtool new-version -all $BUILD_NUMBER
- name: Update Entitlements
run: |
echo "##### Updating Entitlements"
perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>beta<\/string>/' ./${{ env.ios_folder_path }}/Entitlements.plist
- name: Get certificates
run: |
mkdir -p $HOME/certificates
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/ios-distribution |
jq -r .value | base64 -d > $HOME/certificates/ios-distribution.p12
- name: Set up Keychain
env:
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
MOBILE_KEY_PASSWORD: ${{ secrets.IOS_KEY_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 $HOME/certificates/ios-distribution.p12 -k build.keychain -P "" -T /usr/bin/codesign \
-T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
- name: Set up provisioning profiles
run: |
AUTOFILL_PROFILE_PATH=$HOME/secrets/dist_beta_autofill.mobileprovision
BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_beta_bitwarden.mobileprovision
EXTENSION_PROFILE_PATH=$HOME/secrets/dist_beta_extension.mobileprovision
SHARE_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_beta_share_extension.mobileprovision
WATCH_APP_PROFILE_PATH=$HOME/secrets/dist_beta_bitwarden_watch_app.mobileprovision
WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_beta_bitwarden_watch_app_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"
SHARE_EXTENSION_UUID=$(grep UUID -A1 -a $SHARE_EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $SHARE_EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$SHARE_EXTENSION_UUID.mobileprovision"
WATCH_APP_UUID=$(grep UUID -A1 -a $WATCH_APP_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $WATCH_APP_PROFILE_PATH "$PROFILES_DIR_PATH/$WATCH_APP_UUID.mobileprovision"
WATCH_APP_EXTENSION_UUID=$(grep UUID -A1 -a $WATCH_APP_EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
cp $WATCH_APP_EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$WATCH_APP_EXTENSION_UUID.mobileprovision"
- name: Restore packages
run: |
dotnet restore
- name: Restore tools
run: dotnet tool restore
- name: Setup iOS build CAKE (Testing)
run: dotnet cake build.cake --target iOS --variant beta
- name: Bulid WatchApp
run: |
echo "##### Build WatchApp with Release Configuration"
xcodebuild archive -workspace ./src/watchOS/bitwarden/bitwarden.xcodeproj/project.xcworkspace -configuration Release -scheme bitwarden\ WatchKit\ App -archivePath ./src/watchOS/bitwarden
echo "##### Done"
- name: Archive Build for App Store
shell: pwsh
run: |
Write-Output "##### Archive for Release ios-arm64
dotnet publish ${{ env.main_app_project_path }} -c Release -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=ios-arm64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
Write-Output "##### Done"
- name: Archive Build for Mobile Automation
shell: pwsh
run: |
Write-Output "##### Archive Debug for iossimulator-x64
dotnet build ${{ env.main_app_project_path }} -c Debug -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=iossimulator-x64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
Write-Output "##### Done"
ls $HOME/Library/Developer/Xcode/Archives
- name: Export .ipa for App Store
env:
EXPORT_OPTIONS_PATH: ./.github/resources/export-options-app-store.plist
EXPORT_PATH: ./bitwarden-export
run: |
ARCHIVE_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive"
xcodebuild -exportArchive -archivePath $ARCHIVE_PATH -exportPath $EXPORT_PATH \
-exportOptionsPlist $EXPORT_OPTIONS_PATH
- name: Export .app for Automation CI
env:
ARCHIVE_PATH: ./${{ env.main_app_folder_path }}/bin/Debug/${{ env.target-net-version }}-ios/iossimulator-x64
EXPORT_PATH: ./bitwarden-export
run: |
zip -r -q ${{ env.app_ci_output_filename }}.app.zip $ARCHIVE_PATH
mv ${{ env.app_ci_output_filename }}.app.zip $EXPORT_PATH
- name: Copy all dSYMs files to upload
env:
EXPORT_PATH: ./bitwarden-export
WATCH_ARCHIVE_DSYMS_PATH: ./src/watchOS/bitwarden.xcarchive/dSYMs/
WATCH_DSYMS_EXPORT_PATH: ./bitwarden-export/Watch_dSYMs
run: |
ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
cp -r -v $ARCHIVE_DSYMS_PATH $EXPORT_PATH
mkdir $WATCH_DSYMS_EXPORT_PATH
cp -r -v $WATCH_ARCHIVE_DSYMS_PATH $WATCH_DSYMS_EXPORT_PATH
- name: Upload App Store .ipa & dSYMs artifacts
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: Bitwarden iOS
path: |
./bitwarden-export/Bitwarden.ipa
./bitwarden-export/dSYMs/*.*
if-no-files-found: error
- name: Upload .app file for Automation CI
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: ${{ env.app_ci_output_filename }}.app.zip
path: ./bitwarden-export/${{ env.app_ci_output_filename }}.app.zip
if-no-files-found: error
- name: Install AppCenter CLI
run: npm install -g appcenter-cli
- name: Upload dSYMs to App Center
env:
APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }}
run: appcenter crashes upload-symbols -a bitwarden/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
- name: Upload Watch dSYMs to Firebase Crashlytics
run: |
echo "##### Uploading Watch dSYMs to Firebase"
find "$HOME/Library/Developer/XCode/DerivedData" -name "upload-symbols" -exec chmod +x {} \; -exec {} -gsp "./src/watchOS/bitwarden/GoogleService-Info.plist" -p ios "./bitwarden-export/Watch_dSYMs" \;
- name: Validate app in App Store
env:
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
run: |
xcrun altool --validate-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
shell: bash
- name: Deploy to App Store
env:
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
run: |
xcrun altool --upload-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
# crowdin-push:
# name: Crowdin Push
# if: github.ref == 'refs/heads/main'
# needs:
# - ios
# runs-on: ubuntu-22.04
# env:
# _CROWDIN_PROJECT_ID: "269690"
# steps:
# - name: Checkout repo
# uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
# - name: Login to Azure - CI Subscription
# uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
# with:
# creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
# - name: Retrieve secrets
# id: retrieve-secrets
# uses: bitwarden/gh-actions/get-keyvault-secrets@main
# with:
# keyvault: "bitwarden-ci"
# secrets: "crowdin-api-token"
# - name: Upload Sources
# uses: crowdin/github-action@198daeb2d30636c4608d6a6bb96c009dbefc02a2 # v1.18.0
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
# with:
# config: crowdin.yml
# crowdin_branch_name: main
# upload_sources: true
# upload_translations: false
# check-failures:
# name: Check for failures
# if: always()
# runs-on: ubuntu-22.04
# needs:
# - ios
# - crowdin-push
# steps:
# - name: Check if any job failed
# if: |
# (github.ref == 'refs/heads/main')
# || (github.ref == 'refs/heads/rc')
# || (github.ref == 'refs/heads/hotfix-rc')
# env:
# CLOC_STATUS: ${{ needs.cloc.result }}
# ANDROID_STATUS: ${{ needs.android.result }}
# F_DROID_STATUS: ${{ needs.f-droid.result }}
# IOS_STATUS: ${{ needs.ios.result }}
# CROWDIN_PUSH_STATUS: ${{ needs.crowdin-push.result }}
# run: |
# if [ "$CLOC_STATUS" = "failure" ]; then
# exit 1
# elif [ "$ANDROID_STATUS" = "failure" ]; then
# exit 1
# elif [ "$F_DROID_STATUS" = "failure" ]; then
# exit 1
# elif [ "$IOS_STATUS" = "failure" ]; then
# exit 1
# elif [ "$CROWDIN_PUSH_STATUS" = "failure" ]; then
# exit 1
# fi
# - name: Login to Azure - CI Subscription
# uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
# if: failure()
# with:
# creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
# - name: Retrieve secrets
# id: retrieve-secrets
# uses: bitwarden/gh-actions/get-keyvault-secrets@main
# if: failure()
# with:
# keyvault: "bitwarden-ci"
# secrets: "devops-alerts-slack-webhook-url"
# - name: Notify Slack on failure
# uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0
# if: failure()
# env:
# SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
# with:
# status: ${{ job.status }}

View File

@@ -31,7 +31,6 @@ jobs:
- name: Print lines of code
run: cloc --vcs git --exclude-dir Resources,store,test,Properties --include-lang C#,XAML
setup:
name: Setup
runs-on: ubuntu-22.04
@@ -59,7 +58,6 @@ jobs:
echo "hotfix_branch_exists=0" >> $GITHUB_OUTPUT
fi
android:
name: Android
runs-on: windows-2022
@@ -69,8 +67,7 @@ jobs:
matrix:
variant: ["prod", "qa"]
env:
android_folder_path: src\App\Platforms\Android
android_folder_path_bash: src/App/Platforms/Android
android_folder_path: src/App/Platforms/Android
steps:
- name: Setup NuGet
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
@@ -85,7 +82,7 @@ jobs:
- name: Set up MSBuild
uses: microsoft/setup-msbuild@ede762b26a2de8d110bb5a3db4d7e0e080c0e917 # v1.3.3
# This step might be obsolete at some point as .NET MAUI workloads
# This step might be obsolete at some point as .NET MAUI workloads
# are starting to come pre-installed on the GH Actions build agents.
- name: Install MAUI Workload
run: dotnet workload install maui --ignore-failed-sources
@@ -96,8 +93,7 @@ jobs:
- name: Install Microsoft OpenJDK 11
run: |
choco install microsoft-openjdk11 --no-progress
Write-Output "JAVA_HOME=$(Get-ChildItem -Path 'C:\Program Files\Microsoft\jdk*' | `
Select -First 1 -ExpandProperty FullName)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Output "JAVA_HOME=$(Get-ChildItem -Path 'C:\Program Files\Microsoft\jdk*' | Select -First 1 -ExpandProperty FullName)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Output "Java Home: $env:JAVA_HOME"
- name: Print environment
@@ -113,43 +109,39 @@ jobs:
with:
fetch-depth: 0
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Download secrets
- name: Decrypt secrets
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
run: |
mkdir -p $HOME/secrets
mkdir -p ~/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name app_play-keystore.jks --file ./${{ env.android_folder_path_bash }}/app_play-keystore.jks --output none
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name app_upload-keystore.jks --file ./${{ env.android_folder_path_bash }}/app_upload-keystore.jks --output none
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name play_creds.json --file $HOME/secrets/play_creds.json --output none
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./${{ env.main_app_folder_path }}/app_play-keystore.jks ./.github/secrets/app_play-keystore.jks.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./${{ env.main_app_folder_path }}/app_upload-keystore.jks ./.github/secrets/app_upload-keystore.jks.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: Download secrets - Google Services
- name: Decrypt secrets - Google Services
if: ${{ matrix.variant == 'prod' }}
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
run: |
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name google-services.json --file ./${{ env.android_folder_path_bash }}/google-services.json --output none
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./${{ env.android_folder_path }}/google-services.json ./.github/secrets/google-services.json.gpg
shell: bash
- name: Increment version
run: |
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
echo "##### Setting Android Version Code to $BUILD_NUMBER" | tee -a $GITHUB_STEP_SUMMARY
echo "########################################"
echo "##### Setting Version Code $BUILD_NUMBER"
echo "########################################"
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
./${{ env.android_folder_path_bash }}/AndroidManifest.xml
./${{ env.android_folder_path }}/AndroidManifest.xml
shell: bash
- name: Restore packages
@@ -158,70 +150,78 @@ jobs:
- name: Restore tools
run: dotnet tool restore
# - name: Run Core tests
# run: |
# dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx" `
# /p:CustomConstants=UT
# - name: Verify Format
# run: dotnet tool run dotnet-format --check
# - name: Report test results
# uses: dorny/test-reporter@eaa763f6ffc21c7a37837f56cd5f9737f27fc6c8 # v1.8.0
# if: always()
# with:
# name: Test Results
# path: "**/test-results.trx"
# reporter: dotnet-trx
# fail-on-error: true
- name: Run Core tests
run: dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx" /p:CustomConstants=UT
- name: Report test results
uses: dorny/test-reporter@eaa763f6ffc21c7a37837f56cd5f9737f27fc6c8 # v1.8.0
if: always()
with:
name: Test Results
path: "**/test-results.trx"
reporter: dotnet-trx
fail-on-error: true
- name: Build Play Store publisher
if: ${{ matrix.variant == 'prod' }}
run: dotnet build .\store\google\Publisher\Publisher.csproj /p:Configuration=Release
run: dotnet build ./store/google/Publisher/Publisher.csproj -p:Configuration=Release
- name: Setup Android build (${{ matrix.variant }})
run: dotnet cake build.cake --target Android --variant ${{ matrix.variant }}
- name: Build & Sign Android
- name: Build Android
run: |
$configuration = "Release";
$projToBuild = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
Write-Output "########################################"
Write-Output "##### Build $configuration Configuration"
Write-Output "########################################"
dotnet build $projToBuild -c $configuration -f ${{ env.target-net-version }}-android
- name: Sign Android Build
env:
PLAY_KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD }}
UPLOAD_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }}
run: |
$projToBuild = "$($env:GITHUB_WORKSPACE)/${{ env.main_app_project_path }}";
$projToBuild = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
$packageName = "com.x8bit.bitwarden";
if ("${{ matrix.variant }}" -ne "prod")
{
$packageName = "com.x8bit.bitwarden.${{ matrix.variant }}";
}
Write-Output "########################################"
Write-Output "##### Sign Google Play Bundle Release Configuration"
Write-Output "########################################"
$signingUploadKeyStore = "$($env:GITHUB_WORKSPACE)\${{ env.android_folder_path }}\app_upload-keystore.jks"
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android `
/p:AndroidPackageFormats=aab `
/p:AndroidKeyStore=true `
/p:AndroidSigningKeyStore=$signingUploadKeyStore `
/p:AndroidSigningKeyAlias=upload `
/p:AndroidSigningKeyPass="$($env:UPLOAD_KEYSTORE_PASSWORD)" `
/p:AndroidSigningStorePass="$($env:UPLOAD_KEYSTORE_PASSWORD)" --no-restore
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android /p:AndroidPackageFormats=aab /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=$("app_upload-keystore.jks") /p:AndroidSigningKeyAlias=upload /p:AndroidSigningKeyPass="$($env:UPLOAD_KEYSTORE_PASSWORD)" /p:AndroidSigningStorePass="$($env:UPLOAD_KEYSTORE_PASSWORD)" --no-restore
Write-Output "########################################"
Write-Output "##### Copy Google Play Bundle to project root"
Write-Output "########################################"
$signedAabPath = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_folder_path }}\bin\Release\${{ env.target-net-version }}-android\publish\$($packageName)-Signed.aab";
$signedAabDestPath = "$($env:GITHUB_WORKSPACE)\$($packageName).aab";
$signedAabPath = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_folder_path }}/bin/Release/${{ env.target-net-version }}-android/publish/$($packageName)-Signed.aab");
$signedAabDestPath = $($env:GITHUB_WORKSPACE + "/$($packageName).aab");
Copy-Item $signedAabPath $signedAabDestPath
Write-Output "########################################"
Write-Output "##### Sign APK Release Configuration"
Write-Output "########################################"
$signingPlayKeyStore = "$($env:GITHUB_WORKSPACE)\${{ env.android_folder_path }}\app_play-keystore.jks"
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android `
/p:AndroidKeyStore=true `
/p:AndroidSigningKeyStore=$signingPlayKeyStore `
/p:AndroidSigningKeyAlias=bitwarden `
/p:AndroidSigningKeyPass="$($env:PLAY_KEYSTORE_PASSWORD)" `
/p:AndroidSigningStorePass="$($env:PLAY_KEYSTORE_PASSWORD)" --no-restore
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=$("app_play-keystore.jks") /p:AndroidSigningKeyAlias=bitwarden /p:AndroidSigningKeyPass="$($env:PLAY_KEYSTORE_PASSWORD)" /p:AndroidSigningStorePass="$($env:PLAY_KEYSTORE_PASSWORD)" --no-restore
Write-Output "########################################"
Write-Output "##### Copy Release APK to project root"
Write-Output "########################################"
$signedApkPath = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_folder_path }}/bin/Release/${{ env.target-net-version }}-android/publish/$($packageName)-Signed.apk");
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/$($packageName).apk");
$signedApkPath = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_folder_path }}\bin\Release\${{ env.target-net-version }}-android\publish\$($packageName)-Signed.apk";
$signedApkDestPath = "$($env:GITHUB_WORKSPACE)\$($packageName).apk";
Copy-Item $signedApkPath $signedApkDestPath
- name: Upload Prod .aab artifact
@@ -283,20 +283,20 @@ jobs:
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|| github.ref == 'refs/heads/hotfix-rc' ) }}
run: |
$publisherPath = "$($env:GITHUB_WORKSPACE)\store\google\Publisher\bin\Release\net8.0\Publisher.dll"
$credsPath = "$($HOME)\secrets\play_creds.json"
$aabPath = "$($env:GITHUB_WORKSPACE)\com.x8bit.bitwarden.aab"
$track = "internal"
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/net7.0/Publisher.dll"
CREDS_PATH="$HOME/secrets/play_creds.json"
AAB_PATH="$GITHUB_WORKSPACE/com.x8bit.bitwarden.aab"
TRACK="internal"
dotnet $publisherPath $credsPath $aabPath $track
dotnet $PUBLISHER_PATH $CREDS_PATH $AAB_PATH $TRACK
shell: bash
f-droid:
name: F-Droid Build
runs-on: windows-2022
env:
android_folder_path: src\App\Platforms\Android
android_folder_path_bash: src/App/Platforms/Android
android_folder_path: src/App/Platforms/Android
android_manifest_path: src/App/Platforms/Android/AndroidManifest.xml
steps:
- name: Setup NuGet
@@ -312,7 +312,7 @@ jobs:
- name: Set up MSBuild
uses: microsoft/setup-msbuild@ede762b26a2de8d110bb5a3db4d7e0e080c0e917 # v1.3.3
# This step might be obsolete at some point as .NET MAUI workloads
# This step might be obsolete at some point as .NET MAUI workloads
# are starting to come pre-installed on the GH Actions build agents.
- name: Install MAUI Workload
run: dotnet workload install maui --ignore-failed-sources
@@ -337,25 +337,23 @@ jobs:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Download secrets
- name: Decrypt secrets
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
FILE: app_fdroid-keystore.jks
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
run: |
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file ${{ env.android_folder_path_bash }}/$FILE --output none
mkdir -p ~/secrets
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./${{ env.main_app_folder_path }}/app_fdroid-keystore.jks ./.github/secrets/app_fdroid-keystore.jks.gpg
shell: bash
- name: Increment version
run: |
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
echo "##### Setting F-Droid Version Code to $BUILD_NUMBER" | tee -a $GITHUB_STEP_SUMMARY
echo "########################################"
echo "##### Setting Version Code $BUILD_NUMBER"
echo "########################################"
sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
./${{ env.android_manifest_path }}
@@ -363,16 +361,21 @@ jobs:
- name: Clean for F-Droid
run: |
$directoryBuildProps = $($env:GITHUB_WORKSPACE + "/Directory.Build.props");
$appPath = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
$corePath = $($env:GITHUB_WORKSPACE + "/src/Core/Core.csproj");
$androidManifest = $($env:GITHUB_WORKSPACE + "/${{ env.android_manifest_path }}");
Write-Output "##### Back up project files"
Write-Output "########################################"
Write-Output "##### Backup project files"
Write-Output "########################################"
Copy-Item $androidManifest $($androidManifest + ".original");
Copy-Item $directoryBuildProps $($directoryBuildProps + ".original");
Copy-Item $appPath $($appPath + ".original");
Write-Output "########################################"
Write-Output "##### Cleanup Android Manifest"
Write-Output "########################################"
$xml=New-Object XML;
$xml.Load($androidManifest);
@@ -382,34 +385,39 @@ jobs:
$xml.Save($androidManifest);
Write-Output "##### Enabling FDROID constant"
(Get-Content $directoryBuildProps).Replace('<!-- <CustomConstants>FDROID</CustomConstants> -->', '<CustomConstants>FDROID</CustomConstants>') | Set-Content $directoryBuildProps
- name: Restore packages
run: dotnet restore
- name: Build & Sign F-Droid
- name: Build for F-Droid
run: |
$configuration = "Release";
$projToBuild = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
Write-Output "########################################"
Write-Output "##### Build $configuration FDROID
Write-Output "########################################"
dotnet build $projToBuild -c $configuration -f ${{ env.target-net-version }}-android /p:CustomConstants="FDROID"
- name: Sign for F-Droid
env:
FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
run: |
$projToBuild = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_project_path }}";
$projToBuild = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}");
$packageName = "com.x8bit.bitwarden";
Write-Output "########################################"
Write-Output "##### Sign FDroid"
Write-Output "########################################"
$signingFdroidKeyStore = "$($env:GITHUB_WORKSPACE)\${{ env.android_folder_path }}\app_fdroid-keystore.jks"
dotnet build $projToBuild -c Release -f ${{ env.target-net-version }}-android `
/p:AndroidKeyStore=true `
/p:AndroidSigningKeyStore=$signingFdroidKeyStore `
/p:AndroidSigningKeyAlias=bitwarden `
/p:AndroidSigningKeyPass="$($env:FDROID_KEYSTORE_PASSWORD)" `
/p:AndroidSigningStorePass="$($env:FDROID_KEYSTORE_PASSWORD)" ` --no-restore
dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=$("app_fdroid-keystore.jks") /p:AndroidSigningKeyAlias=bitwarden /p:AndroidSigningKeyPass="$($env:FDROID_KEYSTORE_PASSWORD)" /p:AndroidSigningStorePass="$($env:FDROID_KEYSTORE_PASSWORD)" /p:CustomConstants="FDROID" --no-restore
Write-Output "########################################"
Write-Output "##### Copy FDroid apk to project root"
Write-Output "########################################"
$signedApkPath = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_folder_path }}\bin\Release\${{ env.target-net-version }}-android\$($packageName)-Signed.apk";
$signedApkDestPath = "$($env:GITHUB_WORKSPACE)\com.x8bit.bitwarden-fdroid.apk";
$signedApkPath = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_folder_path }}/bin/Release/${{ env.target-net-version }}-android/publish/$($packageName)-Signed.apk");
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden-fdroid.apk");
Copy-Item $signedApkPath $signedApkDestPath
@@ -432,7 +440,6 @@ jobs:
path: ./bw-fdroid-apk-sha256.txt
if-no-files-found: error
ios:
name: Apple iOS
runs-on: macos-13
@@ -451,13 +458,13 @@ jobs:
uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
with:
nuget-version: 6.4.0
- name: Set up .NET
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
with:
dotnet-version: '8.0.x'
# This step might be obsolete at some point as .NET MAUI workloads
# This step might be obsolete at some point as .NET MAUI workloads
# are starting to come pre-installed on the GH Actions build agents.
- name: Install MAUI Workload
run: dotnet workload install maui --ignore-failed-sources
@@ -486,71 +493,73 @@ jobs:
keyvault: "bitwarden-ci"
secrets: "appcenter-ios-token"
- name: Download Provisioning Profiles secrets
- name: Decrypt secrets
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: profiles
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
run: |
mkdir -p $HOME/secrets
profiles=(
"dist_autofill.mobileprovision"
"dist_bitwarden.mobileprovision"
"dist_extension.mobileprovision"
"dist_share_extension.mobileprovision"
"dist_bitwarden_watch_app.mobileprovision"
"dist_bitwarden_watch_app_extension.mobileprovision"
)
mkdir -p ~/secrets
for FILE in "${profiles[@]}"
do
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file $HOME/secrets/$FILE --output none
done
- name: Download Google Services secret
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
FILE: GoogleService-Info.plist
run: |
mkdir -p $HOME/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file src/watchOS/bitwarden/$FILE --output none
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
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_share_extension.mobileprovision \
./.github/secrets/dist_share_extension.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_watch_app.mobileprovision \
./.github/secrets/dist_watch_app.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output $HOME/secrets/dist_watch_app_extension.mobileprovision \
./.github/secrets/dist_watch_app_extension.mobileprovision.gpg
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
--output ./src/watchOS/bitwarden/GoogleService-Info.plist ./.github/secrets/GoogleService-Info.plist.gpg
- name: Increment version
run: |
BUILD_NUMBER=$((100 + $GITHUB_RUN_NUMBER))
echo "##### Setting iOS CFBundleVersion to $BUILD_NUMBER" | tee -a $GITHUB_STEP_SUMMARY
echo "########################################"
echo "##### Setting CFBundleVersion $BUILD_NUMBER"
echo "########################################"
echo "### CFBundleVersion $BUILD_NUMBER" >> $GITHUB_STEP_SUMMARY
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./${{ env.ios_folder_path }}/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.ShareExtension/Info.plist
cd src/watchOS/bitwarden
agvtool new-version -all $BUILD_NUMBER
agvtool new-version -all $BUILD_NUMBER
- name: Update Entitlements
run: |
echo "########################################"
echo "##### Updating Entitlements"
echo "########################################"
perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>production<\/string>/' ./${{ env.ios_folder_path }}/Entitlements.plist
- name: Get certificates
run: |
mkdir -p $HOME/certificates
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/ios-distribution |
jq -r .value | base64 -d > $HOME/certificates/ios-distribution.p12
- name: Set up Keychain
env:
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
MOBILE_KEY_PASSWORD: ${{ secrets.IOS_KEY_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 $HOME/certificates/ios-distribution.p12 -k build.keychain -P "" -T /usr/bin/codesign \
-T /usr/bin/security
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
- name: Set up provisioning profiles
@@ -559,8 +568,8 @@ jobs:
BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_bitwarden.mobileprovision
EXTENSION_PROFILE_PATH=$HOME/secrets/dist_extension.mobileprovision
SHARE_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_share_extension.mobileprovision
WATCH_APP_PROFILE_PATH=$HOME/secrets/dist_bitwarden_watch_app.mobileprovision
WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_bitwarden_watch_app_extension.mobileprovision
WATCH_APP_PROFILE_PATH=$HOME/secrets/dist_watch_app.mobileprovision
WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_watch_app_extension.mobileprovision
PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles
mkdir -p "$PROFILES_DIR_PATH"
@@ -588,44 +597,68 @@ jobs:
- name: Bulid WatchApp
run: |
echo "########################################"
echo "##### Build WatchApp with Release Configuration"
echo "########################################"
xcodebuild archive -workspace ./src/watchOS/bitwarden/bitwarden.xcodeproj/project.xcworkspace -configuration Release -scheme bitwarden\ WatchKit\ App -archivePath ./src/watchOS/bitwarden
echo "########################################"
echo "##### Done"
echo "########################################"
- name: Archive Build for App Store
run: |
echo "##### Archive for Release ios-arm64"
Write-Output "########################################"
Write-Output "##### Archive for Release ios-arm64
Write-Output "########################################"
dotnet publish ${{ env.main_app_project_path }} -c Release -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=ios-arm64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
Write-Output "########################################"
Write-Output "##### Done"
Write-Output "########################################"
shell: pwsh
- name: Archive Build for Mobile Automation
run: |
echo "##### Archive Debug for iossimulator-x64"
Write-Output "########################################"
Write-Output "##### Archive Debug for iossimulator-x64
Write-Output "########################################"
dotnet build ${{ env.main_app_project_path }} -c Debug -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=iossimulator-x64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
ls $HOME/Library/Developer/Xcode/Archives
Write-Output "########################################"
Write-Output "##### Done"
Write-Output "########################################"
ls ~/Library/Developer/Xcode/Archives
shell: pwsh
- name: Export .ipa for App Store
env:
EXPORT_OPTIONS_PATH: ./.github/resources/export-options-app-store.plist
EXPORT_PATH: ./bitwarden-export
run: |
EXPORT_OPTIONS_PATH="./.github/resources/export-options-app-store.plist"
ARCHIVE_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive"
EXPORT_PATH="./bitwarden-export"
xcodebuild -exportArchive -archivePath $ARCHIVE_PATH -exportPath $EXPORT_PATH \
-exportOptionsPlist $EXPORT_OPTIONS_PATH
- name: Export .app for Automation CI
env:
ARCHIVE_PATH: ./${{ env.main_app_folder_path }}/bin/Debug/${{ env.target-net-version }}-ios/iossimulator-x64
EXPORT_PATH: ./bitwarden-export
run: |
ARCHIVE_PATH="./${{ env.main_app_folder_path }}/bin/Debug/${{ env.target-net-version }}-ios/iossimulator-x64"
EXPORT_PATH="./bitwarden-export"
zip -r -q ${{ env.app_ci_output_filename }}.app.zip $ARCHIVE_PATH
mv ${{ env.app_ci_output_filename }}.app.zip $EXPORT_PATH
- name: Copy all dSYMs files to upload
env:
EXPORT_PATH: ./bitwarden-export
WATCH_ARCHIVE_DSYMS_PATH: ./src/watchOS/bitwarden.xcarchive/dSYMs/
WATCH_DSYMS_EXPORT_PATH: ./bitwarden-export/Watch_dSYMs
run: |
ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
EXPORT_PATH="./bitwarden-export"
WATCH_ARCHIVE_DSYMS_PATH="./src/watchOS/bitwarden.xcarchive/dSYMs/"
WATCH_DSYMS_EXPORT_PATH="$EXPORT_PATH/Watch_dSYMs"
cp -r -v $ARCHIVE_DSYMS_PATH $EXPORT_PATH
mkdir $WATCH_DSYMS_EXPORT_PATH
cp -r -v $WATCH_ARCHIVE_DSYMS_PATH $WATCH_DSYMS_EXPORT_PATH
@@ -674,7 +707,10 @@ jobs:
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|| github.ref == 'refs/heads/hotfix-rc'
run: |
echo "########################################"
echo "##### Uploading Watch dSYMs to Firebase"
echo "########################################"
find "$HOME/Library/Developer/XCode/DerivedData" -name "upload-symbols" -exec chmod +x {} \; -exec {} -gsp "./src/watchOS/bitwarden/GoogleService-Info.plist" -p ios "./bitwarden-export/Watch_dSYMs" \;
- name: Validate app in App Store
@@ -690,6 +726,7 @@ jobs:
run: |
xcrun altool --validate-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
--username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
shell: bash
- name: Deploy to App Store
if: |
@@ -739,7 +776,7 @@ jobs:
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
with:
config: crowdin.yml
crowdin_branch_name: main
crowdin_branch_name: main
upload_sources: true
upload_translations: false
@@ -757,11 +794,27 @@ jobs:
steps:
- name: Check if any job failed
if: |
(github.ref == 'refs/heads/main'
|| github.ref == 'refs/heads/rc'
|| github.ref == 'refs/heads/hotfix-rc')
&& contains(needs.*.result, 'failure')
run: exit 1
(github.ref == 'refs/heads/main')
|| (github.ref == 'refs/heads/rc')
|| (github.ref == 'refs/heads/hotfix-rc')
env:
CLOC_STATUS: ${{ needs.cloc.result }}
ANDROID_STATUS: ${{ needs.android.result }}
F_DROID_STATUS: ${{ needs.f-droid.result }}
IOS_STATUS: ${{ needs.ios.result }}
CROWDIN_PUSH_STATUS: ${{ needs.crowdin-push.result }}
run: |
if [ "$CLOC_STATUS" = "failure" ]; then
exit 1
elif [ "$ANDROID_STATUS" = "failure" ]; then
exit 1
elif [ "$F_DROID_STATUS" = "failure" ]; then
exit 1
elif [ "$IOS_STATUS" = "failure" ]; then
exit 1
elif [ "$CROWDIN_PUSH_STATUS" = "failure" ]; then
exit 1
fi
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0

View File

@@ -1,53 +0,0 @@
---
name: Cleanup RC Branch
on:
push:
tags:
- v**
jobs:
delete-rc:
name: Delete RC Branch
runs-on: ubuntu-22.04
steps:
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve bot secrets
id: retrieve-bot-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: bitwarden-ci
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
- name: Checkout main
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: main
token: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
- name: Check if a RC branch exists
id: branch-check
run: |
hotfix_rc_branch_check=$(git ls-remote --heads origin hotfix-rc | wc -l)
rc_branch_check=$(git ls-remote --heads origin rc | wc -l)
if [[ "${hotfix_rc_branch_check}" -gt 0 ]]; then
echo "hotfix-rc branch exists." | tee -a $GITHUB_STEP_SUMMARY
echo "name=hotfix-rc" >> $GITHUB_OUTPUT
elif [[ "${rc_branch_check}" -gt 0 ]]; then
echo "rc branch exists." | tee -a $GITHUB_STEP_SUMMARY
echo "name=rc" >> $GITHUB_OUTPUT
fi
- name: Delete RC branch
env:
BRANCH_NAME: ${{ steps.branch-check.outputs.name }}
run: |
if ! [[ -z "$BRANCH_NAME" ]]; then
git push --quiet origin --delete $BRANCH_NAME
echo "Deleted $BRANCH_NAME branch." | tee -a $GITHUB_STEP_SUMMARY
fi

View File

@@ -28,7 +28,7 @@ jobs:
branch-name: ${{ steps.branch.outputs.branch-name }}
steps:
- name: Branch check
if: inputs.release_type != 'Dry Run'
if: github.event.inputs.release_type != 'Dry Run'
run: |
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
echo "==================================="
@@ -44,9 +44,9 @@ jobs:
id: version
uses: bitwarden/gh-actions/release-version-check@main
with:
release-type: ${{ inputs.release_type }}
release-type: ${{ github.event.inputs.release_type }}
project-type: xamarin
file: src/App/Platforms/Android/AndroidManifest.xml
file: src/Android/Properties/AndroidManifest.xml
- name: Get branch name
id: branch
@@ -55,7 +55,7 @@ jobs:
echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT
- name: Create GitHub deployment
if: ${{ inputs.release_type != 'Dry Run' }}
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
id: deployment
with:
@@ -67,16 +67,16 @@ jobs:
- name: Download all artifacts
if: ${{ inputs.release_type != 'Dry Run' }}
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: dawidd6/action-download-artifact@72aaadce3bc708349fc665eee3785cbb1b6e51d0 # v3.1.1
with:
workflow: build.yml
workflow_conclusion: success
branch: ${{ steps.branch.outputs.branch-name }}
- name: Dry Run - Download all artifacts
if: ${{ inputs.release_type == 'Dry Run' }}
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: dawidd6/action-download-artifact@72aaadce3bc708349fc665eee3785cbb1b6e51d0 # v3.1.1
with:
workflow: build.yml
workflow_conclusion: success
@@ -86,7 +86,7 @@ jobs:
run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS
- name: Create release
if: ${{ inputs.release_type != 'Dry Run' }}
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0
with:
artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab,
@@ -103,16 +103,16 @@ jobs:
draft: true
- name: Update deployment status to Success
if: ${{ inputs.release_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1
with:
token: '${{ secrets.GITHUB_TOKEN }}'
state: 'success'
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
- name: Update deployment status to Failure
if: ${{ inputs.release_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1
with:
token: '${{ secrets.GITHUB_TOKEN }}'
state: 'failure'
@@ -129,8 +129,8 @@ jobs:
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- name: Download F-Droid .apk artifact
if: ${{ inputs.release_type != 'Dry Run' }}
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: dawidd6/action-download-artifact@72aaadce3bc708349fc665eee3785cbb1b6e51d0 # v3.1.1
with:
workflow: build.yml
workflow_conclusion: success
@@ -138,8 +138,8 @@ jobs:
name: com.x8bit.bitwarden-fdroid.apk
- name: Dry Run - Download F-Droid .apk artifact
if: ${{ inputs.release_type == 'Dry Run' }}
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: dawidd6/action-download-artifact@72aaadce3bc708349fc665eee3785cbb1b6e51d0 # v3.1.1
with:
workflow: build.yml
workflow_conclusion: success
@@ -176,19 +176,13 @@ jobs:
- name: Install Node dependencies
run: npm install
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Download secrets
- name: Decrypt secrets
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
run: |
mkdir -p $HOME/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name store_fdroid-keystore.jks --file ./store/fdroid/keystore.jks --output none
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:
@@ -217,5 +211,5 @@ jobs:
cd $GITHUB_WORKSPACE
- name: Deploy to gh-pages
if: ${{ inputs.release_type != 'Dry Run' }}
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
run: npm run deploy

View File

@@ -11,6 +11,24 @@ jobs:
name: Bump Mobile Version
runs-on: ubuntu-22.04
steps:
- name: Checkout Branch
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Calculate bumped version
id: version
env:
RELEASE_TAG: ${{ github.ref }}
run: |
CURR_MAJOR=$(echo $RELEASE_TAG | sed -r 's/refs\/tags\/v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\1/')
CURR_PATCH=$(echo $RELEASE_TAG | sed -r 's/refs\/tags\/v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\2/')
echo "Current Major: $CURR_MAJOR"
echo "Current Patch: $CURR_PATCH"
NEW_PATCH=$((CURR_PATCH+1))
NEW_VER=$CURR_MAJOR.$NEW_PATCH
echo "New Version: $NEW_VER"
echo "new_version=$NEW_VER" >> $GITHUB_OUTPUT
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
@@ -23,9 +41,9 @@ jobs:
keyvault: bitwarden-ci
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
- name: Trigger Version Bump workflow
- name: "Bump version to ${{ steps.version.outputs.new_version }}"
env:
GH_TOKEN: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
run: |
echo '{"cut_rc_branch": "false"}' | \
gh workflow run version-bump.yml --json --repo bitwarden/mobile
echo '{"cut_rc_branch": "false", "version_number": "${{ steps.version.outputs.new_version }}"}' | \
gh workflow run version-bump.yml --json --repo bitwarden/mobile

View File

@@ -1,13 +1,13 @@
---
name: Version Bump
run-name: Version Bump - v${{ inputs.version_number }}
on:
workflow_dispatch:
inputs:
version_number_override:
description: "New version override (leave blank for automatic calculation, example: '2024.1.0')"
required: false
type: string
version_number:
description: "New version (example: '2024.1.0')"
required: true
cut_rc_branch:
description: "Cut RC branch?"
default: true
@@ -15,16 +15,22 @@ on:
jobs:
bump_version:
name: Bump Version
name: "Bump Version to v${{ inputs.version_number }}"
runs-on: ubuntu-22.04
outputs:
version: ${{ steps.set-final-version-output.outputs.version }}
steps:
- name: Validate version input
if: ${{ inputs.version_number_override != '' }}
uses: bitwarden/gh-actions/version-check@main
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
version: ${{ inputs.version_number_override }}
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "github-gpg-private-key,
github-gpg-private-key-passphrase,
github-pat-bitwarden-devops-bot-repo-scope"
- name: Checkout Branch
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
@@ -41,20 +47,6 @@ jobs:
exit 1
fi
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "github-gpg-private-key,
github-gpg-private-key-passphrase,
github-pat-bitwarden-devops-bot-repo-scope"
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
with:
@@ -63,38 +55,25 @@ jobs:
git_user_signingkey: true
git_commit_gpgsign: true
- name: Setup git
run: |
git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
git config --local user.name "bitwarden-devops-bot"
- name: Create Version Branch
id: create-branch
run: |
NAME=version_bump_${{ github.ref_name }}_$(date +"%Y-%m-%d")
NAME=version_bump_${{ github.ref_name }}_${{ inputs.version_number }}
git switch -c $NAME
echo "name=$NAME" >> $GITHUB_OUTPUT
- name: Install xmllint
run: |
sudo apt-get update
sudo apt-get install -y libxml2-utils
- name: Get current version
id: current-version
run: |
CURRENT_VERSION=$(xmllint --xpath '
string(/manifest/@*[local-name()="versionName"
and namespace-uri()="http://schemas.android.com/apk/res/android"])
' src/App/Platforms/Android/AndroidManifest.xml)
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
run: sudo apt install -y libxml2-utils
- name: Verify input version
if: ${{ inputs.version_number_override != '' }}
env:
CURRENT_VERSION: ${{ steps.current-version.outputs.version }}
NEW_VERSION: ${{ inputs.version_number_override }}
NEW_VERSION: ${{ inputs.version_number }}
run: |
CURRENT_VERSION=$(xmllint --xpath '
string(/manifest/@*[local-name()="versionName"
and namespace-uri()="http://schemas.android.com/apk/res/android"])
' src/App/Platforms/Android/AndroidManifest.xml)
# Error if version has not changed.
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
echo "Version has not changed."
@@ -110,93 +89,40 @@ jobs:
exit 1
fi
- name: Calculate next release version
if: ${{ inputs.version_number_override == '' }}
id: calculate-next-version
uses: bitwarden/gh-actions/version-next@main
with:
version: ${{ steps.current-version.outputs.version }}
- name: Bump Version - Android XML - Version Override
if: ${{ inputs.version_number_override != '' }}
id: bump-version-override
- name: Bump Version - Android XML
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ inputs.version_number }}
file_path: "src/App/Platforms/Android/AndroidManifest.xml"
version: ${{ inputs.version_number_override }}
- name: Bump Version - Android XML - Automatic Calculation
if: ${{ inputs.version_number_override == '' }}
id: bump-version-automatic
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "src/App/Platforms/Android/AndroidManifest.xml"
version: ${{ steps.calculate-next-version.outputs.version }}
- name: Bump Version - iOS.Autofill - Version Override
if: ${{ inputs.version_number_override != '' }}
- name: Bump Version - iOS.Autofill
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ inputs.version_number }}
file_path: "src/iOS.Autofill/Info.plist"
version: ${{ inputs.version_number_override }}
- name: Bump Version - iOS.Autofill - Automatic Calculation
if: ${{ inputs.version_number_override == '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "src/iOS.Autofill/Info.plist"
version: ${{ steps.calculate-next-version.outputs.version }}
- name: Bump Version - iOS.Extension - Version Override
if: ${{ inputs.version_number_override != '' }}
- name: Bump Version - iOS.Extension
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ inputs.version_number }}
file_path: "src/iOS.Extension/Info.plist"
version: ${{ inputs.version_number_override }}
- name: Bump Version - iOS.Extension - Automatic Calculation
if: ${{ inputs.version_number_override == '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "src/iOS.Extension/Info.plist"
version: ${{ steps.calculate-next-version.outputs.version }}
- name: Bump Version - iOS.ShareExtension - Version Override
if: ${{ inputs.version_number_override != '' }}
- name: Bump Version - iOS.ShareExtension
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ inputs.version_number }}
file_path: "src/iOS.ShareExtension/Info.plist"
version: ${{ inputs.version_number_override }}
- name: Bump Version - iOS.ShareExtension - Automatic Calculation
if: ${{ inputs.version_number_override == '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "src/iOS.ShareExtension/Info.plist"
version: ${{ steps.calculate-next-version.outputs.version }}
- name: Bump Version - iOS - Version Override
if: ${{ inputs.version_number_override != '' }}
- name: Bump Version - iOS
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ inputs.version_number }}
file_path: "src/App/Platforms/iOS/Info.plist"
version: ${{ inputs.version_number_override }}
- name: Bump Version - iOS - Automatic Calculation
if: ${{ inputs.version_number_override == '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "src/App/Platforms/iOS/Info.plist"
version: ${{ steps.calculate-next-version.outputs.version }}
- name: Set Job output
id: set-final-version-output
- name: Setup git
run: |
if [[ "${{ steps.bump-version-override.outcome }}" == "success" ]]; then
echo "version=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
elif [[ "${{ steps.bump-version-automatic.outcome }}" == "success" ]]; then
echo "version=${{ steps.calculate-next-version.outputs.version }}" >> $GITHUB_OUTPUT
fi
git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
git config --local user.name "bitwarden-devops-bot"
- name: Check if version changed
id: version-changed
@@ -210,7 +136,7 @@ jobs:
- name: Commit files
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
run: git commit -m "Bumped version to ${{ steps.set-final-version-output.outputs.version }}" -a
run: git commit -m "Bumped version to ${{ inputs.version_number }}" -a
- name: Push changes
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
@@ -224,7 +150,7 @@ jobs:
env:
GH_TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
PR_BRANCH: ${{ steps.create-branch.outputs.name }}
TITLE: "Bump version to ${{ steps.set-final-version-output.outputs.version }}"
TITLE: "Bump version to ${{ inputs.version_number }}"
run: |
PR_URL=$(gh pr create --title "$TITLE" \
--base "main" \
@@ -240,18 +166,16 @@ jobs:
- [X] Other
## Objective
Automated version bump to ${{ steps.set-final-version-output.outputs.version }}")
Automated version bump to ${{ inputs.version_number }}")
echo "pr_number=${PR_URL##*/}" >> $GITHUB_OUTPUT
- name: Approve PR
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.create-pr.outputs.pr_number }}
run: gh pr review $PR_NUMBER --approve
- name: Merge PR
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
env:
GH_TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
PR_NUMBER: ${{ steps.create-pr.outputs.pr_number }}
@@ -259,30 +183,28 @@ jobs:
cut_rc:
name: Cut RC branch
if: ${{ inputs.cut_rc_branch == true }}
needs: bump_version
if: ${{ inputs.cut_rc_branch == true }}
runs-on: ubuntu-22.04
steps:
- name: Checkout Branch
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: main
- name: Install xmllint
run: |
sudo apt-get update
sudo apt-get install -y libxml2-utils
run: sudo apt install -y libxml2-utils
- name: Verify version has been updated
env:
NEW_VERSION: ${{ needs.bump_version.outputs.version }}
NEW_VERSION: ${{ inputs.version_number }}
run: |
# Wait for version to change.
while : ; do
echo "Waiting for version to be updated..."
git pull --force
CURRENT_VERSION=$(xmllint --xpath '
string(/manifest/@*[local-name()="versionName"
string(/manifest/@*[local-name()="versionName"
and namespace-uri()="http://schemas.android.com/apk/res/android"])
' src/App/Platforms/Android/AndroidManifest.xml)

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<MauiVersion>8.0.7</MauiVersion>
<MauiVersion>8.0.4-nightly.*</MauiVersion>
<ReleaseCodesignProvision>Automatic:AppStore</ReleaseCodesignProvision>
<ReleaseCodesignKey>iPhone Distribution</ReleaseCodesignKey>
<IncludeBitwardeniOSExtensions>True</IncludeBitwardeniOSExtensions>
@@ -9,8 +9,5 @@
<!-- Uncomment this when Unit Testing-->
<!-- <CustomConstants>UT</CustomConstants> -->
<!-- Uncomment this when building FDROID-->
<!-- <CustomConstants>FDROID</CustomConstants> -->
</PropertyGroup>
</Project>
</Project>

View File

@@ -43,7 +43,10 @@ int CreateBuildNumber(int previousNumber) => ++previousNumber;
Task("GetGitInfo")
.Does(()=> {
_gitVersion = GitVersion(new GitVersionSettings());
_gitVersion = GitVersion(new GitVersionSettings()
{
Verbosity = GitVersionVerbosity.Diagnostic
});
if(debugScript)
{

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2024.3.3" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2024.2.2" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
@@ -43,9 +43,6 @@
<!-- Support for Xamarin.Essentials.Browser.OpenAsync (for Android > 11) -->
<!-- Related docs: https://learn.microsoft.com/en-us/xamarin/essentials/open-browser?tabs=android -->
<queries>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />

View File

@@ -347,7 +347,7 @@ namespace Bit.Droid.Autofill
// InlinePresentation requires nonNull pending intent (even though we only utilize one for the
// "my vault" presentation) so we're including an empty one here
pendingIntent = PendingIntent.GetService(context, 0, new Intent(),
AndroidHelpers.AddPendingIntentMutabilityFlag(PendingIntentFlags.OneShot | PendingIntentFlags.UpdateCurrent, false));
AndroidHelpers.AddPendingIntentMutabilityFlag(PendingIntentFlags.OneShot | PendingIntentFlags.UpdateCurrent, true));
}
var slice = CreateInlinePresentationSlice(
inlinePresentationSpec,

View File

@@ -72,28 +72,17 @@ namespace Bit.Droid.Services
public bool LaunchApp(string appName)
{
try
{
if ((int)Build.VERSION.SdkInt < 33)
{
// API 33 required to avoid using wildcard app visibility or dangerous permissions
// https://developer.android.com/reference/android/content/pm/PackageManager#getLaunchIntentSenderForPackage(java.lang.String)
return false;
}
var activity = Microsoft.Maui.ApplicationModel.Platform.CurrentActivity;
appName = appName.Replace("androidapp://", string.Empty);
var launchIntentSender = activity?.PackageManager?.GetLaunchIntentSenderForPackage(appName);
launchIntentSender?.SendIntent(activity, Result.Ok, null, null, null);
return launchIntentSender != null;
}
catch (IntentSender.SendIntentException)
{
return false;
}
catch (Android.Util.AndroidException)
if ((int)Build.VERSION.SdkInt < 33)
{
// API 33 required to avoid using wildcard app visibility or dangerous permissions
// https://developer.android.com/reference/android/content/pm/PackageManager#getLaunchIntentSenderForPackage(java.lang.String)
return false;
}
var activity = Microsoft.Maui.ApplicationModel.Platform.CurrentActivity;
appName = appName.Replace("androidapp://", string.Empty);
var launchIntentSender = activity?.PackageManager?.GetLaunchIntentSenderForPackage(appName);
launchIntentSender?.SendIntent(activity, Result.Ok, null, null, null);
return launchIntentSender != null;
}
public async Task ShowLoadingAsync(string text)

View File

@@ -8,7 +8,6 @@ using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Bit.Droid.Accessibility;
using Java.Lang;
using Bit.App.Droid.Utilities;
namespace Bit.Droid.Tile
{
@@ -77,7 +76,7 @@ namespace Bit.Droid.Tile
var intent = new Intent(this, typeof(AccessibilityActivity));
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
intent.PutExtra("autofillTileClicked", true);
this.StartActivityAndCollapseWithIntent(intent, isMutable: true);
StartActivityAndCollapse(intent);
}
private void ShowConfigErrorDialog()

View File

@@ -1,8 +1,15 @@
using Android.App;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Service.QuickSettings;
using Bit.App.Droid.Utilities;
using Android.Views;
using Android.Widget;
using Java.Lang;
namespace Bit.Droid.Tile
@@ -55,7 +62,7 @@ namespace Bit.Droid.Tile
var intent = new Intent(this, typeof(MainActivity));
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
intent.PutExtra("generatorTile", true);
this.StartActivityAndCollapseWithIntent(intent, isMutable: false);
StartActivityAndCollapse(intent);
}
}
}

View File

@@ -1,8 +1,15 @@
using Android.App;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Service.QuickSettings;
using Bit.App.Droid.Utilities;
using Android.Views;
using Android.Widget;
using Java.Lang;
namespace Bit.Droid.Tile
@@ -56,7 +63,7 @@ namespace Bit.Droid.Tile
var intent = new Intent(this, typeof(MainActivity));
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.SingleTop | ActivityFlags.ClearTop);
intent.PutExtra("myVaultTile", true);
this.StartActivityAndCollapseWithIntent(intent, isMutable: false);
StartActivityAndCollapse(intent);
}
}
}

View File

@@ -2,7 +2,6 @@
using Android.Content;
using Android.OS;
using Android.Provider;
using Android.Service.QuickSettings;
using Bit.App.Utilities;
namespace Bit.App.Droid.Utilities
@@ -65,26 +64,5 @@ namespace Bit.App.Droid.Utilities
return pendingIntentFlags;
}
public static void StartActivityAndCollapseWithIntent(this TileService service, Intent intent, bool isMutable)
{
//For Android 14+ We need to use PendingIntent instead of Intent directly. Older versions still need to use Intent.
if (Build.VERSION.SdkInt < BuildVersionCodes.UpsideDownCake)
{
service.StartActivityAndCollapse(intent);
return;
}
var pendingIntent = PendingIntent.GetActivity(
service.ApplicationContext,
0,
intent,
AddPendingIntentMutabilityFlag(PendingIntentFlags.UpdateCurrent, isMutable)
);
if (pendingIntent == null)
{
return;
}
service.StartActivityAndCollapse(pendingIntent);
}
}
}

View File

@@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden</string>
<key>CFBundleShortVersionString</key>
<string>2024.3.3</string>
<string>2024.2.2</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleIconName</key>

View File

@@ -5,8 +5,7 @@ namespace Bit.Core.Abstractions
{
public enum AwaiterPrecondition
{
EnvironmentUrlsInited,
AndroidWindowCreated
EnvironmentUrlsInited
}
public interface IConditionedAwaiterManager

View File

@@ -9,7 +9,6 @@ using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Response;
using Bit.Core.Pages;
using Bit.Core.Services;
@@ -47,6 +46,7 @@ namespace Bit.App
// This queue keeps those actions so that when the app has resumed they can still be executed.
// Links: https://github.com/dotnet/maui/issues/11501 and https://bitwarden.atlassian.net/wiki/spaces/NMME/pages/664862722/MainPage+Assignments+not+working+on+Android+on+Background+or+App+resume
private readonly Queue<Action> _onResumeActions = new Queue<Action>();
private bool _hasNavigatedToAutofillWindow;
#if ANDROID
@@ -120,8 +120,41 @@ namespace Bit.App
return new Window(new NavigationPage()); //No actual page needed. Only used for auto-filling the fields directly (externally)
}
_isResumed = true;
return new ResumeWindow(new NavigationPage(new AndroidNavigationRedirectPage(Options)));
//"Internal" Autofill and Uri/Otp/CreateSend. This is where we create the autofill specific Window
if (Options != null && (Options.FromAutofillFramework || Options.Uri != null || Options.OtpData != null || Options.CreateSend != null))
{
_isResumed = true; //Specifically for the Autofill scenario we need to manually set the _isResumed here
_hasNavigatedToAutofillWindow = true;
return new AutoFillWindow(new NavigationPage(new AndroidNavigationRedirectPage()));
}
var homePage = new HomePage(Options);
// WORKAROUND: If the user autofills with Accessibility Services enabled and goes back to the application then there is currently an issue
// where this method is called again
// thus it goes through here and the user goes to HomePage as we see here.
// So to solve this, the next flag check has been added which then turns on a flag on the home page
// that will trigger a navigation on the accounts manager when it loads; workarounding this behavior and navigating the user
// to the proper page depending on its state.
// WARNING: this doens't navigate the user to where they were but it acts as if the user had changed their account.
if(_hasNavigatedToAutofillWindow)
{
homePage.PerformNavigationOnAccountChangedOnLoad = true;
// this is needed because when coming back from AutofillWindow OnResume won't be called and we need this flag
// so that void Navigate(NavigationTarget navTarget, INavigationParams navParams) doesn't enqueue the navigation
// and it performs it directly.
_isResumed = true;
_hasNavigatedToAutofillWindow = false;
}
//If we have an existing MainAppWindow we can use that one
var mainAppWindow = Windows.OfType<MainAppWindow>().FirstOrDefault();
if (mainAppWindow != null)
{
mainAppWindow.PendingPage = new NavigationPage(homePage);
}
//Create new main window
return new MainAppWindow(new NavigationPage(homePage));
}
#else
//iOS doesn't use the CreateWindow override used in Android so we just set the Application.Current.MainPage directly
@@ -138,7 +171,7 @@ namespace Bit.App
Application.Current.MainPage = value;
}
}
}
}
#endif
public App() : this(null)
@@ -168,153 +201,132 @@ namespace Bit.App
_accountsManager.Init(() => Options, this);
_broadcasterService.Subscribe(nameof(App), BroadcastServiceMessageCallbackAsync);
Bootstrap();
}
private async void BroadcastServiceMessageCallbackAsync(Message message)
{
try
{
ArgumentNullException.ThrowIfNull(message);
if (message.Command == "showDialog")
_broadcasterService.Subscribe(nameof(App), async (message) =>
{
try
{
var details = message.Data as DialogDetails;
ArgumentNullException.ThrowIfNull(details);
ArgumentNullException.ThrowIfNull(MainPage);
var confirmed = true;
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ?
AppResources.Ok : details.ConfirmText;
await MainThread.InvokeOnMainThreadAsync(ShowDialogAction);
async Task ShowDialogAction()
if (message.Command == "showDialog")
{
if (!string.IsNullOrWhiteSpace(details.CancelText))
var details = message.Data as DialogDetails;
var confirmed = true;
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ?
AppResources.Ok : details.ConfirmText;
await MainThread.InvokeOnMainThreadAsync(async () =>
{
confirmed = await MainPage.DisplayAlert(details.Title, details.Text, confirmText,
details.CancelText);
}
else
{
await MainPage.DisplayAlert(details.Title, details.Text, confirmText);
}
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
if (!string.IsNullOrWhiteSpace(details.CancelText))
{
confirmed = await MainPage.DisplayAlert(details.Title, details.Text, confirmText,
details.CancelText);
}
else
{
await MainPage.DisplayAlert(details.Title, details.Text, confirmText);
}
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
});
}
}
#if IOS
else if (message.Command == AppHelpers.RESUMED_MESSAGE_COMMAND)
{
ResumedAsync().FireAndForget();
}
else if (message.Command == "slept")
{
await SleptAsync();
}
#endif
else if (message.Command == "migrated")
{
await Task.Delay(1000);
await _accountsManager.NavigateOnAccountChangeAsync();
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_GENERATOR_MESSAGE ||
message.Command == POP_ALL_AND_GO_TO_TAB_MYVAULT_MESSAGE ||
message.Command == POP_ALL_AND_GO_TO_TAB_SEND_MESSAGE ||
message.Command == POP_ALL_AND_GO_TO_AUTOFILL_CIPHERS_MESSAGE ||
message.Command == DeepLinkContext.NEW_OTP_MESSAGE)
{
if (message.Command == DeepLinkContext.NEW_OTP_MESSAGE)
else if (message.Command == AppHelpers.RESUMED_MESSAGE_COMMAND)
{
Options.OtpData = new OtpData((string)message.Data);
ResumedAsync().FireAndForget();
}
await MainThread.InvokeOnMainThreadAsync(ExecuteNavigationAction);
async Task ExecuteNavigationAction()
else if (message.Command == "slept")
{
if (MainPage is TabsPage tabsPage)
await SleptAsync();
}
#endif
else if (message.Command == "migrated")
{
await Task.Delay(1000);
await _accountsManager.NavigateOnAccountChangeAsync();
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_GENERATOR_MESSAGE ||
message.Command == POP_ALL_AND_GO_TO_TAB_MYVAULT_MESSAGE ||
message.Command == POP_ALL_AND_GO_TO_TAB_SEND_MESSAGE ||
message.Command == POP_ALL_AND_GO_TO_AUTOFILL_CIPHERS_MESSAGE ||
message.Command == DeepLinkContext.NEW_OTP_MESSAGE)
{
if (message.Command == DeepLinkContext.NEW_OTP_MESSAGE)
{
ArgumentNullException.ThrowIfNull(tabsPage.Navigation);
ArgumentNullException.ThrowIfNull(tabsPage.Navigation.ModalStack);
while (tabsPage.Navigation.ModalStack.Count > 0)
Options.OtpData = new OtpData((string)message.Data);
}
await MainThread.InvokeOnMainThreadAsync(async () =>
{
if (MainPage is TabsPage tabsPage)
{
await tabsPage.Navigation.PopModalAsync(false);
}
if (message.Command == POP_ALL_AND_GO_TO_AUTOFILL_CIPHERS_MESSAGE)
{
MainPage = new NavigationPage(new CipherSelectionPage(Options));
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_MYVAULT_MESSAGE)
{
Options.MyVaultTile = false;
tabsPage.ResetToVaultPage();
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_GENERATOR_MESSAGE)
{
Options.GeneratorTile = false;
tabsPage.ResetToGeneratorPage();
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_SEND_MESSAGE)
{
tabsPage.ResetToSendPage();
}
else if (message.Command == DeepLinkContext.NEW_OTP_MESSAGE)
{
tabsPage.ResetToVaultPage();
ArgumentNullException.ThrowIfNull(tabsPage.Navigation);
await tabsPage.Navigation.PushModalAsync(new NavigationPage(new CipherSelectionPage(Options)));
while (tabsPage.Navigation.ModalStack.Count > 0)
{
await tabsPage.Navigation.PopModalAsync(false);
}
if (message.Command == POP_ALL_AND_GO_TO_AUTOFILL_CIPHERS_MESSAGE)
{
MainPage = new NavigationPage(new CipherSelectionPage(Options));
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_MYVAULT_MESSAGE)
{
Options.MyVaultTile = false;
tabsPage.ResetToVaultPage();
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_GENERATOR_MESSAGE)
{
Options.GeneratorTile = false;
tabsPage.ResetToGeneratorPage();
}
else if (message.Command == POP_ALL_AND_GO_TO_TAB_SEND_MESSAGE)
{
tabsPage.ResetToSendPage();
}
else if (message.Command == DeepLinkContext.NEW_OTP_MESSAGE)
{
tabsPage.ResetToVaultPage();
await tabsPage.Navigation.PushModalAsync(new NavigationPage(new CipherSelectionPage(Options)));
}
}
});
}
else if (message.Command == "convertAccountToKeyConnector")
{
await MainThread.InvokeOnMainThreadAsync(async () =>
{
await MainPage.Navigation.PushModalAsync(
new NavigationPage(new RemoveMasterPasswordPage()));
});
}
else if (message.Command == Constants.ForceUpdatePassword)
{
await MainThread.InvokeOnMainThreadAsync(async () =>
{
await MainPage.Navigation.PushModalAsync(
new NavigationPage(new UpdateTempPasswordPage()));
});
}
else if (message.Command == Constants.ForceSetPassword)
{
await MainThread.InvokeOnMainThreadAsync(() => MainPage.Navigation.PushModalAsync(
new NavigationPage(new SetPasswordPage(orgIdentifier: (string)message.Data))));
}
else if (message.Command == "syncCompleted")
{
await _configService.GetAsync(true);
}
else if (message.Command == Constants.PasswordlessLoginRequestKey
|| message.Command == "unlocked"
|| message.Command == AccountsManagerMessageCommands.ACCOUNT_SWITCH_COMPLETED)
{
lock (_processingLoginRequestLock)
{
// lock doesn't allow for async execution
CheckPasswordlessLoginRequestsAsync().Wait();
}
}
}
else if (message.Command == "convertAccountToKeyConnector")
catch (Exception ex)
{
ArgumentNullException.ThrowIfNull(MainPage);
await MainThread.InvokeOnMainThreadAsync(NavigateToRemoveMasterPasswordPageAction);
async Task NavigateToRemoveMasterPasswordPageAction()
{
await MainPage.Navigation.PushModalAsync(
new NavigationPage(new RemoveMasterPasswordPage()));
}
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
else if (message.Command == Constants.ForceUpdatePassword)
{
ArgumentNullException.ThrowIfNull(MainPage);
await MainThread.InvokeOnMainThreadAsync(NavigateToUpdateTempPasswordPageAction);
async Task NavigateToUpdateTempPasswordPageAction()
{
await MainPage.Navigation.PushModalAsync(
new NavigationPage(new UpdateTempPasswordPage()));
}
}
else if (message.Command == Constants.ForceSetPassword)
{
ArgumentNullException.ThrowIfNull(MainPage);
await MainThread.InvokeOnMainThreadAsync(NavigateToSetPasswordPageAction);
void NavigateToSetPasswordPageAction()
{
MainPage.Navigation.PushModalAsync(
new NavigationPage(new SetPasswordPage(orgIdentifier: (string)message.Data)));
}
}
else if (message.Command == "syncCompleted")
{
await _configService.GetAsync(true);
}
else if (message.Command == Constants.PasswordlessLoginRequestKey
|| message.Command == "unlocked"
|| message.Command == AccountsManagerMessageCommands.ACCOUNT_SWITCH_COMPLETED)
{
lock (_processingLoginRequestLock)
{
// lock doesn't allow for async execution
CheckPasswordlessLoginRequestsAsync().Wait();
}
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
});
}
private async Task CheckPasswordlessLoginRequestsAsync()
@@ -329,6 +341,7 @@ namespace Bit.App
{
return;
}
var notification = await _stateService.GetPasswordlessLoginNotificationAsync();
if (notification == null)
{

View File

@@ -53,13 +53,12 @@ namespace Bit.App.Controls
if (BindingContext is CipherItemViewModel cipherItemVM)
{
cipherItemVM.IconImageSuccesfullyLoaded = true;
MainThread.BeginInvokeOnMainThread(() =>
{
Icon.IsVisible = cipherItemVM.ShowIconImage;
IconPlaceholder.IsVisible = !cipherItemVM.ShowIconImage;
});
}
MainThread.BeginInvokeOnMainThread(() =>
{
Icon.IsVisible = true;
IconPlaceholder.IsVisible = false;
});
}
public void Icon_Error(object sender, FFImageLoading.Maui.CachedImageEvents.ErrorEventArgs e)

View File

@@ -77,7 +77,6 @@
<Folder Include="Resources\Localization\" />
<Folder Include="Controls\Picker\" />
<Folder Include="Controls\Avatar\" />
<Folder Include="Utilities\WebAuthenticatorMAUI\" />
</ItemGroup>
<ItemGroup>
<MauiImage Include="Resources\Images\dotnet_bot.svg">
@@ -108,6 +107,5 @@
<ItemGroup>
<None Remove="Controls\Picker\" />
<None Remove="Controls\Avatar\" />
<None Remove="Utilities\WebAuthenticatorMAUI\" />
</ItemGroup>
</Project>

View File

@@ -1,7 +1,6 @@
using Bit.Core.Abstractions;
using Bit.Core.Resources.Localization;
using Bit.Core.Utilities;
using Microsoft.Maui.Platform;
namespace Bit.App.Pages
{
@@ -27,7 +26,7 @@ namespace Bit.App.Pages
_apiEntry.ReturnCommand = new Command(() => _identityEntry.Focus());
_identityEntry.ReturnType = ReturnType.Next;
_identityEntry.ReturnCommand = new Command(() => _iconsEntry.Focus());
_vm.SubmitSuccessTask = () => MainThread.InvokeOnMainThreadAsync(SubmitSuccessAsync);
_vm.SubmitSuccessAction = () => MainThread.BeginInvokeOnMainThread(async () => await SubmitSuccessAsync());
_vm.CloseAction = async () =>
{
await Navigation.PopModalAsync();
@@ -38,12 +37,6 @@ namespace Bit.App.Pages
{
_platformUtilsService.ShowToast("success", null, AppResources.EnvironmentSaved);
await Navigation.PopModalAsync();
#if ANDROID
if (Platform.CurrentActivity.CurrentFocus != null)
{
Platform.CurrentActivity.HideKeyboard(Platform.CurrentActivity.CurrentFocus);
}
#endif
}
private void Close_Clicked(object sender, EventArgs e)

View File

@@ -44,7 +44,7 @@ namespace Bit.App.Pages
public string WebVaultUrl { get; set; }
public string IconsUrl { get; set; }
public string NotificationsUrls { get; set; }
public Func<Task> SubmitSuccessTask { get; set; }
public Action SubmitSuccessAction { get; set; }
public Action CloseAction { get; set; }
public async Task SubmitAsync()
@@ -73,10 +73,7 @@ namespace Bit.App.Pages
IconsUrl = resUrls.Icons;
NotificationsUrls = resUrls.Notifications;
if (SubmitSuccessTask != null)
{
await SubmitSuccessTask();
}
SubmitSuccessAction?.Invoke();
}
public bool ValidateUrls()

View File

@@ -12,14 +12,12 @@ namespace Bit.App.Pages
private readonly HomeViewModel _vm;
private readonly AppOptions _appOptions;
private IBroadcasterService _broadcasterService;
private IConditionedAwaiterManager _conditionedAwaiterManager;
readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
public HomePage(AppOptions appOptions = null)
{
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>();
_conditionedAwaiterManager = ServiceContainer.Resolve<IConditionedAwaiterManager>();
_appOptions = appOptions;
InitializeComponent();
_vm = BindingContext as HomeViewModel;
@@ -58,8 +56,6 @@ namespace Bit.App.Pages
PerformNavigationOnAccountChangedOnLoad = false;
accountsManager.NavigateOnAccountChangeAsync().FireAndForget();
}
_conditionedAwaiterManager.SetAsCompleted(AwaiterPrecondition.AndroidWindowCreated);
#endif
}

View File

@@ -39,7 +39,7 @@
FontSize="Small"
FontAttributes="Bold"/>
<controls:MonoLabel
Text="{Binding LoginRequest.FingerprintPhrase}"
FormattedText="{Binding LoginRequest.FingerprintPhrase}"
FontSize="Medium"
TextColor="{DynamicResource FingerprintPhrase}"
Margin="0,0,0,27"
@@ -85,6 +85,7 @@
<Button
Text="{u:I18n DenyLogIn}"
Command="{Binding RejectRequestCommand}"
StyleClass="btn-secundary"
AutomationId="DenyLoginButton" />
</StackLayout>

View File

@@ -41,7 +41,7 @@
FontSize="Small"
FontAttributes="Bold" />
<controls:MonoLabel
Text="{Binding FingerprintPhrase}"
FormattedText="{Binding FingerprintPhrase}"
FontSize="Small"
TextColor="{DynamicResource FingerprintPhrase}"
AutomationId="FingerprintPhraseValue" />

View File

@@ -21,7 +21,6 @@ namespace Bit.App.Pages
InitializeComponent();
_vm = BindingContext as LoginSsoPageViewModel;
_vm.Page = this;
_vm.FromIosExtension = _appOptions?.IosExtension ?? false;
_vm.StartTwoFactorAction = () => MainThread.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
_vm.StartSetPasswordAction = () =>
MainThread.BeginInvokeOnMainThread(async () => await StartSetPasswordAsync());

View File

@@ -15,16 +15,6 @@ using Bit.Core.Utilities;
using Microsoft.Maui.Authentication;
using Microsoft.Maui.Networking;
using NetworkAccess = Microsoft.Maui.Networking.NetworkAccess;
using Org.BouncyCastle.Asn1.Ocsp;
#if IOS
using AuthenticationServices;
using Foundation;
using UIKit;
using WebAuthenticator = Bit.Core.Utilities.MAUI.WebAuthenticator;
using WebAuthenticatorResult = Bit.Core.Utilities.MAUI.WebAuthenticatorResult;
using WebAuthenticatorOptions = Bit.Core.Utilities.MAUI.WebAuthenticatorOptions;
#endif
namespace Bit.App.Pages
{
@@ -74,8 +64,6 @@ namespace Bit.App.Pages
set => SetProperty(ref _orgIdentifier, value);
}
public bool FromIosExtension { get; set; }
public ICommand LogInCommand { get; }
public Action StartTwoFactorAction { get; set; }
public Action StartSetPasswordAction { get; set; }
@@ -165,9 +153,6 @@ namespace Bit.App.Pages
CallbackUrl = new Uri(REDIRECT_URI),
Url = new Uri(url),
PrefersEphemeralWebBrowserSession = _useEphemeralWebBrowserSession,
#if IOS
ShouldUseSharedApplicationKeyWindow = FromIosExtension
#endif
});
var code = GetResultCode(authResult, state);

View File

@@ -1,40 +1,20 @@
using Bit.App.Abstractions;
using Bit.App.Models;
using Bit.App.Pages;
using Bit.Core.Abstractions;
using Bit.Core.Services;
using Bit.Core.Utilities;
namespace Bit.Core.Pages;
public partial class AndroidNavigationRedirectPage : ContentPage
{
private AppOptions _options;
public AndroidNavigationRedirectPage(AppOptions options)
{
_options = options ?? new AppOptions();
private readonly IAccountsManager _accountsManager;
public AndroidNavigationRedirectPage()
{
_accountsManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
InitializeComponent();
}
private void AndroidNavigationRedirectPage_OnLoaded(object sender, EventArgs e)
{
if (ServiceContainer.TryResolve<IAccountsManager>(out var accountsManager))
{
accountsManager.NavigateOnAccountChangeAsync().FireAndForget();
}
else
{
Bit.App.App.MainPage = new NavigationPage(new HomePage(_options)); //Fallback scenario to load HomePage just in case something goes wrong when resolving IAccountsManager
}
if (ServiceContainer.TryResolve<IConditionedAwaiterManager>(out var conditionedAwaiterManager))
{
conditionedAwaiterManager?.SetAsCompleted(AwaiterPrecondition.AndroidWindowCreated);
}
else
{
LoggerHelper.LogEvenIfCantBeResolved(new InvalidOperationException("ConditionedAwaiterManager can't be resolved on Android Navigation redirection"));
}
_accountsManager.NavigateOnAccountChangeAsync().FireAndForget();
}
}

View File

@@ -266,8 +266,6 @@
AutomationId="SendShowHideOptionsButton" />
<controls:IconButton
x:Name="_btnOptionsUp"
InputTransparent="True"
MinimumWidthRequest="25"
Text="{Binding Source={x:Static core:BitwardenIcons.ChevronUp}}"
StyleClass="box-row-button"
TextColor="{DynamicResource PrimaryColor}"
@@ -276,8 +274,6 @@
AutomationId="SendOptionsDisplayed" />
<controls:IconButton
x:Name="_btnOptionsDown"
InputTransparent="True"
MinimumWidthRequest="25"
Text="{Binding Source={x:Static core:BitwardenIcons.AngleDown}}"
StyleClass="box-row-button"
TextColor="{DynamicResource PrimaryColor}"

View File

@@ -39,7 +39,7 @@
FontSize="Small"
FontAttributes="Bold"/>
<controls:MonoLabel
Text="{Binding FingerprintPhrase}"
FormattedText="{Binding FingerprintPhrase}"
Grid.Row="1"
Grid.ColumnSpan="2"
FontSize="Small"

View File

@@ -66,6 +66,7 @@ namespace Bit.App.Pages
{
try
{
IsRefreshing = true;
LoginRequests.ReplaceRange(await _authService.GetActivePasswordlessLoginRequestsAsync());
}
catch (Exception ex)

View File

@@ -96,14 +96,6 @@ namespace Bit.App.Pages
if (message.Command == "syncCompleted")
{
MainThread.BeginInvokeOnMainThread(async () => await UpdateVaultButtonTitleAsync());
try
{
await ForcePasswordResetIfNeededAsync();
}
catch (Exception ex)
{
_logger.Value.Exception(ex);
}
}
});
await UpdateVaultButtonTitleAsync();

View File

@@ -2876,13 +2876,4 @@ Wil u na die rekening omskakel?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -276,7 +276,7 @@
<value>هل أنت متأكد من أنك تريد تسجيل الخروج؟</value>
</data>
<data name="RemoveAccount" xml:space="preserve">
<value>أزِل الحساب</value>
<value>إزالة الحساب</value>
</data>
<data name="RemoveAccountConfirmation" xml:space="preserve">
<value>هل أنت متأكد من أنك تريد إزالة هذا الحساب؟</value>
@@ -2822,7 +2822,7 @@
<value>مواصلة الاتصال بالدعم؟</value>
</data>
<data name="ContinueToPrivacyPolicy" xml:space="preserve">
<value>هل تريد المتابعة إلى سياسة الخصوصية؟</value>
<value>Continue to privacy policy?</value>
</data>
<data name="ContinueToAppStore" xml:space="preserve">
<value>هل تريد المتابعة إلى متجر التطبيقات؟</value>
@@ -2844,7 +2844,7 @@
<value>لا يمكن العثور على ما تبحث عنه؟ قم بالتواصل مع دعم Bitwarden على bitwarden.com.</value>
</data>
<data name="PrivacyPolicyDescriptionLong" xml:space="preserve">
<value>اطلع على سياستنا للخصوصية على bitwarden.com.</value>
<value>Check out our privacy policy on bitwarden.com.</value>
</data>
<data name="ExploreMoreFeaturesOfYourBitwardenAccountOnTheWebApp" xml:space="preserve">
<value>استكشف المزيد من الميزات لحساب Bitwarden الخاص بك على تطبيق الويب.</value>
@@ -2877,13 +2877,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>أعدنّ ميزة إلغاء القُفْل لتغيير إجراء مهلة المخزن الخاص بك.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>تسجيل الدخول لـ Duo من خطوتين مطلوب لحسابك. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>اتبع الخطوات من Duo لإنهاء تسجيل الدخول.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>تشغيل Duo</value>
</data>
</root>

View File

@@ -2875,13 +2875,4 @@ Bu hesaba keçmək istəyirsiniz?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Anbar vaxt bitməsi əməliyyatınızı dəyişdirmək üçün bir kilid açma seçimi qurun.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Hesabınız üçün Duo iki addımlı giriş tələb olunur. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Giriş etməni tamamlamaq üçün Duo-dakı addımları izləyin.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Duo-nu başlat</value>
</data>
</root>

View File

@@ -2623,22 +2623,22 @@
<value>Бягучы асноўны пароль</value>
</data>
<data name="LoggedIn" xml:space="preserve">
<value>Вы ўвайшлі!</value>
<value>Logged in!</value>
</data>
<data name="ApproveWithMyOtherDevice" xml:space="preserve">
<value>Approve with my other device</value>
</data>
<data name="RequestAdminApproval" xml:space="preserve">
<value>Запытаць ухваленне адміністратара</value>
<value>Request admin approval</value>
</data>
<data name="ApproveWithMasterPassword" xml:space="preserve">
<value>Ухваліць з дапамогай асноўнага пароля</value>
<value>Approve with master password</value>
</data>
<data name="TurnOffUsingPublicDevice" xml:space="preserve">
<value>Turn off using a public device</value>
</data>
<data name="RememberThisDevice" xml:space="preserve">
<value>Запомніць гэту прыладу</value>
<value>Remember this device</value>
</data>
<data name="Passkey" xml:space="preserve">
<value>Passkey</value>
@@ -2677,19 +2677,19 @@
<value>Памылковы токен API</value>
</data>
<data name="AdminApprovalRequested" xml:space="preserve">
<value>Патрабуецца ўхваленне адміністратара</value>
<value>Admin approval requested</value>
</data>
<data name="YourRequestHasBeenSentToYourAdmin" xml:space="preserve">
<value>Ваш запыт адпраўлены адміністратару.</value>
<value>Your request has been sent to your admin.</value>
</data>
<data name="YouWillBeNotifiedOnceApproved" xml:space="preserve">
<value>Вы атрымаеце апавяшчэння пасля яго ўхвалення. </value>
<value>You will be notified once approved. </value>
</data>
<data name="TroubleLoggingIn" xml:space="preserve">
<value>Праблемы з уваходам?</value>
<value>Trouble logging in?</value>
</data>
<data name="LoggingInAsX" xml:space="preserve">
<value>Увайсці як {0}</value>
<value>Logging in as {0}</value>
</data>
<data name="VaultTimeoutActionChangedToLogOut" xml:space="preserve">
<value>Vault timeout action changed to log out</value>
@@ -2738,7 +2738,7 @@
<value>Немагчыма рэдагаваць некалькі URI адначасова</value>
</data>
<data name="LoginApproved" xml:space="preserve">
<value>Уваход ухвалены</value>
<value>Login approved</value>
</data>
<data name="LogInWithDeviceMustBeSetUpInTheSettingsOfTheBitwardenAppNeedAnotherOption" xml:space="preserve">
<value>Log in with device must be set up in the settings of the Bitwarden app. Need another option?</value>
@@ -2747,28 +2747,28 @@
<value>Log in with device</value>
</data>
<data name="LoggingInOn" xml:space="preserve">
<value>Увайсці на</value>
<value>Logging in on</value>
</data>
<data name="Vault" xml:space="preserve">
<value>Сховішча</value>
<value>Vault</value>
</data>
<data name="Appearance" xml:space="preserve">
<value>Знешні выгляд</value>
<value>Appearance</value>
</data>
<data name="AccountSecurity" xml:space="preserve">
<value>Бяспеке акаўнта</value>
<value>Account security</value>
</data>
<data name="BitwardenHelpCenter" xml:space="preserve">
<value>Даведачны цэнтр Bitwarden</value>
<value>Bitwarden Help Center</value>
</data>
<data name="ContactBitwardenSupport" xml:space="preserve">
<value>Звярніцеся ў службу падтрымкі Bitwarden</value>
<value>Contact Bitwarden support</value>
</data>
<data name="CopyAppInformation" xml:space="preserve">
<value>Copy app information</value>
</data>
<data name="SyncNow" xml:space="preserve">
<value>Сінхранізаваць зараз</value>
<value>Sync now</value>
</data>
<data name="UnlockOptions" xml:space="preserve">
<value>Unlock options</value>
@@ -2805,7 +2805,7 @@
<value>Use inline autofill if your selected keyboard supports it. Otherwise, use the default overlay.</value>
</data>
<data name="AdditionalOptions" xml:space="preserve">
<value>Дадатковы параметры</value>
<value>Additional options</value>
</data>
<data name="ContinueToWebApp" xml:space="preserve">
<value>Continue to web app?</value>
@@ -2876,13 +2876,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Задайте начин за отключване, за да може да промените действието при изтичане на времето за достъп до трезора.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Вашата регистрация изисква двустепенно удостоверяване чрез Duo. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Следвайте стъпките от Duo, за да завършите вписването.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Стартиране на Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2875,13 +2875,4 @@ Skeniranje će biti izvršeno automatski.</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@ Voleu canviar a aquest compte?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Configura una opció de desbloqueig per canviar l'acció de temps d'espera de la caixa forta.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Es requereix l'inici de sessió en dos passos de DUO al vostre compte. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Seguiu els passos de Duo per acabar d'iniciar la sessió.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Inicia DUO</value>
</data>
</root>

View File

@@ -2875,13 +2875,4 @@ Chcete se přepnout na tento účet?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Nastavte volbu odemknutí, abyste změnili časový limit Vašeho trezoru.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Pro Váš účet je vyžadováno dvoufázové přihlášení DUO. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Postupujte podle kroků od DUO pro dokončení přihlášení.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Spustit DUO</value>
</data>
</root>

View File

@@ -315,7 +315,7 @@
<comment>Label for notes.</comment>
</data>
<data name="Ok" xml:space="preserve">
<value>Iawn</value>
<value>Ok</value>
<comment>Acknowledgement.</comment>
</data>
<data name="Password" xml:space="preserve">
@@ -346,7 +346,7 @@
<comment>Confirmation message after successfully deleting a login.</comment>
</data>
<data name="Submit" xml:space="preserve">
<value>Cyflwyno</value>
<value>Submit</value>
</data>
<data name="Sync" xml:space="preserve">
<value>Cysoni</value>
@@ -371,7 +371,7 @@
<comment>Label for a username.</comment>
</data>
<data name="ValidationFieldRequired" xml:space="preserve">
<value>Mae'r maes {0} yn ofynnol.</value>
<value>The {0} field is required.</value>
<comment>Validation message for when a form field is left blank and is required to be entered.</comment>
</data>
<data name="ValueHasBeenCopied" xml:space="preserve">
@@ -382,7 +382,7 @@
<value>Verify fingerprint</value>
</data>
<data name="VerifyMasterPassword" xml:space="preserve">
<value>Gwirio'r prif gyfrinair</value>
<value>Verify master password</value>
</data>
<data name="VerifyPIN" xml:space="preserve">
<value>Verify PIN</value>
@@ -407,7 +407,7 @@
<value>Cyfrif</value>
</data>
<data name="AccountCreated" xml:space="preserve">
<value>Mae eich cyfrif newydd wedi cael ei greu! Gallwch bellach fewngofnodi.</value>
<value>Your new account has been created! You may now log in.</value>
</data>
<data name="AddAnItem" xml:space="preserve">
<value>Ychwanegu eitem</value>
@@ -784,10 +784,10 @@
<value>Do you want to auto-fill or view this item?</value>
</data>
<data name="BitwardenAutofillServiceMatchConfirm" xml:space="preserve">
<value>Ydych chi'n siŵr eich bod am lenwi'r eitem hon? Dyw hi ddim yn cyfateb yn llwyr i "{0}".</value>
<value>Are you sure you want to auto-fill this item? It is not a complete match for "{0}".</value>
</data>
<data name="MatchingItems" xml:space="preserve">
<value>Eitemau sy'n cyfateb</value>
<value>Matching items</value>
</data>
<data name="PossibleMatchingItems" xml:space="preserve">
<value>Possible matching items</value>
@@ -799,22 +799,22 @@
<value>You are searching for an auto-fill item for "{0}".</value>
</data>
<data name="LearnOrg" xml:space="preserve">
<value>Dysgu am sefydliadau</value>
<value>Learn about organizations</value>
</data>
<data name="CannotOpenApp" xml:space="preserve">
<value>Methu agor yr ap "{0}".</value>
<value>Cannot open the app "{0}".</value>
<comment>Message shown when trying to launch an app that does not exist on the user's device.</comment>
</data>
<data name="AuthenticatorAppTitle" xml:space="preserve">
<value>Ap dilysu</value>
<value>Authenticator app</value>
<comment>For 2FA</comment>
</data>
<data name="EnterVerificationCodeApp" xml:space="preserve">
<value>Rhowch y cod dilysu 6 nod o'ch ap dilysu.</value>
<value>Enter the 6 digit verification code from your authenticator app.</value>
<comment>For 2FA</comment>
</data>
<data name="EnterVerificationCodeEmail" xml:space="preserve">
<value>Rhowch y cod dilysu 6 nod anfonwyd i {0}.</value>
<value>Enter the 6 digit verification code that was emailed to {0}.</value>
<comment>For 2FA</comment>
</data>
<data name="LoginUnavailable" xml:space="preserve">
@@ -895,7 +895,7 @@
Scanning will happen automatically.</value>
</data>
<data name="ScanQrTitle" xml:space="preserve">
<value>Sganio cod QR</value>
<value>Scan QR Code</value>
</data>
<data name="Camera" xml:space="preserve">
<value>Camera</value>
@@ -1143,7 +1143,7 @@ Scanning will happen automatically.</value>
<value>Icons server URL</value>
</data>
<data name="AutofillWithBitwarden" xml:space="preserve">
<value>Llenwi'n awtomatig â Bitwarden</value>
<value>Auto-fill with Bitwarden</value>
</data>
<data name="VaultIsLocked" xml:space="preserve">
<value>Vault is locked</value>
@@ -1173,7 +1173,7 @@ Scanning will happen automatically.</value>
<value>Use the Bitwarden auto-fill service to fill login information into other apps.</value>
</data>
<data name="BitwardenAutofillServiceOpenAutofillSettings" xml:space="preserve">
<value>Agor gosodiadau llenwi awtomatig</value>
<value>Open Autofill Settings</value>
</data>
<data name="FaceID" xml:space="preserve">
<value>Face ID</value>
@@ -1256,7 +1256,7 @@ Scanning will happen automatically.</value>
<value>Yes, and save</value>
</data>
<data name="AutofillAndSave" xml:space="preserve">
<value>Llenwi'n awtomatig a chadw</value>
<value>Auto-fill and save</value>
</data>
<data name="Organization" xml:space="preserve">
<value>Sefydliad</value>
@@ -1486,7 +1486,7 @@ Scanning will happen automatically.</value>
<value>Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application.</value>
</data>
<data name="LoggedInAsOn" xml:space="preserve">
<value>Rydych wedi mewngofnodi fel {0} ar {1}.</value>
<value>Logged in as {0} on {1}.</value>
<comment>ex: Logged in as user@example.com on bitwarden.com.</comment>
</data>
<data name="VaultLockedMasterPassword" xml:space="preserve">
@@ -1541,7 +1541,7 @@ Scanning will happen automatically.</value>
<comment>Color theme</comment>
</data>
<data name="ThemeDescription" xml:space="preserve">
<value>Newid thema liwiau'r ap</value>
<value>Newid thema liwiau'r ap.</value>
</data>
<data name="ThemeDefault" xml:space="preserve">
<value>Rhagosodiad (system)</value>
@@ -1577,7 +1577,7 @@ Scanning will happen automatically.</value>
<value>Auto-fill blocked URIs</value>
</data>
<data name="AskToAddLogin" xml:space="preserve">
<value>Gofyn i ychwanegu manylion mewngofnodi</value>
<value>Ask to add login</value>
</data>
<data name="AskToAddLoginDescription" xml:space="preserve">
<value>Ask to add an item if one isn't found in your vault.</value>
@@ -1669,7 +1669,7 @@ Scanning will happen automatically.</value>
<comment>Title for the alert to confirm vault exports.</comment>
</data>
<data name="Warning" xml:space="preserve">
<value>Rhybudd</value>
<value>Warning</value>
</data>
<data name="ExportVaultFailure" xml:space="preserve">
<value>There was a problem exporting your vault. If the problem persists, you'll need to export from the web vault.</value>
@@ -1945,7 +1945,7 @@ Scanning will happen automatically.</value>
<value>Current access count</value>
</data>
<data name="NewPassword" xml:space="preserve">
<value>Cyfrinair newydd</value>
<value>New password</value>
</data>
<data name="PasswordInfo" xml:space="preserve">
<value>Optionally require a password for users to access this Send.</value>
@@ -2310,7 +2310,7 @@ select Add TOTP to store the key safely</value>
<value>Caniatáu sgrinluniau</value>
</data>
<data name="AreYouSureYouWantToEnableScreenCapture" xml:space="preserve">
<value>Ydych chi'n siŵr eich bod am ganiatáu sgrinluniau?</value>
<value>Are you sure you want to turn on screen capture?</value>
</data>
<data name="LogInRequested" xml:space="preserve">
<value>Login requested</value>
@@ -2394,16 +2394,16 @@ select Add TOTP to store the key safely</value>
<value>Gair ar hap</value>
</data>
<data name="EmailRequiredParenthesis" xml:space="preserve">
<value>Ebost (gofynnol)</value>
<value>Email (required)</value>
</data>
<data name="DomainNameRequiredParenthesis" xml:space="preserve">
<value>Enw parth (gofynnol)</value>
</data>
<data name="APIKeyRequiredParenthesis" xml:space="preserve">
<value>Allwedd API (gofynnol)</value>
<value>API key (required)</value>
</data>
<data name="Service" xml:space="preserve">
<value>Gwasanaeth</value>
<value>Service</value>
</data>
<data name="AddyIo" xml:space="preserve">
<value>addy.io</value>
@@ -2549,7 +2549,7 @@ Do you want to switch to this account?</value>
<value>Bydd angen ailgychwyn yr ap</value>
</data>
<data name="DefaultSystem" xml:space="preserve">
<value>Rhagosodiad (system)</value>
<value>Default (System)</value>
</data>
<data name="Important" xml:space="preserve">
<value>Pwysig</value>
@@ -2624,7 +2624,7 @@ Do you want to switch to this account?</value>
<value>Prif gyfrinair presennol</value>
</data>
<data name="LoggedIn" xml:space="preserve">
<value>Wedi mewngofnodi!</value>
<value>Logged in!</value>
</data>
<data name="ApproveWithMyOtherDevice" xml:space="preserve">
<value>Approve with my other device</value>
@@ -2812,17 +2812,17 @@ Do you want to switch to this account?</value>
<value>Parhau i'r ap gwe?</value>
</data>
<data name="ContinueToX" xml:space="preserve">
<value>Parhau i {0}?</value>
<value>Continue to {0}?</value>
<comment>The parameter is an URL, like bitwarden.com.</comment>
</data>
<data name="ContinueToHelpCenter" xml:space="preserve">
<value>Parhau i'r ganolfan gymorth?</value>
<value>Continue to Help center?</value>
</data>
<data name="ContinueToContactSupport" xml:space="preserve">
<value>Continue to contact support?</value>
</data>
<data name="ContinueToPrivacyPolicy" xml:space="preserve">
<value>Parhau i'r polisi preifatrwydd?</value>
<value>Continue to privacy policy?</value>
</data>
<data name="ContinueToAppStore" xml:space="preserve">
<value>Continue to app store?</value>
@@ -2859,11 +2859,11 @@ Do you want to switch to this account?</value>
<value>Choose the dark theme to use when your devices dark mode is in use</value>
</data>
<data name="CreatedXY" xml:space="preserve">
<value>Crëwyd ar {0} am {1}</value>
<value>Created {0}, {1}</value>
<comment>To state the date/time in which the cipher was created: Created 03/21/2023, 09:25 AM. First parameter is the date and the second parameter is the time.</comment>
</data>
<data name="TooManyAttempts" xml:space="preserve">
<value>Gormod o geisiadau</value>
<value>Too many attempts</value>
</data>
<data name="AccountLoggedOutBiometricExceeded" xml:space="preserve">
<value>Account logged out.</value>
@@ -2877,13 +2877,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Lansio Duo</value>
</data>
</root>

View File

@@ -907,7 +907,7 @@ Skanning vil ske automatisk.</value>
<value>Kopiér TOTP</value>
</data>
<data name="CopyTotpAutomaticallyDescription" xml:space="preserve">
<value>Har et login en godkendelsesnøgle, kopiér da TOTP-bekræftelseskoden til udklipsholderen, når login autoudfyldes.</value>
<value>Har et login en godkendelsesnøgle, kopiér TOTP-bekræftelseskoden til udklipsholderen, når login auto-udfyldes.</value>
</data>
<data name="CopyTotpAutomatically" xml:space="preserve">
<value>Kopiér TOTP automatisk</value>
@@ -2128,7 +2128,7 @@ Skanning vil ske automatisk.</value>
<value>Denne organisation har en virksomhedspolitik, der automatisk tilmelder dig til nulstilling af adgangskode. Tilmelding giver organisationsadministratorer mulighed for at skifte din hovedadgangskode.</value>
</data>
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
<value>Organisationspolitikkerne har sat den maksimalt tilladte bokstimeout til {0} tim(er) og {1} minut(ter).</value>
<value>Dine organisationspolitikker påvirker din boks-timeout. Maksimum tilladte boks-timeout er {0} time(r) og {1} minut(ter)</value>
</data>
<data name="VaultTimeoutPolicyWithActionInEffect" xml:space="preserve">
<value>Organisationspolitikkerne påvirker boks-timeout. Maks. tilladt boks-timeout er {0} time(r) og {1} minut(ter). Boks-timeout er pt. sat til {2}.</value>
@@ -2354,7 +2354,7 @@ vælg Tilføj TOTP for at gemme nøglen sikkert</value>
<value>Godkend loginanmodninger</value>
</data>
<data name="UseThisDeviceToApproveLoginRequestsMadeFromOtherDevices" xml:space="preserve">
<value>Brug denne enhed til at godkende loginanmodninger fra andre enheder</value>
<value>Brug denne enhed til at godkende loginanmodninger fra andre enheder.</value>
</data>
<data name="AllowNotifications" xml:space="preserve">
<value>Tillad notifikationer</value>
@@ -2668,7 +2668,7 @@ Vil du skifte til denne konto?</value>
<value>Hjælp til genanmodning om hovedadgangskode</value>
</data>
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
<value>Oplåsning kan fejle grundet utilstrækkelig hukommelse. Reducér KDF-hukommelsesindstillinger eller opsæt biometrisk oplåsning for at afhjælpe.</value>
<value>Unlocking may fail due to insufficient memory. Decrease your KDF memory settings or set up biometric unlock to resolve.</value>
</data>
<data name="InvalidAPIKey" xml:space="preserve">
<value>Ugyldig API-nøgle</value>
@@ -2876,13 +2876,4 @@ Vil du skifte til denne konto?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Opsæt en oplåsningsmetode for at ændre Bokstimeouthandlingen.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo-totrinsindlogning kræves for kontoen. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Følg trinnene fra Duo for at færdiggøre indlogningen.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Start Duo</value>
</data>
</root>

View File

@@ -2875,13 +2875,4 @@ Möchtest du zu diesem Konto wechseln?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Richte eine Entsperroption ein, um deine Aktion bei Tresor-Timeout zu ändern.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Für dein Konto ist die Duo Zwei-Faktor-Authentifizierung erforderlich. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Folge den Schritten von Duo, um die Anmeldung abzuschließen.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Duo starten</value>
</data>
</root>

View File

@@ -152,11 +152,11 @@
<comment>Copy some value to your clipboard.</comment>
</data>
<data name="CopyPassword" xml:space="preserve">
<value>Αντιγραφή κωδικού</value>
<value>Αντιγραφή Κωδικού</value>
<comment>The button text that allows a user to copy the login's password to their clipboard.</comment>
</data>
<data name="CopyUsername" xml:space="preserve">
<value>Αντιγραφή ονόματος χρήστη</value>
<value>Αντιγραφή Ονόματος Χρήστη</value>
<comment>The button text that allows a user to copy the login's username to their clipboard.</comment>
</data>
<data name="Credits" xml:space="preserve">
@@ -179,14 +179,14 @@
<value>Επεξεργασία</value>
</data>
<data name="EditFolder" xml:space="preserve">
<value>Επεξεργασία φακέλου</value>
<value>Επεξεργασία Φακέλου</value>
</data>
<data name="Email" xml:space="preserve">
<value>Email</value>
<comment>Short label for an email address.</comment>
</data>
<data name="EmailAddress" xml:space="preserve">
<value>Διεύθυνση email</value>
<value>Διεύθυνση Email</value>
<comment>Full label for a email address.</comment>
</data>
<data name="EmailUs" xml:space="preserve">
@@ -203,7 +203,7 @@
<comment>Title for your favorite items in the vault.</comment>
</data>
<data name="FileBugReport" xml:space="preserve">
<value>Υποβολή αναφοράς σφάλματος</value>
<value>Υποβολή Αναφοράς Σφάλματος</value>
</data>
<data name="FileBugReportDescription" xml:space="preserve">
<value>Αναφέρετε το προβλήμα στο GitHub.</value>
@@ -229,14 +229,14 @@
<value>Φάκελοι</value>
</data>
<data name="FolderUpdated" xml:space="preserve">
<value>Ο φάκελος ενημερώθηκε</value>
<value>Ο φάκελος ενημερώθηκε.</value>
</data>
<data name="GoToWebsite" xml:space="preserve">
<value>Μετάβαση στην ιστοσελίδα</value>
<comment>The button text that allows user to launch the website to their web browser.</comment>
</data>
<data name="HelpAndFeedback" xml:space="preserve">
<value>Βοήθεια και σχόλια</value>
<value>Βοήθεια και Σχόλια</value>
</data>
<data name="Hide" xml:space="preserve">
<value>Απόκρυψη</value>
@@ -251,7 +251,7 @@
<comment>Title for the alert when internet connection is required to continue.</comment>
</data>
<data name="InvalidMasterPassword" xml:space="preserve">
<value>Μη έγκυρος κύριος κωδικός. Δοκιμάστε ξανά.</value>
<value>Μη έγκυρος κύριος κωδικός, δοκιμάστε ξανά.</value>
</data>
<data name="InvalidPIN" xml:space="preserve">
<value>Μη έγκυρο PIN. Προσπάθηστε ξανά.</value>
@@ -282,7 +282,7 @@
<value>Είστε βέβαιοι ότι θέλετε να καταργήσετε αυτόν τον λογαριασμό?</value>
</data>
<data name="AccountAlreadyAdded" xml:space="preserve">
<value>Ο λογαριασμός έχει ήδη προστεθεί</value>
<value>Ο Λογαριασμός Προστέθηκε Ήδη</value>
</data>
<data name="SwitchToAlreadyAddedAccountConfirmation" xml:space="preserve">
<value>Θα θέλατε να το αλλάξετε τώρα?</value>
@@ -296,11 +296,11 @@
<comment>Text to define that there are more options things to see.</comment>
</data>
<data name="MyVault" xml:space="preserve">
<value>Το vault μου</value>
<value>Το Vault μου</value>
<comment>The title for the vault page.</comment>
</data>
<data name="Authenticator" xml:space="preserve">
<value>Αυθεντικοποιητής</value>
<value>Εφαρμογή Επαλήθευσης</value>
<comment>Authenticator TOTP feature</comment>
</data>
<data name="Name" xml:space="preserve">
@@ -342,7 +342,7 @@
<comment>Reveal a hidden value (password).</comment>
</data>
<data name="ItemDeleted" xml:space="preserve">
<value>Το στοιχείο διαγράφτηκε</value>
<value>Το στοιχείο έχει διαγραφεί.</value>
<comment>Confirmation message after successfully deleting a login.</comment>
</data>
<data name="Submit" xml:space="preserve">
@@ -375,14 +375,14 @@
<comment>Validation message for when a form field is left blank and is required to be entered.</comment>
</data>
<data name="ValueHasBeenCopied" xml:space="preserve">
<value>{0} έχει αντιγραφεί</value>
<value>{0} έχει αντιγραφεί.</value>
<comment>Confirmation message after successfully copying a value to the clipboard.</comment>
</data>
<data name="VerifyFingerprint" xml:space="preserve">
<value>Επαλήθευση δακτυλικού αποτυπώματος</value>
<value>Επαλήθευση Δακτυλικού Αποτυπώματος</value>
</data>
<data name="VerifyMasterPassword" xml:space="preserve">
<value>Επαλήθευση κύριου κωδικού</value>
<value>Επαλήθευση Κύριου Κωδικού</value>
</data>
<data name="VerifyPIN" xml:space="preserve">
<value>Επαλήθευση PIN</value>
@@ -413,22 +413,22 @@
<value>Προσθήκη στοιχείου</value>
</data>
<data name="AppExtension" xml:space="preserve">
<value>Επέκταση εφαρμογής</value>
<value>Επέκταση Εφαρμογής</value>
</data>
<data name="AutofillAccessibilityDescription" xml:space="preserve">
<value>Χρησιμοποιείστε την υπηρεσία προσβασιμότητας Bitwarden, για την αυτόματη συμπλήρωση συνδέσεων σε εφαρμογές και στο διαδίκτυο.</value>
</data>
<data name="AutofillService" xml:space="preserve">
<value>Υπηρεσία αυτόματης συμπλήρωσης</value>
<value>Υπηρεσία Αυτόματης Συμπλήρωσης</value>
</data>
<data name="AvoidAmbiguousCharacters" xml:space="preserve">
<value>Αποφυγή αμφιλεγόμενων χαρακτήρων</value>
<value>Αποφυγή Αμφιλεγόμενων Χαρακτήρων</value>
</data>
<data name="BitwardenAppExtension" xml:space="preserve">
<value>Επέκταση εφαρμογής Bitwarden</value>
<value>Επέκταση Εφαρμογής Bitwarden</value>
</data>
<data name="BitwardenAppExtensionAlert2" xml:space="preserve">
<value>Ο ευκολότερος τρόπος για να προσθέστε νέες συνδέσεις στο vault σας, είναι μέσω της επέκτασης εφαρμογής Bitwarden. Μάθετε περισσότερα σχετικά με τη χρήση της επέκτασης εφαρμογής Bitwarden, μεταβαίνοντας στις "Ρυθμίσεις".</value>
<value>Ο ευκολότερος τρόπος για να προσθέστε νέες συνδέσεις στο vault σας, είναι μέσω της επέκτασης εφαρμογής Bitwarden. Μάθετε περισσότερα σχετικά με τη χρήση της επέκτασης, μεταβαίνοντας στις "Ρυθμίσεις".</value>
</data>
<data name="BitwardenAppExtensionDescription" xml:space="preserve">
<value>Χρήση του Bitwarden στο Safari και σε άλλες εφαρμογές για αυτόματη συμπλήρωση των συνδέσεων σας.</value>
@@ -440,13 +440,13 @@
<value>Χρησιμοποιείστε την υπηρεσία προσβασιμότητας Bitwarden, για την αυτόματη συμπλήρωση συνδέσεων.</value>
</data>
<data name="ChangeEmail" xml:space="preserve">
<value>Αλλαγή email</value>
<value>Αλλαγή Email</value>
</data>
<data name="ChangeEmailConfirmation" xml:space="preserve">
<value>Μπορείτε να αλλάξετε τη διεύθυνση του email σας στο bitwarden.com. Θέλετε να επισκεφθείτε την ιστοσελίδα τώρα;</value>
</data>
<data name="ChangeMasterPassword" xml:space="preserve">
<value>Αλλαγή κύριου κωδικού</value>
<value>Αλλαγή Κύριου Κωδικού</value>
</data>
<data name="Close" xml:space="preserve">
<value>Κλείσιμο</value>
@@ -455,29 +455,29 @@
<value>Συνέχεια</value>
</data>
<data name="CreateAccount" xml:space="preserve">
<value>Δημιουργία λογαριασμού</value>
<value>Δημιουργία Λογαριασμού</value>
</data>
<data name="CreatingAccount" xml:space="preserve">
<value>Δημιουργία λογαριασμού...</value>
<comment>Message shown when interacting with the server</comment>
</data>
<data name="EditItem" xml:space="preserve">
<value>Επεξεργασία στοιχείου</value>
<value>Επεξεργασία Στοιχείου</value>
</data>
<data name="EnableAutomaticSyncing" xml:space="preserve">
<value>Επιτρέψτε τον αυτόματο συγχρονισμό</value>
<value>Ενεργοποίηση Αυτόματου Συγχρονισμού</value>
</data>
<data name="EnterEmailForHint" xml:space="preserve">
<value>Εισάγετε την διεύθυνση email του λογαριασμού σας, προκειμένου για να λάβετε υπόδειξη του κύριου κωδικού.</value>
</data>
<data name="ExntesionReenable" xml:space="preserve">
<value>Εκ νέου ενεργοποίηση επέκτασης εφαρμογής</value>
<value>Ενεργοποιείστε εκ νέου την Επέκτασης Εφαρμογής</value>
</data>
<data name="ExtensionAlmostDone" xml:space="preserve">
<value>Σχεδόν ολοκληρώθηκε!</value>
</data>
<data name="ExtensionEnable" xml:space="preserve">
<value>Ενεργοποίηση επέκτασης εφαρμογής</value>
<value>Ενεργοποίηση Επέκτασης Εφαρμογής</value>
</data>
<data name="ExtensionInSafari" xml:space="preserve">
<value>Στο Safari, βρείτε το Bitwarden χρησιμοποιώντας το εικονίδιο κοινής χρήσης (μετακινηθείτε προς τα δεξιά στην κάτω σειρά του μενού).</value>
@@ -508,13 +508,13 @@
<value>Δακτυλικό αποτύπωμα</value>
</data>
<data name="GeneratePassword" xml:space="preserve">
<value>Παραγωγή κωδικού</value>
<value>Δημιουργία Κωδικού</value>
</data>
<data name="GetPasswordHint" xml:space="preserve">
<value>Λήψη υπόδειξης κύριου κωδικού</value>
</data>
<data name="ImportItems" xml:space="preserve">
<value>Εισαγωγή στοιχείων</value>
<value>Εισαγωγή Στοιχείων</value>
</data>
<data name="ImportItemsConfirmation" xml:space="preserve">
<value>Μπορείτε να εισαγάγετε μαζικά στοιχεία διαδικτυακά στο bitwarden.com. Θέλετε να επισκεφθείτε την ιστοσελίδα τώρα;</value>
@@ -523,7 +523,7 @@
<value>Γρήγορη μαζική εισαγωγή των στοιχείων σας από άλλες εφαρμογές διαχείρισης κωδικών.</value>
</data>
<data name="LastSync" xml:space="preserve">
<value>Τελευταίος συγχρονισμός:</value>
<value>Τελευταίος Συγχρονισμός:</value>
</data>
<data name="Length" xml:space="preserve">
<value>Μήκος</value>
@@ -547,10 +547,10 @@
<value>Άμεσα</value>
</data>
<data name="VaultTimeout" xml:space="preserve">
<value>Χρόνος λήξης vault</value>
<value>Χρόνος Λήξης Vault</value>
</data>
<data name="VaultTimeoutAction" xml:space="preserve">
<value>Ενέργεια χρόνου λήξης vault</value>
<value>Ενέργεια Χρόνου Λήξης Vault</value>
</data>
<data name="VaultTimeoutLogOutConfirmation" xml:space="preserve">
<value>Η αποσύνδεση θα καταργήσει όλη την πρόσβαση στο vault σας και απαιτεί online έλεγχο ταυτότητας μετά το χρονικό όριο λήξης. Είστε βέβαιοι ότι θέλετε να χρησιμοποιήσετε αυτήν τη ρύθμιση;</value>
@@ -572,7 +572,7 @@
<value>Ο κύριος κωδικός χρησιμοποιείται για πρόσβαση στο vault σας. Είναι πολύ σημαντικό να μην τον ξεχάσετε. Δεν υπάρχει τρόπος να τον ανακτήσετε σε αυτή την περίπτωση.</value>
</data>
<data name="MasterPasswordHint" xml:space="preserve">
<value>Υπόδειξη κύριου κωδικού (προαιρετικό)</value>
<value>Υπόδειξη Κύριου Κωδικού (προαιρετικό)</value>
</data>
<data name="MasterPasswordHintDescription" xml:space="preserve">
<value>Η υπόδειξη κύριου κωδικού μπορεί να σας βοηθήσει να θυμηθείτε τον κωδικό σας αν τον ξεχάσετε.</value>
@@ -581,15 +581,15 @@
<value>Ο κύριος κωδικός πρέπει να έχει μήκος τουλάχιστον {0} χαρακτήρες.</value>
</data>
<data name="MinNumbers" xml:space="preserve">
<value>Ελάχιστα αριθμητικά ψηφία</value>
<value>Ελάχιστα Αριθμητικά Ψηφία</value>
<comment>Minimum numeric characters for password generator settings</comment>
</data>
<data name="MinSpecial" xml:space="preserve">
<value>Ελάχιστοι ειδικοί χαρακτήρες</value>
<value>Ελάχιστο Ειδικών Χαρακτήρων</value>
<comment>Minimum special characters for password generator settings</comment>
</data>
<data name="MoreSettings" xml:space="preserve">
<value>Περισσότερες ρυθμίσεις</value>
<value>Περισσότερες Ρυθμίσεις</value>
</data>
<data name="MustLogInMainApp" xml:space="preserve">
<value>Πρέπει να συνδεθείτε στην κύρια εφαρμογή Bitwarden για να μπορέσετε να χρησιμοποιήσετε την επέκταση.</value>
@@ -598,7 +598,7 @@
<value>Ποτέ</value>
</data>
<data name="NewItemCreated" xml:space="preserve">
<value>Το στοιχείο προστέθηκε</value>
<value>Δημιουργήθηκε νέο στοιχείο.</value>
</data>
<data name="NoFavorites" xml:space="preserve">
<value>Δεν υπάρχουν αγαπημένα στο vault σας.</value>
@@ -626,13 +626,13 @@
<value>Άλλες</value>
</data>
<data name="PasswordGenerated" xml:space="preserve">
<value>Ο κωδικός δημιουργήθηκε</value>
<value>Ο κωδικός δημιουργήθηκε.</value>
</data>
<data name="PasswordGenerator" xml:space="preserve">
<value>Γεννήτρια κωδικού</value>
<value>Γεννήτρια Κωδικού</value>
</data>
<data name="PasswordHint" xml:space="preserve">
<value>Υπόδειξη κωδικού</value>
<value>Υπόδειξη Κωδικού</value>
</data>
<data name="PasswordHintAlert" xml:space="preserve">
<value>Σας στείλαμε ένα email με υπόδειξη του κύριου κωδικού.</value>
@@ -641,20 +641,20 @@
<value>Είστε βέβαιοι ότι θέλετε να αντικαταστήσετε τον τρέχον κωδικό;</value>
</data>
<data name="PushNotificationAlert" xml:space="preserve">
<value>Το Bitwarden κρατάει συγχρονισμένο το vault σας αυτόματα, με τη χρήση ειδοποιήσεων push. Για την καλύτερη δυνατή εμπειρία, επιλέξτε "Να επιτρέπεται" στην παρακάτω προτροπή όταν σας ζητηθεί να ενεργοποιήσετε τις ειδοποιήσεις push.</value>
<value>Το Bitwarden κρατάει αυτόματα το vault σας, με τη χρήση ειδοποιήσεων push. Για την καλύτερη δυνατή εμπειρία, επιλέξτε "Να επιτρέπεται" στην παρακάτω ερώτηση, όταν σας ζητηθεί να ενεργοποιήσετε τις ειδοποιήσεις push.</value>
<comment>Push notifications for apple products</comment>
</data>
<data name="RateTheApp" xml:space="preserve">
<value>Αξιολογήστε την εφαρμογή</value>
<value>Αξιολογήστε την Εφαρμογή</value>
</data>
<data name="RateTheAppDescription" xml:space="preserve">
<value>Παρακαλούμε σκεφτείτε να μας βοηθήσετε με μια καλή κριτική!</value>
</data>
<data name="RegeneratePassword" xml:space="preserve">
<value>Επαναδημιουργία κωδικού</value>
<value>Επαναδημιουργία Κωδικού</value>
</data>
<data name="RetypeMasterPassword" xml:space="preserve">
<value>Εισάγετε ξανά τον κύριο κωδικό</value>
<value>Εισάγετε Ξανά τον Κύριο Κωδικό</value>
</data>
<data name="SearchVault" xml:space="preserve">
<value>Αναζήτηση στο vault</value>
@@ -672,10 +672,10 @@
<value>Εισαγωγή τετραψήφιου αριθμού PIN για ξεκλείδωμα εφαρμογής.</value>
</data>
<data name="ItemInformation" xml:space="preserve">
<value>Πληροφορίες στοιχείου</value>
<value>Πληροφορίες Στοιχείου</value>
</data>
<data name="ItemUpdated" xml:space="preserve">
<value>Το στοιχείο αποθηκεύτηκε</value>
<value>Το στοιχείο ενημερώθηκε.</value>
</data>
<data name="Submitting" xml:space="preserve">
<value>Υποβολή...</value>
@@ -686,39 +686,39 @@
<comment>Message shown when interacting with the server</comment>
</data>
<data name="SyncingComplete" xml:space="preserve">
<value>Ο συγχρονισμός ολοκληρώθηκε</value>
<value>Ο συγχρονισμός ολοκληρώθηκε.</value>
</data>
<data name="SyncingFailed" xml:space="preserve">
<value>Ο συγχρονισμός απέτυχε</value>
<value>Ο συγχρονισμός απέτυχε.</value>
</data>
<data name="SyncVaultNow" xml:space="preserve">
<value>Συγχρονισμός του vault τώρα</value>
<value>Συγχρονισμός Vault Τώρα</value>
</data>
<data name="TouchID" xml:space="preserve">
<value>Touch ID</value>
<comment>What Apple calls their fingerprint reader.</comment>
</data>
<data name="TwoStepLogin" xml:space="preserve">
<value>Σύνδεση δύο βημάτων</value>
<value>Σύνδεση σε δύο βήματα</value>
</data>
<data name="UnlockWith" xml:space="preserve">
<value>Ξεκλείδωμα με {0}</value>
</data>
<data name="UnlockWithPIN" xml:space="preserve">
<value>Ξεκλείδωμα με κωδικό PIN</value>
<value>Ξεκλείδωμα με PIN</value>
</data>
<data name="Validating" xml:space="preserve">
<value>Επαλήθευση</value>
<comment>Message shown when interacting with the server</comment>
</data>
<data name="VerificationCode" xml:space="preserve">
<value>Κωδικός επαλήθευσης</value>
<value>Κωδικός Επαλήθευσης</value>
</data>
<data name="ViewItem" xml:space="preserve">
<value>Προβολή στοιχείου</value>
<value>Προβολή Στοιχείου</value>
</data>
<data name="WebVault" xml:space="preserve">
<value>Διαδικτυακό vault Bitwarden</value>
<value>Bitwarden Web Vault</value>
</data>
<data name="Lost2FAApp" xml:space="preserve">
<value>Χάσατε την εφαρμογή επαλήθευσης;</value>
@@ -728,7 +728,7 @@
<comment>Screen title</comment>
</data>
<data name="ExtensionActivated" xml:space="preserve">
<value>Η επέκταση ενεργοποιήθηκε!</value>
<value>Η Επέκταση Ενεργοποιήθηκε!</value>
</data>
<data name="Icons" xml:space="preserve">
<value>Εικονίδια</value>
@@ -799,14 +799,14 @@
<value>Αναζήτηση στοιχείου αυτόματης συμπλήρωσης για "{0}".</value>
</data>
<data name="LearnOrg" xml:space="preserve">
<value>Μάθετε για τους οργανισμούς</value>
<value>Μάθετε για τους Οργανισμούς</value>
</data>
<data name="CannotOpenApp" xml:space="preserve">
<value>Δεν είναι δυνατή η πρόσβαση στην εφαρμογή "{0}".</value>
<comment>Message shown when trying to launch an app that does not exist on the user's device.</comment>
</data>
<data name="AuthenticatorAppTitle" xml:space="preserve">
<value>Εφαρμογή αυθεντικοποίησης</value>
<value>Εφαρμογή Επαλήθευσης</value>
<comment>For 2FA</comment>
</data>
<data name="EnterVerificationCodeApp" xml:space="preserve">
@@ -818,14 +818,14 @@
<comment>For 2FA</comment>
</data>
<data name="LoginUnavailable" xml:space="preserve">
<value>Μη διαθέσιμη σύνδεση</value>
<value>Μη Διαθέσιμη Σύνδεση</value>
<comment>For 2FA whenever there are no available providers on this device.</comment>
</data>
<data name="NoTwoStepAvailable" xml:space="preserve">
<value>Αυτός ο λογαριασμός έχει ενεργοποιημένη τη σύνδεση δύο βημάτων, ωστόσο, σε αυτήν τη συσκευή δεν υποστηρίζεται κανένας από τους διαμορφωμένους παρόχους δύο βημάτων. Παρακαλώ χρησιμοποιήστε μια υποστηριζόμενη συσκευή και/ή προσθέστε επιπλέον παρόχους που υποστηρίζονται καλύτερα σε όλες τις συσκευές (όπως μια εφαρμογή αυθεντικοποίησης).</value>
<value>Αυτός ο λογαριασμός έχει ενεργοποιημένη τη σύνδεση σε δύο βήματα, ωστόσο, σε αυτήν τη συσκευή δεν υποστηρίζεται κανένας από τους διαμορφωμένους παροχείς δύο βημάτων. Χρησιμοποιήστε μια υποστηριζόμενη συσκευή και / ή προσθέστε επιπλέον παρόχους που υποστηρίζονται καλύτερα σε όλες τις συσκευές (όπως μια εφαρμογή επαλήθευσης).</value>
</data>
<data name="RecoveryCodeTitle" xml:space="preserve">
<value>Κωδικός ανάκτησης</value>
<value>Κωδικός Ανάκτησης</value>
<comment>For 2FA</comment>
</data>
<data name="RememberMe" xml:space="preserve">
@@ -837,7 +837,7 @@
<comment>For 2FA</comment>
</data>
<data name="TwoStepLoginOptions" xml:space="preserve">
<value>Επιλογές σύνδεσης δύο βημάτων</value>
<value>Επιλογές σύνδεσης δύο παραγόντων</value>
</data>
<data name="UseAnotherTwoStepMethod" xml:space="preserve">
<value>Χρήση άλλης μεθόδου δύο παραγόντων</value>
@@ -847,18 +847,18 @@
<comment>For 2FA</comment>
</data>
<data name="VerificationEmailSent" xml:space="preserve">
<value>Το email επιβεβαίωσης στάλθηκε</value>
<value>Το email επιβεβαίωσης στάλθηκε.</value>
<comment>For 2FA</comment>
</data>
<data name="YubiKeyInstruction" xml:space="preserve">
<value>Για να συνεχίσετε, κρατήστε το YubiKey NEO στο πίσω μέρος της συσκευής ή τοποθετήστε το YubiKey στη θύρα USB της συσκευής σας, και στη συνέχεια πατήστε το κουμπί του.</value>
</data>
<data name="YubiKeyTitle" xml:space="preserve">
<value>Κλειδί ασφαλείας YubiKey</value>
<value>Κλειδί Ασφαλείας YubiKey</value>
<comment>"YubiKey" is the product name and should not be translated.</comment>
</data>
<data name="AddNewAttachment" xml:space="preserve">
<value>Προσθήκη συνημμένου</value>
<value>Προσθήκη Νέου Συνημμένου</value>
</data>
<data name="Attachments" xml:space="preserve">
<value>Συνημμένα</value>
@@ -878,10 +878,10 @@
<comment>The placeholder will show the file size of the attachment. Ex "25 MB"</comment>
</data>
<data name="AuthenticatorKey" xml:space="preserve">
<value>Κλειδί αυθεντικοποίησης (TOTP)</value>
<value>Κλειδί επαλήθευσης (TOTP)</value>
</data>
<data name="VerificationCodeTotp" xml:space="preserve">
<value>Κωδικός επαλήθευσης (TOTP)</value>
<value>Κωδικός Επαλήθευσης (TOTP)</value>
<comment>Totp code label</comment>
</data>
<data name="AuthenticatorKeyAdded" xml:space="preserve">
@@ -891,8 +891,7 @@
<value>Αδυναμία ανάγνωσης κλειδιού επαλήθευσης.</value>
</data>
<data name="PointYourCameraAtTheQRCode" xml:space="preserve">
<value>Σημαδέψτε με την κάμερα σας τον κωδικό QR.
Η σάρωση θα γίνει αυτόματα.</value>
<value>Σημαδέψτε τον κωδικό QR με τη κάμερα. Η σάρωση θα γίνει αυτόματα.</value>
</data>
<data name="ScanQrTitle" xml:space="preserve">
<value>Σάρωση κώδικα QR</value>
@@ -907,7 +906,7 @@
<value>Αντιγραφή TOTP</value>
</data>
<data name="CopyTotpAutomaticallyDescription" xml:space="preserve">
<value>Εάν η σύνδεση έχει κάποιο κλειδί αυθεντικοποίησης, αντιγράψτε τον κωδικό επαλήθευσης TOTP στο πρόχειρο σας όταν κάνετε με αυτόματη συμπλήρωση τη συνδεση.</value>
<value>Εάν η σύνδεση έχει κάποιο κλειδί επαλήθευσης, αντιγράψτε τον κωδικό επαλήθευσης TOTP στο πρόχειρο κάθε φορά που κάνετε αυτόματη συμπλήρωση τα στοιχεία σύνδεσης.</value>
</data>
<data name="CopyTotpAutomatically" xml:space="preserve">
<value>Αυτόματη αντιγραφή TOTP</value>
@@ -922,7 +921,7 @@
<value>Το συνημμένο διαγράφηκε</value>
</data>
<data name="ChooseFile" xml:space="preserve">
<value>Επιλογή αρχείου</value>
<value>Επιλογή Αρχείου</value>
</data>
<data name="File" xml:space="preserve">
<value>Αρχείο</value>
@@ -946,16 +945,16 @@
<value>Δεν μπορείτε να χρησιμοποιήσετε αυτήν τη δυνατότητα μέχρι να ενημερώσετε το κλειδί κρυπτογράφησης.</value>
</data>
<data name="EncryptionKeyMigrationRequiredDescriptionLong" xml:space="preserve">
<value>Απαιτείται μεταφορά του κλειδιού κρυπτογράφησης. Παρακαλούμε συνδεθείτε μέσω του διαδικτυακού vault για να ενημερώσετε το κλειδί κρυπτογράφησης.</value>
<value>Απαιτείται μεταφορά του κλειδιού κρυπτογράφησης. Παρακαλούμε συνδεθείτε μέσω του web vault για να ενημερώσετε το κλειδί κρυπτογράφησης.</value>
</data>
<data name="LearnMore" xml:space="preserve">
<value>Μάθετε περισσότερα</value>
<value>Μάθετε Περισσότερα</value>
</data>
<data name="ApiUrl" xml:space="preserve">
<value>URL διακομιστή API</value>
<value>URL Διακομιστή API</value>
</data>
<data name="CustomEnvironment" xml:space="preserve">
<value>Προσαρμοσμένο περιβάλλον</value>
<value>Προσαρμοσμένο Περιβάλλον</value>
</data>
<data name="CustomEnvironmentFooter" xml:space="preserve">
<value>Για προχωρημένους χρήστες. Μπορείτε να ορίσετε τo URL κάθε υπηρεσίας ανεξάρτητα.</value>
@@ -968,7 +967,7 @@
<comment>Validation error when something is not formatted correctly, such as a URL or email address.</comment>
</data>
<data name="IdentityUrl" xml:space="preserve">
<value>URL διακομιστή ταυτότητας</value>
<value>URL Ταυτότητας Διακομιστή</value>
<comment>"Identity" refers to an identity server. See more context here https://en.wikipedia.org/wiki/Identity_management</comment>
</data>
<data name="SelfHostedEnvironment" xml:space="preserve">
@@ -981,25 +980,25 @@
<value>URL Διακομιστή</value>
</data>
<data name="WebVaultUrl" xml:space="preserve">
<value>URL διακομιστή διαδικτυακού vault</value>
<value>URL Διακομιστή Web Vault</value>
</data>
<data name="BitwardenAutofillServiceNotificationContentOld" xml:space="preserve">
<value>Πατήστε στην ειδοποίηση για να συμπληρώσετε αυτόματα ένα στοιχείο από τη λίστα σας.</value>
</data>
<data name="CustomFields" xml:space="preserve">
<value>Προσαρμοσμένα πεδία</value>
<value>Προσαρμοσμένα Πεδία</value>
</data>
<data name="CopyNumber" xml:space="preserve">
<value>Αντιγραφή αριθμού</value>
<value>Αντιγραφή Αριθμού</value>
</data>
<data name="CopySecurityCode" xml:space="preserve">
<value>Αντιγραφή κωδικού ασφαλείας</value>
<value>Αντιγραφή Κωδικού Ασφαλείας</value>
</data>
<data name="Number" xml:space="preserve">
<value>Αριθμός</value>
</data>
<data name="SecurityCode" xml:space="preserve">
<value>Κωδικός ασφαλείας</value>
<value>Κωδικός Ασφαλείας</value>
</data>
<data name="TypeCard" xml:space="preserve">
<value>Κάρτα</value>
@@ -1011,7 +1010,7 @@
<value>Σύνδεση</value>
</data>
<data name="TypeSecureNote" xml:space="preserve">
<value>Ασφαλής σημείωση</value>
<value>Ασφαλής Σημείωση</value>
</data>
<data name="Address1" xml:space="preserve">
<value>Διεύθυνση 1</value>
@@ -1050,10 +1049,10 @@
<value>Dr</value>
</data>
<data name="ExpirationMonth" xml:space="preserve">
<value>Μήνας λήξης</value>
<value>Μήνας Λήξης</value>
</data>
<data name="ExpirationYear" xml:space="preserve">
<value>Έτος λήξης</value>
<value>Έτος Λήξης</value>
</data>
<data name="February" xml:space="preserve">
<value>Φεβρουάριος</value>
@@ -1077,7 +1076,7 @@
<value>Ονοματεπώνυμο</value>
</data>
<data name="LicenseNumber" xml:space="preserve">
<value>Αριθμός άδειας</value>
<value>Αριθμός Άδειας</value>
</data>
<data name="March" xml:space="preserve">
<value>Μάρτιος</value>
@@ -1086,7 +1085,7 @@
<value>Μάιος</value>
</data>
<data name="MiddleName" xml:space="preserve">
<value>Μεσαίο όνομα</value>
<value>Μεσαίο Όνομα</value>
</data>
<data name="Mr" xml:space="preserve">
<value>Κος</value>
@@ -1107,7 +1106,7 @@
<value>Οκτώβριος</value>
</data>
<data name="PassportNumber" xml:space="preserve">
<value>Αριθμός διαβατηρίου</value>
<value>Αριθμός Διαβατηρίου</value>
</data>
<data name="Phone" xml:space="preserve">
<value>Τηλέφωνο</value>
@@ -1125,7 +1124,7 @@
<value>Τίτλος</value>
</data>
<data name="ZipPostalCode" xml:space="preserve">
<value>Ταχυδρομικός κώδικας</value>
<value>Ταχυδρομικός Κώδικας</value>
</data>
<data name="Address" xml:space="preserve">
<value>Διεύθυνση</value>
@@ -1140,7 +1139,7 @@
<value>Εμφάνιση μιας αναγνωρίσιμης εικόνας δίπλα σε κάθε σύνδεση.</value>
</data>
<data name="IconsUrl" xml:space="preserve">
<value>URL διακομιστή εικονιδίων</value>
<value>Εικονίδια Διακομιστή URL</value>
</data>
<data name="AutofillWithBitwarden" xml:space="preserve">
<value>Αυτόματη συμπλήρωση με Bitwarden</value>
@@ -1195,7 +1194,7 @@
<value>Δεν ήταν δυνατό να ανοίξουμε αυτόματα το μενού ρυθμίσεων αυτόματης συμπλήρωσης Android για εσάς. Μπορείτε να πλοηγηθείτε στο μενού ρυθμίσεων αυτόματης συμπλήρωσης με μη αυτόματο τρόπο από τις Ρυθμίσεις Android &gt; Σύστημα &gt; Γλώσσες και εισαγωγή &gt; Σύνθετες &gt; Υπηρεσία αυτόματης συμπλήρωσης.</value>
</data>
<data name="CustomFieldName" xml:space="preserve">
<value>Όνομα προσαρμοσμένου πεδίου</value>
<value>Όνομα Προσαρμοσμένου Πεδίου</value>
</data>
<data name="FieldTypeBoolean" xml:space="preserve">
<value>Δυαδικό</value>
@@ -1210,7 +1209,7 @@
<value>Κείμενο</value>
</data>
<data name="NewCustomField" xml:space="preserve">
<value>Νέο προσαρμοσμένο πεδίο</value>
<value>Νέο Προσαρμοσμένο Πεδίο</value>
</data>
<data name="SelectTypeField" xml:space="preserve">
<value>Τι τύπος προσαρμοσμένου πεδίου είναι αυτό που θέλετε να προσθέσετε;</value>
@@ -1249,11 +1248,11 @@
<value>Εντοπισμός Αντιστοίχισης URI</value>
</data>
<data name="MatchDetection" xml:space="preserve">
<value>Εντοπισμός αντιστοίχισης</value>
<value>Εντοπισμός Αντιστοίχισης</value>
<comment>URI match detection for auto-fill.</comment>
</data>
<data name="YesAndSave" xml:space="preserve">
<value>Ναι, και αποθήκευση</value>
<value>Αποδοχή και Αποθήκευση</value>
</data>
<data name="AutofillAndSave" xml:space="preserve">
<value>Αυτόματη συμπλήρωση και αποθήκευση</value>
@@ -1275,7 +1274,7 @@
<value>Η υπηρεσία προσβασιμότητας μπορεί να είναι χρήσιμη όταν οι εφαρμογές δεν υποστηρίζουν την τυπική υπηρεσία αυτόματης συμπλήρωσης.</value>
</data>
<data name="DatePasswordUpdated" xml:space="preserve">
<value>Ο κωδικός ενημερώθηκε</value>
<value>Ο Κωδικός Ενημερώθηκε</value>
<comment>ex. Date this password was updated</comment>
</data>
<data name="DateUpdated" xml:space="preserve">
@@ -1298,7 +1297,7 @@
<value>Αποκτήστε πρόσβαση στη λίστα σας απευθείας από το πληκτρολόγιό σας για γρήγορη αυτόματη συμπλήρωση κωδικών.</value>
</data>
<data name="AutofillTurnOn" xml:space="preserve">
<value>Για να ενεργοποιήσετε την αυτόματη συμπλήρωση κωδικού στη συσκευή σας, ακολουθήστε αυτές τις οδηγίες:</value>
<value>Για να ενεργοποιήσετε την αυτόματη συμπλήρωση του κωδικού στη συσκευή σας, ακολουθήστε αυτές τις οδηγίες:</value>
</data>
<data name="AutofillTurnOn1" xml:space="preserve">
<value>1. Μεταβείτε στις "Ρυθμίσεις" του iOS</value>
@@ -1316,7 +1315,7 @@
<value>5. Επιλέξτε "Bitwarden"</value>
</data>
<data name="PasswordAutofill" xml:space="preserve">
<value>Αυτόματη συμπλήρωση κωδικού</value>
<value>Αυτόματη Συμπλήρωση Κωδικού</value>
</data>
<data name="BitwardenAutofillAlert2" xml:space="preserve">
<value>Ο ευκολότερος τρόπος για να προσθέσετε νέες συνδέσεις στο vault σας, είναι με την επέκταση αυτόματης συμπλήρωσης κωδικών Bitwarden. Μάθετε περισσότερα σχετικά με τη χρήση της επέκτασης αυτής, μεταβαίνοντας στις "Ρυθμίσεις".</value>
@@ -1334,10 +1333,10 @@
<value>Συνδέσεις</value>
</data>
<data name="SecureNotes" xml:space="preserve">
<value>Ασφαλείς σημειώσεις</value>
<value>Ασφαλείς Σημειώσεις</value>
</data>
<data name="AllItems" xml:space="preserve">
<value>Όλα τα στοιχεία</value>
<value>Όλα τα Στοιχεία</value>
</data>
<data name="URIs" xml:space="preserve">
<value>URIs</value>
@@ -1357,13 +1356,13 @@
<value>Ο κωδικός αυτός δεν βρέθηκε σε γνωστές διαρροές δεδομένων. Είναι ασφαλής για χρήση.</value>
</data>
<data name="IdentityName" xml:space="preserve">
<value>Όνομα ταυτότητας</value>
<value>Όνομα Ταυτότητας</value>
</data>
<data name="Value" xml:space="preserve">
<value>Τιμή</value>
</data>
<data name="PasswordHistory" xml:space="preserve">
<value>Ιστορικό κωδικού</value>
<value>Ιστορικό Κωδικού</value>
</data>
<data name="Types" xml:space="preserve">
<value>Τύποι</value>
@@ -1434,13 +1433,13 @@
<value>Επιλέξτε έναν οργανισμό στον οποίο θέλετε να μετακινήσετε αυτό το στοιχείο. Η μετακίνηση σε έναν οργανισμό μεταβιβάζει την ιδιοκτησία του στοιχείου σε αυτό τον οργανισμό. Δεν θα είστε πλέον ο άμεσος ιδιοκτήτης αυτού του στοιχείου μόλις το μετακινήσετε.</value>
</data>
<data name="NumberOfWords" xml:space="preserve">
<value>Αριθμός λέξεων</value>
<value>Αριθμός Λέξεων</value>
</data>
<data name="Passphrase" xml:space="preserve">
<value>Συνθηματικό</value>
</data>
<data name="WordSeparator" xml:space="preserve">
<value>Διαχωριστής λέξεων</value>
<value>Διαχωριστής Λέξεων</value>
</data>
<data name="Clear" xml:space="preserve">
<value>Εκκαθάριση</value>
@@ -1454,7 +1453,7 @@
<value>Δεν υπάρχουν φάκελοι προς εμφάνιση.</value>
</data>
<data name="FingerprintPhrase" xml:space="preserve">
<value>Φράση δακτυλικών αποτυπωμάτων</value>
<value>Φράση Δακτυλικών Αποτυπωμάτων</value>
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="YourAccountsFingerprint" xml:space="preserve">
@@ -1465,10 +1464,10 @@
<value>Το Bitwarden επιτρέπει να μοιράζεστε τα στοιχεία του vault σας με άλλους χρησιμοποιώντας ένα λογαριασμό οργανισμού. Θέλετε να επισκεφθείτε την ιστοσελίδα bitwarden.com για να μάθετε περισσότερα;</value>
</data>
<data name="ExportVault" xml:space="preserve">
<value>Εξαγωγή του vault</value>
<value>Εξαγωγή Vault</value>
</data>
<data name="LockNow" xml:space="preserve">
<value>Κλείδωμα τώρα</value>
<value>Κλείδωμα Τώρα</value>
</data>
<data name="PIN" xml:space="preserve">
<value>PIN</value>
@@ -1477,7 +1476,7 @@
<value>Ξεκλείδωμα</value>
</data>
<data name="UnlockVault" xml:space="preserve">
<value>Ξεκλείδωμα του vault</value>
<value>Ξεκλείδωμα Vault</value>
</data>
<data name="ThirtyMinutes" xml:space="preserve">
<value>30 λεπτά</value>
@@ -1522,7 +1521,7 @@
<value>2 λεπτά</value>
</data>
<data name="ClearClipboard" xml:space="preserve">
<value>Εκκαθάριση πρόχειρου</value>
<value>Εκκαθάριση Πρόχειρου</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="ClearClipboardDescription" xml:space="preserve">
@@ -1550,7 +1549,7 @@
<value>Προεπιλεγμένο σκοτεινό θέμα</value>
</data>
<data name="CopyNotes" xml:space="preserve">
<value>Αντιγραφή σημείωσης</value>
<value>Αντιγραφή Σημειώσεων</value>
</data>
<data name="Exit" xml:space="preserve">
<value>Έξοδος</value>
@@ -1574,19 +1573,19 @@
<comment>'Solarized Dark' is the name of a specific color scheme. It should not be translated.</comment>
</data>
<data name="AutofillBlockedUris" xml:space="preserve">
<value>Αυτόματη συμπλήρωση μπλοκαρισμένων URI</value>
<value>Αυτόματη συμπλήρωση μπλοκαρισμένων URIs</value>
</data>
<data name="AskToAddLogin" xml:space="preserve">
<value>Ρωτήστε για να προσθέστε σύνδεση</value>
<value>Ζητήστε να προσθέστε σύνδεση</value>
</data>
<data name="AskToAddLoginDescription" xml:space="preserve">
<value>Ρωτήστε για να προσθέσετε ένα αντικείμενο αν δε βρεθεί στο vault σας.</value>
<value>Ζητήστε να προσθέσετε ένα αντικείμενο αν δε βρεθεί στην κρύπτη σας.</value>
</data>
<data name="OnRestart" xml:space="preserve">
<value>Κατά την επανεκκίνηση της εφαρμογής</value>
<value>Κατά την Επανεκκίνηση Εφαρμογής</value>
</data>
<data name="AutofillServiceNotEnabled" xml:space="preserve">
<value>Η αυτόματη συμπλήρωση διευκολύνει την ασφαλή πρόσβαση στο vault του Bitwarden από άλλες ιστοσελίδες και εφαρμογές. Φαίνεται ότι δεν έχετε ενεργοποιήσει την υπηρεσία αυτόματης συμπλήρωσης για το Bitwarden. Ενεργοποιήστε την αυτόματη συμπλήρωση από τις "Ρυθμίσεις".</value>
<value>Η αυτόματη συμπλήρωση διευκολύνει την ασφαλή πρόσβαση στη λίστα από άλλες ιστοσελίδες και εφαρμογές. Φαίνεται ότι δεν έχετε ενεργοποιήσει την υπηρεσία αυτόματης συμπλήρωσης για το Bitwarden. Ενεργοποιήστε την από τις "Ρυθμίσεις".</value>
</data>
<data name="ThemeAppliedOnRestart" xml:space="preserve">
<value>Οι αλλαγές θεμάτων θα ισχύουν όταν γίνει επανεκκίνηση της εφαρμογής.</value>
@@ -1596,7 +1595,7 @@
<comment>ex. Uppercase the first character of a word.</comment>
</data>
<data name="IncludeNumber" xml:space="preserve">
<value>Συμπερίληψη αριθμών</value>
<value>Συμπερίληψη Αριθμών</value>
</data>
<data name="Download" xml:space="preserve">
<value>Λήψη</value>
@@ -1611,13 +1610,13 @@
<value>Η περίοδος σύνδεσης σας έχει λήξει.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Βιομετρική επαλήθευση</value>
<value>Χρήση βιομετρικής μεθόδου για επαλήθευση.</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Βιομετρική</value>
</data>
<data name="UseBiometricsToUnlock" xml:space="preserve">
<value>Χρήση βιομετρικών για ξεκλείδωμα</value>
<value>Χρήση βιομετρικής μεθόδου για ξεκλείδωμα</value>
</data>
<data name="AccessibilityOverlayPermissionAlert" xml:space="preserve">
<value>Το Bitwarden χρειάζεται προσοχή - Ανατρέξτε στην ενότητα "Υπηρεσία προσβασιμότητας αυτόματης συμπλήρωσης" από τις ρυθμίσεις Bitwarden</value>
@@ -1641,7 +1640,7 @@
<value>Χορηγήθηκε</value>
</data>
<data name="FileFormat" xml:space="preserve">
<value>Μορφή αρχείου</value>
<value>Μορφή Αρχείου</value>
</data>
<data name="ExportVaultMasterPasswordDescription" xml:space="preserve">
<value>Εισαγάγετε τον κύριο κωδικό για εξαγωγή των δεδομένων vault.</value>
@@ -1650,7 +1649,7 @@
<value>Στείλτε έναν κωδικό επαλήθευσης στο email σας</value>
</data>
<data name="CodeSent" xml:space="preserve">
<value>Ο κωδικός στάλθηκε!</value>
<value>Ο Κωδικός Στάλθηκε</value>
</data>
<data name="ConfirmYourIdentity" xml:space="preserve">
<value>Επιβεβαιώστε την ταυτότητα σας για να συνεχίσετε.</value>
@@ -1665,7 +1664,7 @@
<value>Τα κλειδιά κρυπτογράφησης λογαριασμού είναι μοναδικά για κάθε λογαριασμό χρήστη Bitwarden, οπότε δεν μπορείτε να εισάγετε μια κρυπτογραφημένη εξαγωγή σε διαφορετικό λογαριασμό.</value>
</data>
<data name="ExportVaultConfirmationTitle" xml:space="preserve">
<value>Επιβεβαίωση εξαγωγής vault</value>
<value>Επιβεβαίωση εξαγωγής Vault</value>
<comment>Title for the alert to confirm vault exports.</comment>
</data>
<data name="Warning" xml:space="preserve">
@@ -1908,7 +1907,7 @@
<value>Ημερομηνία διαγραφής</value>
</data>
<data name="DeletionTime" xml:space="preserve">
<value>Χρόνος διαγραφής</value>
<value>Χρόνος Διαγραφής</value>
</data>
<data name="DeletionDateInfo" xml:space="preserve">
<value>Το Send θα διαγραφεί οριστικά την καθορισμένη ημερομηνία και ώρα.</value>
@@ -1918,7 +1917,7 @@
<value>Εκκρεμεί διαγραφή</value>
</data>
<data name="ExpirationDate" xml:space="preserve">
<value>Ημερομηνία λήξης</value>
<value>Ημερομηνία Λήξης</value>
</data>
<data name="ExpirationTime" xml:space="preserve">
<value>Χρόνος λήξης</value>
@@ -1944,14 +1943,14 @@
<value>Τρέχων Αριθμός Πρόσβασης</value>
</data>
<data name="NewPassword" xml:space="preserve">
<value>Νέος κωδικός πρόσβασης</value>
<value>Νέος Κωδικός</value>
</data>
<data name="PasswordInfo" xml:space="preserve">
<value>Προαιρετικά απαιτείται κωδικός πρόσβασης για τους χρήστες για να έχουν πρόσβαση σε αυτό το Send.</value>
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
</data>
<data name="RemovePassword" xml:space="preserve">
<value>Αφαίρεση κωδικού πρόσβασης</value>
<value>Αφαίρεση κωδικού</value>
</data>
<data name="AreYouSureRemoveSendPassword" xml:space="preserve">
<value>Είστε βέβαιοι ότι θέλετε να καταργήσετε τον κωδικό πρόσβασης;</value>
@@ -1979,10 +1978,10 @@
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
</data>
<data name="CopyLink" xml:space="preserve">
<value>Αντιγραφή συνδέσμου</value>
<value>Αντιγραφή Συνδέσμου</value>
</data>
<data name="ShareLink" xml:space="preserve">
<value>Κοινοποίηση συνδέσμου</value>
<value>Κοινοποίηση Συνδέσμου</value>
</data>
<data name="SendLink" xml:space="preserve">
<value>Σύνδεσμος Send</value>
@@ -2077,7 +2076,7 @@
<value>Το Captcha απέτυχε. Παρακαλώ προσπάθησε ξανα.</value>
</data>
<data name="UpdatedMasterPassword" xml:space="preserve">
<value>Ενημερωμένος κύριος κωδικός πρόσβασης</value>
<value>Ενημερώθηκε ο κύριος κωδικός πρόσβασης</value>
</data>
<data name="UpdateMasterPassword" xml:space="preserve">
<value>Ενημερώστε τον κύριο κωδικό πρόσβασης</value>
@@ -2092,7 +2091,7 @@
<value>Δεν είναι δυνατή η ενημέρωση του κωδικού πρόσβασης</value>
</data>
<data name="RemoveMasterPassword" xml:space="preserve">
<value>Αφαίρεση κύριου κωδικού πρόσβασης</value>
<value>Αφαίρεση Κύριου Κωδικού Πρόσβασης</value>
</data>
<data name="RemoveMasterPasswordWarning" xml:space="preserve">
<value>{0} χρησιμοποιεί SSO με κρυπτογράφηση διαχείρισης πελατών. Συνεχίζοντας θα καταργήσετε τον Κύριο Κωδικό από το λογαριασμό σας και θα απαιτήσετε SSO για να συνδεθείτε.</value>
@@ -2821,7 +2820,7 @@
<value>Συνέχεια στην επικοινωνία με την υποστήριξη;</value>
</data>
<data name="ContinueToPrivacyPolicy" xml:space="preserve">
<value>Συνέχεια στην πολιτική απορρήτου;</value>
<value>Continue to privacy policy?</value>
</data>
<data name="ContinueToAppStore" xml:space="preserve">
<value>Συνέχεια στο κατάστημα εφαρμογών;</value>
@@ -2843,7 +2842,7 @@
<value>Δεν μπορείτε να βρείτε αυτό που ψάχνετε; Επικοινωνήστε με την υποστήριξη Bitwarden στο bitwarden.com.</value>
</data>
<data name="PrivacyPolicyDescriptionLong" xml:space="preserve">
<value>Δείτε την πολιτική απορρήτου μας στο bitwarden.com.</value>
<value>Check out our privacy policy on bitwarden.com.</value>
</data>
<data name="ExploreMoreFeaturesOfYourBitwardenAccountOnTheWebApp" xml:space="preserve">
<value>Εξερευνήστε περισσότερες δυνατότητες του Bitwarden λογαριασμού σας, στην εφαρμογή διαδικτύου.</value>
@@ -2876,13 +2875,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Ρυθμίστε μια επιλογή κλειδώματος για να αλλάξετε την ενέργεια στη λήξη χρόνου του vault σας.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo σύνδεση δύο βημάτων απαιτείται για το λογαριασμό σας. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Ακολουθήστε τα βήματα από το Duo για να ολοκληρώσετε τη σύνδεση.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Εκκίνηση Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2890,13 +2890,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2823,7 +2823,7 @@ seleccione Agregar TOTP para almacenar la clave de forma segura</value>
<value>¿Continuar con el servicio de asistencia?</value>
</data>
<data name="ContinueToPrivacyPolicy" xml:space="preserve">
<value>¿Continuar a la política de privacidad?</value>
<value>Continue to privacy policy?</value>
</data>
<data name="ContinueToAppStore" xml:space="preserve">
<value>¿Continuar a la App Store?</value>
@@ -2845,7 +2845,7 @@ seleccione Agregar TOTP para almacenar la clave de forma segura</value>
<value>¿No encuentras lo que estás buscando? Contacta con el soporte de Bitwarden en bitwarden.com.</value>
</data>
<data name="PrivacyPolicyDescriptionLong" xml:space="preserve">
<value>Revise nuestra política de privacidad en bitwarden.com.</value>
<value>Check out our privacy policy on bitwarden.com.</value>
</data>
<data name="ExploreMoreFeaturesOfYourBitwardenAccountOnTheWebApp" xml:space="preserve">
<value>Explora más características de tu cuenta de Bitwarden en la aplicación web.</value>
@@ -2878,13 +2878,4 @@ seleccione Agregar TOTP para almacenar la clave de forma segura</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Configura una opción de desbloqueo para cambiar tu acción de tiempo de espera de tu caja fuerte.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Se requiere el inicio de sesión en dos pasos Duo para su cuenta. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Siga los pasos de Duo para terminar de iniciar sesión.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Iniciar Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@ Soovid selle konto peale lülituda?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2875,13 +2875,4 @@ Kontu honetara aldatu nahi duzu?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Haluatko vaihtaa tähän tiliin?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Muuta holvisi aikakatkaisutoimintoa määrittämällä lukituksen avaustapa.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Tilillesi kirjautuminen vaatii Duo-vahvistuksen.</value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Viimeistele kirjautuminen seuraamalla Duon ohjeita.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Avaa Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Gusto mo bang pumunta sa account na ito?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Voulez-vous basculer vers ce compte ?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Configurez une méthode de déverrouillage pour modifier l'action après délai d'expiration de votre coffre.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>L'authentification à deux facteurs Duo est requise pour votre compte. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Lancer Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2879,13 +2879,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2874,13 +2874,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Za promjenu vremena isteka trezora, odredi način otključavanja.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2875,13 +2875,4 @@ Szeretnénk átváltani erre a fiókra?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Állítsunk be egy feloldási módot a széf időkifutási műveletének módosításához.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>DUO kétlépéses bejelentkezés szükséges a fiókhoz. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Kövessük a Duo lépéseit a bejelentkezés befejezéséhez.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Duo indítása</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -172,7 +172,7 @@
<comment>Message shown when interacting with the server</comment>
</data>
<data name="DoYouReallyWantToDelete" xml:space="preserve">
<value>Sei sicuro di volerlo eliminare? Questa operazione è irreversibile.</value>
<value>Vuoi davvero eliminarlo? Questa operazione non può essere annullata.</value>
<comment>Confirmation alert message when deleteing something.</comment>
</data>
<data name="Edit" xml:space="preserve">
@@ -193,10 +193,10 @@
<value>Contattaci</value>
</data>
<data name="EmailUsDescription" xml:space="preserve">
<value>Inviaci un'email per ottenere aiuto o lasciare un feedback.</value>
<value>Mandaci un'email per ottenere aiuto o lasciare un feedback</value>
</data>
<data name="EnterPIN" xml:space="preserve">
<value>Inserisci il tuo PIN.</value>
<value>Digita il tuo PIN.</value>
</data>
<data name="Favorites" xml:space="preserve">
<value>Preferiti</value>
@@ -2876,13 +2876,4 @@ Vuoi passare a questo account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Imposta un metodo di sblocco per modificare l'azione timeout cassaforte.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>保管庫のタイムアウト動作を変更するには、ロック解除方法を設定してください。</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>アカウントには Duo 二段階認証が必要です。</value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Duo の手順に従ってログインを完了してください。</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Duo を起動</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -946,7 +946,7 @@ Nuskaitymas vyks automatiškai.</value>
<value>Negalite naudoti šios funkcijos, kol neatnaujinsite šifravimo raktą.</value>
</data>
<data name="EncryptionKeyMigrationRequiredDescriptionLong" xml:space="preserve">
<value>Privaloma migruoti šifravimo raktą. Prašome prisijungti per internetinę saugyklą norint jį atnaujinti.</value>
<value>Encryption key migration required. Please login through the web vault to update your encryption key.</value>
</data>
<data name="LearnMore" xml:space="preserve">
<value>Sužinoti daugiau</value>
@@ -1741,7 +1741,7 @@ Nuskaitymas vyks automatiškai.</value>
<comment>Confirmation alert message when soft-deleting a cipher.</comment>
</data>
<data name="AccountBiometricInvalidated" xml:space="preserve">
<value>Biometrinis atrakinimas išjungtas, kol neįvesite pagrindinio slaptažodžio.</value>
<value>Biometric unlock for this account is disabled pending verification of master password.</value>
</data>
<data name="AccountBiometricInvalidatedExtension" xml:space="preserve">
<value>Autofill biometric unlock for this account is disabled pending verification of master password.</value>
@@ -2739,7 +2739,7 @@ Ar norite pereiti prie šios paskyros?</value>
<value>Cannot edit multiple URIs at once</value>
</data>
<data name="LoginApproved" xml:space="preserve">
<value>Prisijungimas patvirtintas</value>
<value>Login approved</value>
</data>
<data name="LogInWithDeviceMustBeSetUpInTheSettingsOfTheBitwardenAppNeedAnotherOption" xml:space="preserve">
<value>Log in with device must be set up in the settings of the Bitwarden app. Need another option?</value>
@@ -2748,13 +2748,13 @@ Ar norite pereiti prie šios paskyros?</value>
<value>Log in with device</value>
</data>
<data name="LoggingInOn" xml:space="preserve">
<value>Prisijungiama prie</value>
<value>Logging in on</value>
</data>
<data name="Vault" xml:space="preserve">
<value>Vault</value>
</data>
<data name="Appearance" xml:space="preserve">
<value>Išvaizda</value>
<value>Appearance</value>
</data>
<data name="AccountSecurity" xml:space="preserve">
<value>Account security</value>
@@ -2769,7 +2769,7 @@ Ar norite pereiti prie šios paskyros?</value>
<value>Copy app information</value>
</data>
<data name="SyncNow" xml:space="preserve">
<value>Sinchronizuoti dabar</value>
<value>Sync now</value>
</data>
<data name="UnlockOptions" xml:space="preserve">
<value>Unlock options</value>
@@ -2859,31 +2859,22 @@ Ar norite pereiti prie šios paskyros?</value>
<value>Choose the dark theme to use when your devices dark mode is in use</value>
</data>
<data name="CreatedXY" xml:space="preserve">
<value>Sukurta {0}, {1}</value>
<value>Created {0}, {1}</value>
<comment>To state the date/time in which the cipher was created: Created 03/21/2023, 09:25 AM. First parameter is the date and the second parameter is the time.</comment>
</data>
<data name="TooManyAttempts" xml:space="preserve">
<value>Viršytas bandymų skaičius</value>
<value>Too many attempts</value>
</data>
<data name="AccountLoggedOutBiometricExceeded" xml:space="preserve">
<value>Atsijungta nuo paskyros.</value>
<value>Account logged out.</value>
</data>
<data name="YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" xml:space="preserve">
<value>Dėl pasikeitusių Jūsų organizacijos nuostatų, Jums reikia nustatyti pagrindinį slaptažodį.</value>
<value>Your organization permissions were updated, requiring you to set a master password.</value>
</data>
<data name="YourOrganizationRequiresYouToSetAMasterPassword" xml:space="preserve">
<value>Jūsų organizacijos nuostatos reikalauja Jus nustatyti pagrindinį slaptažodį.</value>
<value>Your organization requires you to set a master password.</value>
</data>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Nustatykite atrakinimo būdą, kad pakeistumėte Jūsų saugyklos laiko limito veiksmą.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Paleisti „Duo“</value>
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Vai pārslēgties uz šo kontu?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Jāuzstāda atslēgšanas iespēja, lai mainītu glabātavas noildzes darbību.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Kontam ir nepieciešama Duo divpakāpju pieteikšanās. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Jāseko Duo norādēm, lai pabeigtu pieteikšanos.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Palaist Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Vil du bytte til denne kontoen?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -371,7 +371,7 @@
<comment>Label for a username.</comment>
</data>
<data name="ValidationFieldRequired" xml:space="preserve">
<value>Vul het veld {0} in.</value>
<value>Het {0}-veld is vereist.</value>
<comment>Validation message for when a form field is left blank and is required to be entered.</comment>
</data>
<data name="ValueHasBeenCopied" xml:space="preserve">
@@ -946,7 +946,7 @@ Het scannen gebeurt automatisch.</value>
<value>Je kunt deze functie pas gebruiken als je je encryptiesleutel bijwerkt.</value>
</data>
<data name="EncryptionKeyMigrationRequiredDescriptionLong" xml:space="preserve">
<value>Migratie van de encryptiesleutel vereist. Login via de website om je encryptiesleutel bij te werken.</value>
<value>Encryption key migration required. Please login through the web vault to update your encryption key.</value>
</data>
<data name="LearnMore" xml:space="preserve">
<value>Meer informatie</value>
@@ -996,7 +996,7 @@ Het scannen gebeurt automatisch.</value>
<value>Beveiligingscode kopiëren</value>
</data>
<data name="Number" xml:space="preserve">
<value>Kaartnummer</value>
<value>Kaartummer</value>
</data>
<data name="SecurityCode" xml:space="preserve">
<value>Beveiligingscode</value>
@@ -2623,22 +2623,22 @@ Wilt u naar dit account wisselen?</value>
<value>Huidig hoofdwachtwoord</value>
</data>
<data name="LoggedIn" xml:space="preserve">
<value>Ingelogd!</value>
<value>Logged in!</value>
</data>
<data name="ApproveWithMyOtherDevice" xml:space="preserve">
<value>Goedkeuren met mijn andere apparaat</value>
<value>Approve with my other device</value>
</data>
<data name="RequestAdminApproval" xml:space="preserve">
<value>Goedkeuring van beheerder vragen</value>
<value>Request admin approval</value>
</data>
<data name="ApproveWithMasterPassword" xml:space="preserve">
<value>Goedkeuren met hoofdwachtwoord</value>
<value>Approve with master password</value>
</data>
<data name="TurnOffUsingPublicDevice" xml:space="preserve">
<value>Uitschakelen met een openbaar apparaat</value>
<value>Turn off using a public device</value>
</data>
<data name="RememberThisDevice" xml:space="preserve">
<value>Dit apparaat onthouden</value>
<value>Remember this device</value>
</data>
<data name="Passkey" xml:space="preserve">
<value>Passkey</value>
@@ -2668,7 +2668,7 @@ Wilt u naar dit account wisselen?</value>
<value>Hulptekst hoofdwachtwoord opnieuw vragen</value>
</data>
<data name="UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve" xml:space="preserve">
<value>Ontgrendelen kan mislukken als er onvoldoende geheugen is. Verminder je KDF-geheugeninstellingen of stel biometrische ontgrendeling in om dit op te lossen.</value>
<value>Unlocking may fail due to insufficient memory. Decrease your KDF memory settings or set up biometric unlock to resolve.</value>
</data>
<data name="InvalidAPIKey" xml:space="preserve">
<value>Ongeldige API-sleutel</value>
@@ -2677,22 +2677,22 @@ Wilt u naar dit account wisselen?</value>
<value>Ongeldige API-token</value>
</data>
<data name="AdminApprovalRequested" xml:space="preserve">
<value>Goedkeuring van beheerder aangevraagd</value>
<value>Admin approval requested</value>
</data>
<data name="YourRequestHasBeenSentToYourAdmin" xml:space="preserve">
<value>Je verzoek is naar je beheerder verstuurd.</value>
<value>Your request has been sent to your admin.</value>
</data>
<data name="YouWillBeNotifiedOnceApproved" xml:space="preserve">
<value>Je krijgt een melding zodra je bent goedgekeurd.</value>
<value>You will be notified once approved. </value>
</data>
<data name="TroubleLoggingIn" xml:space="preserve">
<value>Problemen met inloggen?</value>
<value>Trouble logging in?</value>
</data>
<data name="LoggingInAsX" xml:space="preserve">
<value>Inloggen als {0}</value>
<value>Logging in as {0}</value>
</data>
<data name="VaultTimeoutActionChangedToLogOut" xml:space="preserve">
<value>Kluis time-out actie gewijzigd naar uitloggen</value>
<value>Vault timeout action changed to log out</value>
</data>
<data name="BlockAutoFill" xml:space="preserve">
<value>Automatisch aanvullen blokkeren</value>
@@ -2738,16 +2738,16 @@ Wilt u naar dit account wisselen?</value>
<value>Meerdere URIs in één keer bewerken kan niet</value>
</data>
<data name="LoginApproved" xml:space="preserve">
<value>Inloggen goedgekeurd</value>
<value>Login approved</value>
</data>
<data name="LogInWithDeviceMustBeSetUpInTheSettingsOfTheBitwardenAppNeedAnotherOption" xml:space="preserve">
<value>Je moet Inloggen met apparaat instellen in de instellingen van de Bitwarden-app. Behoefte aan een andere mogelijkheid?</value>
<value>Log in with device must be set up in the settings of the Bitwarden app. Need another option?</value>
</data>
<data name="LogInWithDevice" xml:space="preserve">
<value>Inloggen met apparaat</value>
<value>Log in with device</value>
</data>
<data name="LoggingInOn" xml:space="preserve">
<value>Inloggen op</value>
<value>Logging in on</value>
</data>
<data name="Vault" xml:space="preserve">
<value>Kluis</value>
@@ -2862,27 +2862,18 @@ Wilt u naar dit account wisselen?</value>
<comment>To state the date/time in which the cipher was created: Created 03/21/2023, 09:25 AM. First parameter is the date and the second parameter is the time.</comment>
</data>
<data name="TooManyAttempts" xml:space="preserve">
<value>Te veel pogingen</value>
<value>Too many attempts</value>
</data>
<data name="AccountLoggedOutBiometricExceeded" xml:space="preserve">
<value>Account uitgelogd.</value>
<value>Account logged out.</value>
</data>
<data name="YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" xml:space="preserve">
<value>De organisatierechten zijn bijgewerkt, je moet een hoofdwachtwoord instellen.</value>
<value>Your organization permissions were updated, requiring you to set a master password.</value>
</data>
<data name="YourOrganizationRequiresYouToSetAMasterPassword" xml:space="preserve">
<value>Je organisatie vereist dat je een hoofdwachtwoord instelt.</value>
<value>Your organization requires you to set a master password.</value>
</data>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Stel een ontgrendelingsmethode in om je kluis time-out actie te wijzigen.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Jouw account vereist Duo-tweestapsaanmelding. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Volg de stappen van Duo om in te loggen.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Duo starten</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@ Czy chcesz przełączyć się na to konto?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Ustaw opcje odblokowania, aby zmienić czas blokowania sejfu.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Dwustopniowe logowanie Duo jest wymagane dla Twojego konta. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Wykonaj kroki od Duo, aby zakończyć logowanie.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Uruchom Duo</value>
</data>
</root>

View File

@@ -2822,16 +2822,16 @@ Você deseja mudar para esta conta?</value>
<value>Continuar e contatar o suporte?</value>
</data>
<data name="ContinueToPrivacyPolicy" xml:space="preserve">
<value>Continuar para a política de privacidade?</value>
<value>Continue to privacy policy?</value>
</data>
<data name="ContinueToAppStore" xml:space="preserve">
<value>Continuar para a loja de apps?</value>
</data>
<data name="TwoStepLoginDescriptionLong" xml:space="preserve">
<value>Torne sua conta mais segura configurando o login em duas etapas no aplicativo web do Bitwarden.</value>
<value>Make your account more secure by setting up two-step login in the Bitwarden web app.</value>
</data>
<data name="ChangeMasterPasswordDescriptionLong" xml:space="preserve">
<value>Você pode alterar a sua senha mestra no aplicativo web Bitwarden.</value>
<value>You can change your master password on the Bitwarden web app.</value>
</data>
<data name="YouCanImportDataToYourVaultOnX" xml:space="preserve">
<value>Você pode importar dados para o seu cofre no {0}.</value>
@@ -2841,19 +2841,19 @@ Você deseja mudar para esta conta?</value>
<value>Saiba mais sobre como usar o Bitwarden no centro de ajuda.</value>
</data>
<data name="ContactSupportDescriptionLong" xml:space="preserve">
<value>Não encontrou o que está procurando? Entre em contato com o suporte do Bitwarden em bitwarden.com.</value>
<value>Cant find what you are looking for? Reach out to Bitwarden support on bitwarden.com.</value>
</data>
<data name="PrivacyPolicyDescriptionLong" xml:space="preserve">
<value>Confira a nossa política de privacidade em bitwarden.com.</value>
<value>Check out our privacy policy on bitwarden.com.</value>
</data>
<data name="ExploreMoreFeaturesOfYourBitwardenAccountOnTheWebApp" xml:space="preserve">
<value>Explore mais recursos da sua conta no Bitwarden no aplicativo web.</value>
<value>Explore more features of your Bitwarden account on the web app.</value>
</data>
<data name="LearnAboutOrganizationsDescriptionLong" xml:space="preserve">
<value>O Bitwarden permite compartilhar os seus itens do cofre com outros ao utilizar uma organização. Gostaria de visitar o site bitwarden.com para saber mais?</value>
<value>Bitwarden allows you to share your vault items with others by using an organization. Learn more on the bitwarden.com website.</value>
</data>
<data name="RateAppDescriptionLong" xml:space="preserve">
<value>Ajude outros a descobrir se o Bitwarden é adequado para eles. Visite a loja de aplicativos e deixe uma avaliação agora.</value>
<value>Help others find out if Bitwarden is right for them. Visit the app store and leave a rating now.</value>
</data>
<data name="DefaultDarkThemeDescriptionLong" xml:space="preserve">
<value>Usar o tema escuro quando o modo escuro do seu dispositivo estiver ativado</value>
@@ -2869,21 +2869,12 @@ Você deseja mudar para esta conta?</value>
<value>Conta desconectada.</value>
</data>
<data name="YourOrganizationPermissionsWereUpdatedRequeringYouToSetAMasterPassword" xml:space="preserve">
<value>As permissões da sua organização foram atualizadas, exigindo que você defina uma senha mestra.</value>
<value>Your organization permissions were updated, requiring you to set a master password.</value>
</data>
<data name="YourOrganizationRequiresYouToSetAMasterPassword" xml:space="preserve">
<value>Sua organização requer que você defina uma senha mestra.</value>
<value>Your organization requires you to set a master password.</value>
</data>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Configure um método de desbloqueio para alterar o tempo limite do cofre.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
</root>

View File

@@ -1570,7 +1570,7 @@ A leitura será efetuada automaticamente.</value>
<comment>'Nord' is the name of a specific color scheme. It should not be translated.</comment>
</data>
<data name="SolarizedDark" xml:space="preserve">
<value>Solarized (escuro)</value>
<value>Solarized Dark</value>
<comment>'Solarized Dark' is the name of a specific color scheme. It should not be translated.</comment>
</data>
<data name="AutofillBlockedUris" xml:space="preserve">
@@ -2826,7 +2826,7 @@ Deseja mudar para esta conta?</value>
<value>Continuar para a loja de aplicações?</value>
</data>
<data name="TwoStepLoginDescriptionLong" xml:space="preserve">
<value>Torne a sua conta mais segura configurando a verificação de dois passos na aplicação Web Bitwarden.</value>
<value>Torne a sua conta mais segura configurando a verificação em dois passos na aplicação Web Bitwarden.</value>
</data>
<data name="ChangeMasterPasswordDescriptionLong" xml:space="preserve">
<value>Pode alterar a sua palavra-passe mestra na aplicação Web Bitwarden.</value>
@@ -2875,13 +2875,4 @@ Deseja mudar para esta conta?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Configure uma opção de desbloqueio para alterar a ação de tempo limite do seu cofre.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>A verificação de dois passos Duo é necessária para a sua conta. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Siga os passos do Duo para concluir o início de sessão.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Iniciar o Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@ Doriți să comutați la acest cont?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2878,13 +2878,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Настройте опцию разблокировки для изменения действия по тайм-ауту хранилища.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Для вашего аккаунта требуется двухэтапная аутентификация Duo. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Следуйте указаниям Duo, чтобы завершить авторизацию.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Запустить Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@ Chcete prepnúť na toto konto?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Nastavte možnosť odomknutia, aby ste zmenili akciu pri vypršaní času trezoru.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Pre váš účet je potrebné dvojstupňové prihlásenie Duo. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Postupujte podľa krokov z aplikácie Duo a dokončite prihlasovanie.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Spustiť Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2878,13 +2878,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Подесите опцију откључавања да бисте променили радњу временског ограничења сефа.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo пријава у два корака је потребна за ваш налог. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Пратите кораке од Duo да завршите пријављивање.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Покренути Duo</value>
</data>
</root>

View File

@@ -2878,13 +2878,4 @@ Vill du byta till detta konto?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Ställ in ett upplåsningsalternativ för att ändra vad som händer när tidsgränsen uppnås.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo tvåstegsverifiering krävs för ditt konto. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Följ stegen från Duo för att slutföra inloggningen.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Starta Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2884,13 +2884,4 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -2875,13 +2875,4 @@ Bu hesaba geçmek ister misiniz?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Kasa zaman aşımı eyleminizi değiştirmek için bir kilit açma yöntemi ayarlayın.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Hesabınız için Duo'ya iki adımlı giriş yapmanız gerekiyor. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Oturum açmayı tamamlamak için Duo'daki adımları izleyin.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Duo'yu başlat</value>
</data>
</root>

View File

@@ -2447,7 +2447,7 @@
<value>Сталася невідома помилка {0}.</value>
</data>
<data name="PlusAddressedEmailDescription" xml:space="preserve">
<value>Використовуйте розширені можливості адрес вашого провайдера електронної пошти</value>
<value>Використовуйте розширені можливості адрес вашого постачальника електронної пошти</value>
</data>
<data name="CatchAllEmailDescription" xml:space="preserve">
<value>Використовуйте свою скриньку вхідних Catch-All власного домену.</value>
@@ -2876,13 +2876,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Налаштуйте спосіб розблокування, щоб змінити час очікування сховища.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Для вашого облікового запису необхідна двоетапна перевірка з Duo. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Виконайте дії з Duo для завершення входу.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Запустити Duo</value>
</data>
</root>

View File

@@ -2877,13 +2877,4 @@ Bạn có muốn chuyển sang tài khoản này không?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Thiết lập khóa khi hết thời gian chờ kho.</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Tài khoản của bạn bắt buộc đăng nhập 2 bước Dou. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Làm theo các bước từ Dou để hoàn tất đăng nhập.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Khởi động Dou</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>设置解锁选项以更改您的密码库超时操作。</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>您的账户要求使用 Duo 两步登录。 </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>按照 Duo 的步骤完成登录。</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>启动 Duo</value>
</data>
</root>

View File

@@ -2876,13 +2876,4 @@
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>設定一個解鎖方式來變更您的密碼庫逾時動作。</value>
</data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root>

View File

@@ -143,10 +143,6 @@
Value="Medium" />
<Setter Property="Margin"
Value="0, 5, 0, 0" />
<Setter Property="CornerRadius"
Value="5" />
<Setter Property="MinimumHeightRequest"
Value="45" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
@@ -187,6 +183,10 @@
Value="Bold" />
<Setter Property="Margin"
Value="0, 5, 0, 0" />
<Setter Property="CornerRadius"
Value="5" />
<Setter Property="MinimumHeightRequest"
Value="45" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
@@ -223,6 +223,8 @@
Value="{DynamicResource ButtonTextColorOpacity}" />
<Setter Property="FontSize"
Value="Medium" />
<Setter Property="CornerRadius"
Value="5" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">

View File

@@ -32,4 +32,14 @@
IsActive = false;
}
}
public class MainAppWindow : ResumeWindow
{
public MainAppWindow(Page page) : base(page) { }
}
public class AutoFillWindow : ResumeWindow
{
public AutoFillWindow(Page page) : base(page){ }
}
}

View File

@@ -10,8 +10,7 @@ namespace Bit.Core.Services
{
private readonly ConcurrentDictionary<AwaiterPrecondition, TaskCompletionSource<bool>> _preconditionsTasks = new ConcurrentDictionary<AwaiterPrecondition, TaskCompletionSource<bool>>
{
[AwaiterPrecondition.EnvironmentUrlsInited] = new TaskCompletionSource<bool>(),
[AwaiterPrecondition.AndroidWindowCreated] = new TaskCompletionSource<bool>()
[AwaiterPrecondition.EnvironmentUrlsInited] = new TaskCompletionSource<bool>()
};
public Task GetAwaiterForPrecondition(AwaiterPrecondition awaiterPrecondition)

View File

@@ -1,295 +0,0 @@
#if ANDROID
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Security;
using Android.Security.Keystore;
using Java.Security;
using Javax.Crypto;
using Javax.Crypto.Spec;
using System.Text;
namespace Bit.Core.Services;
class AndroidKeyStore
{
const string androidKeyStore = "AndroidKeyStore"; // this is an Android const value
const string aesAlgorithm = "AES";
const string cipherTransformationAsymmetric = "RSA/ECB/PKCS1Padding";
const string cipherTransformationSymmetric = "AES/GCM/NoPadding";
const string prefsMasterKey = "SecureStorageKey";
const int initializationVectorLen = 12; // Android supports an IV of 12 for AES/GCM
internal AndroidKeyStore(Context context, string keystoreAlias, bool alwaysUseAsymmetricKeyStorage)
{
alwaysUseAsymmetricKey = alwaysUseAsymmetricKeyStorage;
appContext = context;
alias = keystoreAlias;
keyStore = KeyStore.GetInstance(androidKeyStore);
keyStore.Load(null);
}
readonly Context appContext;
readonly string alias;
readonly bool alwaysUseAsymmetricKey;
readonly string useSymmetricPreferenceKey = "essentials_use_symmetric";
KeyStore keyStore;
bool useSymmetric = false;
ISecretKey GetKey()
{
// check to see if we need to get our key from past-versions or newer versions.
// we want to use symmetric if we are >= 23 or we didn't set it previously.
var hasApiLevel = Build.VERSION.SdkInt >= BuildVersionCodes.M;
useSymmetric = Preferences.Get(useSymmetricPreferenceKey, hasApiLevel, alias);
// If >= API 23 we can use the KeyStore's symmetric key
if (useSymmetric && !alwaysUseAsymmetricKey)
return GetSymmetricKey();
// NOTE: KeyStore in < API 23 can only store asymmetric keys
// specifically, only RSA/ECB/PKCS1Padding
// So we will wrap our symmetric AES key we just generated
// with this and save the encrypted/wrapped key out to
// preferences for future use.
// ECB should be fine in this case as the AES key should be
// contained in one block.
// Get the asymmetric key pair
var keyPair = GetAsymmetricKeyPair();
var existingKeyStr = Preferences.Get(prefsMasterKey, null, alias);
if (!string.IsNullOrEmpty(existingKeyStr))
{
try
{
var wrappedKey = Convert.FromBase64String(existingKeyStr);
var unwrappedKey = UnwrapKey(wrappedKey, keyPair.Private);
var kp = unwrappedKey.JavaCast<ISecretKey>();
return kp;
}
catch (InvalidKeyException ikEx)
{
System.Diagnostics.Debug.WriteLine(
$"Unable to unwrap key: Invalid Key. This may be caused by system backup or upgrades. All secure storage items will now be removed. {ikEx.Message}");
}
catch (IllegalBlockSizeException ibsEx)
{
System.Diagnostics.Debug.WriteLine(
$"Unable to unwrap key: Illegal Block Size. This may be caused by system backup or upgrades. All secure storage items will now be removed. {ibsEx.Message}");
}
catch (BadPaddingException paddingEx)
{
System.Diagnostics.Debug.WriteLine(
$"Unable to unwrap key: Bad Padding. This may be caused by system backup or upgrades. All secure storage items will now be removed. {paddingEx.Message}");
}
LegacySecureStorage.RemoveAll();
}
var keyGenerator = KeyGenerator.GetInstance(aesAlgorithm);
var defSymmetricKey = keyGenerator.GenerateKey();
var newWrappedKey = WrapKey(defSymmetricKey, keyPair.Public);
Preferences.Set(prefsMasterKey, Convert.ToBase64String(newWrappedKey), alias);
return defSymmetricKey;
}
// API 23+ Only
#pragma warning disable CA1416
ISecretKey GetSymmetricKey()
{
Preferences.Set(useSymmetricPreferenceKey, true, alias);
var existingKey = keyStore.GetKey(alias, null);
if (existingKey != null)
{
var existingSecretKey = existingKey.JavaCast<ISecretKey>();
return existingSecretKey;
}
var keyGenerator = KeyGenerator.GetInstance(KeyProperties.KeyAlgorithmAes, androidKeyStore);
var builder = new KeyGenParameterSpec.Builder(alias, KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
.SetBlockModes(KeyProperties.BlockModeGcm)
.SetEncryptionPaddings(KeyProperties.EncryptionPaddingNone)
.SetRandomizedEncryptionRequired(false);
keyGenerator.Init(builder.Build());
return keyGenerator.GenerateKey();
}
#pragma warning restore CA1416
KeyPair GetAsymmetricKeyPair()
{
// set that we generated keys on pre-m device.
Preferences.Set(useSymmetricPreferenceKey, false, alias);
var asymmetricAlias = $"{alias}.asymmetric";
var privateKey = keyStore.GetKey(asymmetricAlias, null)?.JavaCast<IPrivateKey>();
var publicKey = keyStore.GetCertificate(asymmetricAlias)?.PublicKey;
// Return the existing key if found
if (privateKey != null && publicKey != null)
return new KeyPair(publicKey, privateKey);
var originalLocale = Java.Util.Locale.Default;
try
{
// Force to english for known bug in date parsing:
// https://issuetracker.google.com/issues/37095309
SetLocale(Java.Util.Locale.English);
// Otherwise we create a new key
#pragma warning disable CA1416
var generator = KeyPairGenerator.GetInstance(KeyProperties.KeyAlgorithmRsa, androidKeyStore);
#pragma warning restore CA1416
var end = DateTime.UtcNow.AddYears(20);
var startDate = new Java.Util.Date();
#pragma warning disable CS0618 // Type or member is obsolete
var endDate = new Java.Util.Date(end.Year, end.Month, end.Day);
#pragma warning restore CS0618 // Type or member is obsolete
#pragma warning disable CS0618
var builder = new KeyPairGeneratorSpec.Builder(Platform.AppContext)
.SetAlias(asymmetricAlias)
.SetSerialNumber(Java.Math.BigInteger.One)
.SetSubject(new Javax.Security.Auth.X500.X500Principal($"CN={asymmetricAlias} CA Certificate"))
.SetStartDate(startDate)
.SetEndDate(endDate);
generator.Initialize(builder.Build());
#pragma warning restore CS0618
return generator.GenerateKeyPair();
}
finally
{
SetLocale(originalLocale);
}
}
byte[] WrapKey(IKey keyToWrap, IKey withKey)
{
var cipher = Cipher.GetInstance(cipherTransformationAsymmetric);
cipher.Init(CipherMode.WrapMode, withKey);
return cipher.Wrap(keyToWrap);
}
#pragma warning disable CA1416
IKey UnwrapKey(byte[] wrappedData, IKey withKey)
{
var cipher = Cipher.GetInstance(cipherTransformationAsymmetric);
cipher.Init(CipherMode.UnwrapMode, withKey);
var unwrapped = cipher.Unwrap(wrappedData, KeyProperties.KeyAlgorithmAes, KeyType.SecretKey);
return unwrapped;
}
#pragma warning restore CA1416
internal byte[] Encrypt(string data)
{
var key = GetKey();
// Generate initialization vector
var iv = new byte[initializationVectorLen];
var sr = new SecureRandom();
sr.NextBytes(iv);
Cipher cipher;
// Attempt to use GCMParameterSpec by default
try
{
cipher = Cipher.GetInstance(cipherTransformationSymmetric);
cipher.Init(CipherMode.EncryptMode, key, new GCMParameterSpec(128, iv));
}
catch (InvalidAlgorithmParameterException)
{
// If we encounter this error, it's likely an old bouncycastle provider version
// is being used which does not recognize GCMParameterSpec, but should work
// with IvParameterSpec, however we only do this as a last effort since other
// implementations will error if you use IvParameterSpec when GCMParameterSpec
// is recognized and expected.
cipher = Cipher.GetInstance(cipherTransformationSymmetric);
cipher.Init(CipherMode.EncryptMode, key, new IvParameterSpec(iv));
}
var decryptedData = Encoding.UTF8.GetBytes(data);
var encryptedBytes = cipher.DoFinal(decryptedData);
// Combine the IV and the encrypted data into one array
var r = new byte[iv.Length + encryptedBytes.Length];
Buffer.BlockCopy(iv, 0, r, 0, iv.Length);
Buffer.BlockCopy(encryptedBytes, 0, r, iv.Length, encryptedBytes.Length);
return r;
}
internal string Decrypt(byte[] data)
{
if (data.Length < initializationVectorLen)
return null;
var key = GetKey();
// IV will be the first 16 bytes of the encrypted data
var iv = new byte[initializationVectorLen];
Buffer.BlockCopy(data, 0, iv, 0, initializationVectorLen);
Cipher cipher;
// Attempt to use GCMParameterSpec by default
try
{
cipher = Cipher.GetInstance(cipherTransformationSymmetric);
cipher.Init(CipherMode.DecryptMode, key, new GCMParameterSpec(128, iv));
}
catch (InvalidAlgorithmParameterException)
{
// If we encounter this error, it's likely an old bouncycastle provider version
// is being used which does not recognize GCMParameterSpec, but should work
// with IvParameterSpec, however we only do this as a last effort since other
// implementations will error if you use IvParameterSpec when GCMParameterSpec
// is recognized and expected.
cipher = Cipher.GetInstance(cipherTransformationSymmetric);
cipher.Init(CipherMode.DecryptMode, key, new IvParameterSpec(iv));
}
// Decrypt starting after the first 16 bytes from the IV
var decryptedData = cipher.DoFinal(data, initializationVectorLen, data.Length - initializationVectorLen);
return Encoding.UTF8.GetString(decryptedData);
}
internal void SetLocale(Java.Util.Locale locale)
{
Java.Util.Locale.Default = locale;
var resources = appContext.Resources;
var config = resources.Configuration;
if (Build.VERSION.SdkInt >= BuildVersionCodes.N)
config.SetLocale(locale);
else
#pragma warning disable CS0618 // Type or member is obsolete
config.Locale = locale;
#pragma warning restore CS0618 // Type or member is obsolete
#pragma warning disable CS0618 // Type or member is obsolete
resources.UpdateConfiguration(config, resources.DisplayMetrics);
#pragma warning restore CS0618 // Type or member is obsolete
}
}
#endif

View File

@@ -1,128 +0,0 @@
#if IOS
using System.Diagnostics;
using Foundation;
using Security;
namespace Bit.Core.Services;
internal class KeyChain
{
SecAccessible accessible;
internal KeyChain(SecAccessible accessible) =>
this.accessible = accessible;
SecRecord ExistingRecordForKey(string key, string service)
{
return new SecRecord(SecKind.GenericPassword)
{
Account = key,
Service = service
};
}
internal string ValueForKey(string key, string service)
{
using (var record = ExistingRecordForKey(key, service))
using (var match = SecKeyChain.QueryAsRecord(record, out var resultCode))
{
if (resultCode == SecStatusCode.Success)
return NSString.FromData(match.ValueData, NSStringEncoding.UTF8);
else
return null;
}
}
internal void SetValueForKey(string value, string key, string service)
{
using (var record = ExistingRecordForKey(key, service))
{
if (string.IsNullOrEmpty(value))
{
if (!string.IsNullOrEmpty(ValueForKey(key, service)))
RemoveRecord(record);
return;
}
// if the key already exists, remove it
if (!string.IsNullOrEmpty(ValueForKey(key, service)))
RemoveRecord(record);
}
using (var newRecord = CreateRecordForNewKeyValue(key, value, service))
{
var result = SecKeyChain.Add(newRecord);
switch (result)
{
case SecStatusCode.DuplicateItem:
{
Debug.WriteLine("Duplicate item found. Attempting to remove and add again.");
// try to remove and add again
if (Remove(key, service))
{
result = SecKeyChain.Add(newRecord);
if (result != SecStatusCode.Success)
throw new Exception($"Error adding record: {result}");
}
else
{
Debug.WriteLine("Unable to remove key.");
}
}
break;
case SecStatusCode.Success:
return;
default:
throw new Exception($"Error adding record: {result}");
}
}
}
internal bool Remove(string key, string service)
{
using (var record = ExistingRecordForKey(key, service))
using (var match = SecKeyChain.QueryAsRecord(record, out var resultCode))
{
if (resultCode == SecStatusCode.Success)
{
RemoveRecord(record);
return true;
}
}
return false;
}
internal void RemoveAll(string service)
{
using (var query = new SecRecord(SecKind.GenericPassword) { Service = service })
{
SecKeyChain.Remove(query);
}
}
SecRecord CreateRecordForNewKeyValue(string key, string value, string service)
{
return new SecRecord(SecKind.GenericPassword)
{
Account = key,
Service = service,
Label = key,
Accessible = accessible,
ValueData = NSData.FromString(value, NSStringEncoding.UTF8),
};
}
bool RemoveRecord(SecRecord record)
{
var result = SecKeyChain.Remove(record);
if (result != SecStatusCode.Success && result != SecStatusCode.ItemNotFound)
throw new Exception($"Error removing record: {result}");
return true;
}
}
#endif

View File

@@ -1,108 +0,0 @@
#nullable enable
#if IOS
using Security;
#endif
#if ANDROID
using Javax.Crypto;
#endif
namespace Bit.Core.Services;
public class LegacySecureStorage
{
internal static readonly string Alias = $"{AppInfo.PackageName}.xamarinessentials";
#if IOS
private static SecAccessible DefaultAccessible { get; set; } = SecAccessible.AfterFirstUnlock;
#endif
public static Task<string?> GetAsync(string key)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(nameof(key));
#if ANDROID
return Task.Run(() =>
{
object locker = new object();
string? encVal = Preferences.Get(key, null, Alias);
if (!string.IsNullOrEmpty(encVal))
{
try
{
byte[] encData = Convert.FromBase64String(encVal);
lock (locker)
{
AndroidKeyStore keyStore = new AndroidKeyStore(Platform.AppContext, Alias, false);
return keyStore.Decrypt(encData);
}
}
catch (AEADBadTagException)
{
System.Diagnostics.Debug.WriteLine($"Unable to decrypt key, {key}, which is likely due to an app uninstall. Removing old key and returning null.");
Remove(key);
}
}
return null;
});
#elif IOS
var keyChain = new KeyChain(DefaultAccessible);
return Task.FromResult<string?>(keyChain.ValueForKey(key, Alias));
#else
return Task.FromResult((string?)null);
#endif
}
public static Task SetAsync(string key, string value)
{
#if ANDROID
return Task.Run(() =>
{
var context = Platform.AppContext;
byte[] encryptedData = null;
object locker = new object();
lock (locker)
{
AndroidKeyStore keyStore = new AndroidKeyStore(Platform.AppContext, Alias, false);
encryptedData = keyStore.Encrypt(value);
}
var encStr = Convert.ToBase64String(encryptedData);
Preferences.Set(key, encStr, Alias);
});
#elif IOS
KeyChain keyChain = new KeyChain(DefaultAccessible);
keyChain.SetValueForKey(value, key, Alias);
#endif
return Task.CompletedTask;
}
public static bool Remove(string key)
{
#if ANDROID
Preferences.Remove(key, Alias);
return true;
#elif IOS
var keyChain = new KeyChain(DefaultAccessible);
return keyChain.Remove(key, Alias);
#else
return false;
#endif
}
public static void RemoveAll()
{
#if ANDROID
Preferences.Clear(Alias);
#elif IOS
var keyChain = new KeyChain(DefaultAccessible);
keyChain.RemoveAll(Alias);
#endif
}
}

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