Mehdi Tmimi Mobile

Mobile Development Lab 1 of 6 · Java · XML

Notes, your first Android app

Write a note, see it as a coloured card, hold it to delete it, open it, share it. This is the course’s Notes app, and you will rebuild it in each lab.

  • Part 0 at home, 1–2 h
  • 8 parts in class, two sessions of about 3 h
  • You know Java
  • A laptop: 8 GB of RAM, about 20 GB free

Tested with Android Studio Rabbit 2 (2026.2.2) and the Android 17 emulator (API 37), November 2026.

The finished notes screen: an orange-to-rose header with the count, the composer card, and five pastel note cards. The finished note page: “Ideas for the app” on a big mint sheet, with Back to notes and Share.
What you build: the notes screen and the note page, one Intent between them.

Nine parts, one app

Each part has one goal, one visible result, and a catch-up at its top. Behind? Open the next part and start from there.

  1. Part 01–2 h, home

    Before class: install and prepare

    I can install Android Studio, start a virtual phone and create my project.

  2. Part 118 min

    Find your way in the project

    I can read the five Gradle values and add a library.

  3. Part 226 min

    Your first screen, piece by piece

    I can write the three pieces of a screen and run it.

  4. Part 342–72 min

    Place the views with ConstraintLayout

    I can place views that keep their shape on any phone.

  5. Part 435 min

    Make it beautiful: resources

    I can define texts, colours, shapes and the theme once, in res/.

  6. Part 528 min

    From XML to Java: the activity and its life

    I can follow my activity’s life in Logcat.

  7. Part 637 min

    Java takes over the screen

    I can change views from Java and add note cards.

  8. Part 743 min

    The screen answers: events

    I can react to taps, long presses and typing.

  9. Part 835 min

    A second screen: Intents

    I can open a second screen with an Intent, and share a note.

Where you work

  1. Android Studio You write the XML of res/ and the Java of kotlin+java.
  2. Gradle Builds the app.
  3. The emulator Runs your app on a virtual phone. There, your XML becomes objects (Part 5).
  4. Logcat Shows what Log.d() writes.
Amber: your code. Sky: the system and the phone. Each step names its places before you act.
For the teacher The two sessions 1 min
  • Session 1, 121 min of steps (151 if 3.5 is built by hand). Open with the finished app, live, and this route (5 min). Parts 1 and 2, then sync point A (“Hello, Notes!” with no bar); Part 3, sync point B (the skeleton), then a break; Part 4, sync point C (the styled screen).
  • Session 2, 143 min of steps. Part 5, sync point D (Logcat after a rotation); Part 6, sync point E (three cards); Part 7, then a break; Part 8, then the quick check.
  • At a sync point, project the part’s “Your app now”. A student who is behind starts the next part from its catch-up.
  • Running late? Session 1: skip 4.6, then the “Now you”. Session 2: skip the “Now you”, then 8.3’s Predict; 8.6 becomes your 2-minute demo. Never skip 7.6, 8.5 or the cards of Part 6.
  • Time left after 8.6? E5, the double tap (below), is an 8-minute class activity.

Before the first session

Part 0 · at home · 1–2 h

Install and prepare

Everything that downloads is done at home, the evening before. In class you only write code.

Your entry ticket

One screenshot

Android Studio with your Notes project, BUILD SUCCESSFUL, and the phone running beside it (Part 0, step 0.5). The project lives in C:\Users\you\AndroidStudioProjects\Notes.

Without JavaScript, the code says com.yourname.notes: replace yourname by the name you chose.

Three rules, for every part

  • Every part starts with a catch-up. Behind? Start from the top of the next part.
  • Type means you type it, helped only by Android Studio’s completion (Ctrl+Space). No AI assistant here: you type by hand once what an AI will write for you later, so you can check it.
  • Paste means the Copy button of the code panel.

Extensions, for fast students and for home

E1 · about 10 min

The app speaks French

One file, res/values-fr/strings.xml, and every text of the app follows the phone’s language.

How

Right-click res, then NewAndroid Resource File. File name strings, Resource type Values. Pick Locale, click >>, then pick fr: Directory name now says values-fr. Click OK. Select all in the new file, then paste this file. Then ▶, and in the phone’s settings, add French and move it to the top.

res › values › strings.xml (fr)New file
<resources>    <string name="app_name">Notes</string>    <string name="logo_description">Logo de Notes</string>    <string name="count_start">0</string>    <string name="title_hint">Titre</string>    <string name="body_hint">Écrivez quelque chose…</string>    <string name="error_no_title">Donnez d’abord un titre à votre note.</string>    <string name="counter_start">0 / 140</string>    <string name="counter">%1$d / 140</string>    <string name="clear">Effacer</string>    <string name="add">Ajouter</string>    <string name="your_notes">VOS NOTES</string>    <string name="list_hint">Touchez pour ouvrir · maintenez pour supprimer</string>    <string name="empty">Pas encore de note.\nÉcrivez la première ci-dessus.</string>    <string name="note_added">Note ajoutée</string>    <string name="note_deleted">Note supprimée</string>    <string name="seed_title_1">Samedi</string>    <string name="seed_body_1">Football à 10&#160;h, puis réviser ConstraintLayout.</string>    <string name="seed_title_2">Idées pour l’application</string>    <string name="seed_body_2">Des cartes pastel, un bouton Partager, un jour peut-être une photo. Puis enregistrer les notes, pour qu’une rotation n’efface plus rien.</string>    <string name="seed_title_3">Examen&#160;: le cycle de vie</string>    <string name="seed_body_3">onCreate, onStart, onResume… puis onPause, onStop, onDestroy.</string>    <string name="note_kicker">NOTE</string>    <string name="back_to_notes">Retour aux notes</string>    <string name="share">Partager</string></resources>

Check the list: the longer French hint wraps onto a second line beside « VOS NOTES », never over it, and the list starts below it.

E5 · after Part 8

Double tap to enlarge

On the note page, a double tap switches the text between 18 and 24 sp. View has no double-tap listener: it takes a GestureDetector.

How

In NoteActivity.java, add the marked lines: two imports and an instance variable.

kotlin+java › com.yourname.notes › NoteActivity.javaAdd the marked lines
package com.yourname.notes;
import android.content.Intent;import android.os.Bundle;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class NoteActivity extends AppCompatActivity {
public static final String EXTRA_TITLE = "com.yourname.notes.EXTRA_TITLE"; public static final String EXTRA_BODY = "com.yourname.notes.EXTRA_BODY"; public static final String EXTRA_BACKGROUND = "com.yourname.notes.EXTRA_BACKGROUND"; private static final String TAG = "Lifecycle";
private boolean bigText = false;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_note); Log.d(TAG, "Note onCreate");
String title = getIntent().getStringExtra(EXTRA_TITLE); String body = getIntent().getStringExtra(EXTRA_BODY); int background = getIntent().getIntExtra(EXTRA_BACKGROUND, R.drawable.bg_note_butter);
TextView textTitle = findViewById(R.id.textTitle); TextView textBody = findViewById(R.id.textBody); textTitle.setText(title); textBody.setText(body); findViewById(R.id.noteSheet).setBackgroundResource(background);
GestureDetector detector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDown(MotionEvent e) { return true; }
@Override public boolean onDoubleTap(MotionEvent e) { bigText = !bigText; textBody.setTextSize(bigText ? 24 : 18); return true; } }); textBody.setOnTouchListener((v, event) -> detector.onTouchEvent(event));
findViewById(R.id.buttonBack).setOnClickListener(v -> finish()); findViewById(R.id.buttonShare).setOnClickListener(v -> share(title, body)); }
private void share(String title, String body) { Intent send = new Intent(Intent.ACTION_SEND); send.setType("text/plain"); send.putExtra(Intent.EXTRA_TEXT, title + "\n" + body); startActivity(Intent.createChooser(send, null)); }
@Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Note onDestroy"); }}

Then, in onCreate(), below the setBackgroundResource line and its empty line, the detector and its listener:

kotlin+java › com.yourname.notes › NoteActivity.javaAdd the marked lines
package com.yourname.notes;
import android.content.Intent;import android.os.Bundle;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class NoteActivity extends AppCompatActivity {
public static final String EXTRA_TITLE = "com.yourname.notes.EXTRA_TITLE"; public static final String EXTRA_BODY = "com.yourname.notes.EXTRA_BODY"; public static final String EXTRA_BACKGROUND = "com.yourname.notes.EXTRA_BACKGROUND"; private static final String TAG = "Lifecycle";
private boolean bigText = false;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_note); Log.d(TAG, "Note onCreate");
String title = getIntent().getStringExtra(EXTRA_TITLE); String body = getIntent().getStringExtra(EXTRA_BODY); int background = getIntent().getIntExtra(EXTRA_BACKGROUND, R.drawable.bg_note_butter);
TextView textTitle = findViewById(R.id.textTitle); TextView textBody = findViewById(R.id.textBody); textTitle.setText(title); textBody.setText(body); findViewById(R.id.noteSheet).setBackgroundResource(background);
GestureDetector detector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDown(MotionEvent e) { return true; }
@Override public boolean onDoubleTap(MotionEvent e) { bigText = !bigText; textBody.setTextSize(bigText ? 24 : 18); return true; } }); textBody.setOnTouchListener((v, event) -> detector.onTouchEvent(event));
findViewById(R.id.buttonBack).setOnClickListener(v -> finish()); findViewById(R.id.buttonShare).setOnClickListener(v -> share(title, body)); }
private void share(String title, String body) { Intent send = new Intent(Intent.ACTION_SEND); send.setType("text/plain"); send.putExtra(Intent.EXTRA_TEXT, title + "\n" + body); startActivity(Intent.createChooser(send, null)); }
@Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "Note onDestroy"); }}

Lint then warns ClickableViewAccessibility on the setOnTouchListener line: with TalkBack, a double tap does not reach this listener. Acceptable here: the double tap is only an extra.

E6 · a challenge

Lock your notes

A sign-in screen in front, in Notes’ colours. It reuses bg_field and the theme, checks only the form (3+ characters, then 6+), and opens MainActivity. A real app asks its server: chapter 13.

  • An older Android: the optional API 28 phone of Part 0.
  • Your own phone: turn on USB debugging, as in the Try it of chapter 02.
lab1-final.zip zip · 105 KB

The finished project, to compare with yours, not to work in. If you run it, a second “Notes” appears on the phone: uninstall it afterwards.

Go deeper Older Android Studio? 1 min
  • “Phone and Tablet” is now Phone and large screens.
  • Old tutorials start from Empty Views Activity, which writes the activity for you. In Rabbit 2 its code calls WindowCompat.enableEdgeToEdge and an insets listener: this lab needs neither.
  • Old projects use Groovy build.gradle and compileSdk 34. Rabbit 2 writes .kts and compileSdk { version = release(37) }.
  • Device Manager replaces the old AVD Manager. The emulator runs inside Android Studio, in Running Devices (SettingsToolsEmulator to change that).
  • Before 2022, the Logcat window used drop-down menus; now it is a query field (package:mine).
  • Old tutorials show the code folder as java in the Android display; Rabbit 2 labels it kotlin+java, Java included. On disk it is still app/src/main/java.
  • Old tutorials name the device wizard’s pages “Select Hardware” and “System Image”; Rabbit 2 has Add Device, then Configure virtual device.
  • android:onClick is in old tutorials everywhere; it has been deprecated since API 31.
  • The lab’s projects use AGP 9.5.0, which needs Rabbit 2 or newer: update Android Studio. A newer one may write higher version numbers; keep yours.

Quick check · after Part 8

In the layout, a view has no vertical constraint. Where is it drawn on the phone?

See the answer

Nothing holds it vertically, so it goes to the top, in the editor too, with a red mark in the Component Tree. It still builds and runs: ▶ does not run lint (Part 3).

Which line turns activity_main.xml into objects?

See the answer

setContentView() inflates the layout: one object per tag. findViewById() only finds an object that already exists (Part 5).

You press Home. Which methods of your activity run?

See the answer

Home stops the activity; it does not destroy it. Leaving is not closing (Part 5).

findViewById() returns null. What is the most likely cause?

See the answer

It searches the objects of the current layout. Before setContentView() there are none; a hidden view is still there; a missing import does not compile (Part 6).

Why does the course prefer setOnClickListener() to android:onClick?

See the answer

android:onClick still works, but it is deprecated: a typo in its name builds, then crashes at the tap; and it cannot handle a long press, nor reach a view made with new (Part 7).

After a rotation, the notes you added are gone, but your half-typed title is still there. Why?

See the answer

Rotation destroys the activity and creates a new one, in the same process: onCreate() adds only the seeds. A text field with an id comes back through saved state. Keeping the notes is the next milestone (chapters 03 and 09).

← All chapters