From 312062aa364a3244234d0ea9355c870ec3247bc2 Mon Sep 17 00:00:00 2001 From: Zane Schepke Date: Wed, 23 Apr 2025 01:23:01 -0400 Subject: [PATCH] refactor: app versioning and flavors --- .github/workflows/build.yml | 84 ++++---- .github/workflows/publish.yml | 153 ++++----------- .gitignore | 2 +- app/build.gradle.kts | 133 ++++--------- app/fdroid-rules.pro | 0 app/src/full/AndroidManifest.xml | 5 + app/src/main/AndroidManifest.xml | 2 - .../data/network/GitHubApi.kt | 2 + .../data/network/KtorGitHubApi.kt | 29 +++ .../data/repository/GitHubUpdateRepository.kt | 25 ++- .../ui/screens/support/SupportScreen.kt | 33 ++-- .../support/components/UpdateSection.kt | 26 +-- .../wireguardautotunnel/util/NumberUtils.kt | 12 +- app/src/main/res/values/strings.xml | 4 +- buildSrc/build.gradle.kts | 5 + buildSrc/src/main/kotlin/Constants.kt | 7 +- buildSrc/src/main/kotlin/Extensions.kt | 185 +++++++++++------- buildSrc/src/main/kotlin/LocalProperties.kt | 19 ++ fastlane/Fastfile | 8 +- gradle.properties | 2 +- gradle/libs.versions.toml | 4 +- networkmonitor/build.gradle.kts | 4 +- versionCode.txt | 1 - 23 files changed, 373 insertions(+), 372 deletions(-) delete mode 100644 app/fdroid-rules.pro create mode 100644 app/src/full/AndroidManifest.xml create mode 100644 buildSrc/src/main/kotlin/LocalProperties.kt delete mode 100644 versionCode.txt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9f1ae34f..92be4195 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,5 @@ -name: build +name: Build + on: workflow_dispatch: inputs: @@ -12,6 +13,14 @@ on: - prerelease - nightly - release + flavor: + type: choice + description: "Product flavor" + required: true + default: fdroid + options: + - fdroid + - full secrets: SIGNING_KEY_ALIAS: required: false @@ -30,6 +39,11 @@ on: description: "Build type" required: true default: debug + flavor: + type: string + description: "Product flavor" + required: false + default: fdroid secrets: SIGNING_KEY_ALIAS: required: false @@ -41,6 +55,7 @@ on: required: false KEYSTORE: required: false + env: UPLOAD_DIR_ANDROID: android_artifacts @@ -48,15 +63,17 @@ jobs: build: runs-on: ubuntu-latest env: - SIGNING_KEY_ALIAS: ${{ secrets.ANDROID_SIGNING_KEY_ALIAS }} - SIGNING_KEY_PASSWORD: ${{ secrets.ANDROID_SIGNING_KEY_PASSWORD }} - SIGNING_STORE_PASSWORD: ${{ secrets.ANDROID_SIGNING_STORE_PASSWORD }} + SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }} + SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }} + SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }} KEY_STORE_FILE: 'android_keystore.jks' KEY_STORE_LOCATION: ${{ github.workspace }}/app/keystore/ outputs: UPLOAD_DIR_ANDROID: ${{ env.UPLOAD_DIR_ANDROID }} steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up JDK 17 uses: actions/setup-java@v4 with: @@ -65,61 +82,46 @@ jobs: cache: gradle - name: Grant execute permission for gradlew run: chmod +x gradlew - - # Here we need to decode keystore.jks from base64 string and place it - # in the folder specified in the release signing configuration - name: Decode Keystore id: decode_keystore uses: timheuer/base64-to-file@v1.2 with: fileName: ${{ env.KEY_STORE_FILE }} fileDir: ${{ env.KEY_STORE_LOCATION }} - encodedString: ${{ secrets.ANDROID_KEYSTORE }} - - # create keystore path for gradle to read + encodedString: ${{ secrets.KEYSTORE }} - name: Create keystore path env var if: ${{ inputs.build_type != 'debug' }} run: | store_path=${{ env.KEY_STORE_LOCATION }}${{ env.KEY_STORE_FILE }} echo "KEY_STORE_PATH=$store_path" >> $GITHUB_ENV - - name: Create service_account.json if: ${{ inputs.build_type != 'debug' }} id: createServiceAccount - run: echo '${{ secrets.ANDROID_SERVICE_ACCOUNT_JSON }}' > service_account.json - - - name: Build Fdroid Release APK - if: ${{ inputs.build_type == 'release' }} - run: ./gradlew :app:assembleFdroidRelease --info - - - name: Build Fdroid Prerelease APK - if: ${{ inputs.build_type == 'prerelease' }} - run: ./gradlew :app:assembleFdroidPrerelease --info - - - name: Build Fdroid Nightly APK - if: ${{ inputs.build_type == 'nightly' }} - run: ./gradlew :app:assembleFdroidNightly --info - - - name: Build Debug APK - if: ${{ inputs.build_type == 'debug' }} - run: ./gradlew :app:assembleFdroidDebug --stacktrace - - # bump versionCode for nightly and prerelease builds - - name: Commit and push versionCode changes - if: ${{ inputs.build_type == 'nightly' || inputs.build_type == 'prerelease' }} + run: echo '${{ secrets.SERVICE_ACCOUNT_JSON }}' > service_account.json + - name: Build APK run: | - git config --global user.name 'GitHub Actions' - git config --global user.email 'actions@github.com' - git add versionCode.txt - git commit -m "Automated build update" - + flavor=${{ inputs.flavor }} + build_type=${{ inputs.build_type }} + case $build_type in + "release") + ./gradlew :app:assemble${flavor^}Release --info + ;; + "prerelease") + ./gradlew :app:assemble${flavor^}Prerelease --info + ;; + "nightly") + ./gradlew :app:assemble${flavor^}Nightly --info + ;; + "debug") + ./gradlew :app:assemble${flavor^}Debug --stacktrace + ;; + esac - name: Get release apk path id: apk-path - run: echo "path=$(find . -regex '^.*/build/outputs/apk/fdroid/${{ inputs.build_type }}/.*\.apk$' -type f | head -1 | tail -c+2)" >> $GITHUB_OUTPUT - + run: echo "path=$(find . -regex '^.*/build/outputs/apk/${{ inputs.flavor }}/${{ inputs.build_type }}/.*\.apk$' -type f | head -1 | tail -c+2)" >> $GITHUB_OUTPUT - name: Upload release apk uses: actions/upload-artifact@v4 with: name: ${{ env.UPLOAD_DIR_ANDROID }} - path: ${{github.workspace}}/${{ steps.apk-path.outputs.path }} - retention-days: 1 + path: ${{ github.workspace }}/${{ steps.apk-path.outputs.path }} + retention-days: 1 \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3508eb10..3d0ac53f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,12 +2,12 @@ name: publish on: schedule: - - cron: "4 3 * * *" + - cron: "4 3 * * *" workflow_dispatch: inputs: track: type: choice - description: "Google play release track" + description: "Google Play release track" options: - none - internal @@ -30,7 +30,22 @@ on: description: "Tag name for release" required: false default: nightly + flavor: + type: choice + description: "Product flavor" + required: true + default: full + options: + - fdroid + - full workflow_call: + inputs: + flavor: + type: string + description: "Product flavor" + required: false + default: full + env: UPLOAD_DIR_ANDROID: android_artifacts @@ -43,66 +58,69 @@ jobs: runs-on: ubuntu-latest outputs: has_new_commits: ${{ steps.check.outputs.new_commits }} - steps: - name: Checkout Repository uses: actions/checkout@v4 with: - fetch-depth: 0 # This fetches all history so we can check commits - + fetch-depth: 0 - name: Check for new commits id: check env: GITHUB_TOKEN: ${{ secrets.PAT }} run: | - # This script checks for commits newer than 23 hours ago NEW_COMMITS=$(git rev-list --count --after="$(date -Iseconds -d '23 hours ago')" ${{ github.sha }}) echo "new_commits=$NEW_COMMITS" >> $GITHUB_OUTPUT - build: - if: ${{ inputs.release_type != 'none' }} + build-fdroid: + if: ${{ inputs.release_type == 'release' || inputs.flavor == 'fdroid' }} uses: ./.github/workflows/build.yml secrets: inherit with: build_type: ${{ inputs.release_type == '' && 'nightly' || inputs.release_type }} + flavor: fdroid + + build-full: + if: ${{ inputs.release_type == 'release' || inputs.release_type == 'nightly' || inputs.release_type == 'prerelease' || inputs.flavor == 'full' }} + uses: ./.github/workflows/build.yml + secrets: inherit + with: + build_type: ${{ inputs.release_type == '' && 'nightly' || inputs.release_type }} + flavor: full publish: needs: - check_commits - - build + - build-fdroid + - build-full if: ${{ needs.check_commits.outputs.has_new_commits > 0 && inputs.release_type != 'none' }} name: publish-github runs-on: ubuntu-latest env: GH_USER: ${{ secrets.PAT_USERNAME }} - # GH needed for gh cli GH_TOKEN: ${{ secrets.PAT }} GH_REPO: ${{ github.repository }} - steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Install system dependencies run: | sudo apt update && sudo apt install -y gh apksigner - - # update latest tag - name: Set latest tag uses: rickstaa/action-create-tag@v1 id: tag_creation with: - tag: "latest" # or any tag name you wish to use + tag: "latest" message: "Automated tag for HEAD commit" force_push_tag: true github_token: ${{ secrets.GITHUB_TOKEN }} tag_exists_error: false - - name: Get latest release id: latest_release uses: kaliber5/action-get-release@v1 with: token: ${{ secrets.GITHUB_TOKEN }} latest: true - - name: Generate Changelog id: changelog uses: requarks/changelog-action@v1 @@ -110,65 +128,43 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} toTag: ${{ github.event_name == 'schedule' && 'nightly' || steps.latest_release.outputs.tag_name }} fromTag: "latest" - writeToFile: false # we won't write to file, just output - - - name: Get version code - if: ${{ inputs.release_type == 'release' }} - run: | - version_code=$(grep "VERSION_CODE" buildSrc/src/main/kotlin/Constants.kt | awk '{print $5}' | tr -d '\n') - echo "VERSION_CODE=$version_code" >> $GITHUB_ENV - - - name: Push changes - if: ${{ inputs.release_type == '' || inputs.release_type == 'nightly' || inputs.release_type == 'prerelease' }} - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.PAT }} - branch: ${{ github.ref }} - + writeToFile: false - name: Make download dir run: mkdir ${{ github.workspace }}/temp - - name: Download artifacts uses: actions/download-artifact@v4 with: name: ${{ env.UPLOAD_DIR_ANDROID }} path: ${{ github.workspace }}/temp - - # Setup TAG_NAME, which is used as a general "name" - if: github.event_name == 'workflow_dispatch' run: echo "TAG_NAME=${{ github.event.inputs.tag_name }}" >> $GITHUB_ENV - if: github.event_name == 'schedule' run: echo "TAG_NAME=nightly" >> $GITHUB_ENV - - name: Set version release notes if: ${{ inputs.release_type == 'release' }} run: | - RELEASE_NOTES="$(cat ${{ github.workspace }}/fastlane/metadata/android/en-US/changelogs/${{ env.VERSION_CODE }}.txt)" + VERSION_NAME=$(grep "const val VERSION_NAME" buildSrc/src/main/kotlin/Constants.kt | awk -F'"' '{print $2}') + RELEASE_NOTES="$(cat ${{ github.workspace }}/fastlane/metadata/android/en-US/changelogs/${VERSION_NAME}.txt || echo "No changelog found for ${VERSION_NAME}")" echo "RELEASE_NOTES<> $GITHUB_ENV echo "$RELEASE_NOTES" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV - - name: On nightly release notes if: ${{ contains(env.TAG_NAME, 'nightly') }} run: | echo "RELEASE_NOTES=Nightly build for the latest development version of the app." >> $GITHUB_ENV gh release delete nightly --yes || true git push origin :nightly || true - - name: On prerelease release notes if: ${{ inputs.release_type == 'prerelease' }} run: | echo "RELEASE_NOTES=Testing version of app for specific feature." >> $GITHUB_ENV gh release delete ${{ github.event.inputs.tag_name }} --yes || true - - name: Get checksum id: checksum run: | - file_path=$(find ${{ github.workspace }}/temp -type f -iname "*.apk" | tail -n1) + file_path=$(find ${{ github.workspace }}/temp -type f -iname "*.apk" | tail -1) echo "checksum=$(apksigner verify -print-certs $file_path | grep -Po "(?<=SHA-256 digest:) .*" | tr -d "[:blank:]")" >> $GITHUB_OUTPUT - - - - name: Create Release with Fastlane changelog notes + - name: Create Release id: create_release uses: softprops/action-gh-release@v2 env: @@ -195,73 +191,4 @@ jobs: prerelease: ${{ inputs.release_type == 'prerelease' || inputs.release_type == '' || inputs.release_type == 'nightly' }} make_latest: ${{ inputs.release_type == 'release' }} files: | - ${{ github.workspace }}/temp/* - - publish-fdroid: - runs-on: ubuntu-latest - needs: - - build - if: inputs.release_type == 'release' - steps: - - name: Dispatch update for fdroid repo - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ secrets.PAT }} - repository: zaneschepke/fdroid - event-type: fdroid-update - - publish-play: - if: ${{ inputs.track != 'none' && inputs.track != '' }} - name: Publish to Google Play - runs-on: ubuntu-latest - - env: - SIGNING_KEY_ALIAS: ${{ secrets.ANDROID_SIGNING_KEY_ALIAS }} - SIGNING_KEY_PASSWORD: ${{ secrets.ANDROID_SIGNING_KEY_PASSWORD }} - SIGNING_STORE_PASSWORD: ${{ secrets.ANDROID_SIGNING_STORE_PASSWORD }} - KEY_STORE_FILE: 'android_keystore.jks' - KEY_STORE_LOCATION: ${{ github.workspace }}/app/keystore/ - GH_USER: ${{ secrets.PAT_USERNAME }} - GH_TOKEN: ${{ secrets.PAT }} - - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '17' - cache: gradle - - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - # Here we need to decode keystore.jks from base64 string and place it - # in the folder specified in the release signing configuration - - name: Decode Keystore - id: decode_keystore - uses: timheuer/base64-to-file@v1.2 - with: - fileName: ${{ env.KEY_STORE_FILE }} - fileDir: ${{ env.KEY_STORE_LOCATION }} - encodedString: ${{ secrets.ANDROID_KEYSTORE }} - - # create keystore path for gradle to read - - name: Create keystore path env var - run: | - store_path=${{ env.KEY_STORE_LOCATION }}${{ env.KEY_STORE_FILE }} - echo "KEY_STORE_PATH=$store_path" >> $GITHUB_ENV - - - name: Create service_account.json - id: createServiceAccount - run: echo '${{ secrets.SERVICE_ACCOUNT_JSON }}' > service_account.json - - - name: Deploy with fastlane - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.2' # Not needed with a .ruby-version file - bundler-cache: true - - - name: Distribute app to Prod track 🚀 - run: (cd ${{ github.workspace }} && bundle install && bundle exec fastlane ${{ inputs.track }}) - + ${{ github.workspace }}/temp/* \ No newline at end of file diff --git a/.gitignore b/.gitignore index fbd8370e..9f591bfc 100644 --- a/.gitignore +++ b/.gitignore @@ -70,5 +70,5 @@ lint/tmp/ app/release/output.json .idea/codeStyles/ # where we keep our signing secrets locally -app/signing.properties /.kotlin/ +/app/keystore/ diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f12162bb..9cb3f4aa 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,33 +9,14 @@ plugins { alias(libs.plugins.licensee) } -val versionFile = file("$rootDir/versionCode.txt") - -val versionCodeIncrement = - with(getBuildTaskName().lowercase()) { - when { - this.contains(Constants.NIGHTLY) || this.contains(Constants.PRERELEASE) -> { - if (versionFile.exists()) { - versionFile.readText().trim().toInt() + 1 - } else { - 1 - } - } - else -> 0 - } - } - android { namespace = Constants.APP_ID compileSdk = Constants.TARGET_SDK androidResources { generateLocaleConfig = true } - // reproducibility dependenciesInfo { - // Disables dependency metadata when building APKs. includeInApk = false - // Disables dependency metadata when building Android App Bundles. includeInBundle = false } @@ -43,14 +24,12 @@ android { applicationId = Constants.APP_ID minSdk = Constants.MIN_SDK targetSdk = Constants.TARGET_SDK - versionCode = Constants.VERSION_CODE + versionCodeIncrement - versionName = determineVersionName() + versionCode = computeVersionCode() + versionName = computeVersionName() ksp { arg("room.schemaLocation", "$projectDir/schemas") } - sourceSets { - getByName("debug").assets.srcDirs(files("$projectDir/schemas")) // Room - } + sourceSets { getByName("debug").assets.srcDirs(files("$projectDir/schemas")) } buildConfigField( "String[]", @@ -64,15 +43,18 @@ android { signingConfigs { create(Constants.RELEASE) { - storeFile = getStoreFile() - storePassword = getSigningProperty(Constants.STORE_PASS_VAR) - keyAlias = getSigningProperty(Constants.KEY_ALIAS_VAR) - keyPassword = getSigningProperty(Constants.KEY_PASS_VAR) + storeFile = file(System.getenv("KEY_STORE_PATH") ?: "keystore/android_keystore.jks") + storePassword = + LocalProperties.get("SIGNING_STORE_PASSWORD") + ?: System.getenv("SIGNING_STORE_PASSWORD") + keyAlias = + LocalProperties.get("SIGNING_KEY_ALIAS") ?: System.getenv("SIGNING_KEY_ALIAS") + keyPassword = + LocalProperties.get("SIGNING_KEY_PASSWORD") ?: System.getenv("SIGNING_KEY_PASSWORD") } } buildTypes { - // don't strip packaging.jniLibs.keepDebugSymbols.addAll( listOf("libwg-go.so", "libwg-quick.so", "libwg.so") ) @@ -88,6 +70,7 @@ android { signingConfig = signingConfigs.getByName(Constants.RELEASE) resValue("string", "provider", "\"${Constants.APP_NAME}.provider\"") } + debug { applicationIdSuffix = ".debug" resValue("string", "app_name", "WG Tunnel - Debug") @@ -108,27 +91,21 @@ android { resValue("string", "app_name", "WG Tunnel - Nightly") resValue("string", "provider", "\"${Constants.APP_NAME}.provider.nightly\"") } - - applicationVariants.all { - val variant = this - variant.outputs - .map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl } - .forEach { output -> - val outputFileName = - "${Constants.APP_NAME}-${variant.flavorName}-" + - "${variant.buildType.name}-${variant.versionName}.apk" - output.outputFileName = outputFileName - } - } } - flavorDimensions.add(Constants.TYPE) + + flavorDimensions.add("type") productFlavors { create("fdroid") { - dimension = Constants.TYPE - proguardFile("fdroid-rules.pro") + dimension = "type" + buildConfigField("String", "FLAVOR", "\"fdroid\"") } - create("general") { dimension = Constants.TYPE } + create("google") { + dimension = "type" + buildConfigField("String", "FLAVOR", "\"google\"") + } + create("full") { dimension = "type" } } + compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -144,10 +121,23 @@ android { licensee { Constants.allowedLicenses.forEach { allow(it) } allowUrl(Constants.XZING_LICENSE_URL) - - // Fix for qrcode-kotlin (MIT, custom URL) allowUrl("https://rafaellins.mit-license.org/2021/") } + + applicationVariants.all { + val variant = this + variant.outputs + .map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl } + .forEach { output -> + val outputFileName = + if (variant.flavorName == "fdroid" && variant.buildType.name == "release") { + "${Constants.APP_NAME}-fdroid-release-${variant.versionName}.apk" + } else { + "${Constants.APP_NAME}-${variant.flavorName}-v${variant.versionName}.apk" + } + output.outputFileName = outputFileName + } + } } dependencies { @@ -156,8 +146,6 @@ dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) - - // helpers for implementing LifecycleOwner in a Service implementation(libs.androidx.lifecycle.service) implementation(libs.androidx.activity.compose) implementation(platform(libs.androidx.compose.bom)) @@ -169,7 +157,6 @@ dependencies { implementation(libs.material) implementation(libs.androidx.storage) - // test testImplementation(libs.junit) testImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit) @@ -180,107 +167,63 @@ dependencies { debugImplementation(libs.androidx.compose.ui.tooling) debugImplementation(libs.androidx.compose.manifest) - // tunnel implementation(libs.tunnel) implementation(libs.amneziawg.android) coreLibraryDesugaring(libs.desugar.jdk.libs) - // logging implementation(libs.timber) - // compose navigation implementation(libs.androidx.navigation.compose) implementation(libs.androidx.hilt.navigation.compose) - // hilt implementation(libs.hilt.android) ksp(libs.hilt.android.compiler) ksp(libs.androidx.hilt.compiler) - // accompanist implementation(libs.accompanist.permissions) implementation(libs.accompanist.drawablepainter) - // storage implementation(libs.androidx.room.runtime) ksp(libs.androidx.room.compiler) implementation(libs.androidx.room.ktx) implementation(libs.androidx.datastore.preferences) - // lifecycle implementation(libs.lifecycle.runtime.compose) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.lifecycle.process) - // serialization implementation(libs.kotlinx.serialization.json) - // ui implementation(libs.zxing.android.embedded) implementation(libs.material.icons.extended) - // bio implementation(libs.androidx.biometric.ktx) implementation(libs.pin.lock.compose) - // shortcuts implementation(libs.androidx.core) - // splash implementation(libs.androidx.core.splashscreen) - // worker implementation(libs.androidx.work.runtime) implementation(libs.androidx.hilt.work) - // util implementation(libs.qrcode.kotlin) implementation(libs.semver4j) - // Ktor implementation(libs.ktor.client.core) implementation(libs.ktor.client.okhttp) implementation(libs.ktor.client.cio) implementation(libs.ktor.client.content.negotiation) implementation(libs.ktor.serialization.kotlinx.json) -} - -fun determineVersionName(): String { - return with(getBuildTaskName().lowercase()) { - when { - contains(Constants.NIGHTLY) || contains(Constants.PRERELEASE) -> - Constants.VERSION_NAME + "-${grgitService.service.get().grgit.head().abbreviatedId}" - else -> Constants.VERSION_NAME - } - } -} - -val incrementVersionCode by - tasks.registering { - doLast { - val versionFile = file("$rootDir/versionCode.txt") - if (versionFile.exists()) { - versionFile.writeText(versionCodeIncrement.toString()) - println("Incremented versionCode to $versionCodeIncrement") - } - } - } - -tasks.whenTaskAdded { - if (name.startsWith("assemble") && !name.lowercase().contains("debug")) { - dependsOn(incrementVersionCode) - } + implementation(libs.slf4j.android) } tasks.register("copyLicenseeJsonToAssets") { dependsOn("licensee") - val outputAssets = layout.projectDirectory.dir("src/main/assets") - from(layout.buildDirectory.file("reports/licensee/androidFdroidRelease/artifacts.json")) { rename("artifacts.json", "licenses.json") } - into(outputAssets) } diff --git a/app/fdroid-rules.pro b/app/fdroid-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/app/src/full/AndroidManifest.xml b/app/src/full/AndroidManifest.xml new file mode 100644 index 00000000..ce285949 --- /dev/null +++ b/app/src/full/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7ed4eda9..1eab6c8b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,6 @@ - - diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/network/GitHubApi.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/network/GitHubApi.kt index d0eb662d..c664493c 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/network/GitHubApi.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/network/GitHubApi.kt @@ -4,4 +4,6 @@ import com.zaneschepke.wireguardautotunnel.data.model.GitHubRelease interface GitHubApi { suspend fun getLatestRelease(owner: String, repo: String): Result + + suspend fun getNightlyRelease(owner: String, repo: String): Result } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/network/KtorGitHubApi.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/network/KtorGitHubApi.kt index 5173dc51..892595e2 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/network/KtorGitHubApi.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/network/KtorGitHubApi.kt @@ -24,4 +24,33 @@ class KtorGitHubApi(private val client: HttpClient) : GitHubApi { Result.failure(e) } } + + override suspend fun getNightlyRelease(owner: String, repo: String): Result { + return try { + // Fetch all releases + val releases: List = + client.get("https://api.github.com/repos/$owner/$repo/releases").body() + + // Find the first release with "nightly" in the tag_name (case-insensitive) + val nightlyRelease = + releases.firstOrNull { release -> + release.tagName.contains("nightly", ignoreCase = true) + } + + if (nightlyRelease != null) { + Result.success(nightlyRelease) + } else { + Result.failure(Exception("No release with 'nightly' tag found")) + } + } catch (e: ClientRequestException) { + when (e.response.status) { + HttpStatusCode.Forbidden -> Result.failure(Exception("Rate limit exceeded")) + HttpStatusCode.NotFound -> + Result.failure(Exception("Repository or release not found")) + else -> Result.failure(e) + } + } catch (e: Exception) { + Result.failure(e) + } + } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/GitHubUpdateRepository.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/GitHubUpdateRepository.kt index 0651af28..486499fd 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/GitHubUpdateRepository.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/data/repository/GitHubUpdateRepository.kt @@ -1,6 +1,7 @@ package com.zaneschepke.wireguardautotunnel.data.repository import android.content.Context +import com.zaneschepke.wireguardautotunnel.BuildConfig import com.zaneschepke.wireguardautotunnel.data.network.GitHubApi import com.zaneschepke.wireguardautotunnel.di.IoDispatcher import com.zaneschepke.wireguardautotunnel.domain.entity.AppUpdate @@ -16,6 +17,7 @@ import io.ktor.utils.io.readAvailable import java.io.File import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext +import timber.log.Timber class GitHubUpdateRepository( private val gitHubApi: GitHubApi, @@ -27,11 +29,24 @@ class GitHubUpdateRepository( ) : UpdateRepository { override suspend fun checkForUpdate(currentVersion: String): Result = withContext(ioDispatcher) { - gitHubApi.getLatestRelease(githubOwner, githubRepo).map { release -> - if ( - NumberUtils.compareVersions(release.tagName.removePrefix("v"), currentVersion) > - 0 - ) { + Timber.i("Checking for update") + val release = + if (BuildConfig.VERSION_NAME.contains("nightly")) { + gitHubApi.getNightlyRelease(githubOwner, githubRepo) + } else { + gitHubApi.getLatestRelease(githubOwner, githubRepo) + } + release.map { release -> + val apkAsset = + release.assets.find { asset -> + asset.name.startsWith("wgtunnel-full-v") && asset.name.endsWith(".apk") + } + val newVersion = + apkAsset?.name?.removePrefix("wgtunnel-full-v")?.removeSuffix(".apk") + ?: return@map null + + Timber.i("Latest version: $newVersion, current version: $currentVersion") + if (NumberUtils.compareVersions(newVersion, currentVersion) > 0) { release.toAppUpdate() } else { null diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt index 4976506d..afaae541 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/SupportScreen.kt @@ -15,7 +15,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.google.zxing.client.android.BuildConfig +import com.zaneschepke.wireguardautotunnel.BuildConfig import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.ui.common.SectionDivider import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog @@ -23,8 +23,8 @@ import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel import com.zaneschepke.wireguardautotunnel.ui.screens.support.components.ContactSupportOptions import com.zaneschepke.wireguardautotunnel.ui.screens.support.components.GeneralSupportOptions import com.zaneschepke.wireguardautotunnel.ui.screens.support.components.UpdateSection -import com.zaneschepke.wireguardautotunnel.util.Constants import com.zaneschepke.wireguardautotunnel.util.extensions.canInstallPackages +import com.zaneschepke.wireguardautotunnel.util.extensions.openWebUrl import com.zaneschepke.wireguardautotunnel.util.extensions.requestInstallPackagesPermission import com.zaneschepke.wireguardautotunnel.util.extensions.showToast import com.zaneschepke.wireguardautotunnel.viewmodel.AppViewModel @@ -54,6 +54,10 @@ fun SupportScreen(viewModel: SupportViewModel = hiltViewModel(), appViewModel: A InfoDialog( onDismiss = { viewModel.handleUpdateShown() }, onAttest = { + if (BuildConfig.FLAVOR != "full") { + uiState.appUpdate?.apkUrl?.let { context.openWebUrl(it) } + return@InfoDialog + } if (context.canInstallPackages()) { viewModel.handleDownloadAndInstallApk() } else { @@ -80,7 +84,12 @@ fun SupportScreen(viewModel: SupportViewModel = hiltViewModel(), appViewModel: A } } }, - confirmText = { Text(stringResource(R.string.download_and_install)) }, + confirmText = { + Text( + if (BuildConfig.FLAVOR != "full") stringResource(R.string.download) + else stringResource(R.string.download_and_install) + ) + }, ) } @@ -110,15 +119,15 @@ fun SupportScreen(viewModel: SupportViewModel = hiltViewModel(), appViewModel: A stringResource(R.string.thank_you), modifier = Modifier.padding(horizontal = 12.dp), ) - if (BuildConfig.BUILD_TYPE == Constants.RELEASE) { - UpdateSection( - onUpdateCheck = { - context.showToast(R.string.checking_for_update) - viewModel.handleUpdateCheck() - } - ) - SectionDivider() - } + UpdateSection( + onUpdateCheck = { + if (BuildConfig.DEBUG || BuildConfig.VERSION_NAME.contains("beta")) + return@UpdateSection context.showToast(R.string.update_check_unsupported) + context.showToast(R.string.checking_for_update) + viewModel.handleUpdateCheck() + } + ) + SectionDivider() GeneralSupportOptions(context) SectionDivider() ContactSupportOptions(context) diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/components/UpdateSection.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/components/UpdateSection.kt index 08718f7b..f08343d1 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/components/UpdateSection.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/ui/screens/support/components/UpdateSection.kt @@ -1,5 +1,6 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.support.components +import androidx.compose.foundation.layout.Column import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CloudDownload import androidx.compose.material.icons.rounded.CloudDownload @@ -11,7 +12,6 @@ import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionIte import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionItemLabel import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SelectionLabelType import com.zaneschepke.wireguardautotunnel.ui.common.button.surface.SurfaceSelectionGroupButton -import com.zaneschepke.wireguardautotunnel.util.Constants @Composable fun UpdateSection(onUpdateCheck: () -> Unit = {}) { @@ -26,16 +26,20 @@ fun UpdateSection(onUpdateCheck: () -> Unit = {}) { ) }, description = { - val versionName = - if (BuildConfig.BUILD_TYPE == Constants.RELEASE) { - "v${BuildConfig.VERSION_NAME}" - } else { - "v${BuildConfig.VERSION_NAME}-${BuildConfig.BUILD_TYPE}" - } - SelectionItemLabel( - stringResource(R.string.version_template, versionName), - SelectionLabelType.DESCRIPTION, - ) + Column { + SelectionItemLabel( + stringResource( + R.string.version_template, + "v${BuildConfig.VERSION_NAME + + if(BuildConfig.DEBUG) "-debug" else "" }", + ), + SelectionLabelType.DESCRIPTION, + ) + SelectionItemLabel( + stringResource(R.string.flavor_template, BuildConfig.FLAVOR), + SelectionLabelType.DESCRIPTION, + ) + } }, onClick = onUpdateCheck, ) diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/NumberUtils.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/NumberUtils.kt index 65802347..f3afaf19 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/NumberUtils.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/util/NumberUtils.kt @@ -5,6 +5,7 @@ import java.math.BigDecimal import java.time.Duration import java.time.Instant import kotlin.math.pow +import timber.log.Timber object NumberUtils { private const val BYTES_IN_KB = 1024.0 @@ -41,8 +42,13 @@ object NumberUtils { } fun compareVersions(newVersion: String, currentVersion: String): Int { - val newSemver = Semver(newVersion, Semver.SemverType.LOOSE) - val currentSemver = Semver(currentVersion, Semver.SemverType.LOOSE) - return newSemver.compareTo(currentSemver) + try { + val newSemver = Semver(newVersion, Semver.SemverType.LOOSE) + val currentSemver = Semver(currentVersion, Semver.SemverType.LOOSE) + return newSemver.compareTo(currentSemver) + } catch (e: Exception) { + Timber.e(e, "Failed to compare versions $newVersion and $currentVersion") + return 0 + } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8211399c..56eb07c9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -220,7 +220,8 @@ Tunnel failed with: %1$s Active: %1$s Key: %1$s - Current version: %1$s + Version: %1$s + Flavor: %1$s config error dns resolution error invalid_config_error @@ -253,4 +254,5 @@ This app needs permission to install updates. Allow Licenses + Update check not supported this build type. diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 1a4f1f41..763b58d8 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -6,3 +6,8 @@ repositories { google() mavenCentral() } + +dependencies { + implementation("org.semver4j:semver4j:5.6.0") + implementation("org.ajoberstar.grgit:grgit-core:5.3.0") +} diff --git a/buildSrc/src/main/kotlin/Constants.kt b/buildSrc/src/main/kotlin/Constants.kt index 7f347e42..612cc060 100644 --- a/buildSrc/src/main/kotlin/Constants.kt +++ b/buildSrc/src/main/kotlin/Constants.kt @@ -7,15 +7,10 @@ object Constants { const val APP_ID = "com.zaneschepke.wireguardautotunnel" const val APP_NAME = "wgtunnel" - const val STORE_PASS_VAR = "SIGNING_STORE_PASSWORD" - const val KEY_ALIAS_VAR = "SIGNING_KEY_ALIAS" - const val KEY_PASS_VAR = "SIGNING_KEY_PASSWORD" - const val KEY_STORE_PATH_VAR = "KEY_STORE_PATH" - + // build types const val RELEASE = "release" const val NIGHTLY = "nightly" const val PRERELEASE = "prerelease" - const val TYPE = "type" val allowedLicenses = listOf("MIT", "Apache-2.0", "BSD-3-Clause") const val XZING_LICENSE_URL: String = "https://github.com/journeyapps/zxing-android-embedded/blob/master/COPYING" diff --git a/buildSrc/src/main/kotlin/Extensions.kt b/buildSrc/src/main/kotlin/Extensions.kt index 4bca35e0..a7b33b0f 100644 --- a/buildSrc/src/main/kotlin/Extensions.kt +++ b/buildSrc/src/main/kotlin/Extensions.kt @@ -1,76 +1,7 @@ + +import org.ajoberstar.grgit.Grgit import org.gradle.api.Project -import java.io.File -import java.util.* - -fun Project.getCurrentFlavor(): String { - val taskRequestsStr = gradle.startParameter.taskRequests.toString() - val pattern: java.util.regex.Pattern = - if (taskRequestsStr.contains(":app:assemble")) { - java.util.regex.Pattern.compile(":app:assemble(\\w+)(Release|Debug)") - } else { - java.util.regex.Pattern.compile(":app:bundle(\\w+)(Release|Debug)") - } - - val matcher = pattern.matcher(taskRequestsStr) - val flavor = - if (matcher.find()) { - matcher.group(1).lowercase() - } else { - print("NO FLAVOR FOUND") - "" - } - return flavor -} - -fun Project.getBuildTaskName(): String { - val taskRequestsStr = gradle.startParameter.taskRequests[0].toString() - return taskRequestsStr.also { - project.logger.lifecycle("Build task: $it") - } -} - -fun getLocalProperty(key: String, file: String = "local.properties"): String? { - val properties = Properties() - val localProperties = File(file) - if (localProperties.isFile) { - java.io.InputStreamReader(java.io.FileInputStream(localProperties), Charsets.UTF_8) - .use { reader -> - properties.load(reader) - } - } else return null - return properties.getProperty(key) -} - - -fun Project.getSigningProperties(): Properties { - return Properties().apply { - // created local file for signing details - try { - load(file("signing.properties").reader()) - } catch (_: Exception) { - load(file("signing_template.properties").reader()) - } - } -} - -fun Project.getStoreFile(): File { - return file( - System.getenv() - .getOrDefault( - Constants.KEY_STORE_PATH_VAR, - getSigningProperties().getProperty(Constants.KEY_STORE_PATH_VAR), - ), - ) -} - -fun Project.getSigningProperty(property: String): String { - // try to get secrets from env first for pipeline build, then properties file for local - return System.getenv() - .getOrDefault( - property, - getSigningProperties().getProperty(property), - ) -} +import org.semver4j.Semver fun Project.languageList(): List { return fileTree("../app/src/main/res") { include("**/strings.xml") } @@ -84,6 +15,116 @@ fun Project.languageList(): List { .toList() + "en" } +// Get the Git commit hash +fun Project.getGitCommitHash(): String { + var grgit: Grgit? = null + try { + grgit = Grgit.open(mapOf("currentDir" to projectDir)) + return grgit.head().abbreviatedId + } catch (e: Exception) { + logger.warn("Failed to get Git commit hash: ${e.message}. Using fallback.") + return "unknown" + } finally { + grgit?.close() + } +} +// Get commit count since last commit for versionCode increment +fun Project.getCommitCountSinceLastCommit(): Int { + var grgit: Grgit? = null + try { + grgit = Grgit.open(mapOf("currentDir" to projectDir)) + val headCommit = grgit.head() + val log = grgit.log(mapOf( + "includes" to listOf(headCommit.id) + )) + return log.size + } catch (e: Exception) { + logger.warn("Failed to get commit count: ${e.message}. Using fallback.") + return 0 + } finally { + grgit?.close() + } +} +// Get versionCode increment for nightly/pre-release +fun Project.getVersionCodeIncrement(): Int { + val isNightlyBuild = gradle.startParameter.taskNames.any { it.lowercase().contains("nightly") } + val isPreReleaseBuild = gradle.startParameter.taskNames.any { it.lowercase().contains("prerelease") } + if (!isNightlyBuild && !isPreReleaseBuild) return 0 + return System.getenv("GITHUB_RUN_NUMBER")?.toIntOrNull() + ?: System.getenv("CI_BUILD_NUMBER")?.toIntOrNull() + ?: getCommitCountSinceLastCommit() +} + +// Compute versionName dynamic bumping for nightly/pre-release +fun Project.computeVersionName(): String { + val isNightlyBuild = isNightlyBuild() + val isPreReleaseBuild = isPrereleaseBuild() + + // Static version from Constants.kt + val baseVersion = Semver.parse(Constants.VERSION_NAME) ?: Semver.of(0, 0, 0) + + return when { + isNightlyBuild -> { + // Bump patch for nightly + val nightlyVersion = Semver.of( + baseVersion.major, + baseVersion.minor, + baseVersion.patch + 1 + ) + "${nightlyVersion}-nightly+git.${getGitCommitHash()}" + } + isPreReleaseBuild -> { + // Bump minor for pre-release + val preReleaseVersion = Semver.of( + baseVersion.major, + baseVersion.minor + 1, + 0 + ) + "${preReleaseVersion}-beta+git.${getGitCommitHash()}" + } + else -> Constants.VERSION_NAME + } +} + +fun Project.isNightlyBuild(): Boolean { + return gradle.startParameter.taskNames.any { it.lowercase().contains(Constants.NIGHTLY) } +} + +fun Project.isPrereleaseBuild(): Boolean { + return gradle.startParameter.taskNames.any { it.lowercase().contains(Constants.PRERELEASE) } +} + +// Compute versionCode (static baseline, dynamic bumping for nightly/pre-release) +fun Project.computeVersionCode(): Int { + val isNightlyBuild = isNightlyBuild() + val isPreReleaseBuild = isPrereleaseBuild() + + // Static version from Constants.kt + val baseVersion = Semver.parse(Constants.VERSION_NAME) ?: Semver.of(0, 0, 0) + + val version = when { + isNightlyBuild -> { + // Bump patch for nightly + Semver.of( + baseVersion.major, + baseVersion.minor, + baseVersion.patch + 1 + ) + } + isPreReleaseBuild -> { + // Bump minor for pre-release + Semver.of( + baseVersion.major, + baseVersion.minor + 1, + 0 + ) + } + else -> baseVersion + } + + val baseVersionCode = version.major * 10000 + version.minor * 100 + version.patch + return baseVersionCode + getVersionCodeIncrement() +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/LocalProperties.kt b/buildSrc/src/main/kotlin/LocalProperties.kt new file mode 100644 index 00000000..21d07a84 --- /dev/null +++ b/buildSrc/src/main/kotlin/LocalProperties.kt @@ -0,0 +1,19 @@ +import java.io.File +import java.io.FileInputStream +import java.util.Properties + +object LocalProperties { + + private val properties by lazy { + val props = Properties() + val file = File("local.properties") + if (file.exists()) { + FileInputStream(file).use { props.load(it) } + } + props + } + + fun get(key: String): String? = properties.getProperty(key) + + fun getOrDefault(key: String, default: String): String = properties.getProperty(key, default) +} \ No newline at end of file diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 829eb37e..43ce1995 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -4,25 +4,25 @@ platform :android do desc 'Deploy a new internal version to the Google Play Store' lane :internal do - gradle(task: "clean bundleGeneralRelease") + gradle(task: "clean bundleGoogleRelease") upload_to_play_store(track: 'internal', skip_upload_apk: true) end desc "Deploy an alpha version to the Google Play" lane :alpha do - gradle(task: "clean bundleGeneralRelease") + gradle(task: "clean bundleGoogleRelease") upload_to_play_store(track: 'alpha', skip_upload_apk: true) end desc "Deploy a beta version to the Google Play" lane :beta do - gradle(task: "clean bundleGeneralRelease") + gradle(task: "clean bundleGoogleRelease") upload_to_play_store(track: 'beta', skip_upload_apk: true) end desc "Deploy a new version to the Google Play" lane :production do - gradle(task: "clean bundleGeneralRelease") + gradle(task: "clean bundleGoogleRelease") upload_to_play_store(skip_upload_apk: true) end diff --git a/gradle.properties b/gradle.properties index 2cbd6d19..107c4b29 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f38d4559..1f5a48f4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,6 @@ amneziawgAndroid = "1.3.8" androidx-junit = "1.2.1" appcompat = "1.7.0" biometricKtx = "1.2.0-alpha05" -markdownCompose = "0.5.7" coreKtx = "1.16.0" datastorePreferences = "1.1.4" desugar_jdk_libs = "2.1.5" @@ -22,6 +21,7 @@ pinLockCompose = "1.0.4" qrcodeKotlin = "4.4.1" roomVersion = "2.7.0" semver4j = "3.1.0" +slf4jAndroid = "1.7.36" timber = "5.0.1" tunnel = "1.2.14" androidGradlePlugin = "8.9.2" @@ -95,12 +95,12 @@ ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktorCli ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktorClientCore" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktorClientCore" } lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle-runtime-compose" } -markdown-compose = { module = "com.github.jeziellago:compose-markdown", version.ref = "markdownCompose" } material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "compose" } pin-lock-compose = { module = "com.zaneschepke:pin_lock_compose", version.ref = "pinLockCompose" } qrcode-kotlin = { module = "io.github.g0dkar:qrcode-kotlin", version.ref = "qrcodeKotlin" } semver4j = { module = "com.vdurmont:semver4j", version.ref = "semver4j" } +slf4j-android = { module = "org.slf4j:slf4j-android", version.ref = "slf4jAndroid" } timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } tunnel = { module = "com.zaneschepke:wireguard-android", version.ref = "tunnel" } diff --git a/networkmonitor/build.gradle.kts b/networkmonitor/build.gradle.kts index 7f1ec442..d19cbf4e 100644 --- a/networkmonitor/build.gradle.kts +++ b/networkmonitor/build.gradle.kts @@ -5,10 +5,10 @@ plugins { android { namespace = "com.zaneschepke.networkmonitor" - compileSdk = 34 + compileSdk = Constants.TARGET_SDK defaultConfig { - minSdk = 26 + minSdk = Constants.MIN_SDK testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") diff --git a/versionCode.txt b/versionCode.txt deleted file mode 100644 index c2270834..00000000 --- a/versionCode.txt +++ /dev/null @@ -1 +0,0 @@ -0 \ No newline at end of file