ANTIGRAVITY LABJP
Articles/App Development
App Development/2026-06-12Intermediate

Before Android 17 Stops Honoring Portrait Lock — Auditing Four Wallpaper Apps for Large-Screen Resizability

How I audited four portrait-locked wallpaper apps with an Antigravity agent before Android 17 starts ignoring orientation and resize restrictions on large screens.

android-17large-screenresizabilitywallpaper-appsantigravity341windowmetrics

The moment I dropped one of my own wallpaper apps into split-screen on a Pixel Tablet emulator, a cherry-blossom photo stretched sideways across half the display. It was an image I had picked precisely because it looked beautiful at 9:16 portrait. Something in my chest tightened a little.

Since Android 16, large screens — anything 600dp or wider at its smallest width — ignore the orientation, aspect-ratio, and resizability restrictions an app declares. Android 16 shipped with a temporary compatibility opt-out, but Android 17, expected to roll out to Pixel devices this summer, ends that grace period.

As an indie developer I run four Android wallpaper apps, all of them built on a portrait-only assumption for years. On the iOS side I had just finished replacing a decade-old SKPaymentQueue implementation with StoreKit 2, and apparently it was Android's turn next.

Tablets and foldables are not the majority of my installs. But the Play Console statistics show them growing, quietly and steadily. With Android 17 this close, looking away no longer felt like a reasonable option, so this week I started an audit together with an Antigravity agent.

What Android 17 actually stops honoring

The change applies to displays with a smallest width of 600dp or more. Phone-sized screens keep behaving the way they always have. What gets ignored is, roughly speaking, every mechanism an app uses to dictate the shape of its window:

  • Orientation locks in the manifest, such as android:screenOrientation="portrait"
  • Resizability refusals via android:resizeableActivity="false"
  • Aspect-ratio constraints through android:maxAspectRatio / android:minAspectRatio
  • Runtime calls to setRequestedOrientation()

On Android 16 you can still keep the old behavior temporarily with a compatibility property in the manifest:

<application>
    <!-- Android 16's temporary opt-out. Scheduled to lose effect on Android 17 -->
    <property
        android:name="android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY"
        android:value="true" />
</application>

I think of this property as one line of code that buys time, nothing more. It prevents immediate layout problems, but it comes with an expiry date. I added it to all four apps on the understanding that the real fixes would happen within the time it bought — and started the audit the same day.

Where the "wallpaper apps are portrait apps" assumption breaks

Before touching any code, I wanted to know concretely where things would break. For a wallpaper app, the damage is not limited to awkward layouts.

The first item on the list was preview trustworthiness. A wallpaper preview is a promise: this is what your home screen will look like. Render it edge-to-edge with centerCrop in a landscape window and the user sees something entirely different from what actually lands on their home screen. Nothing looks broken, yet the promise is quietly violated. To me this was the scariest failure mode of all.

Second came the crop math. The older code computed crop regions from Display.getSize(). In split-screen that call returns the window size, which no longer matches the wallpaper's target dimensions.

The rest were more straightforward: a hard-coded two-column grid that sprawls on a large screen, and activity re-creation on fold and unfold that wipes out scroll position and the current selection.

Handing the audit to an Antigravity agent

I did not have the stamina to open four repositories and eyeball them one by one, so detection went to the agent. The request had three parts:

  1. List every screenOrientation / resizeableActivity / maxAspectRatio declaration in the manifests
  2. Find Display.getSize(), defaultDisplay, and hard-coded 9:16 arithmetic
  3. Annotate each finding with a guess about what happens once the restriction is ignored

What the agent actually ran boils down to the equivalent of these two lines:

grep -rn "screenOrientation\|resizeableActivity\|maxAspectRatio" \
  --include=AndroidManifest.xml .
grep -rn "defaultDisplay\|getSize(\|9f / 16f" \
  --include="*.kt" --include="*.java" app/src/

Across the four apps it found 14 portrait-locked activities, 6 calculations depending on getSize(), and 3 hard-coded aspect ratios. The full inventory took less than 40 minutes.

That said, the limits showed up alongside the usefulness. Detection and annotation are exactly what an agent is good at, but deciding which screens to fix and which locks to tolerate still came down to me picturing each screen one at a time. A portrait-locked settings screen, for example, causes no real harm, so I left it alone; the preview and the crop pipeline I decided to rebuild. The agent's annotations turned out to be just the right granularity as a first draft for those decisions.

Decoupling preview "correctness" from the window's shape

This was the part I thought about the longest. There were two possible directions: let the preview morph with the window, or keep rendering the wallpaper as it would actually appear, independent of the window.

I chose the latter. What a wallpaper preview owes the user is not a pleasing fit inside the current window — it is an honest picture of the home screen after the wallpaper is applied.

The implementation pivots on one idea: take the reference dimensions from WallpaperManager, not from the window.

val wm = WallpaperManager.getInstance(context)
val metrics = windowManager.currentWindowMetrics
 
// The wallpaper's target dimensions — independent of the window size
val targetW = wm.desiredMinimumWidth
    .takeIf { it > 0 } ?: metrics.bounds.width()
val targetH = wm.desiredMinimumHeight
    .takeIf { it > 0 } ?: metrics.bounds.height()
val targetAspect = targetW.toFloat() / targetH

The preview renders inside a frame with that aspect ratio and gets letterboxed into whatever window the system provides. In Compose, a centered Box plus aspectRatio is all it takes:

Box(
    modifier = Modifier.fillMaxSize(),
    contentAlignment = Alignment.Center
) {
    AsyncImage(
        model = photo.url,
        contentDescription = null,
        contentScale = ContentScale.Crop,
        modifier = Modifier.aspectRatio(targetAspect)
    )
}

Because the crop region is computed from the same targetW / targetH, the preview and the applied result stay consistent even when the user works in split-screen. A landscape window does leave bars on both sides, but as the price of showing the truth about the end result, I consider it cheap.

Making verification repeatable instead of one-off

Everyone checks their app right after a fix. The real question is whether things are still intact two releases later.

My verification setup is deliberately modest: a resizable emulator device, with representative window states reproduced over adb and captured as screenshots.

adb shell wm size 1080x2424   # phone-sized window
adb exec-out screencap -p > phone.png
adb shell wm size 2208x1840   # unfolded foldable
adb exec-out screencap -p > unfolded.png
adb shell wm size reset

The agent handles this back-and-forth; I compare the images with my own eyes. I did consider automating the diffing as well, but for a wallpaper app, "correct" is ultimately a visual impression, and keeping a human at that point in the loop feels right to me.

The fold/unfold cycle surfaced one regression the static audit had not listed: the selected image vanished on re-creation. Switching that state to rememberSaveable fixed it. The fact that verification finds what scanning cannot is a useful reminder that the two are different instruments.

Next actions

If you are in a similar position, start by running those two grep lines against your own repository. Just seeing the number of portrait-locked activities turns a vague worry into a sized task, and that alone is a relief. Then buy time with the temporary opt-out, and fix the screens that carry your app's promises — like the preview — first. For me, that ordering dissolved most of the hesitation.

Portrait lock gave me the freedom not to think about window shapes for years. I read this change as that lease simply reaching its end. More window shapes ultimately mean more places for wallpapers to shine. If you maintain portrait-first apps of your own, I hope this record helps you plan the work ahead.

Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

App Dev2026-06-12
Replacing a Decade-Old SKPaymentQueue with StoreKit 2 — Migrating 4 Wallpaper Apps with an Antigravity Agent
A field report on migrating SKPaymentQueue-based purchase code to StoreKit 2's Transaction.currentEntitlements across four wallpaper apps — what I delegated to an Antigravity agent, and the traps I only found on real devices.
App Dev2026-05-24
Running iOS Push Notification A/B Tests Weekly With Antigravity Agent — A Self-Improving Loop For Copy, Timing, And Segments
Drawing on 12 years of indie iOS app development across wallpaper and wellness apps with over 50 million cumulative downloads, this article walks through a weekly Push Notification A/B testing loop powered by Antigravity Agent. It covers the FCM bridge, BigQuery measurement, segment design, and a real D7 retention recovery story.
App Dev2026-05-20
Two Weeks of Letting Antigravity Translate Localizable.xcstrings Across 8 Languages
A two-week log of letting Antigravity draft 8-language translations for new keys added to Localizable.xcstrings. What I delegated, what I kept under my own judgment, the unexpected behaviors, and how I reconciled drafts against the existing translation corpus.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →