Most Android developers build for one screen: the phone in their pocket. That made sense in 2015 when phones accounted for 95% of Android activations. In 2026, it is a competitive liability. Google reports over 300 million active large-screen Android devices globally, Wear OS shipments have tripled since 2023, Android TV powers 220 million connected TVs, and Android Auto runs in 200 million vehicles. Then there is Android XR, the newest platform, shipping on headsets later this year.
If your app only works well on a 6-inch phone, you are leaving five growing platforms on the table. Worse, Google Play now penalizes apps that deliver a poor large-screen experience by ranking them lower on tablets and Chromebooks.
This guide covers the technical foundations you need to build a single Android codebase that adapts intelligently across every form factor. We will go deep into adaptive UI primitives, platform-specific constraints, shared code strategies, and the CI/CD infrastructure needed to test and deploy across all of them.
The Android Form Factor Landscape in 2026
Android is no longer a mobile operating system. It is an ambient computing platform that spans pocket-sized screens to 85-inch living room displays to in-car dashboards to wrist-mounted health monitors. Each form factor has its own interaction model, its own design constraints, and its own user expectations.
Figure 1: The Android form factor landscape in 2026 — five distinct platforms unified by a shared adaptive UI layer
The unifying thread is Jetpack Compose with Material 3 Adaptive. Google has invested heavily in making Compose the single UI toolkit that spans all form factors. Combined with WindowSizeClass APIs, Canonical Layouts, and platform-specific extensions, you can write a shared UI layer that adapts gracefully from a 1.5-inch watch face to an 85-inch TV.
But "adapts" does not mean "works perfectly with zero effort." Each platform has unique constraints that require deliberate engineering. Let us walk through each one.
Adaptive UI Foundations: WindowSizeClass and Canonical Layouts
Before diving into individual form factors, you need to understand the two abstractions that Google provides for adaptive design.
WindowSizeClass
WindowSizeClass is Android's answer to CSS media queries. It classifies the current window into one of three width buckets and three height buckets:
Figure 2: WindowSizeClass width breakpoints — the three-tier adaptive system that drives layout decisions
The critical insight is that WindowSizeClass is not static. On a foldable device, opening the hinge changes the size class in real time. On a Chromebook, a user can resize the window. On a Samsung DeX dock, a phone becomes an expanded display. Your composables must observe and react to these changes.
@Composable
fun MyApp() {
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
when (windowSizeClass.windowWidthSizeClass) {
WindowWidthSizeClass.COMPACT -> PhoneLayout()
WindowWidthSizeClass.MEDIUM -> ListDetailLayout()
WindowWidthSizeClass.EXPANDED -> MultiPaneLayout()
}
}
Canonical Layouts
Google defines four Canonical Layouts that cover the vast majority of adaptive screen patterns:
| Layout | Best For | Compact | Medium | Expanded |
|---|---|---|---|---|
| List-Detail | Email, messaging, settings | Stacked (list then detail) | Side-by-side 40/60 | Side-by-side 30/70 |
| Feed | News, social, content browsing | Single column | 2-column grid | 3-column grid |
| Supporting Pane | Maps, editors, dashboards | Full-screen main content | Main + bottom sheet | Main + side panel |
| Navigation | App-level navigation | Bottom bar | Navigation rail | Navigation drawer |
These are not suggestions. They are the patterns that Material 3 provides out-of-the-box composables for. Using them means less custom code and more consistency with the broader Android ecosystem.
Phone: The Baseline You Already Know (Mostly)
Phone development is the default. But even here, there are platform details that many apps handle poorly:
Display cutouts and notches. Since Android 9, the system provides WindowInsets APIs that describe where hardware intrusions exist. In Compose, use Modifier.windowInsetsPadding(WindowInsets.displayCutout) to keep content clear. Too many apps still draw behind cutouts with no padding, causing text to be clipped.
Refresh rate awareness. Modern phones support 60Hz, 90Hz, 120Hz, and even 144Hz displays. If your app performs custom drawing (Canvas, OpenGL, game loops), you need to query Display.getSupportedModes() and target the actual refresh rate. Animations that hardcode 60fps will stutter or waste power on 120Hz panels.
Edge-to-edge by default. Starting with Android 15, all apps targeting API 35 or higher are rendered edge-to-edge. The system bars (status bar and navigation bar) are transparent. If your app does not properly handle insets, content will render underneath system UI elements. This is not optional — it is enforced by the platform.
Apps targeting Android 15 (API 35) are rendered edge-to-edge by default. If you have not adopted WindowInsets handling in your Compose UI or legacy View system, your content will be obscured by system bars. Test this now, before the next targetSdk bump forces the issue in production.
Predictive back gesture. Android 14+ supports an animated back gesture that shows a preview of the destination before the user commits. Your app must opt in by declaring android:enableOnBackInvokedCallback="true" in the manifest and using the OnBackInvokedDispatcher API instead of overriding onBackPressed().
Tablet and Foldable: Large-Screen Design That Actually Works
Large-screen Android has been the platform's weakest point for a decade. Google has finally committed serious resources to fixing this, and the expectations have changed dramatically:
Multi-pane is the minimum bar. An app that shows a single column of content on a 12-inch tablet screen feels broken. The List-Detail canonical layout should be the default for any content-driven app. The ListDetailPaneScaffold composable handles the adaptive switching automatically.
@Composable
fun InboxScreen() {
val navigator = rememberListDetailPaneScaffoldNavigator<EmailId>()
ListDetailPaneScaffold(
directive = navigator.scaffoldDirective,
value = navigator.scaffoldValue,
listPane = {
EmailList(
onEmailClick = { id ->
navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, id)
}
)
},
detailPane = {
navigator.currentDestination?.content?.let { id ->
EmailDetail(emailId = id)
}
}
)
}
Drag and drop. Tablets and foldables in split-screen mode enable cross-app drag and drop. Implementing DragAndDropTarget and DragAndDropSource modifiers in Compose lets users drag images, text, or files between your app and others. This is a strong differentiator that few apps implement.
Keyboard and mouse support. Users on tablets with keyboard cases and Chromebook users expect keyboard shortcuts, mouse hover states, and right-click context menus. Compose provides Modifier.hoverable(), Modifier.pointerHoverIcon(), and KeyboardShortcutGroup APIs for this.
Foldable posture detection. The Jetpack WindowManager library provides FoldingFeature data that tells you the hinge angle and orientation. In tabletop posture (partially folded, like a laptop), you should split your UI at the fold line. In book posture, you can use each half as a separate pane.
The Android Emulator supports foldable posture simulation out of the box. Create a 7.6-inch Foldable device profile, then use the Extended Controls panel to change hinge angle, posture (flat, half-open, tent, closed), and window size class in real time.
Wear OS: Glanceable, Health-Aware, Power-Conscious
Wear OS development is fundamentally different from phone development. The screen is round (usually), the interaction model is glance-and-go (2–5 seconds per interaction), and battery life is measured in hours, not days. Building for Wear OS requires unlearning most phone UI habits.
Compose for Wear OS. The UI toolkit is a separate artifact (androidx.wear.compose) with its own components: ScalingLazyColumn for scrollable lists with curved-edge scaling, TimeText for the always-visible clock, SwipeDismissableNavHost for swipe-to-go-back navigation, and Chip/Button components sized for touch targets on small screens.
@Composable
fun WearWorkoutScreen() {
ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
anchorType = ScalingLazyListAnchorType.ItemCenter
) {
item { TimeText() }
item {
Chip(
onClick = { startWorkout() },
label = { Text("Start Run") },
icon = { Icon(Icons.Default.PlayArrow, "Start") },
colors = ChipDefaults.primaryChipColors()
)
}
item {
TitleCard(
title = { Text("Last Workout") },
onClick = { },
time = { Text("Yesterday, 5.2 km") }
)
}
}
}
Tiles. Tiles are the Wear OS equivalent of widgets. They display on the watch face carousel and update periodically. Use the Tiles Material library (androidx.wear.tiles.material) for pre-built layouts. Tiles use a declarative layout system similar to Compose but with a different rendering pipeline optimized for low-power updates.
Complications. Complications are small data displays embedded directly in watch faces. If your app provides data (step count, stock price, weather), implement a ComplicationDataSourceService to expose it. This is the highest-visibility surface on Wear OS.
Health Services. The Health Services API provides access to heart rate, steps, calories, GPS, and other sensors with automatic batching and power optimization. Use ExerciseClient for active workout tracking and PassiveMonitoringClient for background health data. Never access sensors directly — Health Services handles sensor fusion, power management, and permission enforcement.
Ongoing activities. For long-running tasks (workouts, navigation, media playback), use the Ongoing Activity API to pin your app's status on the watch face. This ensures the user can return to your app with a single tap, even if the system reclaims memory.
Android TV: 10-Foot UI and D-Pad Navigation
Android TV has a fundamentally different interaction model: the user is 10 feet away from the screen and controls the app with a remote that has a D-pad (up, down, left, right, select) and a back button. There is no touch. There is no pointer. Every UI element must be reachable via directional navigation.
The Leanback library. While Compose for TV (androidx.tv.compose) is the modern path, the Leanback library still powers most production TV apps. It provides BrowseSupportFragment for content browsing with rows and cards, DetailsSupportFragment for content detail pages, SearchSupportFragment for voice-enabled search, and PlaybackSupportFragment for media playback controls.
Compose for TV. The newer approach uses Compose with TV-specific components: TvLazyRow and TvLazyColumn for focus-aware scrolling lists, Card with focus-triggered animations, and ImmersiveList for hero banners that animate as the user navigates.
@Composable
fun TvBrowseScreen(categories: List<Category>) {
TvLazyColumn {
items(categories) { category ->
Text(
text = category.name,
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.padding(start = 48.dp, bottom = 8.dp)
)
TvLazyRow(
contentPadding = PaddingValues(horizontal = 48.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
items(category.contents) { content ->
ContentCard(
content = content,
modifier = Modifier
.width(196.dp)
.aspectRatio(16f / 9f)
)
}
}
}
}
}
Focus management is everything. On TV, the focus indicator is the cursor. If focus gets lost (a common bug), the user cannot navigate at all. Always ensure there is a default focusable element, handle focus restoration after navigation transitions, and test every screen with only D-pad input.
Google Play for TV requires: a Leanback launcher banner (320x180 dp), declaration of android.software.leanback in the manifest, no required touchscreen, and a home screen launcher activity with the LEANBACK_LAUNCHER category. Missing any of these means your app will not appear in the TV Play Store at all.
Android Auto: Safety First, Templates Only
Android Auto is the most constrained platform. Every design decision is filtered through one question: does this distract the driver? Google enforces this with a template-based system that severely limits what apps can render.
The Car App Library. Auto apps do not use Activities or Compose directly. Instead, you implement a CarAppService that serves Screen objects using the androidx.car.app library. Each screen uses a predefined Template:
| Template | Use Case | Max Items |
|---|---|---|
ListTemplate |
Browseable content lists (podcasts, contacts) | 6 items visible |
GridTemplate |
Quick actions, category selection | 6 items visible |
MessageTemplate |
Notifications, confirmations | 2 actions |
NavigationTemplate |
Turn-by-turn navigation (maps) | Custom map rendering |
PlaceListNavigationTemplate |
POI lists with map context | 6 items visible |
SearchTemplate |
Search with keyboard or voice | 6 results visible |
Template restrictions. You cannot create custom layouts. You cannot use animations. You cannot show more than a few items at a time. Text sizes are enforced by the host. These restrictions exist because car manufacturers and Google must certify that your app does not create unsafe driving conditions. Fighting these constraints is futile — embrace them.
class PodcastScreen(carContext: CarContext) : Screen(carContext) {
override fun onGetTemplate(): Template {
val itemList = ItemList.Builder().apply {
podcasts.take(6).forEach { podcast ->
addItem(
Row.Builder()
.setTitle(podcast.title)
.addText(podcast.author)
.setImage(
CarIcon.Builder(
IconCompat.createWithResource(
carContext, podcast.iconRes
)
).build()
)
.setOnClickListener { playPodcast(podcast) }
.build()
)
}
}.build()
return ListTemplate.Builder()
.setTitle("Recent Episodes")
.setSingleList(itemList)
.setHeaderAction(Action.BACK)
.build()
}
}
Voice is primary. On Auto, assume the user's eyes are on the road and hands are on the wheel. Voice input via Google Assistant should be the primary interaction path. Implement VoiceInteractionSession for custom voice commands and always provide text-to-speech alternatives for any visual content.
Android XR: Spatial UI and the Third Dimension
Android XR is the newest platform, announced in late 2024 and shipping on Samsung headsets. It extends Android into spatial computing with depth, 3D placement, and gaze/hand interaction.
XR compatibility for existing apps. Android XR runs existing 2D Android apps as floating panels in 3D space. If your app supports adaptive layouts and handles window resize events correctly, it will work on XR with zero changes. This is the baseline — users can resize your app panel, move it in space, and use it alongside other app panels.
Spatial UI enhancements. With the Jetpack XR library, you can place UI elements at different depths, create orbiting menus that float around content, and respond to gaze direction. The key APIs are SpatialPanel for positioning panels in 3D space and SubspaceComposable for embedding 3D content within 2D Compose layouts.
Interaction models. XR supports three input methods: hand tracking (pinch-to-select, grab-to-move), controller input (similar to a pointer), and gaze combined with gesture. Your app should support all three, but hand tracking is the default for consumer headsets.
Unless you are building a dedicated XR experience, do not invest in spatial-specific features yet. The install base is small and the APIs are still evolving. Instead, ensure your app handles window resize gracefully and supports the adaptive layout patterns covered earlier. This gives you XR compatibility for free.
Shared Codebase Strategy: The 80/20 Rule
The goal is not to write one set of composables that magically works everywhere. The goal is to maximize code sharing where it adds value and accept platform-specific code where it is necessary. In practice, this splits roughly 80/20:
Figure 3: The shared codebase architecture — 80% shared business logic, 15% adaptive UI, 5% platform-specific
The 80% shared layer contains everything that has no UI dependency: domain models, business logic, repository implementations, network clients, data classes, use cases, and dependency injection modules. This layer should be in a pure Kotlin module (or KMP module if you also target iOS). It contains zero Android framework imports.
The 15% adaptive UI layer uses Compose with WindowSizeClass and canonical layouts. This code adapts automatically across phone, tablet, foldable, and Chromebook. Navigation structure (bottom bar vs. rail vs. drawer) lives here. Shared composables for lists, cards, and detail screens live here.
The 5% platform-specific layer is where you write Wear OS tiles, TV leanback screens, Auto templates, and XR spatial panels. This code only runs on its target platform and uses platform-exclusive APIs.
Module Structure
A well-structured multi-form-factor project typically uses this Gradle module layout:
:core:model # Data classes, enums, constants :core:data # Repository implementations, data sources :core:domain # Use cases, business logic interfaces :core:network # Retrofit/Ktor clients, API models :core:database # Room DAOs, entities, migrations :feature:inbox # Shared feature UI (Compose, adaptive) :feature:settings # Shared feature UI :feature:player # Shared feature UI :app:phone # Phone/tablet app module :app:wear # Wear OS app module :app:tv # Android TV app module :app:auto # Android Auto app module
Each :app module depends on the shared :core and :feature modules but adds platform-specific entry points, manifests, and resources. This structure keeps platform code isolated and shared code truly shared.
Testing Across Form Factors
Testing multi-form-factor apps requires a deliberate strategy. You cannot manually test every screen on every device size, so automation is essential.
Unit Tests (Shared Layer)
The shared business logic layer should have comprehensive unit tests that run on the JVM with no Android dependencies. These tests are fast (milliseconds per test), run on every CI build, and cover the 80% shared codebase. Use JUnit 5, MockK for mocking, and Turbine for Flow testing.
Screenshot Tests (Adaptive UI Layer)
Compose Preview Screenshot Testing lets you capture golden images of your composables at different window sizes. Define previews with @PreviewScreenSizes annotation and run ./gradlew updateDebugScreenshotTest to generate baseline images. On every PR, ./gradlew validateDebugScreenshotTest compares against baselines and fails on visual regressions.
@PreviewScreenSizes
@Composable
fun InboxScreenPreview() {
MyAppTheme {
InboxScreen(
emails = SampleData.emails,
onEmailClick = {}
)
}
}
Instrumented Tests (Platform Layer)
For platform-specific features, use instrumented tests on real or emulated devices:
- Phone/Tablet: Standard Espresso or Compose UI tests with
createComposeRule() - Wear OS: Use the Wear OS emulator with round screen profile. Test
ScalingLazyColumnscrolling and complication rendering. - TV: Use the TV emulator. Test all screens with D-pad-only navigation. Verify focus behavior explicitly.
- Auto: Use the Desktop Head Unit (DHU) tool that Google provides to simulate an Auto head unit on your development machine.
Firebase Test Lab
Firebase Test Lab provides access to physical and virtual devices across form factors. You can run your test suite on Pixel phones, Samsung foldables, Wear OS watches, and TV devices without owning the hardware. Configure a test matrix in your CI pipeline:
gcloud firebase test android run \ --type instrumentation \ --app app/build/outputs/apk/debug/app-debug.apk \ --test app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk \ --device model=Pixel8,version=35 \ --device model=Pixel Fold,version=35 \ --device model=MediumTablet,version=35 \ --timeout 15m
Follow the standard test pyramid but weight it toward shared-layer unit tests. A good ratio: 70% JVM unit tests (shared business logic), 20% screenshot tests (adaptive UI), 10% instrumented tests (platform-specific). This keeps CI fast while still catching form-factor-specific regressions.
CI/CD for a Multi-Form-Factor App
A multi-form-factor app produces multiple APKs or AABs from a single repository. Your CI/CD pipeline must build, test, and deploy each variant independently.
Build Variants
Each app module produces its own artifact. Use Gradle product flavors for environment-specific configuration (staging vs. production) and build types for debug/release. The matrix looks like:
| Module | Output | Distribution | Store Listing |
|---|---|---|---|
:app:phone |
AAB | Google Play (phone + tablet) | Main listing |
:app:wear |
APK | Google Play (Wear OS) | Wear listing |
:app:tv |
AAB | Google Play (Android TV) | TV listing |
:app:auto |
Bundled in phone APK | Embedded via phone app | N/A (auto-discovered) |
Pipeline Structure
A robust CI/CD pipeline for multi-form-factor apps follows this sequence:
-
Lint and Static Analysis
Run./gradlew lintand./gradlew detektacross all modules. Fail the build on any error. This catches issues like missing leanback declarations or unsupported API usage early. -
Unit Tests
Run./gradlew testfor all shared and feature modules. These are JVM tests, so they complete in seconds. Gate the pipeline here — no point building artifacts if logic is broken. -
Screenshot Tests
Run./gradlew validateDebugScreenshotTestto catch visual regressions across screen sizes. This step catches adaptive layout issues before they reach users. -
Build All Variants
Run./gradlew assembleRelease(orbundleRelease) for each app module. Sign with the appropriate keystore per platform. -
Instrumented Tests on Firebase Test Lab
Upload APKs and test APKs to Firebase Test Lab. Run the device matrix. Parse results and fail on any test failure. -
Deploy to Internal Testing Tracks
Upload each artifact to its respective Google Play internal testing track using the Play Developer API orgradle-play-publisherplugin. Wear and TV each get separate track uploads. -
Promote to Production
After internal testing validation, promote each track to production. Staged rollouts (1% → 10% → 50% → 100%) are recommended for the phone app due to its larger user base.
Dependency Management
With multiple app modules sharing core libraries, version alignment is critical. Use a Gradle version catalog (libs.versions.toml) to centralize all dependency versions. This prevents situations where the phone app uses Compose 1.7 but the Wear app uses Compose 1.6, leading to subtle runtime incompatibilities.
# gradle/libs.versions.toml
[versions]
compose-bom = "2025.12.00"
wear-compose = "1.5.0"
tv-compose = "1.0.0"
car-app = "1.7.0"
[libraries]
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
wear-compose-material = { group = "androidx.wear.compose", name = "compose-material", version.ref = "wear-compose" }
tv-compose-material = { group = "androidx.tv", name = "tv-material", version.ref = "tv-compose" }
car-app-library = { group = "androidx.car.app", name = "app", version.ref = "car-app" }
Practical Advice: Where to Start
If you are starting from a phone-only app and want to expand to multiple form factors, here is the priority order based on effort-to-impact ratio:
-
Adopt WindowSizeClass and adaptive navigation
This single change makes your app work well on tablets, foldables, and Chromebooks. It covers 300 million devices with moderate effort. Most apps can do this in 1–2 sprints. -
Add Wear OS support if your app has glanceable data
If your app shows status, health data, scores, or notifications, a Wear OS tile and a few complications can reach millions of wrists. The development effort is small because you reuse shared business logic. -
Add Android Auto if your app has audio or navigation
Podcast, music, messaging, and navigation apps benefit enormously from Auto support. The template system is restrictive but also fast to implement once you accept the constraints. -
Add Android TV if your app has media content
Streaming, news, fitness, and education apps have natural TV use cases. The leanback UI requires more rework than the other platforms, but the install base is large and growing. -
Prepare for XR by ensuring adaptive layout support
Do not build XR-specific features yet. But if your app handles window resize gracefully, it will work as a 2D panel in XR environments. That is enough for 2026.
Common Mistakes to Avoid
After working with dozens of teams expanding their Android apps across form factors, these are the mistakes we see most frequently:
- Hardcoding pixel dimensions. Use
dpfor layout andspfor text. Always. Screen densities range from 160 dpi (TV) to 560 dpi (flagship phones). Pixel values break across this range. - Assuming touch input. TV has D-pad. Auto has rotary knobs and voice. Wear has rotary crown. Abstract your input handling and test with non-touch input methods.
- One APK for everything. Do not bundle Wear OS, TV, and Auto code into your phone APK. The unused code bloats the download size and can trigger Play Store warnings. Use separate app modules.
- Ignoring configuration changes. Window size class changes, foldable posture changes, and multi-window mode changes trigger configuration changes. If your app loses state during these events, the experience is broken.
- Skipping accessibility. Large screens magnify accessibility issues. Content descriptions, focus order, and minimum touch targets (48dp) matter even more when the UI has multiple panes and complex navigation.
The single most common mistake is treating large-screen support as a stretch goal. Google Play now factors large-screen quality into your app's ranking and visibility on tablets and Chromebooks. A poor large-screen experience does not just look bad — it actively harms your discoverability on 300 million devices.
Building for Every Screen
Android in 2026 is not a phone OS. It is an ambient computing platform that touches every screen in your users' lives: their pocket, their wrist, their living room, their car, and soon their face. The developers who recognize this shift and invest in multi-form-factor architecture will capture markets that single-screen apps cannot reach.
The tools are mature. Jetpack Compose, Material 3 Adaptive, WindowSizeClass, and the Car App Library are production-ready. The module architecture patterns are well-documented. The CI/CD infrastructure with Firebase Test Lab makes cross-device testing practical at scale.
Start with the adaptive UI foundations. Add form factors one at a time, prioritized by user impact. Share 80% of your code across all of them. And test on every screen shape you plan to support — because your users are already using all of them.
Building Android Apps Across Every Form Factor?
Sumvid Solutions builds production-grade Android apps that work beautifully on phones, tablets, Wear OS, TV, and Auto. Our architect-led teams handle adaptive UI, platform integration, and CI/CD across every screen size — so you ship faster with fewer surprises.
Book a Free DART ROI Blueprint Call