Skip to content

Keep Android phones locked to portrait - #6109

Open
j0ntz wants to merge 1 commit into
developfrom
jon/android-disallow-landscape
Open

Keep Android phones locked to portrait#6109
j0ntz wants to merge 1 commit into
developfrom
jon/android-disallow-landscape

Conversation

@j0ntz

@j0ntz j0ntz commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Description

Asana task

Asana: https://app.asana.com/1/9976422036640/project/1215088146871429/task/1216901482732656

QA hit landscape mode on an Android phone (Xiaomi 13T Pro, Android 16). Landscape is not a
supported layout on phones: content overflows the screen and PIN-login gestures stop working. It is
only meant to work on tablets, which is what iOS already does via its
UISupportedInterfaceOrientations~ipad keys.

The phone/tablet split itself was already correct. R.bool.portrait_only is true by default and
overridden to false in values-sw600dp and values-xlarge, and the reported device is ~407dp
wide, so it resolved to true as expected. What failed is the strength of the lock:

  1. No manifest declaration. MainActivity had no android:screenOrientation, so the activity's
    declared ActivityInfo said "unspecified" and the app was only portrait once onCreate had run.
  2. Never re-asserted. The activity declares configChanges="...|orientation|screenSize|...", so
    it is not recreated on a configuration change and onCreate does not run again. If the system
    rotated the activity anyway, nothing ever put it back.
  3. No tablet branch. The if had no else, so the orientation was never explicitly relaxed.
    That meant the decision could not be safely re-evaluated later, and a device that changes
    screen-size class at runtime (a foldable being unfolded, or a display-size change) kept whatever
    it resolved to at launch.

This declares android:screenOrientation="portrait" on MainActivity so portrait holds from the
first frame, and moves the runtime decision into applyOrientationLock(), called from both
onCreate and onConfigurationChanged. Tablets now explicitly relax to
SCREEN_ORIENTATION_UNSPECIFIED, which overrides the manifest default, so tablet landscape is
preserved and the phone lock is re-applied whenever the configuration changes.

Also reorders two existing ## Unreleased CHANGELOG entries. That section already had fixed
before changed on develop, which fails verify-repo.sh's CHANGELOG ordering check regardless of
where a new entry is placed. No entry text was altered.

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Requirements

If you have made any visual changes to the GUI. Make sure you have:

  • Tested on iOS device
  • Tested on Android device
  • Tested on small-screen device (iPod Touch)
  • Tested on large-screen device (tablet)

Verified on an Android 16 emulator built to the reported device's panel (1220x2712 @ 480dpi, so
sw407dp, the same resource bucket as the Xiaomi 13T Pro). Screenshots in the comment below.

Scenario Smallest width requestedOrientation Result
Phone, system rotation forced to landscape sw407dp SCREEN_ORIENTATION_PORTRAIT Stays portrait, 1220x2712
Control: Settings app, same device and same forced rotation sw407dp n/a (rotatable) Rotates to 2712x1220, so the emulator genuinely can rotate
Resized up to tablet metrics while running sw800dp SCREEN_ORIENTATION_UNSPECIFIED Rotates to landscape, 2560x1600
Resized back down to phone metrics while running sw407dp SCREEN_ORIENTATION_PORTRAIT Re-locks to portrait

The last two rows exercise onConfigurationChanged: the app re-evaluated across the tablet
threshold in both directions without being recreated, which is the foldable case the previous
one-shot onCreate lock could not handle. Throughout the phone rows the system rotation preference
was pinned to landscape (user_rotation=1, accelerometer_rotation=0) and the app still held
ROTATION_0.

Not verified: the exact Xiaomi HyperOS force-rotate trigger. It does not reproduce on stock Android
(QA already confirmed a Pixel does not reproduce it), so this proves the intended contract on both
device classes rather than replaying the OEM-specific trigger. Not installed on the physical Android
device attached to this machine, to avoid replacing a real Edge install and its wallet data.


Note

Low Risk
Native Android orientation only; no auth, payments, or wallet logic. Tablet landscape path is explicitly preserved via resource qualifiers.

Overview
Android phones can no longer rotate into landscape (overflow and broken PIN gestures on devices like Xiaomi 13T Pro). Tablets still allow landscape, matching iOS iPad orientation behavior.

MainActivity now declares android:screenOrientation="portrait" in the manifest so portrait applies before onCreate, and applyOrientationLock() sets portrait when R.bool.portrait_only is true (phones) or SCREEN_ORIENTATION_UNSPECIFIED on tablet buckets (values-sw600dp / values-xlarge). The lock runs from onCreate and onConfigurationChanged so rotation or foldable/display-size changes re-apply without activity recreation (given existing configChanges for orientation/screenSize).

CHANGELOG Unreleased entries are reordered so fixed follows changed for the repo verify script; wording unchanged aside from the new Android portrait fix line.

Reviewed by Cursor Bugbot for commit 76f93bb. Bugbot is set up for automated code reviews on this repo. Configure here.

Landscape is only supported on tablets, but the portrait lock was applied
once at runtime in onCreate with no manifest declaration behind it, so the
activity was declared rotatable and nothing re-applied the lock afterwards.
A phone that got rotated by the system stayed rotated.

Declare portrait on MainActivity in the manifest so it holds from the first
frame, and re-apply the lock on configuration changes. The tablet exception
now relaxes the orientation explicitly, which also re-evaluates correctly on
a foldable that changes screen-size class at runtime.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@j0ntz

j0ntz commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence

phone portrait baseline

phone portrait baseline

phone stays portrait after forced rotation

phone stays portrait after forced rotation

control settings rotates landscape

control settings rotates landscape

tablet metrics allows landscape

tablet metrics allows landscape

phone relocks portrait on resize

phone relocks portrait on resize

Captured by the agent's in-app test run (build-and-test).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant