Skip to content

Support UiMediaScope (All Platforms) - #3216

Closed
Apolo (ApoloApps) wants to merge 43 commits into
JetBrains:jb-mainfrom
ApoloApps:uiMediaSupport
Closed

Support UiMediaScope (All Platforms)#3216
Apolo (ApoloApps) wants to merge 43 commits into
JetBrains:jb-mainfrom
ApoloApps:uiMediaSupport

Conversation

@ApoloApps

@ApoloApps Apolo (ApoloApps) commented Jul 11, 2026

Copy link
Copy Markdown

Add support for UiMediaScope in all platforms (some platforms are more complete than others or may have even simpler APIs to get the data from, any suggestion is welcomed)

Fixes https://youtrack.jetbrains.com/issue/CMP-10481/Add-Support-for-UiMediaScope

Release Notes

Features - Multiple Platforms

  • Add support for mediaQuery calls which allows to modify Composables depending on device characteristics

@ApoloApps Apolo (ApoloApps) changed the title Support UiMediaScope (Common and Web) Support UiMediaScope (Common, JVM and Web) Jul 12, 2026
@ApoloApps Apolo (ApoloApps) changed the title Support UiMediaScope (Common, JVM and Web) Support UiMediaScope (All Platforms) Jul 12, 2026
@ApoloApps
Apolo (ApoloApps) marked this pull request as ready for review July 12, 2026 23:46
@ApoloApps

Copy link
Copy Markdown
Author

Alexander Maryanovsky (@m-sasha) for Desktop changes
Oleksandr Karpovich (@eymar) for Web changes
Vendula Švastalová (@svastven) for iOS changes (my knowledge of iOS APIs to get the data from is limited, any suggestion is welcomed, or even after this PR, y'all can create a followup to polish it if needed)

ComposeSceneKeyboardOffsetManager(
view = _overlayView,
keyboardOverlapHeightChanged = { height ->
mediaEnvironment.onKeyboardOverlapHeightChanged(height)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I would rather pair the mediaEnvironment with the KeyboardVisibilityListener directly - it tracks KB state form the very beginning and has keyboardFrame value to get correct height (if needed)

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.

keyboardFrame is not backed by mutableState, and so changes to it will not trigger recomposition on the place isImeShown is read (UimediaScope.KeyboardKind).
So i would have to pass mediaEnvironment to NativeKeyboardVisibilityListener to trigger

    @OptIn(BetaInteropApi::class)
    @ObjCAction
    fun keyboardWillShow(arg: NSNotification) {
        observers.forEach {
            it.keyboardWillShow(arg.endFrame, arg.duration, arg.animationOptions)
        }
        keyboardFrame = arg.endFrame
		mediaEnvironment.isImeShown = true// (substituting onKeyboardOverlapHeightChanged call)
    }

    @OptIn(BetaInteropApi::class)
    @ObjCAction
    fun keyboardWillHide(arg: NSNotification) {
        observers.forEach {
            it.keyboardWillHide(CGRectZero.readValue(), arg.duration, arg.animationOptions)
        }
        keyboardFrame = CGRectZero.readValue()
		mediaEnvironment.isImeShown = false //(substituting onKeyboardOverlapHeightChanged call)

    }

@ASalavei Andrei Salavei (ASalavei) Jul 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Not exactly. You can make MediaEnvironment implement the KeyboardVisibilityObserver and listen for keyboardWillShow/keyboardWillChangeSize/keyboardWillHide notifications there. As initial value, you can use the KeyboardVisibilityListener.keyboardFrame.height > 0.

P.S. Also, it seems like it's fine to convert keyboardFrame to state.

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.

Done. No need to convert KeyboardVisibilityListener.keyboardFrame to MutableState since it is only read to initialize isImeShown the first time. Subsequent updates to isImeShown state is done through MediaEnvironment as KeyboardVisibilityListener listening to keyboard changes

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Please rename file to the MediaEnvironment.ios.kt‎ as well

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.

done (and fixed tests)

…ceneMediator iOS and a rebase error in ComposeWindowInternal Web which added density without the 'y'
… as listener to KeyboardVisibilityListener for changes. No need of MutableState in KeyboardVisibilityListener.keyboardFrame
internal object DefaultHapticFeedback : HapticFeedback {
override fun performHapticFeedback(hapticFeedbackType: HapticFeedbackType) {
}
@InternalComposeUiApi

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.

I wonder if this interface can be internal? As I see it's used only in the ui:ui module.

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.

Nope, PlatformContext requires everything to be public (if not it leaks visibility) that's why it is marked with InternalComposeUiApi (and why we have Platform* interfaces in the first place, since common interfaces also have internal modifier thus cannot be used in PlatformContext)

@ASalavei Andrei Salavei (ASalavei) 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.

iOS part LGTM

}
@InternalComposeUiApi
@OptIn(ExperimentalMediaQueryApi::class)
interface PlatformMediaEnvironment : UiMediaScope {

Choose a reason for hiding this comment

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

Why is this interface needed? Why do systemTheme and systemDensity need to be here?

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.

systemTheme and systemDensity are part of the MediaEnvironment of a device and I found it was a good way of centralizing similar logic into the same class. What I'm surprised is that it is not part of the common API on the first place. It is a good way of unifying scattered logic in a single, coherent place (encapsulating similar logic)

Choose a reason for hiding this comment

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

It makes some sense, but I'm not entirely convinced.

Against it is the fact that UiMediaScope indeed doesn't have it, and we try to mostly adhere to the upstream decisions.
I also don't like that the code for managing system theme polling was moved from a specific to a generic file.

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.

I will be reverting as I do agree that maybe this is not the best place if UiMediaScope does not have it. I'll be removing PlatformMediaEnvironment in favour of directly using UiMediaScope. Also, reverting system theme polling changes in desktop to its own file

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.

reverted Desktop changes

LocalPlatformScreenReader provides platformContext.screenReader,
LocalPlatformWindowInsets provides platformContext.windowInsets,
LocalPlatformPrefetchScheduler provides platformContext.prefetchScheduler,
LocalPlatformPrefetchScheduler providesComputed { platformContext.prefetchScheduler },

Choose a reason for hiding this comment

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

why is providesComputed needed here?

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.

deferring platformContext.prefetchScheduler init

# Conflicts:
#	compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/window/ComposeWindowInternal.web.kt
#	compose/ui/ui/src/webMain/kotlin/androidx/compose/ui/window/SystemThemeObserver.web.kt
@ApoloApps
Apolo (ApoloApps) marked this pull request as draft August 24, 2026 00:13
@ApoloApps

Apolo (ApoloApps) commented Aug 25, 2026

Copy link
Copy Markdown
Author

Closed this PR in favor of smaller platforms based PRs for better reviewing
Common: #3339
iOS: #3340
Desktop : #3341

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.

5 participants