diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index f40eb5e0a..45bbdd79b 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -30,39 +30,45 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - api-level: [ 21, 34 ] + flavor: [Foss, Gplay] steps: - - uses: actions/checkout@v4.2.2 - - name: Fail on bad translations - run: if grep -ri "<xliff" app/src/main/res/values*/strings.xml; then echo "Invalidly escaped translations found"; exit 1; fi - - uses: gradle/actions/wrapper-validation@v4 - - name: set up OpenJDK 17 - run: | - sudo apt-get update - sudo apt-get install -y openjdk-17-jdk-headless - sudo update-alternatives --auto java - - name: Build - run: ./gradlew assembleRelease - - name: Check lint - run: ./gradlew lintRelease - - name: Run unit tests - run: timeout 5m ./gradlew testReleaseUnitTest || { ./gradlew --stop && timeout 5m ./gradlew testReleaseUnitTest; } - - name: Enable KVM - run: | - echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules - sudo udevadm control --reload-rules - sudo udevadm trigger --name-match=kvm - - name: Run instrumented tests - uses: ReactiveCircus/android-emulator-runner@v2 - with: - api-level: ${{ matrix.api-level }} - arch: x86_64 - script: ./gradlew connectedCheck - - name: SpotBugs - run: ./gradlew spotbugsRelease - - name: Archive test results - if: always() - uses: actions/upload-artifact@v4.5.0 - with: - name: test-results-api${{ matrix.api-level }} - path: app/build/reports + - uses: actions/checkout@v4.2.2 + - name: Fail on bad translations + run: if grep -ri "<xliff" app/src/main/res/values*/strings.xml; then echo "Invalidly escaped translations found"; exit 1; fi + - uses: gradle/actions/wrapper-validation@v4 + - name: set up OpenJDK 17 + run: | + sudo apt-get update + sudo apt-get install -y openjdk-17-jdk-headless + sudo update-alternatives --auto java + - name: Build + run: ./gradlew assemble${{ matrix.flavor }}Release + - name: Check lint + run: ./gradlew lint${{ matrix.flavor }}Release + - name: Run unit tests + run: timeout 5m ./gradlew test${{ matrix.flavor }}ReleaseUnitTest || { ./gradlew --stop && timeout 5m ./gradlew test${{ matrix.flavor }}ReleaseUnitTest; } + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + - name: Run instrumented tests (API 21) + uses: ReactiveCircus/android-emulator-runner@v2 + with: + api-level: 21 + arch: x86_64 + script: ./gradlew connected${{ matrix.flavor }}DebugAndroidTest + - name: Run instrumented tests (API 34) + uses: ReactiveCircus/android-emulator-runner@v2 + with: + api-level: 34 + arch: x86_64 + script: ./gradlew connected${{ matrix.flavor }}DebugAndroidTest + - name: SpotBugs + run: ./gradlew spotbugs${{ matrix.flavor }}Release + - name: Archive test results + if: always() + uses: actions/upload-artifact@v4.5.0 + with: + name: test-results-flavor${{ matrix.flavor }} + path: app/build/reports diff --git a/.gitignore b/.gitignore index 78b6b76bf..8250a3527 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ /.bundle/ /vendor/bundle /lib/bundler/man/ + +# Catima-specific +SHA256SUMS diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e20a79a16..515be00c7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -31,6 +31,9 @@ android { resourceConfigurations += listOf("ar", "bg", "bn", "bn-rIN", "bs", "cs", "da", "de", "el-rGR", "en", "eo", "es", "es-rAR", "et", "fi", "fr", "gl", "he-rIL", "hi", "hr", "hu", "in-rID", "is", "it", "ja", "ko", "lt", "lv", "nb-rNO", "nl", "oc", "pl", "pt-rBR", "pt-rPT", "ro-rRO", "ru", "sk", "sl", "sr", "sv", "ta", "tr", "uk", "vi", "zh-rCN", "zh-rTW") testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + buildConfigField("boolean", "showDonate", "true") + buildConfigField("boolean", "showRateOnGooglePlay", "false") } buildTypes { @@ -51,6 +54,21 @@ android { viewBinding = true } + flavorDimensions.add("type") + productFlavors { + create("foss") { + dimension = "type" + isDefault = true + } + create("gplay") { + dimension = "type" + + // Google doesn't allow donation links + buildConfigField("boolean", "showDonate", "false") + buildConfigField("boolean", "showRateOnGooglePlay", "true") + } + } + bundle { language { enableSplit = false diff --git a/app/src/main/java/protect/card_locker/AboutActivity.java b/app/src/main/java/protect/card_locker/AboutActivity.java index 33929bb58..01bceab57 100644 --- a/app/src/main/java/protect/card_locker/AboutActivity.java +++ b/app/src/main/java/protect/card_locker/AboutActivity.java @@ -45,11 +45,10 @@ public class AboutActivity extends CatimaAppCompatActivity { binding.rate.setTag("https://play.google.com/store/apps/details?id=me.hackerchick.catima"); binding.donate.setTag("https://catima.app/donate"); - boolean installedFromGooglePlay = Utils.installedFromGooglePlay(this); // Hide Google Play rate button if not on Google Play - binding.rate.setVisibility(installedFromGooglePlay ? View.VISIBLE : View.GONE); + binding.rate.setVisibility(BuildConfig.showRateOnGooglePlay ? View.VISIBLE : View.GONE); // Hide donate button on Google Play (Google Play doesn't allow donation links) - binding.donate.setVisibility(installedFromGooglePlay ? View.GONE : View.VISIBLE); + binding.donate.setVisibility(BuildConfig.showDonate ? View.VISIBLE : View.GONE); bindClickListeners(); } diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index 473a9da40..280ce209d 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -1032,21 +1032,6 @@ public class Utils { return headerColor; } - public static boolean installedFromGooglePlay(Context context) { - try { - String packageName = context.getPackageName(); - String installer; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - installer = context.getPackageManager().getInstallSourceInfo(packageName).getInstallingPackageName(); - } else { - installer = context.getPackageManager().getInstallerPackageName(packageName); - } - return installer.equals("com.android.vending"); - } catch (Throwable ignored) { - return false; - } - } - public static int getHeaderColor(Context context, LoyaltyCard loyaltyCard) { return loyaltyCard.headerColor != null ? loyaltyCard.headerColor : LetterBitmap.getDefaultColor(context, loyaltyCard.store); } diff --git a/build.sh b/build.sh index e6401352e..8cd9e6b99 100755 --- a/build.sh +++ b/build.sh @@ -3,7 +3,7 @@ set -euo pipefail IFS=$'\n\t' ### build.sh -### Builds Catima the same way F-Droid does for reproducible builds +### Builds Catima the same way rbtlog/IzzyOnDroid does for reproducible builds if [ -z "${ANDROID_SDK_ROOT:-}" ]; then echo "ANDROID_SDK_ROOT is not set, setting to $HOME/Android/Sdk"; @@ -25,7 +25,11 @@ echo "Starting build" ./gradlew clean assembleRelease echo "Build finished (unsigned)" -echo "Your build is at app/build/outputs/apk/release/app-release-unsigned.apk" +flavourDirs=$(find app/build/outputs/apk/ -mindepth 1 -maxdepth 1 -type d) +for flavourDir in $flavourDirs; do + flavourName="$(basename "$flavourDir")" + echo "Your $flavourName flavour is at $flavourDir/release/app-$flavourName-release-unsigned.apk" +done if [ -z "${KEYSTORE:-}" ]; then echo "KEYSTORE not set, skipping signing..." @@ -36,16 +40,26 @@ else fi apksigner_version="$(ls -1 "$HOME/Android/Sdk/build-tools/" | tail -n 1)" - cp app/build/outputs/apk/release/app-release-unsigned.apk app/build/outputs/apk/release/app-release.apk - "$HOME/Android/Sdk/build-tools/$apksigner_version/apksigner" sign -v --ks "$KEYSTORE" --ks-key-alias "$KEYSTORE_ALIAS" app/build/outputs/apk/release/app-release.apk - echo "Build finished (signed)" - echo "Your build is at app/build/outputs/apk/release/app-release.apk" + for flavourDir in $flavourDirs; do + flavourName="$(basename "$flavourDir")" + echo "Signing $flavourName flavour..." + cp "$flavourDir/release/app-$flavourName-release-unsigned.apk" "$flavourDir/release/app-$flavourName-release.apk" + "$HOME/Android/Sdk/build-tools/$apksigner_version/apksigner" sign -v --ks "$KEYSTORE" --ks-key-alias "$KEYSTORE_ALIAS" "$flavourDir/release/app-$flavourName-release.apk" + + echo "Build finished (signed)" + echo "Your $flavourName flavour is at $flavourDir/release/app-$flavourName-release.apk" + done + + shasumPath="$(pwd)/SHA256SUMS" + echo "" > "$shasumPath" + + for flavourDir in $flavourDirs; do + pushd "$flavourDir/release/" + sha256sum -- *.apk >> "$shasumPath" + popd + done + + echo "SHA256SUMS generated" + echo "Your SHA256SUMS are at SHA256SUMS" fi - -pushd app/build/outputs/apk/release/ -sha256sum -- *.apk > SHA256SUMS -popd - -echo "SHA256SUMS generated" -echo "Your SHA256SUMS is at app/build/outputs/apk/release/SHA256SUMS" diff --git a/docs/RELEASE_STEPS.md b/docs/RELEASE_STEPS.md index 84d4185e2..f8dafccfc 100644 --- a/docs/RELEASE_STEPS.md +++ b/docs/RELEASE_STEPS.md @@ -6,8 +6,8 @@ 3. Update `CHANGELOG.md` with the new version name and the release date 4. Update `app/build.gradle.kts` with the new `versionCode` and `versionName` 5. Create a commit for the new release: `git add CHANGELOG.md app/build.gradle.kts && git commit -m "Release Catima "` -6. Build a new .apk: `KEYSTORE=/path/to/keystore KEYSTORE_ALIAS=catima ./build.sh` -7. Upload the APK to Google Play Open Testing +6. Build the new .apks: `KEYSTORE=/path/to/keystore KEYSTORE_ALIAS=catima ./build.sh` +7. Upload `app/build/outputs/apk/gplay/release/app-gplay-release.apk` to Google Play Open Testing 8. Push the version update commit: `git push` -9. Create a new release on GitHub and attach the `app-release.apk` and `SHA256SUMS` files +9. Create a new release on GitHub and attach the `app/build/outputs/apk/foss/release/app-foss-release.apk` and `SHA256SUMS` files 10. After the release has been approved on Google Play Production, update the metadata there: `bundle exec fastlane supply --version_code `