diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5e06fa5de..6bb38c214 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,18 +1,26 @@ -# Please sort lines alphabetically, this will ensure we don't accidentally add duplicates. +# Please sort into logical groups with comment headers. Sort groups in order of specificity. +# For example, default owners should always be the first group. +# Sort lines alphabetically within these groups to avoid accidentally adding duplicates. # # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners -# The following owners will be the default owners for everything in the repo. -# Unless a later match takes precedence -# @bitwarden/tech-leads +# Default file owners +* @bitwarden/dept-development-mobile -@bitwarden/dept-development-mobile +# DevOps for Actions and other workflow changes +.github/workflows @bitwarden/dept-devops + +# DevOps for Version Bumping +src/App/Platforms/Android/AndroidManifest.xml +src/iOS.Autofill/Info.plist +src/iOS.Extension/Info.plist +src/iOS.ShareExtension/Info.plist +src/App/Platforms/iOS/Info.plist ## Auth team files ## ## Platform team files ## appIcons @bitwarden/team-platform-dev -build.cake @bitwarden/team-platform-dev ## Vault team files ## src/watchOS @bitwarden/team-vault-dev @@ -20,12 +28,18 @@ src/watchOS @bitwarden/team-vault-dev ## Tools team files ## src/Core/Services/EmailForwarders @bitwarden/team-tools-dev - ## Crowdin Sync files ## -src/App/Resources @bitwarden/tech-leads -src/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization @bitwarden/tech-leads +src/Core/Resources/Localization @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/App/Resources/AppResources.Designer.cs -src/App/Resources/AppResources.resx +src/Core/Resources/Localization/AppResources.Designer.cs +src/Core/Resources/Localization/AppResources.resx src/watchOS/bitwarden/bitwarden\ WatchKit\ Extension/Localization/en.lproj +store/apple/en +store/google/en + +## Utils ## +store/google/Publisher diff --git a/.github/labeler.yml b/.github/labeler.yml index 8c239208b..47690d044 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,19 +1,26 @@ android: -- src/App/* -- src/Core/* -- src/Android/* +- changed-files: + - any-glob-to-any-file: + - src/App/* + - src/Core/* + - src/Android/* + - 'src/Xamarin.AndroidX.Credentials/*' iOS: -- src/App/* -- src/Core/* -- lib/ios/* -- src/iOS/* -- 'src/iOS.Autofill/*' -- 'src/iOS.Core/*' -- 'src/iOS.Extension/*' -- 'src/iOS.ShareExtension/*' -- 'src/iOS.Widget/*' -- src/watchOS/* +- changed-files: + - any-glob-to-any-file: + - src/App/* + - src/Core/* + - lib/ios/* + - src/iOS/* + - 'src/iOS.Autofill/*' + - 'src/iOS.Core/*' + - 'src/iOS.Extension/*' + - 'src/iOS.ShareExtension/*' + - 'src/iOS.Widget/*' + - src/watchOS/* watchOS: -- src/watchOS/* \ No newline at end of file +- changed-files: + - any-glob-to-any-file: + - src/watchOS/* \ No newline at end of file diff --git a/.github/renovate.json b/.github/renovate.json index 52e80afcf..2f6fafa4a 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -2,22 +2,21 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:base", + "github>bitwarden/renovate-config:pin-actions", ":combinePatchMinorReleases", ":dependencyDashboard", ":maintainLockFilesWeekly", ":pinAllExceptPeerDependencies", ":prConcurrentLimit10", ":rebaseStalePrs", - "schedule:weekends", - ":separateMajorReleases" + ":separateMajorReleases", + "group:monorepos", + "schedule:weekends" ], - "enabledManagers": ["cargo", "github-actions", "npm", "nuget"], + "enabledManagers": ["github-actions", "npm", "nuget"], + "commitMessagePrefix": "[deps]:", + "commitMessageTopic": "{{depName}}", "packageRules": [ - { - "groupName": "cargo minor", - "matchManagers": ["cargo"], - "matchUpdateTypes": ["minor", "patch"] - }, { "groupName": "gh minor", "matchManagers": ["github-actions"], @@ -32,6 +31,6 @@ "groupName": "nuget minor", "matchManagers": ["nuget"], "matchUpdateTypes": ["minor", "patch"] - }, + } ] } diff --git a/.github/secrets/GoogleService-Info.plist.gpg b/.github/secrets/GoogleService-Info.plist.gpg deleted file mode 100644 index 6f04d0929..000000000 Binary files a/.github/secrets/GoogleService-Info.plist.gpg and /dev/null differ diff --git a/.github/secrets/app_fdroid-keystore.jks.gpg b/.github/secrets/app_fdroid-keystore.jks.gpg deleted file mode 100644 index 3c1db4912..000000000 Binary files a/.github/secrets/app_fdroid-keystore.jks.gpg and /dev/null differ diff --git a/.github/secrets/app_play-keystore.jks.gpg b/.github/secrets/app_play-keystore.jks.gpg deleted file mode 100644 index 32d9aa720..000000000 Binary files a/.github/secrets/app_play-keystore.jks.gpg and /dev/null differ diff --git a/.github/secrets/app_upload-keystore.jks.gpg b/.github/secrets/app_upload-keystore.jks.gpg deleted file mode 100644 index b723c00a0..000000000 Binary files a/.github/secrets/app_upload-keystore.jks.gpg and /dev/null differ diff --git a/.github/secrets/bitwarden-mobile-key.p12.gpg b/.github/secrets/bitwarden-mobile-key.p12.gpg deleted file mode 100644 index 85949be8c..000000000 Binary files a/.github/secrets/bitwarden-mobile-key.p12.gpg and /dev/null differ diff --git a/.github/secrets/dist_autofill.mobileprovision.gpg b/.github/secrets/dist_autofill.mobileprovision.gpg deleted file mode 100644 index de955c7b3..000000000 Binary files a/.github/secrets/dist_autofill.mobileprovision.gpg and /dev/null differ diff --git a/.github/secrets/dist_bitwarden.mobileprovision.gpg b/.github/secrets/dist_bitwarden.mobileprovision.gpg deleted file mode 100644 index 02433fe19..000000000 Binary files a/.github/secrets/dist_bitwarden.mobileprovision.gpg and /dev/null differ diff --git a/.github/secrets/dist_extension.mobileprovision.gpg b/.github/secrets/dist_extension.mobileprovision.gpg deleted file mode 100644 index fe25aafd8..000000000 Binary files a/.github/secrets/dist_extension.mobileprovision.gpg and /dev/null differ diff --git a/.github/secrets/dist_share_extension.mobileprovision.gpg b/.github/secrets/dist_share_extension.mobileprovision.gpg deleted file mode 100644 index aca9437c2..000000000 Binary files a/.github/secrets/dist_share_extension.mobileprovision.gpg and /dev/null differ diff --git a/.github/secrets/dist_watch_app.mobileprovision.gpg b/.github/secrets/dist_watch_app.mobileprovision.gpg deleted file mode 100644 index b7f4d4901..000000000 Binary files a/.github/secrets/dist_watch_app.mobileprovision.gpg and /dev/null differ diff --git a/.github/secrets/dist_watch_app_extension.mobileprovision.gpg b/.github/secrets/dist_watch_app_extension.mobileprovision.gpg deleted file mode 100644 index cc843f27a..000000000 Binary files a/.github/secrets/dist_watch_app_extension.mobileprovision.gpg and /dev/null differ diff --git a/.github/secrets/google-services.json.gpg b/.github/secrets/google-services.json.gpg deleted file mode 100644 index a999db757..000000000 --- a/.github/secrets/google-services.json.gpg +++ /dev/null @@ -1,3 +0,0 @@ -  KY#(EI֐߄T?)l"=|'em/~' F>lb[+RiL"~V:paڵel%8t튖y> $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-14 + 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@a21f25cd3998bf370fde17e3f1b4c12c175172f9 # v2.0.0 + with: + nuget-version: 6.4.0 + + - name: Set up .NET + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + with: + dotnet-version: '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@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + with: + fetch-depth: 0 + ref: ${{ inputs.ref }} + submodules: 'true' + + - 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/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$BUILD_NUMBER"'<\/string>/' ./${{ env.ios_folder_path }}/Info.plist + perl -0777 -pi.bak -e 's/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist + perl -0777 -pi.bak -e 's/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist + perl -0777 -pi.bak -e 's/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$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/aps-environment<\/key>\s*development<\/string>/aps-environment<\/key>\n\tbeta<\/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 + 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 ~/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: Show Bitwarden Export + shell: bash + run: ls -a -R ./bitwarden-export + + - 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@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + 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@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + 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 Beta.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 Beta.ipa" \ + --username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD" + + check-failures: + name: Check for failures + if: always() + runs-on: ubuntu-22.04 + needs: + - setup + - ios + 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 + + - 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@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 + if: failure() + env: + SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} + with: + status: ${{ job.status }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 202395839..96dfe7cba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,15 +9,19 @@ on: paths-ignore: - ".github/workflows/**" workflow_dispatch: - inputs: {} + +env: + main_app_folder_path: src/App + main_app_project_path: src/App/App.csproj + target-net-version: net8.0 jobs: cloc: name: CLOC - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Set up CLOC run: | @@ -30,13 +34,13 @@ jobs: setup: name: Setup - runs-on: ubuntu-20.04 + 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@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: submodules: 'true' @@ -54,7 +58,6 @@ jobs: else echo "hotfix_branch_exists=0" >> $GITHUB_OUTPUT fi - shell: bash android: @@ -65,42 +68,38 @@ jobs: fail-fast: false matrix: variant: ["prod", "qa"] + env: + android_folder_path: src\App\Platforms\Android + android_folder_path_bash: src/App/Platforms/Android steps: - name: Setup NuGet - uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0 + uses: nuget/setup-nuget@a21f25cd3998bf370fde17e3f1b4c12c175172f9 # v2.0.0 with: - nuget-version: 5.9.0 + nuget-version: 6.4.0 - name: Set up .NET - uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 with: - dotnet-version: '3.1.x' + dotnet-version: '8.0.x' - name: Set up MSBuild - uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1 + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0 + + # 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: Setup Windows builder run: choco install checksum --no-progress - - name: Work Around for broken Windows 2022 Runner Image + - name: Install Microsoft OpenJDK 11 run: | - Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\" - $InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise" - $componentsToAdd = @( - "Component.Xamarin" - ) - [string]$workloadArgs = $componentsToAdd | ForEach-Object {" --add " + $_} - $Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"",$workloadArgs, '--quiet', '--norestart', '--nocache') - $process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden - if ($process.ExitCode -eq 0) - { - Write-Host "components have been successfully added" - } - else - { - Write-Host "components were not installed" - exit 1 - } + 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: $env:JAVA_HOME" + - name: Print environment run: | nuget help | grep Version @@ -110,40 +109,47 @@ jobs: echo "GitHub event: $GITHUB_EVENT" - name: Checkout repo - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 0 - - name: Decrypt secrets - env: - DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }} - run: | - mkdir -p ~/secrets - gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \ - --output ./src/Android/app_play-keystore.jks ./.github/secrets/app_play-keystore.jks.gpg - gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \ - --output ./src/Android/app_upload-keystore.jks ./.github/secrets/app_upload-keystore.jks.gpg - gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \ - --output $HOME/secrets/play_creds.json ./.github/secrets/play_creds.json.gpg + - name: Login to Azure - CI Subscription + uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 + with: + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + + - name: Download secrets + env: + ACCOUNT_NAME: bitwardenci + CONTAINER_NAME: mobile + run: | + mkdir -p $HOME/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 shell: bash - - name: Decrypt secrets - Google Services + + - name: Download secrets - Google Services if: ${{ matrix.variant == 'prod' }} env: - DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }} + ACCOUNT_NAME: bitwardenci + CONTAINER_NAME: mobile run: | - gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \ - --output ./src/Android/google-services.json ./.github/secrets/google-services.json.gpg + 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 shell: bash + - name: Increment version run: | BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER)) - - echo "########################################" - echo "##### Setting Version Code $BUILD_NUMBER" - echo "########################################" + echo "##### Setting Android Version Code to $BUILD_NUMBER" | tee -a $GITHUB_STEP_SUMMARY sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \ - ./src/Android/Properties/AndroidManifest.xml + ./${{ env.android_folder_path_bash }}/AndroidManifest.xml shell: bash - name: Restore packages @@ -151,93 +157,76 @@ jobs: - name: Restore tools run: dotnet tool restore - shell: pwsh - - name: Verify Format - run: dotnet tool run dotnet-format --check - shell: pwsh + # - name: Run Core tests + # run: | + # dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx" ` + # /p:CustomConstants=UT - - name: Run Core tests - run: dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx" - shell: pwsh - - - name: Report test results - uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 # v1.6.0 - if: always() - with: - name: Test Results - path: "**/test-results.trx" - reporter: dotnet-trx - fail-on-error: true + # - 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 Android - run: | - $configuration = "Release"; - - Write-Output "########################################" - Write-Output "##### Build $configuration Configuration" - Write-Output "########################################" - msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration" - - shell: pwsh - - - name: Sign Android Build + - name: Build & Sign Android env: PLAY_KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD }} UPLOAD_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }} run: | - $androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj"); + $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 "########################################" - msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" ` - "/p:AndroidSigningKeyAlias=upload" "/p:AndroidSigningKeyPass=$($env:UPLOAD_KEYSTORE_PASSWORD)" ` - "/p:AndroidSigningKeyStore=$("app_upload-keystore.jks")" ` - "/p:AndroidSigningStorePass=$($env:UPLOAD_KEYSTORE_PASSWORD)" "/p:AndroidPackageFormat=aab" "/v:quiet" + $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 - Write-Output "########################################" Write-Output "##### Copy Google Play Bundle to project root" - Write-Output "########################################" - $signedAabPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/$($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 "########################################" - msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" ` - "/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:PLAY_KEYSTORE_PASSWORD)" ` - "/p:AndroidSigningKeyStore=$("app_play-keystore.jks")" ` - "/p:AndroidSigningStorePass=$($env:PLAY_KEYSTORE_PASSWORD)" "/v:quiet" + $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 - Write-Output "########################################" Write-Output "##### Copy Release APK to project root" - Write-Output "########################################" - - $signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/$($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 - shell: pwsh + - name: Upload Prod .aab artifact if: ${{ matrix.variant == 'prod' }} - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: com.x8bit.bitwarden.aab path: ./com.x8bit.bitwarden.aab @@ -245,7 +234,7 @@ jobs: - name: Upload Prod .apk artifact if: ${{ matrix.variant == 'prod' }} - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: com.x8bit.bitwarden.apk path: ./com.x8bit.bitwarden.apk @@ -253,7 +242,7 @@ jobs: - name: Upload Other .apk artifact if: ${{ matrix.variant != 'prod' }} - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: com.x8bit.bitwarden.${{ matrix.variant }}.apk path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk @@ -273,7 +262,7 @@ jobs: - name: Upload .apk sha file for prod if: ${{ matrix.variant == 'prod' }} - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: bw-android-apk-sha256.txt path: ./bw-android-apk-sha256.txt @@ -281,62 +270,61 @@ jobs: - name: Upload .apk sha file for other if: ${{ matrix.variant != 'prod' }} - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: bw-android-${{ matrix.variant }}-apk-sha256.txt path: ./bw-android-${{ matrix.variant }}-apk-sha256.txt if-no-files-found: error - name: Deploy to Play Store - if: ${{ matrix.variant == 'prod' && (( github.ref == 'refs/heads/master' + if: ${{ matrix.variant == 'prod' && (( github.ref == 'refs/heads/main' && needs.setup.outputs.rc_branch_exists == 0 && needs.setup.outputs.hotfix_branch_exists == 0) || (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0) || github.ref == 'refs/heads/hotfix-rc' ) }} run: | - PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/netcoreapp3.1/Publisher.dll" - CREDS_PATH="$HOME/secrets/play_creds.json" - AAB_PATH="$GITHUB_WORKSPACE/com.x8bit.bitwarden.aab" - TRACK="internal" + $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" - dotnet $PUBLISHER_PATH $CREDS_PATH $AAB_PATH $TRACK - shell: bash + dotnet $publisherPath $credsPath $aabPath $track 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_manifest_path: src/App/Platforms/Android/AndroidManifest.xml steps: - name: Setup NuGet - uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0 + uses: nuget/setup-nuget@a21f25cd3998bf370fde17e3f1b4c12c175172f9 # v2.0.0 with: - nuget-version: 5.9.0 + nuget-version: 6.4.0 + + - name: Set up .NET + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + with: + dotnet-version: '8.0.x' - name: Set up MSBuild - uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1 + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0 + + # 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: Setup Windows builder run: choco install checksum --no-progress - - name: Work Around for broken Windows 2022 Runner Image + - name: Install Microsoft OpenJDK 11 run: | - Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\" - $InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise" - $componentsToAdd = @( - "Component.Xamarin" - ) - [string]$workloadArgs = $componentsToAdd | ForEach-Object {" --add " + $_} - $Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"",$workloadArgs, '--quiet', '--norestart', '--nocache') - $process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden - if ($process.ExitCode -eq 0) - { - Write-Host "components have been successfully added" - } - else - { - Write-Host "components were not installed" - exit 1 - } + 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: $env:JAVA_HOME" - name: Print environment run: | @@ -347,56 +335,44 @@ jobs: echo "GitHub event: $GITHUB_EVENT" - name: Checkout repo - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - - name: Decrypt secrets + - name: Login to Azure - CI Subscription + uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 + with: + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + + - name: Download secrets env: - DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }} + ACCOUNT_NAME: bitwardenci + CONTAINER_NAME: mobile + FILE: app_fdroid-keystore.jks run: | - mkdir -p ~/secrets - - gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \ - --output ./src/Android/app_fdroid-keystore.jks ./.github/secrets/app_fdroid-keystore.jks.gpg + az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \ + --file ${{ env.android_folder_path_bash }}/$FILE --output none shell: bash - name: Increment version run: | BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER)) - - echo "########################################" - echo "##### Setting Version Code $BUILD_NUMBER" - echo "########################################" + echo "##### Setting F-Droid Version Code to $BUILD_NUMBER" | tee -a $GITHUB_STEP_SUMMARY sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \ - ./src/Android/Properties/AndroidManifest.xml + ./${{ env.android_manifest_path }} shell: bash - name: Clean for F-Droid run: | - $androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj"); - $appPath = $($env:GITHUB_WORKSPACE + "/src/App/App.csproj"); - $corePath = $($env:GITHUB_WORKSPACE + "/src/Core/Core.csproj"); + $directoryBuildProps = $($env:GITHUB_WORKSPACE + "/Directory.Build.props"); - $androidManifest = $($env:GITHUB_WORKSPACE + "/src/Android/Properties/AndroidManifest.xml"); + $androidManifest = $($env:GITHUB_WORKSPACE + "/${{ env.android_manifest_path }}"); - Write-Output "########################################" - Write-Output "##### Clean Android and App" - Write-Output "########################################" - - msbuild "$($androidPath)" "/t:Clean" "/p:Configuration=FDroid" - msbuild "$($appPath)" "/t:Clean" "/p:Configuration=FDroid" - - Write-Output "########################################" - Write-Output "##### Backup project files" - Write-Output "########################################" + Write-Output "##### Back up project files" Copy-Item $androidManifest $($androidManifest + ".original"); - Copy-Item $androidPath $($androidPath + ".original"); - Copy-Item $appPath $($appPath + ".original"); + Copy-Item $directoryBuildProps $($directoryBuildProps + ".original"); - Write-Output "########################################" Write-Output "##### Cleanup Android Manifest" - Write-Output "########################################" $xml=New-Object XML; $xml.Load($androidManifest); @@ -406,83 +382,39 @@ jobs: $xml.Save($androidManifest); - Write-Output "########################################" - Write-Output "##### Uninstall from Android.csproj" - Write-Output "########################################" + Write-Output "##### Enabling FDROID constant" - $xml=New-Object XML; - $xml.Load($androidPath); - - $ns=New-Object System.Xml.XmlNamespaceManager($xml.NameTable); - $ns.AddNamespace("ns", $xml.DocumentElement.NamespaceURI); - - $firebaseNode=$xml.SelectSingleNode(` - "/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Firebase.Messaging']", $ns); - $firebaseNode.ParentNode.RemoveChild($firebaseNode); - - $daggerNode=$xml.SelectSingleNode(` - "/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Google.Dagger']", $ns); - $daggerNode.ParentNode.RemoveChild($daggerNode); - - $safetyNetNode=$xml.SelectSingleNode(` - "/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.GooglePlayServices.SafetyNet']", $ns); - $safetyNetNode.ParentNode.RemoveChild($safetyNetNode); - - $xml.Save($androidPath); - - Write-Output "########################################" - Write-Output "##### Uninstall from Core.csproj" - Write-Output "########################################" - - $xml=New-Object XML; - $xml.Load($corePath); - - $appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']"); - $appCenterNode.ParentNode.RemoveChild($appCenterNode); - - $xml.Save($corePath); - shell: pwsh + (Get-Content $directoryBuildProps).Replace('', 'FDROID') | Set-Content $directoryBuildProps - name: Restore packages - run: nuget restore + run: dotnet restore - - name: Build for F-Droid - run: | - $configuration = "FDroid"; - - Write-Output "########################################" - Write-Output "##### Build $configuration Configuration" - Write-Output "########################################" - - msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration" - shell: pwsh - - - name: Sign for F-Droid + - name: Build & Sign F-Droid env: FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }} run: | - Write-Output "########################################" - Write-Output "##### Sign FDroid Configuration" - Write-Output "########################################" + $projToBuild = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_project_path }}"; + $packageName = "com.x8bit.bitwarden"; - msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" ` - "/t:SignAndroidPackage" "/p:Configuration=FDroid" "/p:AndroidKeyStore=true" ` - "/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:FDROID_KEYSTORE_PASSWORD)" ` - "/p:AndroidSigningKeyStore=$("app_fdroid-keystore.jks")" ` - "/p:AndroidSigningStorePass=$($env:FDROID_KEYSTORE_PASSWORD)" "/v:quiet" + Write-Output "##### Sign FDroid" + + $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 - Write-Output "########################################" Write-Output "##### Copy FDroid apk to project root" - Write-Output "########################################" - $signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/FDroid/com.x8bit.bitwarden-Signed.apk"); - $signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden-fdroid.apk"); + $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"; Copy-Item $signedApkPath $signedApkDestPath - shell: pwsh - name: Upload F-Droid .apk artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: com.x8bit.bitwarden-fdroid.apk path: ./com.x8bit.bitwarden-fdroid.apk @@ -494,7 +426,7 @@ jobs: -t sha256 | Out-File -Encoding ASCII ./bw-fdroid-apk-sha256.txt - name: Upload F-Droid sha file - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: bw-fdroid-apk-sha256.txt path: ./bw-fdroid-apk-sha256.txt @@ -503,117 +435,123 @@ jobs: ios: name: Apple iOS - runs-on: macos-12 + runs-on: macos-14 needs: setup + env: + ios_folder_path: src/App/Platforms/iOS + app_output_name: App + app_ci_output_filename: App_x64_Debug steps: - - name: Setup NuGet - uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0 + - name: Set XCode version + uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0 with: - nuget-version: 5.9.0 + xcode-version: 15.1 + + - name: Setup NuGet + uses: nuget/setup-nuget@a21f25cd3998bf370fde17e3f1b4c12c175172f9 # v2.0.0 + with: + nuget-version: 6.4.0 + + - name: Set up .NET + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + with: + dotnet-version: '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 - msbuild -version dotnet --info echo "GitHub ref: $GITHUB_REF" echo "GitHub event: $GITHUB_EVENT" - name: Checkout repo - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: submodules: 'true' - name: Login to Azure - CI Subscription - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6 + 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: - KEYVAULT: bitwarden-ci - SECRETS: | - appcenter-ios-token + ACCOUNT_NAME: bitwardenci + CONTAINER_NAME: profiles run: | - for i in ${SECRETS//,/ } + 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" + ) + + for FILE in "${profiles[@]}" do - VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv) - echo "::add-mask::$VALUE" - echo "$i=$VALUE" >> $GITHUB_OUTPUT + az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \ + --file $HOME/secrets/$FILE --output none done - - name: Decrypt secrets + - name: Download Google Services secret env: - DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }} + ACCOUNT_NAME: bitwardenci + CONTAINER_NAME: mobile + FILE: GoogleService-Info.plist run: | - mkdir -p ~/secrets - - gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \ - --output $HOME/secrets/bitwarden-mobile-key.p12 ./.github/secrets/bitwarden-mobile-key.p12.gpg - gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \ - --output $HOME/secrets/iphone-distribution-cert.p12 ./.github/secrets/iphone-distribution-cert.p12.gpg - gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \ - --output $HOME/secrets/dist_autofill.mobileprovision ./.github/secrets/dist_autofill.mobileprovision.gpg - gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \ - --output $HOME/secrets/dist_bitwarden.mobileprovision ./.github/secrets/dist_bitwarden.mobileprovision.gpg - gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \ - --output $HOME/secrets/dist_extension.mobileprovision ./.github/secrets/dist_extension.mobileprovision.gpg - 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 - shell: bash + 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 - 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 "########################################" - - perl -0777 -pi.bak -e 's/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$BUILD_NUMBER"'<\/string>/' ./src/iOS/Info.plist + perl -0777 -pi.bak -e 's/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$BUILD_NUMBER"'<\/string>/' ./${{ env.ios_folder_path }}/Info.plist perl -0777 -pi.bak -e 's/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist perl -0777 -pi.bak -e 's/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist perl -0777 -pi.bak -e 's/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.ShareExtension/Info.plist cd src/watchOS/bitwarden - agvtool new-version -all $BUILD_NUMBER - cd ../../.. - shell: bash + agvtool new-version -all $BUILD_NUMBER - name: Update Entitlements run: | - echo "########################################" echo "##### Updating Entitlements" - echo "########################################" + perl -0777 -pi.bak -e 's/aps-environment<\/key>\s*development<\/string>/aps-environment<\/key>\n\tproduction<\/string>/' ./${{ env.ios_folder_path }}/Entitlements.plist - perl -0777 -pi.bak -e 's/aps-environment<\/key>\s*development<\/string>/aps-environment<\/key>\n\tproduction<\/string>/' ./src/iOS/Entitlements.plist - shell: bash + - 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 ~/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 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 - shell: bash - name: Set up provisioning profiles run: | @@ -621,8 +559,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_watch_app.mobileprovision - WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_watch_app_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 PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles mkdir -p "$PROFILES_DIR_PATH" @@ -644,91 +582,56 @@ jobs: 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" - shell: bash + + - name: Restore packages + run: dotnet restore - 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 "########################################" - shell: bash - - - name: Restore packages - run: nuget restore - - name: Archive Build for App Store run: | - $configuration = "AppStore"; - $platform = "iPhone"; - - Write-Output "########################################" - Write-Output "##### Archive $configuration Configuration for $platform Platform" - Write-Output "########################################" - msbuild "$($env:GITHUB_WORKSPACE + "/src/iOS/iOS.csproj")" "/p:Platform=$platform" ` - "/p:Configuration=$configuration" "/p:ArchiveOnBuild=true" "/t:`"Build`"" - - Write-Output "########################################" - Write-Output "##### Done" - Write-Output "########################################" - shell: pwsh + echo "##### 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 - name: Archive Build for Mobile Automation run: | - $configuration = "Release"; - $platform = "iPhoneSimulator"; - - Write-Output "########################################" - Write-Output "##### Archive $configuration Configuration for $platform Platform" - Write-Output "########################################" - msbuild "$($env:GITHUB_WORKSPACE + "/src/iOS/iOS.csproj")" "/p:Platform=$platform" ` - "/p:Configuration=$configuration" "/p:ArchiveOnBuild=true" "/t:`"Build`"" - - Write-Output "########################################" - Write-Output "##### Done" - Write-Output "########################################" - ls ~/Library/Developer/Xcode/Archives - shell: pwsh + echo "##### 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 + 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: | - 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 - shell: bash - 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="./src/iOS/bin/iPhoneSimulator/Release/BitwardeniOS.app" - EXPORT_PATH="./bitwarden-export" - - zip -r -q BitwardeniOS.app.zip $ARCHIVE_PATH - mv BitwardeniOS.app.zip $EXPORT_PATH - shell: bash + 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 - shell: bash - name: Upload App Store .ipa & dSYMs artifacts - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: Bitwarden iOS path: | @@ -737,15 +640,15 @@ jobs: if-no-files-found: error - name: Upload .app file for Automation CI - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: - name: BitwardeniOS.app.zip - path: ./bitwarden-export/BitwardeniOS.app.zip + 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 if: | - (github.ref == 'refs/heads/master' + (github.ref == 'refs/heads/main' && needs.setup.outputs.rc_branch_exists == 0 && needs.setup.outputs.hotfix_branch_exists == 0) || (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0) @@ -754,7 +657,7 @@ jobs: - name: Upload dSYMs to App Center if: | - (github.ref == 'refs/heads/master' + (github.ref == 'refs/heads/main' && needs.setup.outputs.rc_branch_exists == 0 && needs.setup.outputs.hotfix_branch_exists == 0) || (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0) @@ -762,25 +665,19 @@ jobs: env: APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }} run: appcenter crashes upload-symbols -a bitwarden/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN - shell: bash - name: Upload Watch dSYMs to Firebase Crashlytics if: | - (github.ref == 'refs/heads/master' + (github.ref == 'refs/heads/main' && needs.setup.outputs.rc_branch_exists == 0 && needs.setup.outputs.hotfix_branch_exists == 0) || (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0) || github.ref == 'refs/heads/hotfix-rc' run: | - - echo "########################################" echo "##### Uploading Watch dSYMs to Firebase" - echo "########################################" - find "$HOME/Library/Developer/XCode/DerivedData" -name "upload-symbols" -exec chmod +x {} \; -exec {} -gsp "./src/watchOS/bitwarden/GoogleService-Info.plist" -p ios "./bitwarden-export/Watch_dSYMs" \; - shell: bash - - name: Deploy to App Store + - name: Validate app in App Store if: | (github.ref == 'refs/heads/master' && needs.setup.outputs.rc_branch_exists == 0 @@ -790,53 +687,59 @@ jobs: 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" + + - name: Deploy to App Store + if: | + (github.ref == 'refs/heads/main' + && needs.setup.outputs.rc_branch_exists == 0 + && needs.setup.outputs.hotfix_branch_exists == 0) + || (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0) + || github.ref == 'refs/heads/hotfix-rc' + env: + APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }} + APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} run: | xcrun altool --upload-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \ --username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD" - shell: bash crowdin-push: name: Crowdin Push - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' needs: - android - f-droid - ios - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: _CROWDIN_PROJECT_ID: "269690" steps: - name: Checkout repo - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Login to Azure - CI Subscription - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6 + uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - env: - KEYVAULT: bitwarden-ci - SECRETS: | - crowdin-api-token - run: | - for i in ${SECRETS//,/ } - do - VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv) - echo "::add-mask::$VALUE" - echo "$i=$VALUE" >> $GITHUB_OUTPUT - done + uses: bitwarden/gh-actions/get-keyvault-secrets@main + with: + keyvault: "bitwarden-ci" + secrets: "crowdin-api-token" - name: Upload Sources - uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0 + uses: crowdin/github-action@67705afb6985401459cd143d5f5f00c9dc212f23 # v1.20.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} with: config: crowdin.yml - crowdin_branch_name: master + crowdin_branch_name: main upload_sources: true upload_translations: false @@ -844,7 +747,7 @@ jobs: check-failures: name: Check for failures if: always() - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: - cloc - android @@ -854,51 +757,28 @@ jobs: steps: - name: Check if any job failed if: | - (github.ref == 'refs/heads/master') - || (github.ref == 'refs/heads/rc') - || (github.ref == 'refs/heads/hotfix-rc') - 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 + (github.ref == 'refs/heads/main' + || github.ref == 'refs/heads/rc' + || github.ref == 'refs/heads/hotfix-rc') + && contains(needs.*.result, 'failure') + run: exit 1 - name: Login to Azure - CI Subscription - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6 + 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() - env: - KEYVAULT: bitwarden-ci - SECRETS: | - devops-alerts-slack-webhook-url - run: | - for i in ${SECRETS//,/ } - do - VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv) - echo "::add-mask::$VALUE" - echo "$i=$VALUE" >> $GITHUB_OUTPUT - done + with: + keyvault: "bitwarden-ci" + secrets: "devops-alerts-slack-webhook-url" - name: Notify Slack on failure - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 if: failure() env: SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} diff --git a/.github/workflows/cleanup-rc-branch.yml b/.github/workflows/cleanup-rc-branch.yml new file mode 100644 index 000000000..b84539b73 --- /dev/null +++ b/.github/workflows/cleanup-rc-branch.yml @@ -0,0 +1,53 @@ +--- +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@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + 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 diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index 74639f2b3..9aa63324d 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -10,33 +10,33 @@ on: jobs: crowdin-sync: name: Autosync - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: _CROWDIN_PROJECT_ID: "269690" steps: - name: Checkout repo - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Login to Azure - CI Subscription - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6 + 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@4a7ddc1b38ca5cb4e3e43578f4df5cabe4f55a67 + uses: bitwarden/gh-actions/get-keyvault-secrets@main with: keyvault: "bitwarden-ci" secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase" - name: Download translations - uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0 + uses: crowdin/github-action@67705afb6985401459cd143d5f5f00c9dc212f23 # v1.20.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} with: config: crowdin.yml - crowdin_branch_name: master + crowdin_branch_name: main upload_sources: false upload_translations: false download_translations: true diff --git a/.github/workflows/enforce-labels.yml b/.github/workflows/enforce-labels.yml index 4e3127761..556fa2670 100644 --- a/.github/workflows/enforce-labels.yml +++ b/.github/workflows/enforce-labels.yml @@ -7,7 +7,7 @@ on: jobs: enforce-label: name: EnforceLabel - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Enforce Label uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # 2.2.2 diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 7bb6f0d14..f35900ea7 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -10,8 +10,8 @@ jobs: permissions: contents: read pull-requests: write - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/labeler@ac9175f8a1f3625fd0d4fb234536d26811351594 # v4.3.0 + - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0 with: sync-labels: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f9182f7d5..8330ad9b3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,12 +23,12 @@ on: jobs: release: name: Create Release - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: branch-name: ${{ steps.branch.outputs.branch-name }} steps: - name: Branch check - if: github.event.inputs.release_type != 'Dry Run' + if: inputs.release_type != 'Dry Run' run: | if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then echo "===================================" @@ -38,15 +38,15 @@ jobs: fi - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@4a7ddc1b38ca5cb4e3e43578f4df5cabe4f55a67 + uses: bitwarden/gh-actions/release-version-check@main with: - release-type: ${{ github.event.inputs.release_type }} + release-type: ${{ inputs.release_type }} project-type: xamarin - file: src/Android/Properties/AndroidManifest.xml + file: src/App/Platforms/Android/AndroidManifest.xml - name: Get branch name id: branch @@ -55,8 +55,8 @@ jobs: echo "branch-name=$BRANCH_NAME" >> $GITHUB_OUTPUT - name: Create GitHub deployment - if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5 + if: ${{ inputs.release_type != 'Dry Run' }} + uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7 id: deployment with: token: '${{ secrets.GITHUB_TOKEN }}' @@ -67,27 +67,27 @@ jobs: - name: Download all artifacts - if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 + if: ${{ inputs.release_type != 'Dry Run' }} + uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4 with: workflow: build.yml workflow_conclusion: success branch: ${{ steps.branch.outputs.branch-name }} - name: Dry Run - Download all artifacts - if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 + if: ${{ inputs.release_type == 'Dry Run' }} + uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4 with: workflow: build.yml workflow_conclusion: success - branch: master + branch: main - name: Prep Bitwarden iOS release asset run: zip -r Bitwarden\ iOS.zip Bitwarden\ iOS - name: Create release - if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: ncipollo/release-action@6c75be85e571768fa31b40abf38de58ba0397db5 # v1.13.0 + if: ${{ inputs.release_type != 'Dry Run' }} + uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0 with: artifacts: "./com.x8bit.bitwarden.aab/com.x8bit.bitwarden.aab, ./com.x8bit.bitwarden.apk/com.x8bit.bitwarden.apk, @@ -103,16 +103,16 @@ jobs: draft: true - name: Update deployment status to Success - if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }} - uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 + if: ${{ inputs.release_type != 'Dry Run' && success() }} + uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3 with: token: '${{ secrets.GITHUB_TOKEN }}' state: 'success' deployment-id: ${{ steps.deployment.outputs.deployment_id }} - name: Update deployment status to Failure - if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }} - uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 + if: ${{ inputs.release_type != 'Dry Run' && failure() }} + uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3 with: token: '${{ secrets.GITHUB_TOKEN }}' state: 'failure' @@ -121,16 +121,16 @@ jobs: f-droid: name: F-Droid Release - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: release if: inputs.fdroid_publish steps: - name: Checkout repo - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Download F-Droid .apk artifact - if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 + if: ${{ inputs.release_type != 'Dry Run' }} + uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4 with: workflow: build.yml workflow_conclusion: success @@ -138,16 +138,16 @@ jobs: name: com.x8bit.bitwarden-fdroid.apk - name: Dry Run - Download F-Droid .apk artifact - if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 + if: ${{ inputs.release_type == 'Dry Run' }} + uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4 with: workflow: build.yml workflow_conclusion: success - branch: master + branch: main name: com.x8bit.bitwarden-fdroid.apk - name: Set up Node - uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1 + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: node-version: '16.x' @@ -176,13 +176,19 @@ jobs: - name: Install Node dependencies run: npm install - - name: Decrypt secrets + - name: Login to Azure - CI Subscription + uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 + with: + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + + - name: Download secrets env: - DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }} + ACCOUNT_NAME: bitwardenci + CONTAINER_NAME: mobile run: | - mkdir -p ~/secrets - gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \ - --output ./store/fdroid/keystore.jks ./.github/secrets/store_fdroid-keystore.jks.gpg + 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 - name: Compile for F-Droid Store env: @@ -211,5 +217,5 @@ jobs: cd $GITHUB_WORKSPACE - name: Deploy to gh-pages - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ inputs.release_type != 'Dry Run' }} run: npm run deploy diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml index 5d605f0a0..4d3085ce0 100644 --- a/.github/workflows/stale-bot.yml +++ b/.github/workflows/stale-bot.yml @@ -8,10 +8,10 @@ on: jobs: stale: name: 'Check for stale issues and PRs' - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: 'Run stale action' - uses: actions/stale@f7176fd3007623b69d27091f9b9d4ab7995f0a06 # v5.2.1 + uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 with: stale-issue-label: 'needs-reply' stale-pr-label: 'needs-changes' @@ -27,4 +27,4 @@ jobs: If you’re still working on this, please respond here after you’ve made the changes we’ve requested and our team will re-open it for further review. - Please make sure to resolve any conflicts with the master branch before requesting another review. + Please make sure to resolve any conflicts with the main branch before requesting another review. diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index 89c3431a0..aa2fc20e9 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -1,5 +1,5 @@ --- -name: Version Auto Bump +name: Auto Bump Mobile Version on: push: @@ -7,33 +7,25 @@ on: - v** jobs: - setup: - name: "Setup" + bump-version: + name: Bump Mobile Version runs-on: ubuntu-22.04 - outputs: - version_number: ${{ steps.version.outputs.new-version }} steps: - - name: Checkout Branch - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Login to Azure - CI Subscription + uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 + with: + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - - name: Calculate bumped version - id: version + - 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: Trigger Version Bump workflow env: - RELEASE_TAG: ${{ github.ref }} + GH_TOKEN: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }} 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 - - trigger_version_bump: - name: Bump version to ${{ needs.setup.outputs.version_number }} - needs: setup - uses: ./.github/workflows/version-bump.yml - with: - version_number: ${{ needs.setup.outputs.version_number }} + echo '{"cut_rc_branch": "false"}' | \ + gh workflow run version-bump.yml --json --repo bitwarden/mobile diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index 43cb6007e..41ebddb50 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -4,81 +4,200 @@ name: Version Bump on: workflow_dispatch: inputs: - version_number: - description: "New Version" - required: true - workflow_call: - inputs: - version_number: - required: true + version_number_override: + description: "New version override (leave blank for automatic calculation, example: '2024.1.0')" + required: false type: string + cut_rc_branch: + description: "Cut RC branch?" + default: true + type: boolean jobs: bump_version: - name: "Create version_bump_${{ github.event.inputs.version_number }} branch" - runs-on: ubuntu-20.04 + name: Bump Version + 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 + with: + version: ${{ inputs.version_number_override }} + - name: Checkout Branch - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + with: + ref: main + + - name: Check if RC branch exists + if: ${{ inputs.cut_rc_branch == true }} + run: | + remote_rc_branch_check=$(git ls-remote --heads origin rc | wc -l) + if [[ "${remote_rc_branch_check}" -gt 0 ]]; then + echo "Remote RC branch exists." + echo "Please delete current RC branch before running again." + exit 1 + fi - name: Login to Azure - CI Subscription - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 + uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 with: - creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@4a7ddc1b38ca5cb4e3e43578f4df5cabe4f55a67 + uses: bitwarden/gh-actions/get-keyvault-secrets@main with: keyvault: "bitwarden-ci" - secrets: "github-gpg-private-key, github-gpg-private-key-passphrase" + 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@d6f3f49f3345e29369fe57596a3ca8f94c4d2ca7 # v5.4.0 + uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0 with: gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }} passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }} git_user_signingkey: true git_commit_gpgsign: true - - name: Create Version Branch - run: git switch -c version_bump_${{ github.event.inputs.version_number }} - - - name: Bump Version - Android XML - uses: bitwarden/gh-actions/version-bump@4a7ddc1b38ca5cb4e3e43578f4df5cabe4f55a67 - with: - version: ${{ github.event.inputs.version_number }} - file_path: "./src/Android/Properties/AndroidManifest.xml" - - - name: Bump Version - iOS.Autofill - uses: bitwarden/gh-actions/version-bump@4a7ddc1b38ca5cb4e3e43578f4df5cabe4f55a67 - with: - version: ${{ github.event.inputs.version_number }} - file_path: "./src/iOS.Autofill/Info.plist" - - - name: Bump Version - iOS.Extension - uses: bitwarden/gh-actions/version-bump@4a7ddc1b38ca5cb4e3e43578f4df5cabe4f55a67 - with: - version: ${{ github.event.inputs.version_number }} - file_path: "./src/iOS.Extension/Info.plist" - - - name: Bump Version - iOS.ShareExtension - uses: bitwarden/gh-actions/version-bump@4a7ddc1b38ca5cb4e3e43578f4df5cabe4f55a67 - with: - version: ${{ github.event.inputs.version_number }} - file_path: "./src/iOS.ShareExtension/Info.plist" - - - name: Bump Version - iOS - uses: bitwarden/gh-actions/version-bump@4a7ddc1b38ca5cb4e3e43578f4df5cabe4f55a67 - with: - version: ${{ github.event.inputs.version_number }} - file_path: "./src/iOS/Info.plist" - - 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") + 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 + + - name: Verify input version + if: ${{ inputs.version_number_override != '' }} + env: + CURRENT_VERSION: ${{ steps.current-version.outputs.version }} + NEW_VERSION: ${{ inputs.version_number_override }} + run: | + # Error if version has not changed. + if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then + echo "Version has not changed." + exit 1 + fi + + # Check if version is newer. + printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V + if [ $? -eq 0 ]; then + echo "Version check successful." + else + echo "Version check failed." + 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 + uses: bitwarden/gh-actions/version-bump@main + with: + 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 != '' }} + uses: bitwarden/gh-actions/version-bump@main + with: + 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 != '' }} + uses: bitwarden/gh-actions/version-bump@main + with: + 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 != '' }} + uses: bitwarden/gh-actions/version-bump@main + with: + 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 != '' }} + uses: bitwarden/gh-actions/version-bump@main + with: + 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 + 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 + - name: Check if version changed id: version-changed run: | @@ -91,22 +210,24 @@ jobs: - name: Commit files if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} - run: git commit -m "Bumped version to ${{ github.event.inputs.version_number }}" -a + run: git commit -m "Bumped version to ${{ steps.set-final-version-output.outputs.version }}" -a - name: Push changes if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} - run: git push -u origin version_bump_${{ github.event.inputs.version_number }} + env: + PR_BRANCH: ${{ steps.create-branch.outputs.name }} + run: git push -u origin $PR_BRANCH - name: Create Version PR if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} + id: create-pr env: - PR_BRANCH: "version_bump_${{ github.event.inputs.version_number }}" - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - BASE_BRANCH: master - TITLE: "Bump version to ${{ github.event.inputs.version_number }}" + 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 }}" run: | - gh pr create --title "$TITLE" \ - --base "$BASE" \ + PR_URL=$(gh pr create --title "$TITLE" \ + --base "main" \ --head "$PR_BRANCH" \ --label "version update" \ --label "automated pr" \ @@ -119,4 +240,58 @@ jobs: - [X] Other ## Objective - Automated version bump to ${{ github.event.inputs.version_number }}" + Automated version bump to ${{ steps.set-final-version-output.outputs.version }}") + 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 }} + run: gh pr merge $PR_NUMBER --squash --auto --delete-branch + + cut_rc: + name: Cut RC branch + if: ${{ inputs.cut_rc_branch == true }} + needs: bump_version + runs-on: ubuntu-22.04 + steps: + - name: Checkout Branch + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + with: + ref: main + + - name: Install xmllint + run: | + sudo apt-get update + sudo apt-get install -y libxml2-utils + + - name: Verify version has been updated + env: + NEW_VERSION: ${{ needs.bump_version.outputs.version }} + run: | + # Wait for version to change. + while : ; do + echo "Waiting for version to be updated..." + git pull --force + CURRENT_VERSION=$(xmllint --xpath ' + string(/manifest/@*[local-name()="versionName" + and namespace-uri()="http://schemas.android.com/apk/res/android"]) + ' src/App/Platforms/Android/AndroidManifest.xml) + + # If the versions don't match we continue the loop, otherwise we break out of the loop. + [[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] || break + sleep 10 + done + + - name: Cut RC branch + run: | + git switch --quiet --create rc + git push --quiet --set-upstream origin rc diff --git a/.github/workflows/workflow-linter.yml b/.github/workflows/workflow-linter.yml deleted file mode 100644 index 25e35348b..000000000 --- a/.github/workflows/workflow-linter.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: Workflow Linter - -on: - pull_request: - paths: - - .github/workflows/** - -jobs: - call-workflow: - uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@4a7ddc1b38ca5cb4e3e43578f4df5cabe4f55a67 diff --git a/.gitignore b/.gitignore index a006ce4bf..1e0f2d44b 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ Components/ x64/ x86/ !src/lib/x86/ +!src/App/Platforms/Android/lib/x86/ build/ bld/ [Bb]in/ @@ -147,6 +148,7 @@ publish/ # NuGet Packages *.nupkg +!**/Xamarin.AndroidX.Credentials.1.0.0.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..e455a4843 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,16 @@ + + + 8.0.7 + Automatic:AppStore + iPhone Distribution + True + True + -gcc_flags "-L$(ProjectDir)../../lib/ios -largon2 -force_load $(ProjectDir)../../lib/ios/libargon2.a" + + + + + + + + diff --git a/README.md b/README.md index d740a0266..7ce00cebd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Github Workflow build on master](https://github.com/bitwarden/mobile/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:master) +[![Github Workflow build on main](https://github.com/bitwarden/mobile/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/bitwarden/mobile/actions/workflows/build.yml?query=branch:main) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/bitwarden-mobile/localized.svg)](https://crowdin.com/project/bitwarden-mobile) [![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby) @@ -6,13 +6,13 @@ Get it on Google Play Get it on F-Droid -The Bitwarden mobile application is written in C# with Xamarin Android, Xamarin iOS, and Xamarin Forms. +The Bitwarden mobile application is written in C# using .NET MAUI. # Build/Run -Please refer to the [Mobile section](https://contributing.bitwarden.com/getting-started/clients/mobile/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. +Please refer to the [Mobile section](https://contributing.bitwarden.com/getting-started/mobile/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. # We're Hiring! @@ -20,6 +20,6 @@ Interested in contributing in a big way? Consider joining our team! We're hiring # Contribute -Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [Contributing Guidelines](https://contributing.bitwarden.com/contributing/). Check out the [Contributing Documentation](https://contributing.bitwarden.com/) for how to get started with your first contribution. +Code contributions are welcome! Please commit any pull requests against the `main` branch. Learn more about how to contribute by reading the [Contributing Guidelines](https://contributing.bitwarden.com/contributing/). Check out the [Contributing Documentation](https://contributing.bitwarden.com/) for how to get started with your first contribution. Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file. diff --git a/bitwarden-mobile.sln b/bitwarden-mobile.sln index 07434b50a..a804eca0b 100644 --- a/bitwarden-mobile.sln +++ b/bitwarden-mobile.sln @@ -1,471 +1,233 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29009.5 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34112.27 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android", "src\Android\Android.csproj", "{304400AF-F0ED-40FA-B102-EA3C3EC43E4F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "src\App\App.csproj", "{971FDF07-E288-4239-B47A-E9E7E912193B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App", "src\App\App.csproj", "{EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{11DBC05E-F8B4-49ED-AAC9-96D92336D21C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "src\Core\Core.csproj", "{4B8A8C41-9820-4341-974C-41E65B7F4366}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Playground", "test\Playground\Playground.csproj", "{9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D10CA4A9-F866-40E1-B658-F69051236C71}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{8904C536-C67D-420F-9971-51B26574C3AA}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "store", "store", "{92470CBD-9047-4C3C-8EA3-D972D6622D84}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "google", "google", "{2E399654-26A2-46F6-B9CA-1B496A3F370A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{76690DFB-B7F4-4781-83E4-113FDC450AFE}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .gitignore = .gitignore - .github\workflows\build.yml = .github\workflows\build.yml - CONTRIBUTING.md = CONTRIBUTING.md - crowdin.yml = crowdin.yml - README.md = README.md - SECURITY.md = SECURITY.md - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Publisher", "store\google\Publisher\Publisher.csproj", "{256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Core", "src\iOS.Core\iOS.Core.csproj", "{E71F3053-056C-4381-9638-048ED73BDFF6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS", "src\iOS\iOS.csproj", "{599E0201-420A-4C3E-A7BA-5349F72E0B15}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "iOS.Core", "src\iOS.Core\iOS.Core.csproj", "{E71F3053-056C-4381-9638-048ED73BDFF6}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Extension", "src\iOS.Extension\iOS.Extension.csproj", "{324BE76C-38FA-4F11-8BB1-95C7B3B1B545}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "test\Common\Common.csproj", "{4085B0A5-12A9-4993-B8B8-4ACE72E62E39}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{8AE548D9-A567-4E97-995E-93EC7DB0FDE0}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.ShareExtension", "src\iOS.ShareExtension\iOS.ShareExtension.csproj", "{F8C3F648-EA5A-4719-8005-85D1690B1655}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS.Autofill", "src\iOS.Autofill\iOS.Autofill.csproj", "{83449CC4-1F76-4CFE-92B1-D2E13A62506F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{BB702EBD-3B79-4ECA-A2A6-1237B07F0AF0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B972BBFA-917F-4A10-B07E-B89CFEC6BBDC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{137959BD-073B-4EC7-8ED5-31D73FA7DBC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "test\Common\Common.csproj", "{1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Ad-Hoc|Any CPU = Ad-Hoc|Any CPU - Ad-Hoc|iPhone = Ad-Hoc|iPhone - Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator - AppStore|Any CPU = AppStore|Any CPU - AppStore|iPhone = AppStore|iPhone - AppStore|iPhoneSimulator = AppStore|iPhoneSimulator Debug|Any CPU = Debug|Any CPU - Debug|iPhone = Debug|iPhone - Debug|iPhoneSimulator = Debug|iPhoneSimulator - FDroid|Any CPU = FDroid|Any CPU - FDroid|iPhone = FDroid|iPhone - FDroid|iPhoneSimulator = FDroid|iPhoneSimulator Release|Any CPU = Release|Any CPU - Release|iPhone = Release|iPhone + Debug|iPhoneSimulator = Debug|iPhoneSimulator Release|iPhoneSimulator = Release|iPhoneSimulator + Debug|iPhone = Debug|iPhone + Release|iPhone = Release|iPhone + AppStore|iPhoneSimulator = AppStore|iPhoneSimulator + AppStore|iPhone = AppStore|iPhone + Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator + Ad-Hoc|iPhone = Ad-Hoc|iPhone + FDroid|Any CPU = FDroid|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|Any CPU.Build.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|Any CPU.Deploy.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhone.Build.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhone.Deploy.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhone.Build.0 = Debug|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhone.Deploy.0 = Debug|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|Any CPU.Build.0 = FDroid|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|Any CPU.Deploy.0 = FDroid|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhone.Build.0 = FDroid|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhone.Deploy.0 = FDroid|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.FDroid|iPhoneSimulator.Deploy.0 = FDroid|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|Any CPU.Build.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|Any CPU.Deploy.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhone.ActiveCfg = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhone.Build.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhone.Deploy.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|Any CPU.Deploy.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhone.Deploy.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.Build.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|Any CPU.Deploy.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Build.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhone.Deploy.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.AppStore|iPhoneSimulator.Deploy.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhone.Build.0 = Debug|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhone.Deploy.0 = Debug|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|Any CPU.Build.0 = FDroid|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|iPhone.Build.0 = FDroid|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|Any CPU.Build.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|Any CPU.Deploy.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhone.ActiveCfg = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhone.Build.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhone.Deploy.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|Any CPU.Build.0 = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhone.Build.0 = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhone.Build.0 = Debug|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|Any CPU.Build.0 = FDroid|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|iPhone.Build.0 = FDroid|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|Any CPU.Build.0 = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|iPhone.ActiveCfg = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|iPhone.Build.0 = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4B8A8C41-9820-4341-974C-41E65B7F4366}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|iPhone.Build.0 = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|iPhone.Build.0 = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|Any CPU.Build.0 = FDroid|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|iPhone.Build.0 = FDroid|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|Any CPU.Build.0 = Release|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|iPhone.ActiveCfg = Release|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|iPhone.Build.0 = Release|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|iPhone.Build.0 = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|iPhone.Build.0 = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|Any CPU.Build.0 = FDroid|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|iPhone.Build.0 = FDroid|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|Any CPU.Build.0 = Release|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhone.ActiveCfg = Release|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhone.Build.0 = Release|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone - {E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone - {E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator - {E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator - {E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.ActiveCfg = AppStore|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|Any CPU.Build.0 = AppStore|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = AppStore|iPhone - {E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = AppStore|iPhone - {E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator - {E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator + {971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Release|Any CPU.Build.0 = Release|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Release|Any CPU.Deploy.0 = Release|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Debug|iPhone.Build.0 = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhone.ActiveCfg = Release|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Release|iPhone.Build.0 = Release|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.AppStore|iPhone.Build.0 = Release|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU + {971FDF07-E288-4239-B47A-E9E7E912193B}.FDroid|Any CPU.Build.0 = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|Any CPU.Build.0 = Release|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Debug|iPhone.Build.0 = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhone.ActiveCfg = Release|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Release|iPhone.Build.0 = Release|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.AppStore|iPhone.Build.0 = Release|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C}.FDroid|Any CPU.Build.0 = Debug|Any CPU {E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.ActiveCfg = Debug|iPhone - {E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.Build.0 = Debug|iPhone - {E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator - {E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator - {E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.ActiveCfg = FDroid|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.Build.0 = FDroid|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhone.ActiveCfg = FDroid|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhone.Build.0 = FDroid|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|iPhoneSimulator.Build.0 = FDroid|Any CPU {E71F3053-056C-4381-9638-048ED73BDFF6}.Release|Any CPU.ActiveCfg = Release|Any CPU {E71F3053-056C-4381-9638-048ED73BDFF6}.Release|Any CPU.Build.0 = Release|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.ActiveCfg = Release|Any CPU - {E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.Build.0 = Release|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhone.ActiveCfg = AppStore|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhone.Build.0 = AppStore|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|Any CPU.ActiveCfg = Debug|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.ActiveCfg = Debug|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Build.0 = Debug|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhone.Deploy.0 = Debug|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|Any CPU.ActiveCfg = FDroid|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhone.ActiveCfg = FDroid|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhone.Build.0 = FDroid|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhoneSimulator.ActiveCfg = FDroid|iPhoneSimulator - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.FDroid|iPhoneSimulator.Build.0 = FDroid|iPhoneSimulator - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|Any CPU.ActiveCfg = Release|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhone.ActiveCfg = Release|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhone.Build.0 = Release|iPhone - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator - {599E0201-420A-4C3E-A7BA-5349F72E0B15}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|Any CPU.Build.0 = AppStore|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.ActiveCfg = AppStore|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.Build.0 = AppStore|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.ActiveCfg = Debug|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Build.0 = Debug|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Deploy.0 = Debug|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Release|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.Build.0 = Release|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhone.ActiveCfg = Release|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhone.Build.0 = Release|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.ActiveCfg = Release|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.ActiveCfg = Release|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.Build.0 = Release|iPhone - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|Any CPU.Build.0 = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhone.Build.0 = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhone.Build.0 = Debug|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|Any CPU.ActiveCfg = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|Any CPU.Build.0 = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhone.ActiveCfg = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhone.Build.0 = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|Any CPU.Build.0 = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhone.ActiveCfg = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhone.Build.0 = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|Any CPU.Build.0 = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhone.Build.0 = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhone.Build.0 = Debug|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|Any CPU.ActiveCfg = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|Any CPU.Build.0 = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhone.ActiveCfg = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhone.Build.0 = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhoneSimulator.ActiveCfg = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.FDroid|iPhoneSimulator.Build.0 = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|Any CPU.Build.0 = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.ActiveCfg = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhone.Build.0 = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|Any CPU.ActiveCfg = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|Any CPU.Build.0 = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.ActiveCfg = Release|iPhone - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.Build.0 = Release|iPhone - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|Any CPU.ActiveCfg = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|Any CPU.Build.0 = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.ActiveCfg = Release|iPhone - {F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.Build.0 = Release|iPhone - {F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.ActiveCfg = Debug|iPhone - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.Build.0 = Debug|iPhone - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.ActiveCfg = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.Build.0 = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhone.ActiveCfg = Release|iPhone - {F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhone.Build.0 = Release|iPhone - {F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.ActiveCfg = Release|iPhone - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.Build.0 = Release|iPhone - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.ActiveCfg = Release|iPhone - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.Build.0 = Release|iPhone - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator - {F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|Any CPU.ActiveCfg = Ad-Hoc|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|Any CPU.Build.0 = Ad-Hoc|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Ad-Hoc|iPhoneSimulator - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Ad-Hoc|iPhoneSimulator.Build.0 = Ad-Hoc|iPhoneSimulator - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|Any CPU.ActiveCfg = AppStore|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|Any CPU.Build.0 = AppStore|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhone.ActiveCfg = AppStore|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhone.Build.0 = AppStore|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|Any CPU.ActiveCfg = Debug|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.ActiveCfg = Debug|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Build.0 = Debug|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhone.Deploy.0 = Debug|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.ActiveCfg = Release|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|Any CPU.Build.0 = Release|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhone.ActiveCfg = Release|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhone.Build.0 = Release|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.FDroid|iPhoneSimulator.Build.0 = Release|iPhoneSimulator - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|Any CPU.ActiveCfg = Release|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.ActiveCfg = Release|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhone.Build.0 = Release|iPhone - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator + {E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.Debug|iPhone.Build.0 = Debug|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.ActiveCfg = Release|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.Release|iPhone.Build.0 = Release|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.AppStore|iPhone.Build.0 = Release|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU + {E71F3053-056C-4381-9638-048ED73BDFF6}.FDroid|Any CPU.Build.0 = Debug|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|Any CPU.Build.0 = Debug|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.ActiveCfg = Release|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|Any CPU.Build.0 = Release|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Debug|iPhone.Build.0 = Debug|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.ActiveCfg = Release|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Release|iPhone.Build.0 = Release|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.AppStore|iPhone.Build.0 = Release|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545}.FDroid|Any CPU.Build.0 = Debug|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|Any CPU.Build.0 = Release|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Debug|iPhone.Build.0 = Debug|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.ActiveCfg = Release|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Release|iPhone.Build.0 = Release|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.AppStore|iPhone.Build.0 = Release|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU + {F8C3F648-EA5A-4719-8005-85D1690B1655}.FDroid|Any CPU.Build.0 = Debug|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|Any CPU.Build.0 = Release|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|iPhone.ActiveCfg = Release|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Release|iPhone.Build.0 = Release|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.AppStore|iPhone.Build.0 = Release|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU + {83449CC4-1F76-4CFE-92B1-D2E13A62506F}.FDroid|Any CPU.Build.0 = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|Any CPU.Build.0 = Release|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Debug|iPhone.Build.0 = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhone.ActiveCfg = Release|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Release|iPhone.Build.0 = Release|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.AppStore|iPhone.Build.0 = Release|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6}.FDroid|Any CPU.Build.0 = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|Any CPU.Build.0 = Release|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Debug|iPhone.Build.0 = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhone.ActiveCfg = Release|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Release|iPhone.Build.0 = Release|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.AppStore|iPhone.Build.0 = Release|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.FDroid|Any CPU.ActiveCfg = Debug|Any CPU + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44}.FDroid|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F} = {D10CA4A9-F866-40E1-B658-F69051236C71} - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C} = {D10CA4A9-F866-40E1-B658-F69051236C71} - {4B8A8C41-9820-4341-974C-41E65B7F4366} = {D10CA4A9-F866-40E1-B658-F69051236C71} - {9C8DA5A8-904D-466F-B9B0-1A4AB5A9AFC3} = {8904C536-C67D-420F-9971-51B26574C3AA} - {2E399654-26A2-46F6-B9CA-1B496A3F370A} = {92470CBD-9047-4C3C-8EA3-D972D6622D84} - {256F9E44-0AF5-4D97-A2F9-DA26080C0A5D} = {2E399654-26A2-46F6-B9CA-1B496A3F370A} - {E71F3053-056C-4381-9638-048ED73BDFF6} = {D10CA4A9-F866-40E1-B658-F69051236C71} - {599E0201-420A-4C3E-A7BA-5349F72E0B15} = {D10CA4A9-F866-40E1-B658-F69051236C71} - {324BE76C-38FA-4F11-8BB1-95C7B3B1B545} = {D10CA4A9-F866-40E1-B658-F69051236C71} - {4085B0A5-12A9-4993-B8B8-4ACE72E62E39} = {8904C536-C67D-420F-9971-51B26574C3AA} - {8AE548D9-A567-4E97-995E-93EC7DB0FDE0} = {8904C536-C67D-420F-9971-51B26574C3AA} - {F8C3F648-EA5A-4719-8005-85D1690B1655} = {D10CA4A9-F866-40E1-B658-F69051236C71} - {8A3ECD75-3EC8-4CB3-B3A2-A73A724C279A} = {D10CA4A9-F866-40E1-B658-F69051236C71} - EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {7D436EA3-8B7E-45D2-8D14-0730BD2E0410} + SolutionGuid = {3B3A9B6C-D325-4BB3-97D3-8070630C5D3B} + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + Policies = $0 + $0.DotNetNamingPolicy = $1 + $1.DirectoryNamespaceAssociation = PrefixedHierarchical + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {971FDF07-E288-4239-B47A-E9E7E912193B} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC} + {11DBC05E-F8B4-49ED-AAC9-96D92336D21C} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC} + {83449CC4-1F76-4CFE-92B1-D2E13A62506F} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC} + {E71F3053-056C-4381-9638-048ED73BDFF6} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC} + {324BE76C-38FA-4F11-8BB1-95C7B3B1B545} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC} + {F8C3F648-EA5A-4719-8005-85D1690B1655} = {B972BBFA-917F-4A10-B07E-B89CFEC6BBDC} + {137959BD-073B-4EC7-8ED5-31D73FA7DBC6} = {BB702EBD-3B79-4ECA-A2A6-1237B07F0AF0} + {1AC5ED7F-301E-4B3C-ACDE-C0EADFA5AE44} = {BB702EBD-3B79-4ECA-A2A6-1237B07F0AF0} EndGlobalSection EndGlobal diff --git a/build.cake b/build.cake index 466a96cc8..c0ac48832 100644 --- a/build.cake +++ b/build.cake @@ -15,16 +15,18 @@ abstract record VariantConfig( string AppName, string AndroidPackageName, string iOSBundleId, - string ApsEnvironment + string ApsEnvironment, + string DistProvisioningProfilePrefix ); const string BASE_BUNDLE_ID_DROID = "com.x8bit.bitwarden"; const string BASE_BUNDLE_ID_IOS = "com.8bit.bitwarden"; -record Dev(): VariantConfig("Bitwarden Dev", $"{BASE_BUNDLE_ID_DROID}.dev", $"{BASE_BUNDLE_ID_IOS}.dev", "development"); -record QA(): VariantConfig("Bitwarden QA", $"{BASE_BUNDLE_ID_DROID}.qa", $"{BASE_BUNDLE_ID_IOS}.qa", "development"); -record Beta(): VariantConfig("Bitwarden Beta", $"{BASE_BUNDLE_ID_DROID}.beta", $"{BASE_BUNDLE_ID_IOS}.beta", "production"); -record Prod(): VariantConfig("Bitwarden", $"{BASE_BUNDLE_ID_DROID}", $"{BASE_BUNDLE_ID_IOS}", "production"); +//NOTE: Beta iOS variants have a different ITSEncryptionExportComplianceCode +record Dev(): VariantConfig("Bitwarden Dev", $"{BASE_BUNDLE_ID_DROID}.dev", $"{BASE_BUNDLE_ID_IOS}.dev", "development", "Dist:"); +record QA(): VariantConfig("Bitwarden QA", $"{BASE_BUNDLE_ID_DROID}.qa", $"{BASE_BUNDLE_ID_IOS}.qa", "development", "Dist:"); +record Beta(): VariantConfig("Bitwarden Beta", $"{BASE_BUNDLE_ID_DROID}.beta", $"{BASE_BUNDLE_ID_IOS}.beta", "production", "Dist: Beta"); +record Prod(): VariantConfig("Bitwarden", $"{BASE_BUNDLE_ID_DROID}", $"{BASE_BUNDLE_ID_IOS}", "production", "Dist:"); VariantConfig GetVariant() => variant.ToLower() switch{ "qa" => new QA(), @@ -67,7 +69,7 @@ Task("UpdateAndroidManifest") .Does(()=> { var buildVariant = GetVariant(); - var manifestPath = Path.Combine(_slnPath, "src", "Android", "Properties", "AndroidManifest.xml"); + var manifestPath = Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "AndroidManifest.xml"); // Cake.AndroidAppManifest doesn't currently enable us to access nested items so, quick (not ideal) fix: var manifestText = FileReadText(manifestPath); @@ -119,26 +121,26 @@ Task("UpdateAndroidCodeFiles") //We're not using _androidPackageName here because the codefile is currently slightly different string than the one in AndroidManifest.xml var keyName = "com.8bit.bitwarden"; var fixedPackageName = buildVariant.AndroidPackageName.Replace("x8bit", "8bit"); - var filePath = Path.Combine(_slnPath, "src", "Android", "Services", "BiometricService.cs"); + var filePath = Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Services", "BiometricService.cs"); ReplaceInFile(filePath, keyName, fixedPackageName); var packageFileList = new string[] { - Path.Combine(_slnPath, "src", "Android", "MainActivity.cs"), - Path.Combine(_slnPath, "src", "Android", "MainApplication.cs"), - Path.Combine(_slnPath, "src", "Android", "Constants.cs"), - Path.Combine(_slnPath, "src", "Android", "Accessibility", "AccessibilityService.cs"), - Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillHelpers.cs"), - Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillService.cs"), - Path.Combine(_slnPath, "src", "Android", "Receivers", "ClearClipboardAlarmReceiver.cs"), - Path.Combine(_slnPath, "src", "Android", "Receivers", "EventUploadReceiver.cs"), - Path.Combine(_slnPath, "src", "Android", "Receivers", "PackageReplacedReceiver.cs"), - Path.Combine(_slnPath, "src", "Android", "Receivers", "RestrictionsChangedReceiver.cs"), - Path.Combine(_slnPath, "src", "Android", "Services", "DeviceActionService.cs"), - Path.Combine(_slnPath, "src", "Android", "Services", "FileService.cs"), - Path.Combine(_slnPath, "src", "Android", "Tiles", "AutofillTileService.cs"), - Path.Combine(_slnPath, "src", "Android", "Tiles", "GeneratorTileService.cs"), - Path.Combine(_slnPath, "src", "Android", "Tiles", "MyVaultTileService.cs"), - Path.Combine(_slnPath, "src", "Android", "google-services.json"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "MainActivity.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "MainApplication.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Constants.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Accessibility", "AccessibilityService.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Autofill", "AutofillHelpers.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Autofill", "AutofillService.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "ClearClipboardAlarmReceiver.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "EventUploadReceiver.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "PackageReplacedReceiver.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Receivers", "RestrictionsChangedReceiver.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Services", "DeviceActionService.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Services", "FileService.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Tiles", "AutofillTileService.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Tiles", "GeneratorTileService.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Tiles", "MyVaultTileService.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "google-services.json"), Path.Combine(_slnPath, "store", "google", "Publisher", "Program.cs"), }; @@ -148,7 +150,7 @@ Task("UpdateAndroidCodeFiles") } var labelFileList = new string[] { - Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillService.cs"), + Path.Combine(_slnPath, "src", "App", "Platforms", "Android", "Autofill", "AutofillService.cs"), }; foreach(string path in labelFileList) @@ -197,7 +199,8 @@ private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, Gi var prevBundleId = plist["CFBundleIdentifier"]; var prevBundleName = plist["CFBundleName"]; //var newVersion = CreateBuildNumber(prevVersion).ToString(); - var newVersionName = GetVersionName(prevVersionName, buildVariant, git); + // we need to maintain version formatting here composed of one to three period-separated integers, so we cannot use the GetVersionName method as in Android for non-Prod. + var newVersionName = prevVersionName; var newBundleId = GetiOSBundleId(buildVariant, projectType); var newBundleName = GetiOSBundleName(buildVariant, projectType); @@ -219,6 +222,11 @@ private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, Gi plist["NSExtension"]["NSExtensionAttributes"]["NSExtensionActivationRule"] = keyText.Replace("com.8bit.bitwarden", buildVariant.iOSBundleId); } + if(buildVariant is Beta) + { + plist["ITSEncryptionExportComplianceCode"] = "3dd3e32f-efa6-4d99-b410-28aa28b1cb77"; + } + SerializePlist(plistFile, plist); Information($"Changed app name from {prevBundleName} to {newBundleName}"); @@ -228,12 +236,15 @@ private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, Gi Information($"{plistPath} updated with success!"); } -private void UpdateiOSEntitlementsPlist(string entitlementsPath, VariantConfig buildVariant) +private void UpdateiOSEntitlementsPlist(string entitlementsPath, VariantConfig buildVariant, bool updateApsEnv) { var EntitlementlistFile = File(entitlementsPath); dynamic Entitlements = DeserializePlist(EntitlementlistFile); - Entitlements["aps-environment"] = buildVariant.ApsEnvironment; + if (updateApsEnv) + { + Entitlements["aps-environment"] = buildVariant.ApsEnvironment; + } Entitlements["keychain-access-groups"] = new List() { "$(AppIdentifierPrefix)" + buildVariant.iOSBundleId }; Entitlements["com.apple.security.application-groups"] = new List() { $"group.{buildVariant.iOSBundleId}" };; @@ -272,9 +283,10 @@ private void UpdateWatchPbxproj(string pbxprojPath, string newVersion) const string pattern = @"MARKETING_VERSION = [^;]*;"; fileText = Regex.Replace(fileText, pattern, $"MARKETING_VERSION = {newVersion};"); - + FileWriteText(pbxprojPath, fileText); - Information($"{pbxprojPath} modified successfully."); + + Information($"{pbxprojPath} modified Marketing Version successfully."); } /// @@ -315,7 +327,7 @@ private void UpdateAppleIcons(string target, string appiconsetTarget) Task("UpdateiOSIcons") .Does(()=>{ - UpdateAppleIcons("ios", Path.Combine(_slnPath, "src", "iOS", "Resources", "Assets.xcassets", "AppIcons.appiconset")); + UpdateAppleIcons("ios", Path.Combine(_slnPath, "src", "App", "Platforms", "iOS", "Resources", "Assets.xcassets", "AppIcons.appiconset")); UpdateAppleIcons("watch", Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden WatchKit App", "Assets.xcassets", "AppIcon.appiconset")); // TODO: Update complication icons when they start working }); @@ -324,10 +336,10 @@ Task("UpdateiOSPlist") .IsDependentOn("GetGitInfo") .Does(()=> { var buildVariant = GetVariant(); - var infoPath = Path.Combine(_slnPath, "src", "iOS", "Info.plist"); - var entitlementsPath = Path.Combine(_slnPath, "src", "iOS", "Entitlements.plist"); + var infoPath = Path.Combine(_slnPath, "src", "App", "Platforms", "iOS", "Info.plist"); + var entitlementsPath = Path.Combine(_slnPath, "src", "App", "Platforms", "iOS", "Entitlements.plist"); UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.MainApp); - UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant); + UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, true); }); Task("UpdateiOSAutofillPlist") @@ -338,7 +350,7 @@ Task("UpdateiOSAutofillPlist") var infoPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Info.plist"); var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Entitlements.plist"); UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Autofill); - UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant); + UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, false); }); Task("UpdateiOSExtensionPlist") @@ -349,7 +361,7 @@ Task("UpdateiOSExtensionPlist") var infoPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Info.plist"); var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Entitlements.plist"); UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Extension); - UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant); + UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, false); }); Task("UpdateiOSShareExtensionPlist") @@ -360,7 +372,7 @@ Task("UpdateiOSShareExtensionPlist") var infoPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Info.plist"); var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Entitlements.plist"); UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.ShareExtension); - UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant); + UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant, false); }); Task("UpdateiOSCodeFiles") @@ -397,6 +409,22 @@ Task("UpdateWatchKitAppInfoPlist") UpdateWatchKitAppInfoPlist(infoPath, buildVariant); }); +Task("UpdateDistProfiles") + .IsDependentOn("UpdateiOSCodeFiles") + .Does(()=> { + var buildVariant = GetVariant(); + + var filesToReplace = new string[] { + Path.Combine(".github", "resources", "export-options-app-store.plist"), + Path.Combine(_slnPath, "src", "watchOS", "bitwarden", "bitwarden.xcodeproj", "project.pbxproj") + }; + + foreach(string path in filesToReplace) + { + ReplaceInFile(path, "Dist:", buildVariant.DistProvisioningProfilePrefix); + } + }); + #endregion iOS #region Main Tasks @@ -418,6 +446,7 @@ Task("iOS") .IsDependentOn("UpdateiOSCodeFiles") .IsDependentOn("UpdateWatchProject") .IsDependentOn("UpdateWatchKitAppInfoPlist") + .IsDependentOn("UpdateDistProfiles") .Does(()=> { Information("iOS app updated"); @@ -437,4 +466,4 @@ Options: }); #endregion Main Tasks -RunTarget(target); \ No newline at end of file +RunTarget(target); diff --git a/crowdin.yml b/crowdin.yml index 45e3e516d..9ed426f74 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -2,9 +2,9 @@ project_id_env: _CROWDIN_PROJECT_ID api_token_env: CROWDIN_API_TOKEN preserve_hierarchy: true files: - - source: /src/App/Resources/AppResources.resx - dest: /src/App/Resources/%original_file_name% - translation: /src/App/Resources/AppResources.%two_letters_code%.resx + - source: /src/Core/Resources/Localization/AppResources.resx + dest: /src/Core/Resources/Localization/%original_file_name% + translation: /src/Core/Resources/Localization/AppResources.%two_letters_code%.resx update_option: update_as_unapproved languages_mapping: two_letters_code: diff --git a/lib/android/Xamarin.AndroidX.Credentials/Xamarin.AndroidX.Credentials.1.0.0.nupkg b/lib/android/Xamarin.AndroidX.Credentials/Xamarin.AndroidX.Credentials.1.0.0.nupkg new file mode 100644 index 000000000..0a8cb5a88 Binary files /dev/null and b/lib/android/Xamarin.AndroidX.Credentials/Xamarin.AndroidX.Credentials.1.0.0.nupkg differ diff --git a/lib/android/Xamarin.AndroidX.Credentials/net8.0-android/Xamarin.AndroidX.Credentials.dll b/lib/android/Xamarin.AndroidX.Credentials/net8.0-android/Xamarin.AndroidX.Credentials.dll new file mode 100644 index 000000000..22434e53d Binary files /dev/null and b/lib/android/Xamarin.AndroidX.Credentials/net8.0-android/Xamarin.AndroidX.Credentials.dll differ diff --git a/lib/android/Xamarin.AndroidX.Credentials/net8.0-android/Xamarin.AndroidX.Credentials.xml b/lib/android/Xamarin.AndroidX.Credentials/net8.0-android/Xamarin.AndroidX.Credentials.xml new file mode 100644 index 000000000..f7f8c6217 --- /dev/null +++ b/lib/android/Xamarin.AndroidX.Credentials/net8.0-android/Xamarin.AndroidX.Credentials.xml @@ -0,0 +1,8 @@ + + + + Xamarin.AndroidX.Credentials + + + + diff --git a/lib/android/Xamarin.AndroidX.Credentials/net8.0-android/credentials-1.2.0.aar b/lib/android/Xamarin.AndroidX.Credentials/net8.0-android/credentials-1.2.0.aar new file mode 100644 index 000000000..a3fdcfb52 Binary files /dev/null and b/lib/android/Xamarin.AndroidX.Credentials/net8.0-android/credentials-1.2.0.aar differ diff --git a/src/Android/lib/arm64-v8a/libargon2.so b/lib/android/argon2/arm64-v8a/libargon2.so similarity index 100% rename from src/Android/lib/arm64-v8a/libargon2.so rename to lib/android/argon2/arm64-v8a/libargon2.so diff --git a/src/Android/lib/armeabi-v7a/libargon2.so b/lib/android/argon2/armeabi-v7a/libargon2.so similarity index 100% rename from src/Android/lib/armeabi-v7a/libargon2.so rename to lib/android/argon2/armeabi-v7a/libargon2.so diff --git a/src/Android/lib/x86_64/libargon2.so b/lib/android/argon2/x86_64/libargon2.so similarity index 100% rename from src/Android/lib/x86_64/libargon2.so rename to lib/android/argon2/x86_64/libargon2.so diff --git a/nuget.config b/nuget.config new file mode 100644 index 000000000..e18f80b9a --- /dev/null +++ b/nuget.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 32f51b928..b92128bf8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "bitwarden-mobile", "version": "0.0.0", "devDependencies": { - "gh-pages": "^3.2.3" + "gh-pages": "3.2.3" } }, "node_modules/array-union": { diff --git a/package.json b/package.json index 37d87bd2e..acaf22c51 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,6 @@ "clean:l10n": "git push origin --delete l10n_master" }, "devDependencies": { - "gh-pages": "^3.2.3" + "gh-pages": "3.2.3" } } diff --git a/src/Android/Android.csproj b/src/Android/Android.csproj deleted file mode 100644 index 3997e036b..000000000 --- a/src/Android/Android.csproj +++ /dev/null @@ -1,327 +0,0 @@ - - - - Debug - AnyCPU - {304400AF-F0ED-40FA-B102-EA3C3EC43E4F} - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - {c9e5eea5-ca05-42a1-839b-61506e0a37df} - Library - Bit.Droid - BitwardenAndroid - True - Resources\Resource.designer.cs - Resource - Properties\AndroidManifest.xml - Resources - Assets - v13.0 - Xamarin.Android.Net.AndroidClientHandler - - - - - true - portable - false - bin\Debug - DEBUG; - prompt - 3 - - 1G - - - false - pdbonly - true - bin\Release - prompt - 4 - true - false - armeabi-v7a;x86;x86_64;arm64-v8a - 1G - true - true - - - false - bin\FDroid\ - true - pdbonly - AnyCPU - Off - prompt - MinimumRecommendedRules.ruleset - FDROID - armeabi-v7a;x86;x86_64;arm64-v8a - 1G - false - true - true - - - - - - - - - - - - - 2.1.0.4 - - - 1.9.0 - - - - - - - - 1.8.0 - - - 123.1.2.2 - - - - - 118.0.1.5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {EE44C6A1-2A85-45FE-8D9B-BF1D5F88809C} - App - - - {4b8a8c41-9820-4341-974c-41e65b7f4366} - Core - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MSBuild:UpdateGeneratedFiles - Designer - - - - - MSBuild:UpdateGeneratedFiles - Designer - - - - - MSBuild:UpdateGeneratedFiles - Designer - - - - - - - - - \ No newline at end of file diff --git a/src/Android/Effects/FabShadowEffect.cs b/src/Android/Effects/FabShadowEffect.cs deleted file mode 100644 index 9901a51ba..000000000 --- a/src/Android/Effects/FabShadowEffect.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Android.Graphics.Drawables; -using Bit.Droid.Effects; -using Bit.Droid.Utilities; -using Xamarin.Forms; -using Xamarin.Forms.Platform.Android; - -[assembly: ExportEffect(typeof(FabShadowEffect), "FabShadowEffect")] -namespace Bit.Droid.Effects -{ - public class FabShadowEffect : PlatformEffect - { - protected override void OnAttached () - { - if (Control is Android.Widget.Button button) - { - var gd = new GradientDrawable(); - gd.SetColor(ThemeHelpers.FabColor); - gd.SetCornerRadius(100); - - button.SetBackground(gd); - button.Elevation = 6; - button.TranslationZ = 20; - } - } - - protected override void OnDetached () - { - } - } -} diff --git a/src/Android/Effects/FixedSizeEffect.cs b/src/Android/Effects/FixedSizeEffect.cs deleted file mode 100644 index 98c997e18..000000000 --- a/src/Android/Effects/FixedSizeEffect.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Android.Widget; -using Bit.Droid.Effects; -using Xamarin.Forms; -using Xamarin.Forms.Platform.Android; - -[assembly: ExportEffect(typeof(FixedSizeEffect), "FixedSizeEffect")] -namespace Bit.Droid.Effects -{ - public class FixedSizeEffect : PlatformEffect - { - protected override void OnAttached() - { - if (Element is Label label && Control is TextView textView) - { - textView.SetTextSize(Android.Util.ComplexUnitType.Pt, (float)label.FontSize); - } - } - - protected override void OnDetached() - { - } - } -} diff --git a/src/Android/Effects/RemoveFontPaddingEffect.cs b/src/Android/Effects/RemoveFontPaddingEffect.cs deleted file mode 100644 index 1f7cf1297..000000000 --- a/src/Android/Effects/RemoveFontPaddingEffect.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Android.Widget; -using Bit.Droid.Effects; -using Xamarin.Forms; -using Xamarin.Forms.Platform.Android; - -[assembly: ExportEffect(typeof(RemoveFontPaddingEffect), nameof(RemoveFontPaddingEffect))] -namespace Bit.Droid.Effects -{ - public class RemoveFontPaddingEffect : PlatformEffect - { - protected override void OnAttached() - { - if (Control is TextView textView) - { - textView.SetIncludeFontPadding(false); - } - } - - protected override void OnDetached() - { - } - } -} \ No newline at end of file diff --git a/src/Android/Effects/TabBarEffect.cs b/src/Android/Effects/TabBarEffect.cs deleted file mode 100644 index e684ef452..000000000 --- a/src/Android/Effects/TabBarEffect.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Android.Views; -using Bit.Droid.Effects; -using Google.Android.Material.BottomNavigation; -using Xamarin.Forms; -using Xamarin.Forms.Platform.Android; - -[assembly: ResolutionGroupName("Bitwarden")] -[assembly: ExportEffect(typeof(TabBarEffect), "TabBarEffect")] -namespace Bit.Droid.Effects -{ - public class TabBarEffect : PlatformEffect - { - protected override void OnAttached() - { - if (!(Container.GetChildAt(0) is ViewGroup layout)) - { - return; - } - if (!(layout.GetChildAt(1) is BottomNavigationView bottomNavigationView)) - { - return; - } - bottomNavigationView.LabelVisibilityMode = LabelVisibilityMode.LabelVisibilityLabeled; - } - - protected override void OnDetached() - { - } - } -} diff --git a/src/Android/Properties/AssemblyInfo.cs b/src/Android/Properties/AssemblyInfo.cs deleted file mode 100644 index 7a135b7b7..000000000 --- a/src/Android/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("BitwardenAndroid")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Bitwarden Inc.")] -[assembly: AssemblyProduct("Bitwarden")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Android/Renderers/CustomEditorRenderer.cs b/src/Android/Renderers/CustomEditorRenderer.cs deleted file mode 100644 index 035c2f3eb..000000000 --- a/src/Android/Renderers/CustomEditorRenderer.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.ComponentModel; -using Android.Content; -using Android.Content.Res; -using Android.Views.InputMethods; -using Bit.Droid.Renderers; -using Bit.Droid.Utilities; -using Xamarin.Forms; -using Xamarin.Forms.Platform.Android; - -[assembly: ExportRenderer(typeof(Editor), typeof(CustomEditorRenderer))] -namespace Bit.Droid.Renderers -{ - public class CustomEditorRenderer : EditorRenderer - { - public CustomEditorRenderer(Context context) - : base(context) - { } - - // Workaround for issue described here: - // https://github.com/xamarin/Xamarin.Forms/issues/8291#issuecomment-617456651 - protected override void OnAttachedToWindow() - { - base.OnAttachedToWindow(); - EditText.Enabled = false; - EditText.Enabled = true; - } - - protected override void OnElementChanged(ElementChangedEventArgs e) - { - base.OnElementChanged(e); - UpdateBorderColor(); - if (Control != null && e.NewElement != null) - { - Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight, - Control.PaddingBottom + 20); - Control.ImeOptions = Control.ImeOptions | (ImeAction)ImeFlags.NoPersonalizedLearning | - (ImeAction)ImeFlags.NoExtractUi; - } - } - - protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) - { - base.OnElementPropertyChanged(sender, e); - - if (e.PropertyName == Entry.TextColorProperty.PropertyName) - { - UpdateBorderColor(); - } - } - - private void UpdateBorderColor() - { - if (Control != null) - { - var states = new[] - { - new[] { Android.Resource.Attribute.StateFocused }, // focused - new[] { -Android.Resource.Attribute.StateFocused }, // unfocused - }; - var colors = new int[] - { - ThemeHelpers.PrimaryColor, - ThemeHelpers.MutedColor - }; - Control.BackgroundTintList = new ColorStateList(states, colors); - } - } - } -} diff --git a/src/Android/Renderers/CustomEntryRenderer.cs b/src/Android/Renderers/CustomEntryRenderer.cs deleted file mode 100644 index fd1eb03b3..000000000 --- a/src/Android/Renderers/CustomEntryRenderer.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System.ComponentModel; -using Android.Content; -using Android.Content.Res; -using Android.Graphics; -using Android.Text; -using Android.Views.InputMethods; -using Android.Widget; -using Bit.Droid.Renderers; -using Bit.Droid.Utilities; -using Xamarin.Forms; -using Xamarin.Forms.Platform.Android; - -[assembly: ExportRenderer(typeof(Entry), typeof(CustomEntryRenderer))] -namespace Bit.Droid.Renderers -{ - public class CustomEntryRenderer : EntryRenderer - { - public CustomEntryRenderer(Context context) - : base(context) - { } - - protected override void OnElementChanged(ElementChangedEventArgs e) - { - base.OnElementChanged(e); - UpdateBorderColor(); - if (Control != null && e.NewElement != null) - { - Control.SetPadding(Control.PaddingLeft, Control.PaddingTop - 10, Control.PaddingRight, - Control.PaddingBottom + 20); - Control.ImeOptions = Control.ImeOptions | (ImeAction)ImeFlags.NoPersonalizedLearning | - (ImeAction)ImeFlags.NoExtractUi; - } - } - - // Workaround for bug preventing long-press -> copy/paste on Android 11 - // See https://issuetracker.google.com/issues/37095917 - protected override void OnAttachedToWindow() - { - base.OnAttachedToWindow(); - Control.Enabled = false; - Control.Enabled = true; - } - - // Workaround for failure to disable text prediction on non-password fields - // see https://github.com/xamarin/Xamarin.Forms/issues/10857 - protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) - { - base.OnElementPropertyChanged(sender, e); - - // Check if changed property is "IsPassword", otherwise ignore - if (e.PropertyName == Entry.IsPasswordProperty.PropertyName) - { - // Check if field type is text, otherwise ignore (numeric passwords, etc.) - EditText.InputType = Element.Keyboard.ToInputType(); - bool isText = (EditText.InputType & InputTypes.ClassText) == InputTypes.ClassText, - isNumber = (EditText.InputType & InputTypes.ClassNumber) == InputTypes.ClassNumber; - if (isText || isNumber) - { - if (Element.IsPassword) - { - // Element is a password field, set inputType to TextVariationPassword which disables - // predictive text by default - EditText.InputType = EditText.InputType | - (isText ? InputTypes.TextVariationPassword : InputTypes.NumberVariationPassword); - } - else - { - // Element is not a password field, set inputType to TextVariationVisiblePassword to - // disable predictive text while still displaying the content. - EditText.InputType = EditText.InputType | - (isText ? InputTypes.TextVariationVisiblePassword : InputTypes.NumberVariationNormal); - } - - // The workaround above forces a reset of the style properties, so we need to re-apply the font. - // see https://xamarin.github.io/bugzilla-archives/33/33666/bug.html - var typeface = Typeface.CreateFromAsset(Context.Assets, "RobotoMono_Regular.ttf"); - if (Control is TextView label) - { - label.Typeface = typeface; - } - } - } - else if (e.PropertyName == Entry.TextColorProperty.PropertyName) - { - UpdateBorderColor(); - } - } - - private void UpdateBorderColor() - { - if (Control != null) - { - var states = new[] - { - new[] { Android.Resource.Attribute.StateFocused }, // focused - new[] { -Android.Resource.Attribute.StateFocused }, // unfocused - }; - var colors = new int[] - { - ThemeHelpers.PrimaryColor, - ThemeHelpers.MutedColor - }; - Control.BackgroundTintList = new ColorStateList(states, colors); - } - } - } -} diff --git a/src/Android/Renderers/CustomLabelRenderer.cs b/src/Android/Renderers/CustomLabelRenderer.cs deleted file mode 100644 index 838b9b967..000000000 --- a/src/Android/Renderers/CustomLabelRenderer.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.ComponentModel; -using Android.Content; -using Android.OS; -using Bit.App.Controls; -using Bit.Droid.Renderers; -using Xamarin.Forms; -using Xamarin.Forms.Platform.Android; - -[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRenderer))] -namespace Bit.Droid.Renderers -{ - public class CustomLabelRenderer : LabelRenderer - { - public CustomLabelRenderer(Context context) - : base(context) - { } - - protected override void OnElementChanged(ElementChangedEventArgs