Try emulator, thumb zone, offline or share csv.

3 min readUpdated November 2026

Quick start

Go from one Kotlin file to an app running on an emulator or your phone, then add a second screen and turn on live reload. About ten minutes, most of it the first Gradle download.

Key takeaways

  • A folder with a single MainActivity.kt is a complete project.
  • --run builds, installs and launches. --watch does that again on every save.
  • Add files and screens freely: packages and the manifest are handled for you.

Before you start

Install the requirements: Python 3.8+, JDK 17+ and the Android SDK, plus an emulator or a phone with USB debugging.

1. Write one Kotlin file

Create a folder called tap-counter with a file MainActivity.kt. The button is deliberately large and placed at the bottom, where a thumb can reach it (see the thumb zone):

package com.example.tapcounter

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                Surface(Modifier.fillMaxSize()) {
                    TapCounter()
                }
            }
        }
    }
}

@Composable
fun TapCounter() {
    var taps by rememberSaveable { mutableIntStateOf(0) }
    Column(
        modifier = Modifier.fillMaxSize().padding(24.dp),
        verticalArrangement = Arrangement.SpaceBetween,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("$taps", style = MaterialTheme.typography.displayLarge, modifier = Modifier.padding(top = 96.dp))
        Button(onClick = { taps++ }, modifier = Modifier.fillMaxWidth().height(72.dp)) {
            Text("Tap", style = MaterialTheme.typography.headlineSmall)
        }
    }
}

No build.gradle.kts, no manifest, no res/ folder. rememberSaveable keeps the count when the phone rotates; designing for interruptions explains why that matters.

2. Run it

python3 android_scaffold.py tap-counter --name "Tap Counter" --run

The script prints what it found, then works through each step:

[*] Scanned source 'tap-counter':
    - Scaffold Mode     : Creating new scaffold
    - Kotlin Files      : 1 file(s)
    - Base Package      : com.example.tapcounter
    - Launcher Activity : com.example.tapcounter.MainActivity
    - Output Directory  : android_build
  1. Generates the Gradle project, manifest and launcher icon in android_build/.
  2. Builds a debug APK with the Gradle wrapper.
  3. Boots an emulator, or uses the phone you've plugged in.
  4. Installs and opens the app.
  5. Copies the APK to apk/, ready to send to any Android phone.
First build

The first run downloads the Gradle distribution and the AndroidX libraries, which can take a few minutes. Later builds are incremental and much faster.

3. Add a second screen

Drop another Activity into the folder, in any subfolder or package you like:

package com.example.tapcounter.history

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text

class HistoryActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent { Text("History") }
    }
}

Run the same command again. The file is placed in com/example/tapcounter/history/ and HistoryActivity is registered in the manifest automatically. Most Compose apps keep a single Activity and switch screens inside it, which is what the Thumb Inventory example does, but both work.

4. Turn on live reload

python3 android_scaffold.py tap-counter --watch

Change the button label, save, and the app relaunches with your change a few seconds later. Read live reload with --watch for how it decides what to rebuild.

5. Share the APK

The debug APK in apk/ can be sent through any chat app or email and installed on another phone after allowing installs from that source. It is signed with a debug key, which is fine for prototypes and internal tools but not for the Play Store.

Next steps

Frequently asked questions

What is the smallest possible android_scaffold project?

A folder, or even a single .kt file, containing one class that extends ComponentActivity. You can pass the file directly: python3 android_scaffold.py MainActivity.kt --run.

What if my file has no package line?

It is given the base package, which comes from your MainActivity, or the shortest declared package, or com.example.singlefileapp if no file declares one.

Where does the generated project go?

Into ./android_build by default. Change it with --output. It is a normal Gradle project, so you can also open it in Android Studio.