#!/bin/bash # RSSuper Android Build Script # Native Android build using Gradle set -e # Configuration SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" ANDROID_PROJECT_DIR="$PROJECT_ROOT/native-route/android" # Default values VARIANT="${1:-debug}" ACTION="${2:-assemble}" # Show help show_help() { echo "RSSuper Android Build Script" echo "" echo "Usage: $0 [VARIANT] [ACTION]" echo "" echo "Arguments:" echo " VARIANT Build variant (debug|release) [Default: debug]" echo " ACTION Gradle action (assemble|build|test|clean) [Default: assemble]" echo "" echo "Examples:" echo " $0 # Assemble debug APK" echo " $0 release # Assemble release APK" echo " $0 debug test # Run tests" echo " $0 clean # Clean build" } # Check prerequisites check_prerequisites() { if [ ! -d "$ANDROID_PROJECT_DIR" ]; then echo "Error: Android project directory not found: $ANDROID_PROJECT_DIR" echo "Creating basic Android project structure..." create_android_project return 0 fi if [ ! -f "$ANDROID_PROJECT_DIR/build.gradle.kts" ] && [ ! -f "$ANDROID_PROJECT_DIR/build.gradle" ]; then echo "Warning: No build.gradle found. Creating basic Android project..." create_android_project fi # Check for Java/JDK if ! command -v java &> /dev/null; then echo "Error: Java not found. Please install JDK 17." exit 1 fi echo "Java version:" java -version } # Create basic Android project structure create_android_project() { echo "Creating Android project structure..." mkdir -p "$ANDROID_PROJECT_DIR/app/src/main/java/com/mikefreno/rssuper" mkdir -p "$ANDROID_PROJECT_DIR/app/src/main/res/values" mkdir -p "$ANDROID_PROJECT_DIR/app/src/main/res/mipmap-hdpi" mkdir -p "$ANDROID_PROJECT_DIR/gradle/wrapper" # Root build.gradle.kts cat > "$ANDROID_PROJECT_DIR/build.gradle.kts" << 'EOF' // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { id("com.android.application") version "8.2.0" apply false id("org.jetbrains.kotlin.android") version "1.9.0" apply false } allprojects { repositories { google() mavenCentral() } } task("clean") { delete(rootProject.buildDir) } EOF # settings.gradle.kts cat > "$ANDROID_PROJECT_DIR/settings.gradle.kts" << 'EOF' pluginManagement { repositories { google() mavenCentral() gradlePluginPortal() } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } } rootProject.name = "RSSuper" include(":app") EOF # app/build.gradle.kts cat > "$ANDROID_PROJECT_DIR/app/build.gradle.kts" << 'EOF' plugins { id("com.android.application") id("org.jetbrains.kotlin.android") } android { namespace = "com.mikefreno.rssuper" compileSdk = 34 defaultConfig { applicationId = "com.mikefreno.rssuper" minSdk = 24 targetSdk = 34 versionCode = 1 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { isMinifyEnabled = false proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = "17" } } dependencies { implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.appcompat:appcompat:1.6.1") implementation("com.google.android.material:material:1.11.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") // Navigation implementation("androidx.navigation:navigation-fragment-ktx:2.7.6") implementation("androidx.navigation:navigation-ui-ktx:2.7.6") // Lifecycle implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0") // Coroutines implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") // Networking implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.9.0") implementation("com.squareup.okhttp3:okhttp:4.12.0") // Room Database implementation("androidx.room:room-runtime:2.6.1") implementation("androidx.room:room-ktx:2.6.1") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") } EOF # gradle.properties cat > "$ANDROID_PROJECT_DIR/gradle.properties" << 'EOF' org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 android.useAndroidX=true kotlin.code.style=official android.nonTransitiveRClass=true EOF # gradle wrapper properties cat > "$ANDROID_PROJECT_DIR/gradle/wrapper/gradle-wrapper.properties" << 'EOF' distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists EOF # Main Activity cat > "$ANDROID_PROJECT_DIR/app/src/main/java/com/mikefreno/rssuper/MainActivity.kt" << 'EOF' package com.mikefreno.rssuper import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import com.mikefreno.rssuper.ui.theme.RSSuperTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { RSSuperTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { Greeting("Android") } } } } } @Composable fun Greeting(name: String, modifier: Modifier = Modifier) { Text( text = "RSSuper - $name", modifier = modifier ) } @Preview(showBackground = true) @Composable fun GreetingPreview() { RSSuperTheme { Greeting("Android") } } EOF # Theme mkdir -p "$ANDROID_PROJECT_DIR/app/src/main/java/com/mikefreno/rssuper/ui/theme" cat > "$ANDROID_PROJECT_DIR/app/src/main/java/com/mikefreno/rssuper/ui/theme/Theme.kt" << 'EOF' package com.mikefreno.rssuper.ui.theme import android.app.Activity import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.core.view.WindowCompat private val DarkColorScheme = darkColorScheme( primary = Color(0xFF90CAF9), secondary = Color(0xFF81D4FA), tertiary = Color(0xFFA5D6A7) ) private val LightColorScheme = lightColorScheme( primary = Color(0xFF1976D2), secondary = Color(0xFF03A9F4), tertiary = Color(0xFF4CAF50) ) @Composable fun RSSuperTheme( darkTheme: Boolean = isSystemInDarkTheme(), dynamicColor: Boolean = true, content: @Composable () -> Unit ) { val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } darkTheme -> DarkColorScheme else -> LightColorScheme } val view = LocalView.current if (!view.isInEditMode) { SideEffect { val window = (view.context as Activity).window window.statusBarColor = colorScheme.primary.toArgb() WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme } } MaterialTheme( colorScheme = colorScheme, content = content ) } EOF # AndroidManifest.xml cat > "$ANDROID_PROJECT_DIR/app/src/main/AndroidManifest.xml" << 'EOF' EOF # styles.xml cat > "$ANDROID_PROJECT_DIR/app/src/main/res/values/styles.xml" << 'EOF' EOF # proguard-rules.pro cat > "$ANDROID_PROJECT_DIR/app/proguard-rules.pro" << 'EOF' # Add project specific ProGuard rules here. -keepattributes Signature -keepattributes *Annotation* -keepattributes RuntimeVisibleAnnotations -keepattributes RuntimeInvisibleAnnotations EOF # gradlew script cat > "$ANDROID_PROJECT_DIR/gradlew" << 'EOF' #!/bin/bash # Gradle wrapper placeholder # In a real project, this would be generated by gradle wrapper # For now, we'll use system gradle if available if command -v gradle &> /dev/null; then exec gradle "$@" else echo "Error: Gradle not found. Please install Gradle or use ./gradlew" exit 1 fi EOF chmod +x "$ANDROID_PROJECT_DIR/gradlew" echo "Android project structure created!" } # Build the project build_project() { echo "Building Android $VARIANT..." cd "$ANDROID_PROJECT_DIR" # Determine gradle task based on variant local TASK="${ACTION}${VARIANT}"" if [ "$VARIANT" = "release" ]; then TASK="${ACTION}Release" fi echo "Running: ./gradlew $TASK" ./gradlew $TASK } # Run tests run_tests() { echo "Running Android tests..." cd "$ANDROID_PROJECT_DIR" ./gradlew test } # Clean build clean_build() { echo "Cleaning Android build..." cd "$ANDROID_PROJECT_DIR" ./gradlew clean } # Main case "$ACTION" in clean) check_prerequisites clean_build ;; test) check_prerequisites run_tests ;; assemble|build|*) check_prerequisites build_project ;; esac