From 66bb517765cd3f17466a9d7426024131c6778830 Mon Sep 17 00:00:00 2001 From: Hector Date: Fri, 18 Oct 2024 15:43:00 +0100 Subject: [PATCH] Use build distribution v0.0.1 --- .github/workflows/android_release_build.yml | 65 ++++++------------- android/app/build.gradle.kts | 5 ++ .../features/settings/SettingsScreen.kt | 63 ++++++++++++++++++ android/gradle/libs.versions.toml | 2 + 4 files changed, 89 insertions(+), 46 deletions(-) diff --git a/.github/workflows/android_release_build.yml b/.github/workflows/android_release_build.yml index f929f7fe..f6bc4bfd 100644 --- a/.github/workflows/android_release_build.yml +++ b/.github/workflows/android_release_build.yml @@ -28,6 +28,10 @@ jobs: java-version: '17' distribution: 'adopt' + - name: Install Bundletool + run: | + wget https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar -O /usr/local/bin/bundletool.jar + - name: Decode Keystore env: ENCODED_KEYSTORE: ${{ secrets.ANDROID_RELEASE_KEYSTORE_BASE64 }} @@ -49,52 +53,21 @@ jobs: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} run: ./gradlew :app:bundleRelease - - name: Debug path issues - run: find . - - name: Convert AAB to APK - uses: mukeshsolanki/bundletool-action@v1.0.2 - with: - aabFile: android/app/build/outputs/bundle/release/app-release.aab - base64Keystore: ${{ secrets.ANDROID_RELEASE_KEYSTORE_BASE64 }} - keystoreAlias: ${{ secrets.ANDROID_RELEASE_KEY_ALIAS }} - keyPassword: ${{ secrets.ANDROID_RELEASE_KEY_PASSWORD }} - keystorePassword: ${{ secrets.ANDROID_RELEASE_STORE_PASSWORD }} - bundletoolVersion: '1.9.0' - - - uses: actions/upload-artifact@v3 - with: - name: release-apk - path: ${{ steps.convert_aab.outputs.apkPath }} - - # All the below should only run if we are doing a release. - - - name: Add APK to GitHub release - uses: softprops/action-gh-release@v2 - if: ${{ startsWith(github.event.release.tag_name, 'android-') }} - with: - name: ${{ github.ref_name }} - fail_on_unmatched_files: true - # see https://github.com/softprops/action-gh-release/issues/158 - files: | - ${{ steps.convert_aab.outputs.apkPath }} + run: | + java -jar /usr/local/bin/bundletool.jar build-apks \ + --mode=universal \ + --bundle=./app/build/outputs/bundle/release/app-release.aab \ + --output=./app/build/outputs/bundle/release/app-release.apks \ + --ks=./app/$DECODED_KEYSTORE_PATH \ + --ks-pass=pass:${{ secrets.ANDROID_RELEASE_STORE_PASSWORD }} \ + --ks-key-alias=${{ secrets.ANDROID_RELEASE_KEY_ALIAS }} \ + --key-pass=${{ secrets.ANDROID_RELEASE_KEY_PASSWORD }} + cd ${{ github.workspace }} && unzip ./app/build/outputs/bundle/release/app-release.apks - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v4 - if: ${{ startsWith(github.event.release.tag_name, 'android-') }} + - name: Upload .apk + uses: actions/upload-artifact@v4 with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: us-west-1 - - - name: Extract version from tag - if: ${{ startsWith(github.event.release.tag_name, 'android-') }} - run: echo "VERSION_NAME=$(echo ${{ github.event.release.tag_name }} | sed 's/android-//')" >> $GITHUB_ENV - - - name: Upload AAB to S3 - if: ${{ startsWith(github.event.release.tag_name, 'android-') }} - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - run: | - aws s3 cp app/build/outputs/bundle/release/app-release.aab s3://hn-artifacts/app-release-${{ env.VERSION_NAME }}.aab + path: ${{ github.workspace }}/universal.apk + name: hackernews-universal-${{github.sha}}.apk + if-no-files-found: error diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index ab4c1b20..ae0e6ce2 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -21,6 +21,8 @@ android { versionCode = 10 versionName = "1.0.1" + manifestPlaceholders["emerge.distribution.apiKey"] = "" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary = true @@ -57,6 +59,8 @@ android { ) val signingConfigName = if (runningEnv == "release_workflow") "release" else "debug" signingConfig = signingConfigs.getByName(signingConfigName) + val distributionApiKey = if (runningEnv == "release_workflow") System.getenv("ANDROID_DISTRIBUTION_API_KEY") else "" + manifestPlaceholders["emerge.distribution.apiKey"] = distributionApiKey } } buildFeatures { @@ -151,4 +155,5 @@ dependencies { debugImplementation(libs.androidx.ui.test.manifest) implementation(libs.emerge.reaper) + implementation(libs.emerge.distribution) } diff --git a/android/app/src/main/java/com/emergetools/hackernews/features/settings/SettingsScreen.kt b/android/app/src/main/java/com/emergetools/hackernews/features/settings/SettingsScreen.kt index f88a0d45..3b876e76 100644 --- a/android/app/src/main/java/com/emergetools/hackernews/features/settings/SettingsScreen.kt +++ b/android/app/src/main/java/com/emergetools/hackernews/features/settings/SettingsScreen.kt @@ -1,5 +1,7 @@ package com.emergetools.hackernews.features.settings +import android.content.Intent +import android.net.Uri import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -23,11 +25,20 @@ import androidx.compose.material3.NavigationBarItemDefaults import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.core.content.ContextCompat.startActivity +import com.emergetools.distribution.Distribution +import com.emergetools.distribution.UpdateStatus import com.emergetools.hackernews.R import com.emergetools.hackernews.features.settings.components.BuiltByCard import com.emergetools.hackernews.features.settings.components.LoginCard @@ -40,6 +51,7 @@ import com.emergetools.hackernews.ui.theme.HackerNewsTheme import com.emergetools.hackernews.ui.theme.HackerOrange import com.emergetools.hackernews.ui.theme.HackerRed import com.emergetools.snapshots.annotations.EmergeAppStoreSnapshot +import kotlinx.coroutines.launch @Composable fun SettingsScreen( @@ -79,6 +91,11 @@ fun SettingsScreen( } ) Spacer(modifier = Modifier.height(8.dp)) + if (Distribution.isEnabled(LocalContext.current)) { + SettingsSectionLabel("Version") + VersionCard() + Spacer(modifier = Modifier.height(8.dp)) + } SettingsSectionLabel("About") BuiltByCard { navigation(SettingsNavigation.GoToSettingsLink("https://www.emergetools.com")) @@ -171,6 +188,52 @@ fun SettingsScreen( } } +@Composable +private fun VersionCard() { + val context = LocalContext.current + val scope = rememberCoroutineScope() + var isLoading by remember { mutableStateOf(false) } + var status by remember { mutableStateOf(null) } + return SettingsCard( + leadingIcon = { + Icon( + modifier = Modifier.width(12.dp), + painter = painterResource(R.drawable.ic_settings), + contentDescription = null // Purely decorative + ) + }, + trailingIcon = { + Icon( + modifier = Modifier.width(12.dp), + painter = painterResource(R.drawable.ic_arrow_up_right), + tint = MaterialTheme.colorScheme.onSurface, + contentDescription = "Link" + ) + }, + label = when (isLoading) { + true -> "Loading..." + else -> when (status) { + is UpdateStatus.UpToDate -> "Up to date!" + is UpdateStatus.Error -> (status as UpdateStatus.Error).message + is UpdateStatus.NewRelease -> "New release!" + else -> "Check for updates" + } + } + ) { + scope.launch { + isLoading = true + status = null + status = Distribution.checkForUpdate(context) + val theStatus = status + if (theStatus is UpdateStatus.NewRelease) { + val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(theStatus.info.downloadUrl)) + context.startActivity(browserIntent) + } + isLoading = false + } + } +} + @OptIn(EmergeAppStoreSnapshot::class) @SnapshotPreview @AppStoreSnapshot diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index d436ab1a..572b2d41 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -17,6 +17,7 @@ browser = "1.8.0" emergePlugin = "4.0.1" emergeSnapshots = "1.2.0" emergeReaper = "1.0.0-rc02" +emergeDistribution = "0.0.1" sentry = "4.11.0" shapes = "1.0.1" datastore = "1.1.1" @@ -58,6 +59,7 @@ jsoup = { group = "org.jsoup", name = "jsoup", version.ref = "jsoup" } emerge-snapshots = { group = "com.emergetools.snapshots", name = "snapshots", version.ref = "emergeSnapshots" } emerge-snapshots-annotations = { group = "com.emergetools.snapshots", name = "snapshots-annotations", version.ref = "emergeSnapshots" } emerge-reaper = { group = "com.emergetools.reaper", name = "reaper", version.ref = "emergeReaper" } +emerge-distribution = { group = "com.emergetools.distribution", name = "distribution", version.ref = "emergeDistribution" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }