Skip to content

Add rear-camera selfie and dual-screen preview support for foldables - #779

Open
bigtechstruggler wants to merge 14 commits into
GrapheneOS:mainfrom
bigtechstruggler:feature/fold-display-support
Open

bigtechstruggler wants to merge 14 commits into
GrapheneOS:mainfrom
bigtechstruggler:feature/fold-display-support

Conversation

@bigtechstruggler

Copy link
Copy Markdown

Summary

This contribution adds two foldable-specific features to GrapheneOS Camera:

  • Rear Camera Selfie: Use the outer display as a viewfinder with the rear camera.
  • Dual Screen Preview: Display a live camera preview on both the inner and outer displays simultaneously.

Both features are integrated into the existing camera settings using native Material switches.

The implementation uses Jetpack WindowManager and CameraX, without requiring Google Camera or Google Play Services.

Testing

Device: Google Pixel 9 Pro Fold
Operating system: GrapheneOS
Sandboxed Google Play: Not installed

Manual hardware testing confirmed by the contributor:

  • Both display modes function correctly.
  • Live camera previews work on both displays.
  • Inner-screen swipe gestures remain functional.
  • Existing camera controls work as expected.
  • No Google services are required.

Build: ./gradlew :app:assembleDebug — successful.

Automated tests and testing on other foldable devices are still pending.

Implementation notes

Dual Screen Preview uses a second CameraX preview rather than periodic screenshots. It is currently restricted to standard Camera mode, with fallback handling for unsupported camera configurations.

This is a draft submission for code review and compatibility feedback.

Related to #671.

A Special Thank You to the GrapheneOS Team

I also want to take this opportunity to express a huge thank you to the entire GrapheneOS team.

What you've built is genuinely impressive. Your dedication to privacy, security, transparency, and giving users real control over their own devices is something I deeply appreciate.

It's refreshing to use an operating system that puts its users first, without unnecessary dependencies on Google services.

GrapheneOS has made my Pixel experience so much better, and I'm grateful for all the hard work, time, and effort that goes into developing and maintaining this project.

Being able to contribute even a small improvement to software I genuinely enjoy using means a lot to me.

Keep up the amazing work, and thank you for everything you do! ❤️

resetAutoSleep()
return
}
val mode = selectedTab.tag as? CameraMode ?: return

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Select Dual Screen → Rear Selfie → Camera while the handoff is pending. Exiting Rear calls toggleRearSelfie(), but during Dual teardown that method queues Rear again. The delayed transfer consequently runs after Camera has been selected.

The same cancellation gap affects a pending Dual startup: stopDualForModeChange() returns when the session has not arrived yet. A late callback can then create the outer preview and rebind after switching to Video, where the outer camera use case is excluded.

Add an explicit cancellation path for pending and active operations, and reject callbacks belonging to a cancelled request. Regression coverage should exercise both sequences.

FoldTabMode.REAR -> tab.setText(R.string.fold_tab_rear)
}
tab.tag = foldMode
tab.view.setOnTouchListener { _, e ->

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TalkBack double-tap or a key press on "Dual Screen" or "Rear Selfie" highlights the tab and starts nothing. 2c7cf69 was written to prevent exactly that kind of state.

Rebase onto main and give fold tabs the same click listener, ideally through one shared setup for both kinds of tab.

else -> null
}
val capabilitiesChanged = foldRearSupported != rearSupported || foldDualSupported != dualSupported
val selectionChanged = foldSelectedMode != newSelection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The strip stays on a fold tab after a toggle fails or is refused.

@bigtechstruggler bigtechstruggler Oct 25, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Im looking into it after my coffee 😀 also im taking this in the full camera app i also sent with the panorama mode etc.

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.

2 participants