diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 89392a316..b17758cc6 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - feature/compose-ui - test/** pull_request: types: [opened, synchronize, reopened] @@ -48,6 +49,8 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v4 + with: + cmdline-tools-version: '9477386' - name: Install Android SDK components run: | @@ -66,21 +69,29 @@ jobs: echo "${{ secrets.RUNALYZE }}" > $GITHUB_WORKSPACE/runalyze.properties chmod +x gradlew - - name: Build bundle - run: ./gradlew :app:bundleLatestRelease :wear:bundleRelease + - name: Build AAB and APK + run: ./gradlew :app:bundleLatestRelease :app:assembleLatestRelease :wear:bundleRelease --stacktrace --info + + - name: Find APK files + run: | + echo "Searching for all APK files..." + find app/build -name "*.apk" -type f + echo "Listing apk directory structure..." + ls -la app/build/outputs/apk/ || echo "APK directory not found" - name: Upload build logs if: always() uses: actions/upload-artifact@v7 with: name: build-bundle - # possibly include: app/build/outputs/bundle/latestRelease/ wear/build/outputs/bundle/release/ path: | **/build/outputs/logs/ + app/build/outputs/bundle/latestRelease/*.aab + app/build/outputs/apk/latest/release/*.apk + wear/build/outputs/bundle/release/*.aab - name: Test run: ./gradlew test - - name: Upload test logs if: always() uses: actions/upload-artifact@v7 @@ -128,3 +139,15 @@ jobs: name: fdroid-logs path: | build/reports/problems/ + + - name: List F-Droid APK (for debug) + run: ls -la app/build/outputs/apk/latestRelease/ || echo "Directory not found" + + - name: Upload F-Droid APK + if: always() + uses: actions/upload-artifact@v7 + with: + name: fdroid-apk + path: | + app/build/outputs/apk/latest/release/*.apk + diff --git a/README.md b/README.md index 9e665c4e5..016d32865 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +20260712 RunnerUp ======== diff --git a/app/AndroidManifest.xml b/app/AndroidManifest.xml index 6b112a6da..fb0e0c337 100644 --- a/app/AndroidManifest.xml +++ b/app/AndroidManifest.xml @@ -67,14 +67,23 @@ tools:ignore="UnusedAttribute" android:dataExtractionRules="@xml/data_extraction_rules"> + + + + + + - + diff --git a/app/build.gradle b/app/build.gradle index da9f33c51..1afbb9a8c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,10 +1,16 @@ -apply plugin: 'com.android.application' +// apply plugin: 'com.android.application' +// apply plugin: 'kotlin-android' +plugins { + id 'com.android.application' + id 'kotlin-android' +} def getGitHash = providers.exec { commandLine('git', 'rev-parse', '--short', 'HEAD') }.standardOutput.asText.get().strip() android { + compileSdk 34 buildToolsVersion = rootProject.ext.buildToolsVersion namespace = 'org.runnerup' @@ -13,28 +19,55 @@ android { targetCompatibility = JavaVersion.toVersion("17") } + kotlinOptions { + jvmTarget = "17" + } + sourceSets { + // main { + // manifest.srcFile 'AndroidManifest.xml' + // java.srcDirs = ['src/main'] + // if (rootProject.ext.allowNonFree) { + // java.srcDirs += ['src/play'] + // } else { + // java.srcDirs += ['src/nonplay'] + // } + // if (rootProject.ext.enableWear) { + // java.srcDirs += ['src/wear'] + // } else { + // java.srcDirs += ['src/nonwear'] + // } + // if (rootProject.ext.noMap) { + // java.srcDirs += ['src/nomap'] + // } else if (rootProject.ext.mapboxEnabled) { + // java.srcDirs += ['src/mapbox'] + // } else { + // java.srcDirs += ['src/osmdroid'] + // } + // res.srcDirs = ['res'] + // assets.srcDirs = ['assets'] + // } main { manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = ['src/main'] + java.srcDirs = ['src/main/java'] // 明确指定 if (rootProject.ext.allowNonFree) { - java.srcDirs += ['src/play'] + java.srcDirs += ['src/play/java'] // 明确指定 } else { - java.srcDirs += ['src/nonplay'] + java.srcDirs += ['src/nonplay/java'] // 明确指定 } if (rootProject.ext.enableWear) { - java.srcDirs += ['src/wear'] + java.srcDirs += ['src/wear/java'] // 明确指定 } else { - java.srcDirs += ['src/nonwear'] + java.srcDirs += ['src/nonwear/java'] // 明确指定 } if (rootProject.ext.noMap) { - java.srcDirs += ['src/nomap'] + java.srcDirs += ['src/nomap/java'] // 明确指定 } else if (rootProject.ext.mapboxEnabled) { - java.srcDirs += ['src/mapbox'] + java.srcDirs += ['src/mapbox/java'] // 明确指定 } else { - java.srcDirs += ['src/osmdroid'] + java.srcDirs += ['src/osmdroid/java'] // 明确指定 } - // renderscript.srcDirs = java.srcDirs + // 资源目录保持不变 res.srcDirs = ['res'] assets.srcDirs = ['assets'] } @@ -45,9 +78,8 @@ android { productFlavors { latest { dimension = "all" - // multidexing support, min play support minSdk = rootProject.ext.minSdk - compileSdk = rootProject.ext.compileSdk + //compileSdk = rootProject.ext.compileSdk targetSdk = rootProject.ext.targetSdk versionName = rootProject.ext.versionName versionCode = rootProject.ext.latestBaseVersionCode + rootProject.ext.versionCode @@ -55,10 +87,6 @@ android { } defaultConfig { - // Set a separate applicationId to allow variant installations - // The applicationId is also used in some xml files (android:targetPackage) - // The packageId (in the manifest) is never changed - // See also applicationIdFull in common/build.gradle, must be in sync applicationId = rootProject.ext.applicationId vectorDrawables.useSupportLibrary = true } @@ -70,20 +98,19 @@ android { buildTypes { debug { - // Separate the debug build, sync with common/build.gradle applicationIdSuffix ".debug" versionNameSuffix = "-${getGitHash}" } release { debuggable = false applicationIdSuffix = "" - minifyEnabled = rootProject.ext.allowNonFree shrinkResources = rootProject.ext.allowNonFree proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard.txt' signingConfig = signingConfigs.release } } + buildTypes.configureEach { buildConfigField 'boolean', 'MAPBOX_ENABLED', "${rootProject.ext.mapboxEnabled}" buildConfigField 'String', 'MAPBOX_ACCESS_TOKEN', "\"${rootProject.ext.mapboxAccessToken}\"" @@ -97,6 +124,7 @@ android { buildConfigField 'String', 'DROPBOX_ID', "\"${rootProject.ext.dropboxId}\"" buildConfigField 'String', 'DROPBOX_SECRET', "\"${rootProject.ext.dropboxSecret}\"" } + lint { baseline = file('lint-baseline.xml') checkReleaseBuilds = true @@ -106,23 +134,34 @@ android { lintConfig = file('lint-osmdroid.xml') } showAll = true - //textOutput = 'stdout' textReport = true - // Treat all warnings as errors warningsAsErrors = true - // Halt the build if any errors are found abortOnError = true } + bundle { language { - // Language can be set for audio cues, include all locales enableSplit = false } } + buildFeatures { aidl = true buildConfig = true resValues = true + compose = true + } + + // 1. 启用 composeOptions(关键) + composeOptions { + kotlinCompilerExtensionVersion = rootProject.ext.kotlinCompilerExtensionVersion + } + + // 2. 添加 packaging 配置(与参考项目对齐) + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } } androidResources { generateLocaleConfig = true @@ -131,7 +170,7 @@ android { repositories { google() - mavenCentral() //MapBox GraphView + mavenCentral() if (rootProject.ext.mapboxEnabled) { maven { url = 'https://api.mapbox.com/downloads/v2/releases/maven' @@ -139,10 +178,7 @@ repositories { basic(BasicAuthentication) } credentials { - // Do not change the username below. - // This should always be `mapbox` (not your username). username = 'mapbox' - // Use the secret token you stored in gradle.properties as the password def props = new Properties() props.load(new FileInputStream(rootProject.file("mapbox.properties"))) password = props.mapboxDownloadToken @@ -169,7 +205,6 @@ dependencies { implementation "com.squareup.okhttp3:okhttp:5.4.0" if (rootProject.ext.allowNonFree) { - // MapBox uses telemetry, without Play there may be exceptions from mapbox (OK to ignore) latestImplementation "com.google.android.gms:play-services-location:${rootProject.ext.googlePlayServicesVersion}" } if (rootProject.ext.mapboxEnabled) { @@ -181,22 +216,30 @@ dependencies { testImplementation "junit:junit:${rootProject.ext.junitVersion}" testImplementation "org.mockito:mockito-core:${rootProject.ext.mockitoVersion}" + + // Jetpack Compose 依赖 - 使用根目录定义的 BOM 版本 + implementation platform("androidx.compose:compose-bom:${rootProject.ext.composeBomVersion}") + implementation 'androidx.compose.ui:ui' + implementation 'androidx.compose.ui:ui-graphics' + implementation 'androidx.compose.ui:ui-tooling-preview' + implementation 'androidx.compose.material3:material3' + implementation 'androidx.compose.runtime:runtime-livedata' + + debugImplementation 'androidx.compose.ui:ui-tooling' + debugImplementation 'androidx.compose.ui:ui-test-manifest' } allprojects { tasks.withType(JavaCompile).tap { configureEach { - // options.compilerArgs << "-Xlint:deprecation"; options.compilerArgs << "-Xlint:unchecked" } } } def props = new Properties() -// For locally signed builds only, Google Play signs the bundle if (rootProject.file("release.properties").exists()) { props.load(new FileInputStream(rootProject.file("release.properties"))) - android.signingConfigs.release.storeFile rootProject.file(props.keyStore) android.signingConfigs.release.storePassword props.keyStorePassword android.signingConfigs.release.keyAlias props.keyAlias diff --git a/app/src/main/org/runnerup/compose/ComposeMainActivity.kt b/app/src/main/org/runnerup/compose/ComposeMainActivity.kt new file mode 100644 index 000000000..84292e2c0 --- /dev/null +++ b/app/src/main/org/runnerup/compose/ComposeMainActivity.kt @@ -0,0 +1,28 @@ +package org.runnerup.compose + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview + +class ComposeMainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + Greeting("RunnerUp") + } + } +} + +@Composable +fun Greeting(name: String) { + Text(text = "Hello, $name!") +} + +@Preview +@Composable +fun PreviewGreeting() { + Greeting("RunnerUp") +} diff --git a/build.gradle b/build.gradle index 6d3f0cc0b..545bf50ba 100644 --- a/build.gradle +++ b/build.gradle @@ -4,12 +4,16 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:9.2.1' + //classpath 'com.android.tools.build:gradle:9.2.1' + classpath 'com.android.tools.build:gradle:8.0.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10" } } plugins { id "com.diffplug.spotless" version "8.6.0" + // 移除了错误的 'org.jetbrains.kotlin.kapt' 插件声明 + //id 'org.jetbrains.kotlin.plugin.compose' version '1.8.10' // 添加这一行 } repositories { @@ -27,7 +31,7 @@ project.ext { appcompat_version = "1.7.1" annotation_version = "1.9.1" preference_version = "1.2.1" - googlePlayServicesVersion = '21.3.0' + googlePlayServicesVersion = '21.4.0' googlePlayServicesWearableVersion = '20.0.1' googleWearVersion = '2.9.0' @@ -105,6 +109,12 @@ project.ext { } else { applicationId = "org.runnerup" } + + // 保留了 Compose 版本变量,供 app/build.gradle 引用 + // composeBomVersion = '2024.04.01' + // kotlinCompilerExtensionVersion = '1.5.4' + composeBomVersion = '2023.03.00' + kotlinCompilerExtensionVersion = '1.4.3' } // Special setup for antPlusLib, not allowed to copy the .aar and create mavenLocal @@ -132,4 +142,4 @@ allprojects { google() mavenCentral() } -} \ No newline at end of file +}