# Manifest and resources

A project is an Android project when its `elide.pkl` declares `android`. Only `namespace` and `compileSdk` are
required; everything else has a default or is optional.

```pkl
amends "elide:project.pkl"

import "elide:Android.pkl" as Android

name = "my-app"

android {
  namespace = "com.example.app"
  compileSdk = 36
}

artifacts {
  ["app"] = new Android.AndroidApk { release = false }
}
```

## Settings

| Property | Type | Meaning |
| --- | --- | --- |
| `namespace` | package name | The package `R` lives in and relative class names resolve against; the default `applicationId`. Required. |
| `compileSdk` | API level | The platform the app compiles, links and dexes against. Required. |
| `minSdk` | API level | The oldest release the app runs on. Default `compileSdk`. |
| `targetSdk` | API level | The release the app declares it was tested against. |
| `buildTools` | `"36.0.0"` | The build-tools release; default resolved from the SDK ([Android SDK](/docs/toolchain/android/sdk#which-components-a-build-uses)). |
| `sdk` | path | The SDK root, `"managed"` or `"system"` ([Android SDK](/docs/toolchain/android/sdk)). |
| `application` | object | The generated `AndroidManifest.xml` ([below](#generated-manifest)). |
| `resources` | object | Generated value resources ([below](#string-resources)). |
| `device` | object | The project's emulator ([Run and debug](/docs/toolchain/android/run#the-projects-emulator)). |
| `devices` | mapping | Named test devices ([Android tests](/docs/toolchain/android/testing#test-devices)). |
| `hostTests` | artifact name | The artifact host tests run against ([Android tests](/docs/toolchain/android/testing#host-tests)). |
| `hilt` | Boolean | Run Hilt's bytecode transformation ([Android dependencies](/docs/toolchain/android/dependencies#dagger-and-hilt)). |

### API levels

The build owns the API levels, and the merged manifest carries them:

| Level | Manifest omits it | Manifest declares another value |
| --- | --- | --- |
| `minSdk` | The build's is written. | The build fails: the dex would not run where it installs. |
| `targetSdk` | `targetSdk`, else `compileSdk`, is written. | The build fails when `targetSdk` is declared; otherwise the manifest's stands. |

A library's manifest records its minimum and never a target.

### Namespace and application id

`namespace` is where `R` lives. `applicationId`, declared on an application artifact, is the package the app
installs and publishes as, and defaults to the namespace. A `package` attribute in an `AndroidManifest.xml` must
agree with the namespace, or the build fails.

Two artifacts built from the same sources with different `applicationId`s install side by side, so a debug build
can be installed beside a release build:

```pkl
artifacts {
  ["app"] = new Android.AndroidApk {
    versionCode = 3
    versionName = "1.2.0"
  }
  ["app-dev"] = new Android.AndroidApk {
    applicationId = "com.example.app.dev"
    release = false
    resources { "src/main/res"; "src/debug/res" }
  }
}
```

### Kotlin defaults

A project declaring `android` defaults `kotlin.compilerOptions.jvmTarget` to 17, since the app runs against
`android.jar` on a device rather than on the JVM running the build. It also turns off the Kotlin features
`kotlinx`, `defaultPlugins` and `reflection`, whose runtimes would otherwise be dexed into the app. Declaring a
feature turns it back on:

```pkl
kotlin {
  features {
    reflection = true
  }
}
```

## Generated manifest

`android.application` describes the application, and the build generates `AndroidManifest.xml` from it:

```pkl
android {
  namespace = "com.example.app"
  compileSdk = 36
  minSdk = 30
  targetSdk = 36

  application {
    name = ".App"
    label = "@string/app_name"
    icon = "@mipmap/ic_launcher"
    theme = "@android:style/Theme.Material.Light.NoActionBar"
    allowBackup = false
    supportsRtl = true
    activities {
      ["MainActivity"] { launcher = true }
      [".settings.SettingsActivity"] { label = "@string/settings"; exported = false }
    }
    services {
      [".sync.SyncService"] { foregroundServiceType = "dataSync"; exported = false }
    }
    receivers {
      [".BootReceiver"] {
        exported = true
        actions { "android.intent.action.BOOT_COMPLETED" }
      }
    }
    providers {
      [".data.FilesProvider"] {
        authorities { "${applicationId}.files" }
        exported = false
        grantUriPermissions = true
      }
    }
    permissions {
      "android.permission.INTERNET"
      "android.permission.RECEIVE_BOOT_COMPLETED"
    }
    features {
      ["android.hardware.camera"] { required = false }
    }
  }
}
```

| Pkl | Generates |
| --- | --- |
| `name`, `label`, `icon`, `roundIcon`, `theme`, `allowBackup`, `supportsRtl` | `<application>` attributes of the same name |
| `activities` | `<activity>`; `launcher = true` adds the `MAIN`/`LAUNCHER` intent filter and exports it |
| `services` | `<service>`, with `foregroundServiceType` |
| `receivers` | `<receiver>`; `actions` become its intent filter |
| `providers` | `<provider>`, with `authorities` and `grantUriPermissions` |
| `permissions` | `<uses-permission>` |
| `features` | `<uses-feature>`, `required` by default |

Every component takes `exported` and `permission`; activities also take `label` and `theme`. Components are keyed
by class name, fully qualified or relative to the namespace (`.MainActivity` and `MainActivity` are the same
class). Android 12 and later refuse a component with an intent filter that does not declare `exported`; a launcher
activity is exported unless it says otherwise.

As with any manifest, the build adds the values it owns to the generated one: `uses-sdk`, `versionCode`, `versionName`,
`applicationId`, and `android:debuggable` for a debug build.

### Merging with AndroidManifest.xml

`src/main/AndroidManifest.xml` is optional. When it exists it is merged over the generated document, so XML
overrides and extends Pkl and never the reverse:

- an attribute both declare takes the XML's value, without a conflict;
- an element only the XML declares is added;
- `tools:node="remove"`, `tools:node="replace"` and `tools:remove` in the XML act on what was generated.

Keep in XML what the model does not name: intent filters beyond the launcher, `<meta-data>`, `<queries>`, and
attributes such as `android:networkSecurityConfig`.

```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
  <application android:networkSecurityConfig="@xml/network_security_config">
    <activity android:name=".MainActivity" android:windowSoftInputMode="adjustResize" />
  </application>
  <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" tools:node="remove" />
</manifest>
```

Libraries' manifests merge under the application's with Android's rules: the application wins, an earlier
declaration beats a later one, and two different values for one attribute are an error naming both files unless
`tools:replace` settles it. `tools:node` (`merge`, `replace`, `remove`, `removeAll`, `strict`,
`mergeOnlyAttributes`), `tools:remove` and `tools:overrideLibrary` are honored, and no `tools:` attribute reaches
the APK. A library asking for a higher `minSdkVersion` than the app is an error that `tools:overrideLibrary` waives.

### Placeholders

`${name}` in any manifest is replaced from the artifact's `placeholders`, plus `applicationId`. An unknown
placeholder fails the build. A library substitutes only its own and leaves the rest for the application.

```pkl
["app"] = new Android.AndroidApk {
  placeholders { ["appLabel"] = "My App" }
}
```

## String resources

`android.resources.strings` generates `<string>` resources of the default configuration:

```pkl
android {
  namespace = "com.example.app"
  compileSdk = 36
  resources {
    strings {
      ["app_name"] = "My App"
      ["greeting"] = "Don't panic"
    }
  }
}
```

Values are written as given; quotes, apostrophes, backslashes, newlines and a leading `@` or `?` are escaped for
`aapt2`. The generated resources are linked after any library and before the project's own resource directories,
so a string the project's `res/values` also declares resolves to the XML, and one a library declares resolves to
the project. Translations and other configurations (`values-fr/`, `values-night/`) stay in `res/`.

Code reads them as any resource: `R.string.greeting`, `stringResource(R.string.greeting)`.

## Project layout

| Path | Holds |
| --- | --- |
| `src/main/kotlin`, `src/main/java` | Sources |
| `src/main/AndroidManifest.xml` | Optional; merged over the generated manifest |
| `src/main/res` | Resources |
| `src/main/assets` | Assets, packaged under `assets/` |
| `src/main/jniLibs/<abi>/lib<name>.so` | Native libraries |
| `src/test` | Host tests ([Android tests](/docs/toolchain/android/testing#host-tests)) |
| `src/androidTest` | Instrumented tests ([Android tests](/docs/toolchain/android/testing#instrumented-tests)) |

Declaring `sources`, `resources`, `assets`, `jniLibs` or `manifestFile` on an artifact replaces the convention for
that field alone. A declared directory or source set that does not exist fails the build.

## Artifacts

All Android artifacts take:

| Property | Default | Meaning |
| --- | --- | --- |
| `name` | artifact name plus `.apk`, `.aab` or `.aar` | Published file name. |
| `sources` | every non-test source set | Source sets whose classes it carries. |
| `manifestFile` | `src/main/AndroidManifest.xml` | Manifest merged over the generated one. |
| `resources` | `src/main/res` | Resource directories. |
| `assets` | `src/main/assets` | Asset directories. |
| `jniLibs` | `src/main/jniLibs` | Native library directories. |
| `placeholders` | empty | Manifest placeholder values. |
| `linkArgs` | empty | Extra `aapt2 link` arguments. |

Applications (`AndroidApk`, `AndroidAab`) add:

| Property | Default | Meaning |
| --- | --- | --- |
| `applicationId` | `namespace` | Installed and published package. |
| `versionCode`, `versionName` | none | Replace any the manifest declares. |
| `release` | `true` | `false` is a debuggable debug build. |
| `signing` | none | Keystore ([Signing and release](/docs/toolchain/android/release#signing)). |
| `minify` | `false` | Shrink with R8 in place of `d8`. |
| `proguardFiles` | empty | R8 rules, after the libraries' own. |
| `abiFilters` | empty: every ABI | ABIs whose native libraries ship. |
| `packaging.excludes` | empty | Java resources left out ([below](#java-resources)). |
| `dexArgs` | empty | Extra `d8` or R8 arguments. |

`AndroidApk` adds `instrumentation`, which makes it a test APK ([Android tests](/docs/toolchain/android/testing#declared-test-apks)).
`AndroidAab` adds `bundleArgs` (extra `bundletool build-bundle` arguments) and `features`. `AndroidAar` adds
`consumerProguardFiles`.

### Native libraries

Native libraries are laid out as `<abi>/lib<name>.so` under each `jniLibs` directory and packaged under
`lib/<abi>/`, stored uncompressed and aligned to 16 KB pages, which Android 15 and later require on 16 KB devices.
The `.so` files themselves must be linked for 16 KB pages (`-z max-page-size=16384`). Alignment needs build-tools
35.0.0-rc3 or later; with an older `buildTools`, the newest installed release that has it is used.

```pkl
["app"] = new Android.AndroidApk {
  abiFilters { "arm64-v8a"; "x86_64" }
}
```

### Java resources

Non-class files in the jars an app depends on ship at the APK's root, as the Android Gradle Plugin packages them.
`META-INF/services` files are concatenated; any other duplicated path ships from the first jar carrying it.
Signatures, jar manifests, `META-INF/maven`, `META-INF/proguard`, licence and notice files are left out by default.
`packaging.excludes` leaves out more, with glob patterns matched against the path with a leading `/`:

```pkl
["app"] = new Android.AndroidApk {
  packaging {
    excludes { "/META-INF/{AL2.0,LGPL2.1}"; "**/*.kotlin_builtins" }
  }
}
```

## Libraries

An `AndroidAar` publishes an Android library:

```pkl
artifacts {
  ["ui"] = new Android.AndroidAar {
    consumerProguardFiles { "consumer-rules.pro" }
  }
}
```

`elide build ui` writes `.dev/artifacts/ui.aar` with the library's `AndroidManifest.xml`, `classes.jar`, its
resources as written (`res/`), `R.txt`, `assets/`, `jni/<abi>/` and its `consumerProguardFiles` as `proguard.txt`.
Resources ship as written, not compiled, because an application links its own and its libraries' resources in one
pass, which gives every identifier one numbering.

An application consumes a library of the same workspace through an ordinary project reference:

```pkl
// elide.pkl (workspace root)
amends "elide:project.pkl"

name = "my-workspace"

workspace {
  members {
    "lib"
    "app"
  }
}
```

```pkl
// app/elide.pkl
amends "elide:project.pkl"

import "elide:Android.pkl" as Android

local lib = project("lib")

name = "app"

android {
  namespace = "com.example.app"
  compileSdk = 36
}

dependencies {
  maven {
    packages { lib }
  }
}

artifacts {
  ["app"] = new Android.AndroidApk { release = false }
}
```

The application reads the library's classes, resources, manifest, assets and native libraries straight from its
build, never from its `.aar`: `elide build app` builds the library's classes but not its archive. References are
followed transitively. Precedence is Android's: the application beats its libraries, a nearer reference beats a
transitive one, and an earlier declaration beats a later one. A library's string is overridden by declaring the same
name in the application.

When workspace members resolve one library at different versions, the newest ships, as Gradle's conflict
resolution would pick it, and its resources and manifest are the ones linked, so code and `R` agree. Published
AARs are covered in [Android dependencies](/docs/toolchain/android/dependencies#published-aars).

## App bundles

An `AndroidAab` builds the bundle a store splits into per-device APKs:

```pkl
artifacts {
  ["bundle"] = new Android.AndroidAab {
    versionCode = 3
    versionName = "1.2.0"
    abiFilters { "arm64-v8a" }
    signing {
      keystore = "../keys/upload.jks"
      alias = "upload"
      dname = "CN=Example, O=Example, C=US"
      validity = 9125
      passwordEnv = "UPLOAD_KEYSTORE_PASS"
    }
  }
}
```

`elide build bundle` writes `.dev/artifacts/bundle.aab`. A bundle is signed with the upload key when it declares
`signing` ([Signing and release](/docs/toolchain/android/release#signing)); the store re-signs the APKs it generates with the app
signing key. Nothing installs a bundle directly, so it has no run or debug target.

### Dynamic feature modules

An `AndroidFeature` is a module of a bundle, delivered at install time or on demand. Its sources are a source set of
the app project, and it declares its own manifest, whose `package` is its `R` namespace:

```pkl
amends "elide:project.pkl"

import "elide:Android.pkl" as Android
import "elide:Sources.pkl" as Sources

name = "my-app"

android {
  namespace = "com.example.app"
  compileSdk = 36
}

sources {
  ["main"] = new Sources.SourceSetSpec { paths { "src/main/kotlin/**/*.kt" } }
  ["detail"] = new Sources.SourceSetSpec {
    paths { "src/detail/kotlin/**/*.kt" }
    dependsOn { "main" }
  }
}

artifacts {
  ["bundle"] = new Android.AndroidAab {
    sources { "main" }
    features { "detail" }
  }
  ["detail"] = new Android.AndroidFeature {
    base = "bundle"
    sources { "detail" }
    manifestFile = "src/detail/AndroidManifest.xml"
    resources { "src/detail/res" }
    delivery = "on-demand"
    title = "@string/detail_title"
    fusing = false
  }
}
```

- `title` names a string the base declares.
- `delivery` is `"on-demand"` (default) or `"install-time"`; it and `fusing` generate the `dist:module` element, so
  do not declare one in XML.
- A feature takes no defaults from the layout: declare its `sources`, and its `resources`, `assets` and `jniLibs`
  when it has any.
- `featureDependencies { "common" }` declares a feature it depends on; its source set's `dependsOn` lists both
  `"main"` and `"common"`.
- The base owns every Maven and workspace dependency; features dex only their own classes.

`elide build android-module-detail` builds the feature module alone. On-demand delivery in a shipped app goes
through Play Feature Delivery's `SplitInstallManager`.
