Your first app, running
There is a particular satisfaction in seeing your own words on a phone screen, and it is worth getting there before learning anything else. This lesson creates the Mandi Bhav project, makes it match the course's starter exactly, explains the one decision you make at creation that is hard to undo later, and runs it.
Create the project
From the Welcome screen click New Project, or from inside an open project use File → New → New Project.
- Under Phone and Tablet, choose Empty Activity. It is the Compose template: one screen, with Compose and Material 3 already set up. Templates with "Views" in the name use the older XML toolkit, and are not what you want.
- Fill in the configuration page:
| Field | Value | Why |
|---|---|---|
| Name | Mandi Bhav |
What appears under the launcher icon. You can change it later. |
| Package name | com.riztechacademy.mandibhav |
Becomes your application ID, the app's identity on every phone and on Google Play. Effectively permanent once published. |
| Save location | A folder you will find again | On a Mac, avoid Desktop, Documents and Downloads: macOS privacy protection can stop Android Studio seeing the files. |
| Minimum SDK | API 26 (Android 8.0) | The oldest Android version the app will install on. Explained below. |
| Build configuration language | Kotlin DSL, if the wizard asks | Build files in Kotlin (.kts), as in this course. |
The package name is written backwards from a domain you control, so that two companies' apps can
never collide. If you have no domain, com.yourname.mandibhav is fine for learning; know that
once an app is on Google Play, changing it means publishing a different app.
- Click Finish. Android Studio creates the files and runs a Gradle sync: it downloads the build tools and libraries the project needs and works out how to build it. The first time, on a fresh machine, this takes minutes rather than seconds. The first full build of this project on the course's machine took about a minute and twenty seconds; later builds take a few seconds. Leave it alone until the progress bar at the bottom of the window finishes.
Make it match the course starter
The wizard's output changes from one Android Studio release to the next: different library versions, sometimes extra files. The course needs everyone on the same project, so now you make yours match. This is also your first lesson in not trusting generated code blindly.
MainActivity.kt
Open app/src/main/java/com/riztechacademy/mandibhav/MainActivity.kt (the folder is called java
even though it holds Kotlin; some versions of the template call it kotlin) and replace its contents
with this. Type it rather than pasting it; your fingers learn the shape of an Android file that way.
package com.riztechacademy.mandibhav
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MaterialTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(name = "Mandi Bhav", modifier = Modifier.padding(innerPadding))
}
}
}
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(text = "Namaste from $name", modifier = modifier)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
MaterialTheme {
Greeting("Mandi Bhav")
}
}
Against the template, three things changed: the greeting now says "Namaste from" instead of "Hello",
it greets "Mandi Bhav" instead of "Android", and the content is wrapped in the plain MaterialTheme
instead of a theme the wizard generated for you. Your own theme comes back, built properly, in the
theming module.
Read it top to bottom once, without trying to master it:
class MainActivity : ComponentActivity(): an activity is a screen the system can start. This one is the app's entry point; module 2 explains why most modern apps have exactly one.onCreate: Android calls this when it creates the activity. You did not call it and never will. That inversion, the system calling you, is the heart of Android.enableEdgeToEdge(): lets the app draw behind the status bar and navigation bar.setContent { ... }: hands the screen over to Compose. Everything inside is a description of the UI.Scaffoldgives a standard screen structure and passesinnerPadding: the space taken by the system bars, which your content must stay out of.@Composable fun Greeting: a composable function, a piece of UI. It takes data (name) and describes what to show.@Preview: rendersGreetingPreviewin the editor's design pane without running the app.
The build files
Open app/build.gradle.kts and make it read exactly:
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.compose)
}
android {
namespace = "com.riztechacademy.mandibhav"
compileSdk {
version = release(37) {
minorApiLevel = 2
}
}
defaultConfig {
applicationId = "com.riztechacademy.mandibhav"
minSdk = 26
targetSdk = 36
versionCode = 1
versionName = "0.1"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
buildFeatures {
compose = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
debugImplementation(libs.androidx.compose.ui.tooling)
testImplementation(libs.junit)
}
The libs. names come from gradle/libs.versions.toml, the version catalog. Make it read:
[versions]
agp = "9.5.1"
kotlin = "2.4.21"
coreKtx = "1.19.1"
activityCompose = "1.13.0"
lifecycle = "2.11.0"
composeBom = "2026.09.00"
junit = "4.13.2"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
And the top-level build.gradle.kts (the one next to settings.gradle.kts, not inside app/):
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.compose) apply false
}
If your wizard added an org.jetbrains.kotlin.android plugin anywhere, remove it. From Android Gradle
Plugin 9, Kotlin support is built in and that plugin is no longer applied; only the Compose compiler
plugin remains.
The manifest, and the leftovers
Make app/src/main/AndroidManifest.xml read:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@android:style/Theme.Material.Light.NoActionBar">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Then delete what nothing uses any more: the ui.theme package next to MainActivity.kt, the
themes.xml file under res/values, and the test and androidTest folders under app/src (the
testing module creates them again). Leave the launcher icons, strings.xml and colors.xml alone;
yours may look slightly different from the course's and that is harmless.
After editing build files, Android Studio shows a banner offering to sync. Click Sync Now. A sync that ends without red text means your project now matches the course.
Choosing minSdk: a trade-off, not a default
Three numbers in that build file look alike and mean different things:
| Setting | Here | Meaning |
|---|---|---|
compileSdk |
37.2 | Which Android API you compile against: the newest one you can call. The platform from lesson 2. |
targetSdk |
36 | Which Android version's behaviour you have tested against. Newer phones apply newer rules only once you raise it. |
minSdk |
26 | The oldest Android version that can install the app at all. |
minSdk is the one with consequences for real people. Android 8.0 (API 26) came out in 2017. Every
phone on Android 7 or earlier cannot install Mandi Bhav, and Google Play will not even show it to
them. That is the cost, stated plainly: some of Arjun's neighbours, on the oldest phones, are
excluded. If you sideload the APK onto such a phone anyway, the install is refused. Here is the same
refusal produced the other way round, with minSdk set to 37 and installed on an Android 16 (API 36)
emulator:
adb: failed to install app-debug.apk: Failure [INSTALL_FAILED_OLDER_SDK: Requires newer sdk version #37 (current version is #36)]
Why accept it? Because going lower is not free either, and below a point it is not possible. Here is
what the build does with lower values, on this exact project. With minSdk = 21:
> Manifest merger failed : uses-sdk:minSdkVersion 21 cannot be smaller than version 23 declared in
library [androidx.navigationevent:navigationevent-android:1.0.0] ... as the library might be using
APIs not available in 21
The libraries themselves need API 23. With minSdk = 25:
> Android resource linking failed
com.riztechacademy.mandibhav.app-main-35:/mipmap-anydpi/ic_launcher.xml: error: <adaptive-icon>
elements require a sdk version of at least 26.
The launcher icon uses adaptive icons, which arrived in API 26. You could keep a second set of
old-style icons for API 23 to 25, and then you would also be maintaining workarounds for
java.time (the modern date API, which arrived in 26 and which a price-per-date app uses everywhere)
and for notification channels (also 26, needed for price alerts). Every API level you support is a
version you must test on, and the oldest phones are the slowest and least memory-rich. API 26 is
the trade this course makes: the oldest phones excluded, in return for less code and fewer
untestable paths. For your own apps, the wizard's Help me choose link shows the current share of
devices on each version; make the decision with that open, and write down why.
Run it
At the top of the window is the device menu, and next to it the green Run button (Shift + F10 on
Windows and Linux, Ctrl + R on a Mac). Pick a device and press Run. If the menu is empty, you have no
virtual device yet: create one as the end of the installation lesson describes, then come back.
Android Studio builds the APK, installs it and starts MainActivity. You should see "Namaste from
Mandi Bhav" in the top-left corner, immediately below the status bar.
The same thing from a terminal, in the project folder, shows what Run does for you. (adb lives in
the SDK's platform-tools folder; lesson 4 puts it on your path. On Windows, use gradlew.bat
instead of ./gradlew.)
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk
adb shell am start -W -n com.riztechacademy.mandibhav/.MainActivity
On an emulator, the real output of the last command was:
Starting: Intent { cmp=com.riztechacademy.mandibhav/.MainActivity }
Status: ok
LaunchState: COLD
Activity: com.riztechacademy.mandibhav/.MainActivity
TotalTime: 722
WaitTime: 726
Complete
LaunchState: COLD means the app's process did not exist and Android started it from nothing.
TotalTime: 722 is milliseconds until the first frame was drawn. Remember this number: it is from a
fast emulator on a fast laptop, and lesson 4 explains why your users will not see it.
The trap: padding you thought you could skip
Modifier.padding(innerPadding) looks like decoration. Delete it, so the line reads
Greeting(name = "Mandi Bhav", modifier = Modifier), and run again. The text now sits on top of the
clock and battery icons in the status bar. enableEdgeToEdge() let you draw under the system bars;
innerPadding is how Scaffold tells you where they are. Skip it and your content collides with the
system on every phone. Put it back. Edge-to-edge gets its own lesson in the layout module.
Check your work
What does the package name become, and why is it hard to change? The application ID, the app's identity on devices and on Google Play. Changing it after publishing means a different app.
Which method does Android call when it creates your screen? onCreate.
What is the difference between compileSdk and minSdk? compileSdk is the API you compile
against; minSdk is the oldest version that can install the app.
Why can this project not use minSdk 21? A library it depends on declares minSdk 23, so the manifest merger fails.
What did minSdk 26 buy, and what did it cost? Adaptive icons, java.time and notification
channels without workarounds, and fewer versions to test; phones on Android 7 and older cannot
install the app.
What does TotalTime in am start -W measure? Milliseconds from the launch request to the first
frame drawn.
Practice
- Create the project and make it match the course starter as above. Build it with no errors.
- Run it and see "Namaste from Mandi Bhav".
- Change the name passed to
Greetingto your own town and run again. Then put it back. - Open the design pane on
MainActivity.ktand find the preview. Change the text and watch the preview update without running the app. - Break it on purpose: change
Text(toTxt(and build. Read the first line of the error, which names the file, the line andUnresolved reference 'Txt'. Fix it. - Break it again: delete the closing
"of"Namaste from $name"and read the error. The compiler reportsExpecting '"'at the exact column. Fix it. - Harder: set
minSdk = 25, sync and build. Read the adaptive-icon error, explain in one sentence why it happens, and restore 26. Then run the three terminal commands above and compare yourTotalTimewith 722.
Official documentation
- Create a project — The New Project wizard field by field, including the Help me choose chart for minimum API level.
- Create your first Android app — Google's own walk-through of the Empty Activity template, useful for comparing against what your wizard generated.
- The uses-sdk element — What minSdk, targetSdk and compileSdk mean to the system, with the table of API levels and Android version names.
- Build and run your app — What the Run button does, run configurations, and Apply Changes.
Next: the emulator, and running Mandi Bhav on your own phone.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Selected students who finish a course can join our internship, with real-world projects and code review from a working engineer.
About the internship