Ink API is now in beta and is ready to be integrated in your app.. This milestone was made possible by valuable developer feedback, leading to continuous improvements in the API’s performance, stability, and visual quality.
Google apps, such as Google Docs, Pixel Studio, Google Photos, Chrome PDF, Youtube Effect Maker, and unique features on Android such as Circle to Search all use the latest APIs.
To mark this milestone, we’re excited to announce the launch of Cahier, a comprehensive note-taking app sample optimized for Android devices of all sizes particularly tablets and foldable phones.
What is Cahier?
Cahier (“notebook” in French) is a sample app designed to demonstrate how you can build an application that enables users to capture and organize their thoughts by combining text, drawings, and images.
The sample can serve as the go-to reference for enhancing user productivity and creativity on large screens. It showcases best practices for building such experiences, accelerating developer understanding and adoption of related powerful APIs and techniques. This post walks you through the core features of Cahier, key APIs, and the architectural decisions that make the sample a great reference for your own apps.
Key features demonstrated in the sample include:
-
Versatile note creation: Shows how to implement a flexible content creation system that supports multiple formats within a single note, including text, freeform drawings, and image attachments.
-
Creative inking tools: Implements a high performance, low latency drawing experience using the Ink API. The sample provides a practical example of integrating various brushes, a color picker, undo/redo functionality, and an eraser tool.
-
Fluid content integration with drag and drop: Demonstrates how to handle both incoming and outgoing content using drag and drop. This includes accepting images dropped from other apps and enabling users to drag content out of your app for seamless sharing.
-
Note organization: Mark notes as favorites for quick access. Filter the view to stay organized.
-
Offline first architecture: Built with an offline first architecture using Room, ensuring all data is saved locally and the app remains fully functional without an internet connection.
-
Powerful multi-window and multi-instance support: Showcases how to support multi-instance, allowing your app to be launched in multiple windows so users can work on different notes side by side, enhancing productivity and creativity on large screens.
-
Adaptive UI for all screens: The user interface seamlessly adapts to different screen sizes and orientations using ListDetailPaneScaffold and NavigationSuiteScaffold to provide an optimized user experience on phones, tablets, and foldables.
-
Deep system integration: Provides a guide on how to make your app the default note-taking app on Android 14 and higher by responding to system wide Notes intents, enabling quick content capture from various system entry points.
Built for productivity and creativity on large screens
For the initial launch, we’re centering the announcement on a few core features that make Cahier a key learning resource for both productivity and creativity use cases.
A foundation of adaptivity
Cahier is built to be adaptive from the ground up. The sample utilizes the material3-adaptive library specifically ListDetailPaneScaffold and NavigationSuiteScaffold to seamlessly adapt the app layout to various screen sizes and orientations. This is a crucial element for a modern Android app, and Cahier provides a clear example of how to implement it effectively.

Cahier adaptive UI built with Material 3 Adaptive library.
Showcasing key APIs and integrations
The sample is focused on showcasing powerful productivity APIs that you can leverage in your own applications, including:
A Closer look at key APIs
Let’s dive deeper into two of the cornerstone APIs that Cahier integrates to deliver a first class note-taking experience.
Creating natural inking experiences with the Ink API
Stylus input transforms large screen devices into digital notebooks and sketchbooks. To help you build fluid and natural inking experiences, we’ve made the Ink API a cornerstone of the sample. Ink API makes it easy to create, render, and manipulate beautiful ink strokes with best in class low latency.
Ink API offers a modular architecture, so you can tailor it to your app’s specific stack and needs. The API modules include:
In DrawingSurface, Cahier uses the newly introduced InProgressStrokes composable to handle realtime stylus or touch input. This module is responsible for capturing pointer events and rendering wet ink strokes with the lowest possible latency.
-
Strokes module: Represents the ink input and its visual representation. a user finishes drawing a line, the onStrokesFinished callback provides a finalized/dry Stroke object to the app. This immutable object, representing the completed ink stroke, is then managed in DrawingCanvasViewModel.
-
Rendering module: Efficiently displays ink strokes, allowing them to be combined with Jetpack Compose or Android views.
To display both existing and newly dried strokes, Cahier uses CanvasStrokeRenderer in DrawingSurface for active drawing and in DrawingDetailPanePreview for showing a static preview of the note. This module efficiently draws the Stroke objects onto a Canvas.
The eraser tool within the toolbox and functionality in DrawingCanvasViewModel rely on the geometry module. When the eraser is active, it creates a MutableParallelogram around the path of the user’s gesture. The eraser then checks for intersections between the shape and bounding boxes of existing strokes to determine which strokes to erase, making the eraser feel intuitive and precise.
-
Storage module: Provides efficient serialization and deserialization capabilities for ink data, leading to significant disk and network size savings. To save drawings, Cahier persists the Stroke objects in its Room database. In Converters, the sample uses the storage module’s encode function to serialize the StrokeInputBatch (the raw point data) into a ByteArray. The byte array, along with brush properties, is saved as a JSON string. The decode function is used to reconstruct the strokes when a note is loaded.
Beyond these core modules, recent updates have expanded the Ink API’s capabilities:
Cahier leverages custom brushes, including the unique music brush showcased below, to illustrate advanced creative possibilities.

Rainbow laser created with Ink API’s custom brushes.
Music brush created with Ink API’s custom brushes.
Ink API offers several advantages that make it the ideal choice for productivity and creativity apps over a custom implementation:
-
Ease of use: Ink API abstracts away the complexities of graphics and geometry, allowing you to focus on Cahier’s core features.
-
Performance: Built-in low latency support and optimized rendering ensure a smooth and responsive inking experience.
-
Flexibility: The modular design allows you to pick and choose the components needed, which enables seamless integration of the Ink API into Cahier’s architecture.
Ink API has already been adopted across many Google apps, including for markup in Docs and for Circle to Search as well as partner apps like Orion Notes, and PDF Scanner.
“Ink API was our first choice for Circle-to-Search (CtS). Utilizing their extensive documentation, integrating the Ink API was a breeze, allowing us to reach our first working prototype w/in just one week. Ink’s custom brush texture and animation support allowed us to quickly iterate on the stroke design.” – Jordan Komoda, Software Engineer – Google.
Becoming the default notes app with notes role
Note-taking is a core capability that enhances user productivity on large screen devices. With the notes role feature, users can access your compatible apps from the lock screen or while other apps are running. This feature identifies and sets system wide default note-taking apps and grants them permission to be launched for capturing content.
Implementation in Cahier
Implementing the notes role involves a few key steps, all demonstrated in the sample:
-
Manifest declaration: First, the app must declare its capability to handle note-taking intents. In AndroidManifest.xml, Cahier includes an
for the android.intent.action.CREATE_NOTE action. This signals to the system that the app is a potential candidate for the notes role. -
Checking role status: SettingsViewModel uses Android’s RoleManager to determine the current status. SettingsViewModel checks whether the notes role is available on the device (isRoleAvailable) and whether Cahier currently holds that role (isRoleHeld). This state is exposed to the UI using Kotlin flows.
-
Requesting the role: In the Settings.kt file, a Button is displayed to the user if the role is available but not held. When clicked, the button calls the requestNotesRole function in the ViewModel. The function creates an intent to open the default app settings screen where the user can select Cahier. The process is managed using the rememberLauncherForActivityResult API, which handles launching the intent and receiving the result.
-
Updating the UI: After the user returns from the settings screen, the ActivityResultLauncher callback triggers a function in the ViewModel to update the role status, ensuring the UI accurately reflects whether the app is now the default.
Learn how to integrate the notes role in your app in our create a note-taking app guide.
Cahier launched in a floating window as the default note-taking app on a Lenovo tablet.
A major step forward: Lenovo enables notes role
We’re thrilled to announce a major step forward for large screen Android productivity: Lenovo has enabled support for Notes Role on tablets running Android 15 and higher! With this update, you can now update your note-taking apps to allow users with compatible Lenovo devices to set them as default, granting seamless access from the lock screen and unlocking system level content capture features.
This commitment from a leading OEM demonstrates the growing importance of the notes role in delivering a truly integrated and productive user experience on Android.
Multi-instance, multi-windowing, and desktop windowing
Productivity on a large screen is all about managing information and workflows efficiently. That’s why Cahier is built to fully embrace Android’s advanced windowing capabilities, providing a flexible workspace that adapts to user needs. The app supports:
-
Multi-windowing: The fundamental ability to run alongside another app in split-screen or free-form mode. This is essential for tasks like referencing a web page while taking notes in Cahier.
-
Multi-instance: This is where true multitasking shines. Cahier allows users to open multiple, independent windows of the app simultaneously. Imagine comparing two different notes side by side or referencing a text note in one window while working on a drawing in another. Cahier demonstrates how to manage these separate instances, each with its own state, turning your app into a powerful, multifaceted tool.
-
Desktop windowing: When connected to an external display, Android desktop mode transforms a tablet or foldable into a workstation. Because Cahier is built with an adaptive UI and supports multi-instance, the app performs beautifully in this environment. Users can open, resize, and position multiple Cahier windows just like on a traditional desktop, enabling complex workflows that were previously out of reach on mobile devices.

Cahier running in desktop window mode on Pixel Tablet.
Here’s how we implemented these features in Cahier:
To enable multi-instance, we first needed to signal to the system that the app supports being launched multiple times by adding the PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI property to MainActivity ‘s declaration in AndroidManifest:
android:name=”com.example.cahier.MainActivity”
android:exported=”true”
android:label=”@string/app_name”
android:theme=”@style/Theme.MyApplication”
android:showWhenLocked=”true”
android:turnScreenOn=”true”
android:resizeableActivity=”true”
android:launchMode=”singleInstancePerTask”>
android:name=”android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI”
android:value=”true”/>
…
Next, we implemented the logic to launch a new instance of the app. In CahierHomeScreen.kt, when a user opts to open a note in a new window, we create a new Intent with specific flags that instruct the system on how to handle the new activity launch. The combination of FLAG_ACTIVITY_NEW_TASK, FLAG_ACTIVITY_MULTIPLE_TASK, and FLAG_ACTIVITY_LAUNCH_ADJACENT ensures the note opens in a new, separate window alongside the existing one.
fun openNewWindow(activity: Activity?, note: Note) {
val intent = Intent(activity, MainActivity::class.java)
intent.putExtra(AppArgs.NOTE_TYPE_KEY, note.type)
intent.putExtra(AppArgs.NOTE_ID_KEY, note.id)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
activity?.startActivity(intent)
}
To support multi-window mode, we needed to signal to the system that the app supports resizability by setting the Manifest’s
android:name=“com.example.cahier.MainActivity“
android:resizeableActivity=“true“
…>
The UI itself being built with the Material 3 adaptive library enables it to adapt seamlessly in multi-window scenarios like Android’s split screen mode.
To enhance user experience, we added support for drag and drop. See below how we implemented this in Cahier.
Drag and drop
A truly productive or creative app doesn’t function in isolation; it interacts seamlessly with the rest of the device’s ecosystem. Drag and drop is a cornerstone of this interaction, especially on large screens where users are often working across multiple app windows. Cahier fully embraces this by implementing intuitive drag and drop functionality for both adding and sharing content.
-
Effortless Importing: Users can drag images from other applications—like a web browser, photo gallery, or file manager—and drop them directly onto a note canvas. For this, Cahier uses the dragAndDropTarget modifier to define a drop zone, check for compatible content (like image/*), and process the incoming URI.
-
Simple sharing: Content inside Cahier is just as easy to share as content from other apps. Users can long-press an image within a text note, or long-press the entire canvas of a drawing note and image composite, and drag it out to another application.
Technical deep dive: Dragging from the drawing canvas
Implementing the drag gesture on the drawing canvas presents a unique challenge. In our DrawingSurface, the composables that handle live drawing input (the Ink API’s InProgressStrokes) and the Box that detects the long-press gesture to initiate a drag are sibling composables.
By default, the Jetpack Compose pointer input system is designed so that just one sibling composable —the first one in declaration order that overlaps the touch location—receives the event. In Cahier’s case, we want our drag-and-drop input handling logic to have a chance to run and potentially consume inputs before the InProgressStrokes composable uses all unconsumed input for drawing and then consumes that input. If we don’t arrange things in the right order, our Box won’t detect the long-press gesture to start a drag, or InProgressStrokes won’t receive the input to draw.
To solve this, we created a custom pointerInputWithSiblingFallthrough modifier, and we put our Box using that modifier before InProgressStrokes in the composable code. This utility is a thin wrapper around the standard pointerInput system but with one critical change: it overrides the sharePointerInputWithSiblings() function to return true. This tells the Compose framework to allow pointer events to pass through to sibling composables, even after being consumed.
internal fun Modifier.pointerInputWithSiblingFallthrough(
pointerInputEventHandler: PointerInputEventHandler
) = this then PointerInputSiblingFallthroughElement(pointerInputEventHandler)
private class PointerInputSiblingFallthroughModifierNode(
pointerInputEventHandler: PointerInputEventHandler
) : PointerInputModifierNode, DelegatingNode() {
var pointerInputEventHandler: PointerInputEventHandler
get() = delegateNode.pointerInputEventHandler
set(value) {
delegateNode.pointerInputEventHandler = value
}
val delegateNode = delegate(
SuspendingPointerInputModifierNode(pointerInputEventHandler)
)
override fun onPointerEvent(
pointerEvent: PointerEvent,
pass: PointerEventPass,
bounds: IntSize
) {
delegateNode.onPointerEvent(pointerEvent, pass, bounds)
}
override fun onCancelPointerInput() {
delegateNode.onCancelPointerInput()
}
override fun sharePointerInputWithSiblings() = true
}
private data class PointerInputSiblingFallthroughElement(
val pointerInputEventHandler: PointerInputEventHandler
) : ModifierNodeElement
override fun create() = PointerInputSiblingFallthroughModifierNode(pointerInputEventHandler)
override fun update(node: PointerInputSiblingFallthroughModifierNode) {
node.pointerInputEventHandler = pointerInputEventHandler
}
override fun InspectorInfo.inspectableProperties() {
name = “pointerInputWithSiblingFallthrough”
properties[“pointerInputEventHandler”] = pointerInputEventHandler
}
}
Here’s how it’s used in DrawingSurface:
Box(
modifier = Modifier
.fillMaxSize()
// Our custom modifier enables this gesture to coexist with the drawing input.
.pointerInputWithSiblingFallthrough {
detectDragGesturesAfterLongPress(
onDragStart = { onStartDrag() },
onDrag = { _, _ -> /* consume drag events */ },
onDragEnd = { /* No action needed */ }
)
}
)
// The Ink API’s composable for live drawing sits here as a sibling.
InProgressStrokes(…)
With this in place, the system correctly detects both the drawing strokes and the long-press drag gesture simultaneously. Once the drag is initiated, we create a shareable content:// URI with FileProvider and pass the URI to the system’s drag and drop framework using view.startDragAndDrop(). This solution ensures a robust and intuitive user experience, showcasing how to overcome complex gesture conflicts in layered UIs.
Built with modern architecture
Beyond specific APIs, Cahier demonstrates crucial architectural patterns for building high-quality, adaptive applications.
The presentation layer: Jetpack Compose and adaptability
The presentation layer is built entirely with Jetpack Compose. As mentioned, Cahier adopts the material3-adaptive library for UI adaptability. State management follows a strict Unidirectional Data Flow (UDF) pattern, with ViewModel instances used as data containers that hold note information and UI state.
The data layer: Repositories and Room
For the data layer, Cahier uses a NoteRepository interface to abstract all data operations. This design choice cleanly allows the app to swap between a local data source (Room) and a potential future remote backend. The data flow for an action like editing a note is straightforward:
-
The Jetpack Compose UI triggers a method in the ViewModel.
-
The ViewModel fetches the note from NoteRepository, handles the logic, and passes the updated note back to the repository.
-
NoteRepository saves the update to a Room database.
Comprehensive input support
To be a true productivity powerhouse, an app must handle a variety of input methods flawlessly. Cahier is built to be compliant with large screen input guidelines and supports:
-
Stylus: Integration with the Ink API, palm rejection, registration for the notes role, stylus input in text fields, and immersive mode.
-
Keyboard: Support for most common keyboard shortcuts and combinations (like ctrl+click, meta+click) and clear indication for keyboard focus.
-
Mouse and trackpad: Support for right-click and hover states.
Support for advanced keyboard, mouse, and trackpad interactions is a key focus for further improvements.
Get started today
We hope Cahier serves as a launchpad for your next great app. We built it to be a comprehensive, open source resource that demonstrates how to combine an adaptive UI, powerful APIs like Ink and notes role, and a modern, adaptive architecture.
Ready to dive in?
-
Explore the code: Head over to our GitHub repository to explore the Cahier codebase and see the design principles in action.
-
Build your own: Use Cahier as a foundation for your own note-taking, document markup, or creative application.
-
Contribute: We welcome your contributions! Help us make Cahier an even better resource for the Android developer community.
Check out the official developer guides and start building your next generation productivity and creativity app today. We can’t wait to see what you create!