import app.cash.licensee.SpdxId import com.android.build.gradle.api.AndroidBasePlugin import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id 'com.android.application' version "8.12.1" id 'com.android.library' version "8.12.1" apply false id 'com.android.test' version '8.12.1' apply false id 'androidx.baselineprofile' version '1.4.0' id 'org.jetbrains.kotlin.android' version "2.1.10" id 'org.jetbrains.kotlin.plugin.compose' version "2.1.10" id 'org.jetbrains.kotlin.plugin.parcelize' version "2.1.10" id 'org.jetbrains.kotlin.plugin.serialization' version "2.1.10" id "com.google.devtools.ksp" version "2.1.10-1.0.31" id 'com.google.protobuf' version "0.9.5" id 'app.cash.licensee' version "1.13.0" id 'dev.rikka.tools.refine' version "4.4.0" id 'com.diffplug.spotless' version '7.2.1' } allprojects { plugins.withType(AndroidBasePlugin).configureEach { android { buildToolsVersion "36.0.0" compileSdk 36 defaultConfig { minSdk 26 targetSdk 35 vectorDrawables.useSupportLibrary = true } lint { abortOnError true checkReleaseBuilds false } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } } dependencies { implementation 'androidx.core:core-ktx:1.17.0' } } plugins.withId('com.google.protobuf') { def protocVersion = '4.32.0' protobuf { // Configure the protoc executable protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" } generateProtoTasks { all().configureEach { task -> task.builtins { remove java java { option "lite" } } } } } dependencies { implementation "com.google.protobuf:protobuf-javalite:$protocVersion" } } plugins.withType(JavaBasePlugin).configureEach { java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } } tasks.withType(KotlinCompile).configureEach { compilerOptions { freeCompilerArgs.add( "-Xjvm-default=all", ) jvmTarget = JvmTarget.JVM_17 } } ext { FRAMEWORK_PREBUILTS_DIR = "$rootDir/prebuilts/libs" daggerVersion = '2.55' addFrameworkJar = { String name -> def frameworkJar = new File(FRAMEWORK_PREBUILTS_DIR, name) if (!frameworkJar.exists()) { throw new IllegalArgumentException("Framework jar path ${frameworkJar.path} doesn't exist") } gradle.projectsEvaluated { tasks.withType(JavaCompile).configureEach { classpath = files(frameworkJar, classpath) } tasks.withType(KotlinCompile).configureEach { libraries.from(files(frameworkJar)) } } dependencies { compileOnly files(frameworkJar) } } compileOnlyCommonJars = { dependencies { compileOnly fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'SystemUI-core.jar') compileOnly fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'SystemUI-statsd.jar') compileOnly fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'WindowManager-Shell-15.jar') compileOnly projects.compatLib compileOnly projects.compatLib.compatLibVQ compileOnly projects.compatLib.compatLibVR compileOnly projects.compatLib.compatLibVS compileOnly projects.compatLib.compatLibVT compileOnly projects.compatLib.compatLibVU compileOnly projects.compatLib.compatLibVV } } } } final def buildCommit = providers.exec { commandLine('git', 'rev-parse', '--short=7', 'HEAD') }.standardOutput.asText.get().trim() final def ciBuild = System.getenv("CI") == "true" final def ciRef = System.getenv("GITHUB_REF") ?: "" final def ciRunNumber = System.getenv("GITHUB_RUN_NUMBER") ?: "" final def isReleaseBuild = ciBuild && ciRef.contains("beta") final def devReleaseName = ciBuild ? "Dev.(#${ciRunNumber})" : "Dev.(${buildCommit})" final def version = "15" final def releaseName = "Beta 1" final def versionDisplayName = "${version}.${isReleaseBuild ? releaseName : devReleaseName}" final def majorVersion = versionDisplayName.split("\\.")[0] final def quickstepMinSdk = "29" final def quickstepMaxSdk = "35" android { namespace "com.android.launcher3" defaultConfig { // Lawnchair Launcher 15.0 Beta 1 // See CONTRIBUTING.md#versioning-scheme versionCode 15_00_02_01 versionName "${versionDisplayName}" buildConfigField "String", "VERSION_DISPLAY_NAME", "\"${versionDisplayName}\"" buildConfigField "String", "MAJOR_VERSION", "\"${majorVersion}\"" buildConfigField "String", "COMMIT_HASH", "\"${buildCommit}\"" buildConfigField "boolean", "ENABLE_AUTO_INSTALLS_LAYOUT", "false" manifestPlaceholders.quickstepMinSdk = quickstepMinSdk manifestPlaceholders.quickstepMaxSdk = quickstepMaxSdk buildConfigField "int", "QUICKSTEP_MIN_SDK", quickstepMinSdk buildConfigField "int", "QUICKSTEP_MAX_SDK", quickstepMaxSdk } applicationVariants.configureEach { variant -> variant.outputs.configureEach { def channel = variant.productFlavors.last().name outputFileName = "Lawnchair.${variant.versionName}.$channel.${variant.buildType.name}.apk" } } androidResources { generateLocaleConfig true } buildFeatures { aidl true buildConfig true resValues true } packagingOptions.resources.excludes += [ "**/*.proto", "**/*.bin", "**/*.java", "**/*.properties", "**/*.version", "**/*.*_module", "com/**", "google/**", "kotlin/**", "kotlinx/**", "okhttp3/**", "META-INF/services/**", "META-INF/com/**", "META-INF/licenses/**", "META-INF/AL2.0", "META-INF/LGPL2.1", ] // Load all proguard configs from AOSP def proguardFilesFromAosp = allprojects .collect { it.file("proguard.flags") } .findAll { it.exists() } .toArray() def releaseSigning try { def keystoreProperties = new Properties() keystoreProperties.load(rootProject.file("keystore.properties").newInputStream()) releaseSigning = signingConfigs.create("release") { keyAlias keystoreProperties['keyAlias'] keyPassword keystoreProperties['keyPassword'] storeFile rootProject.file(keystoreProperties['storeFile']) storePassword keystoreProperties['storePassword'] } } catch (Exception ignored) { releaseSigning = signingConfigs.debug } buildTypes { all { signingConfig releaseSigning applicationVariants.configureEach { variant -> variant.outputs.configureEach { def channel = variant.productFlavors.last().name // Override signingConfig to debug if the "nightly" flavor is selected if (channel.any { it == "nightly" }) { signingConfig = signingConfigs.debug } } } pseudoLocalesEnabled true } debug { applicationIdSuffix ".debug" resValue("string", "derived_app_name", "Lawnchair (Debug)") } release { resValue("string", "derived_app_name", "Lawnchair") minifyEnabled true shrinkResources true proguardFiles proguardFilesFromAosp + "proguard.pro" } } compileOptions { coreLibraryDesugaringEnabled true } dependenciesInfo { includeInApk = false includeInBundle = false } // See: https://developer.android.com/studio/build/build-variants#flavor-dimensions flavorDimensions += ["app", "recents", "channel"] productFlavors { lawn { dimension "app" } withQuickstep { dimension "recents" minSdk 26 } github { applicationId 'app.lawnchair' dimension "channel" } nightly { applicationId 'app.lawnchair.nightly' dimension "channel" } play { applicationId "app.lawnchair.play" dimension "channel" isDefault true } configureEach { resValue("string", "launcher_component", "${applicationId}/app.lawnchair.LawnchairLauncher") } } sourceSets { main { res.srcDirs = ['res'] java.srcDirs = ['src', 'src_plugins'] manifest.srcFile 'AndroidManifest-common.xml' proto { srcDirs = ['protos/', 'quickstep/protos_overrides/'] } } lawn { java.srcDirs = ['src_flags', 'src_shortcuts_overrides', 'lawnchair/src', 'tests/shared'] aidl.srcDirs = ['lawnchair/aidl'] res.srcDirs = ['lawnchair/res', 'platform_frameworks_libs_systemui/animationlib/res'] manifest.srcFile "lawnchair/AndroidManifest.xml" assets { srcDirs 'lawnchair/assets' } proto { srcDirs = ['lawnchair/protos/'] } } github { manifest.srcFile "github/AndroidManifest.xml" } nightly { manifest.srcFile "nightly/AndroidManifest.xml" } lawnWithQuickstepGithub { manifest.srcFile "quickstep/AndroidManifest-launcher.xml" } lawnWithQuickstepNightly { manifest.srcFile "quickstep/AndroidManifest-launcher.xml" } lawnWithQuickstepPlay { manifest.srcFile "quickstep/AndroidManifest-launcher.xml" } withQuickstep { res.srcDirs = ['quickstep/res', 'quickstep/recents_ui_overrides/res'] java.srcDirs = ['quickstep/src', 'quickstep/recents_ui_overrides/src'] manifest.srcFile "quickstep/AndroidManifest.xml" } } } composeCompiler { stabilityConfigurationFiles.addAll( [ layout.projectDirectory.file("compose_compiler_config.conf"), ] ) reportsDestination = layout.buildDirectory.dir("compose_build_reports") } addFrameworkJar('framework-15.jar') dependencies { implementation projects.iconloaderlib implementation projects.searchuilib implementation projects.animationlib // Recents lib dependency withQuickstepCompileOnly projects.hiddenApi withQuickstepImplementation projects.shared withQuickstepImplementation projects.anim withQuickstepImplementation projects.unfold withQuickstepImplementation projects.viewcapture withQuickstepImplementation projects.log withQuickstepCompileOnly projects.plugin withQuickstepImplementation projects.plugincore withQuickstepCompileOnly projects.common // QuickSwitch Compat withQuickstepImplementation projects.compatLib withQuickstepImplementation projects.compatLib.compatLibVQ withQuickstepImplementation projects.compatLib.compatLibVR withQuickstepImplementation projects.compatLib.compatLibVS withQuickstepImplementation projects.compatLib.compatLibVT withQuickstepImplementation projects.compatLib.compatLibVU withQuickstepImplementation projects.compatLib.compatLibVV withQuickstepImplementation projects.wmshell withQuickstepImplementation projects.flags implementation 'androidx.dynamicanimation:dynamicanimation:1.1.0' implementation fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'SystemUI-statsd-15.jar') implementation fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'WindowManager-Shell-15.jar') withQuickstepCompileOnly fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'framework-15.jar') coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5' implementation 'androidx.profileinstaller:profileinstaller:1.4.1' baselineProfile projects.baselineProfile implementation "androidx.recyclerview:recyclerview:1.4.0" implementation "androidx.preference:preference-ktx:1.2.1" implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2' implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1' implementation 'com.github.ChickenHook:RestrictionBypass:2.2' implementation 'dev.rikka.tools.refine:runtime:4.4.0' implementation platform("androidx.compose:compose-bom:2025.08.00") implementation "androidx.compose.ui:ui" implementation "androidx.compose.ui:ui-util" debugImplementation "androidx.compose.ui:ui-tooling" implementation "androidx.compose.ui:ui-tooling-preview" implementation "androidx.compose.ui:ui-text-google-fonts" implementation "androidx.compose.foundation:foundation" implementation "androidx.compose.material:material-icons-extended" implementation "androidx.compose.material:material" implementation "androidx.compose.runtime:runtime-livedata" implementation 'androidx.compose.material3:material3' implementation 'androidx.compose.material3:material3-window-size-class' implementation "androidx.activity:activity-compose:1.10.1" implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.9.3" implementation "androidx.navigation:navigation-compose:2.9.3" implementation "androidx.constraintlayout:constraintlayout:2.2.1" implementation "androidx.palette:palette-ktx:1.0.0" implementation "androidx.slice:slice-core:1.1.0-alpha02" def accompanistVersion = '0.37.3' implementation "com.google.accompanist:accompanist-adaptive:$accompanistVersion" implementation "com.google.accompanist:accompanist-drawablepainter:$accompanistVersion" implementation "com.google.accompanist:accompanist-permissions:$accompanistVersion" implementation "com.google.android.material:material:1.12.0" implementation "io.github.fornewid:material-motion-compose-core:1.2.1" implementation 'dev.kdrag0n:colorkt:1.0.5' implementation 'io.coil-kt:coil-compose:2.7.0' implementation 'me.xdrop:fuzzywuzzy:1.4.0' def optoVersion = "1.0.18" implementation "com.patrykmichalik.opto:domain:$optoVersion" implementation "com.patrykmichalik.opto:core:$optoVersion" implementation "com.patrykmichalik.opto:compose:$optoVersion" implementation "androidx.datastore:datastore-preferences:1.1.7" def retrofitVersion = "3.0.0" implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" implementation "com.squareup.retrofit2:converter-kotlinx-serialization:$retrofitVersion" implementation "com.squareup.okhttp3:okhttp:5.1.0" def roomVersion = '2.7.2' implementation "androidx.room:room-runtime:$roomVersion" implementation "androidx.room:room-ktx:$roomVersion" ksp "androidx.room:room-compiler:$roomVersion" implementation "com.github.topjohnwu.libsu:service:6.0.0" // Persian Date implementation 'com.github.samanzamani:PersianDate:1.7.1' implementation 'com.airbnb.android:lottie:6.6.7' // Compose drag and drop library implementation 'sh.calvin.reorderable:reorderable:2.5.1' // Smartspacer implementation('com.kieronquinn.smartspacer:sdk-client:1.1.2') { exclude group: "com.github.skydoves", module: "balloon" } implementation 'io.github.hokofly:hoko-blur:1.5.5' implementation "androidx.window:window:1.4.0" } ksp { arg("room.schemaLocation", "$projectDir/schemas") arg("room.generateKotlin", "true") arg("room.incremental", "true") } spotless { java { target("compatLib/**/src/**/*.java") googleJavaFormat().aosp() removeUnusedImports() } kotlin { target("lawnchair/src/**/*.kt") ktlint().customRuleSets([ "io.nlopez.compose.rules:ktlint:0.4.27", ]).editorConfigOverride([ "ktlint_compose_compositionlocal-allowlist": "disabled", "ktlint_compose_lambda-param-event-trailing": "disabled", "ktlint_compose_content-slot-reused": "disabled", ]) } } licensee { allow(SpdxId.Apache_20) allow(SpdxId.BSD_3_Clause) allow(SpdxId.GPL_20_or_later) allowDependency("com.github.topjohnwu.libsu", "core", "6.0.0") allowDependency("com.github.topjohnwu.libsu", "service", "6.0.0") allowUrl("https://github.com/patrykmichalik/opto/blob/master/LICENSE") allowUrl("https://github.com/RikkaApps/HiddenApiRefinePlugin/blob/main/LICENSE") allowUrl("https://opensource.org/licenses/mit-license.php") bundleAndroidAsset = true }