Skip to content

Fix measureInWindow on Android edge-to-edge#56056

Closed
zoontek wants to merge 2 commits intofacebook:mainfrom
zoontek:fix-measure-in-window
Closed

Fix measureInWindow on Android edge-to-edge#56056
zoontek wants to merge 2 commits intofacebook:mainfrom
zoontek:fix-measure-in-window

Conversation

@zoontek
Copy link
Copy Markdown
Contributor

@zoontek zoontek commented Mar 11, 2026

Summary:

Fixes measureInWindow on Android when edge-to-edge is enabled.

Both ReactSurfaceView.viewportOffset and RootViewUtil.getViewportOffset subtract the visible window frame (status bar insets, split screen offsets) from getLocationOnScreen / getLocationInWindow coordinates. This is incorrect in edge-to-edge mode, where the content already extends behind system bars.

Related issue: #50509

Changelog:

[ANDROID] [FIXED] - Fix measureInWindow returning incorrect coordinates when edge-to-edge is enabled

Test Plan:

Tested measureInWindow with and without edge-to-edge enabled, in both single-window and split-screen configurations. Verified that returned coordinates correctly reflect the view's position relative to the visible content area.

For that, in RNTesterActivity.kt, comment:

// reactDelegate?.reactRootView?.let { rootView ->
//   val insetsType: Int =
//       WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()

//   val windowInsetsListener = { view: View, windowInsets: WindowInsetsCompat ->
//     val insets = windowInsets.getInsets(insetsType)

//     (view.layoutParams as FrameLayout.LayoutParams).apply {
//       setMargins(insets.left, insets.top, insets.right, insets.bottom)
//     }

//     WindowInsetsCompat.CONSUMED
//   }
//   ViewCompat.setOnApplyWindowInsetsListener(rootView, windowInsetsListener)
// }

And in RNTesterAppShared.js replace export default RNTesterApp with:

export default () => {
  const ref = React.useRef<View>(null);
  const [measure, setMeasure] = React.useState<string>('');
  const [measureInWindow, setMeasureInWindow] = React.useState<string>('');
  const [onLayoutOutput, setOnLayoutOutput] = React.useState<string>('');
  React.useLayoutEffect(() => {
    ref.current?.measure(
      (
        x: number,
        y: number,
        width: number,
        height: number,
        pageX: number,
        pageY: number,
      ) => {
        setMeasure(
          `(${x}, ${y}) / ${width} x ${height}; \n  page=(${pageX}, ${pageY})`,
        );
      },
    );
  }, []);
  React.useLayoutEffect(() => {
    ref.current?.measureInWindow(
      (x: number, y: number, width: number, height: number) => {
        setMeasureInWindow(`(${x}, ${y}) / ${width} x ${height};})`);
      },
    );
  }, []);
  return (
    <View
      ref={ref}
      style={{flex: 1, justifyContent: 'center'}}
      onLayout={e => {
        setOnLayoutOutput(JSON.stringify(e.nativeEvent.layout));
      }}>
      <Text>Measure: {measure}</Text>
      <Text>measureInWindow: {measureInWindow}</Text>
      <Text>onLayout: {onLayoutOutput}</Text>
    </View>
  );
};

Screenshots:

Android 16 / edge-to-edge on - current behavior

Android 16 (edge-to-edge on - no fix)

Android 16 / edge-to-edge on - with fix

Android 16 (edge-to-edge on - fixed)

Android 14 / edge-to-edge off - current behavior

Android 14 (edge-to-edge off - no fix)

Android 14 / edge-to-edge off - with fix (no change)

Android 14 (edge-to-edge off - fixed)

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Mar 11, 2026
@facebook-github-bot facebook-github-bot added p: Expo Partner: Expo Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. labels Mar 11, 2026
@zoontek zoontek force-pushed the fix-measure-in-window branch from 04d84f8 to f165005 Compare March 11, 2026 14:04
kirillzyusko added a commit to kirillzyusko/react-native-keyboard-controller that referenced this pull request Mar 12, 2026
## 📜 Description

Fixed broken `measureInWindow` measurements in react-native.

## 💡 Motivation and Context

The `measureInWindow` works unreliably in Fabric + formSheet Modal
(facebook/react-native#56062) or on Android with
edge-to-edge enabled
(facebook/react-native#56056)

Upstream fixes are available, but the problem is that it will require at
least RN 0.85+ to work properly. So in this PR we add internal function
that is capable of measuring given view. In future this functionality
can be removed, but for now this is critical to have it bundled within a
package.

Fixes
#1356

## 📢 Changelog

<!-- High level overview of important changes -->
<!-- For example: fixed status bar manipulation; added new types
declarations; -->
<!-- If your changes don't affect one of platform/language below - then
remove this platform/language -->

### JS

- added `viewPositionInWindow` to `types`;
- added `viewPositionInWindow` to codegen;
- added `viewPositionInWindow` to bindings/module;
- use `viewPositionInWindow` instead of `measureInWindow`;
- added `viewPositionInWindow` to mocks;

### iOS

- implement `viewPositionInWindow`;
- make `activeWindow` objc available;

### Android

- implement `viewPositionInWindow`;
- add `uiManager` and `eventDispatcher` to `ReactContext` extensions;

## 🤔 How Has This Been Tested?

Tested manually on iPhone 17 Pro (iOS 26.2, simulator) and Pixel 9 Pro
(API 35, emulator).

## 📸 Screenshots (if appropriate):

### Android

#### Fabric

|Before|After|
|-------|-----|
|<video
src="https://github.com/user-attachments/assets/afb99e51-043f-459c-90e3-7e1eb1e184ed">|<video
src="https://github.com/user-attachments/assets/8ea0d593-f7bd-47a5-9343-e2fd85f4af45">|

#### Paper

|Before|After|
|-------|-----|
|<video
src="https://github.com/user-attachments/assets/702c7d88-755b-45f0-b39f-fe4aaa60ab2e">|<video
src="https://github.com/user-attachments/assets/f5346bbd-cc85-4a40-9cc5-9422931b04af">|

### iOS

#### Fabric

|Before|After|
|-------|-----|
|<video
src="https://github.com/user-attachments/assets/49fb9ac6-15b8-4842-939c-4ec06a4e0c42">|<video
src="https://github.com/user-attachments/assets/b559a52a-0a91-48bc-911a-7125ce96e013">|

#### Paper

|Before|After|
|-------|-----|
|<video
src="https://github.com/user-attachments/assets/c4d234a7-122a-4023-8d86-fad71ed043dc">|<video
src="https://github.com/user-attachments/assets/d5d48487-16ad-40c2-b6c6-fea85b563e20">|

## 📝 Checklist

- [x] CI successfully passed
- [x] I added new mocks and corresponding unit-tests if library API was
changed

---------

Co-authored-by: thomasvo <thomas.vo@openspace.ai>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: kirillzyusko <zyusko.kirik@gmail.com>
@zoontek zoontek force-pushed the fix-measure-in-window branch 2 times, most recently from 2c7f994 to 0c702a0 Compare April 16, 2026 17:31
@meta-codesync
Copy link
Copy Markdown

meta-codesync Bot commented Apr 17, 2026

@alanleedev has imported this pull request. If you are a Meta employee, you can view this in D101308387.

@zoontek zoontek force-pushed the fix-measure-in-window branch from 0c702a0 to 547fc3a Compare April 17, 2026 10:13
@zoontek zoontek force-pushed the fix-measure-in-window branch from dc2a7ab to be7ec70 Compare April 17, 2026 11:10
@zoontek zoontek force-pushed the fix-measure-in-window branch from c40bf04 to d0ea897 Compare April 17, 2026 12:39
@@ -47,16 +50,24 @@ public class ReactSurfaceView(context: Context?, internal val surface: ReactSurf

private val viewportOffset: Point
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The logic is similar to RootViewUtil.getViewportOffset(v: View), it's just not used here because of it's bad naming (getViewportOffset could be called for any view, not just the root one)

import com.facebook.react.uimanager.JSPointerDispatcher
import com.facebook.react.uimanager.JSTouchDispatcher
import com.facebook.react.uimanager.common.UIManagerType
import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

copying here comment from @javache

I don't think react.runtime can depend on react.views? There seems to be a dependency issue.
Maybe the feature flag should live in UIManager/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

we could move isEdgeToEdgeFeatureFlagOn / setEdgeToEdgeFeatureFlagOn / updateEdgeToEdgeFeatureFlag in a separate file, but that introduces a change in the public API

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

FYI, com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn was add in June 2025.
@javache I also don't see any build issues so is this more of an architectural policy?

@meta-codesync meta-codesync Bot closed this in 9d18367 Apr 23, 2026
@react-native-bot
Copy link
Copy Markdown
Collaborator

This pull request was successfully merged by @zoontek in 9d18367

When will my fix make it into a release? | How to file a pick request?

@react-native-bot react-native-bot added the Merged This PR has been merged. label Apr 23, 2026
@zoontek zoontek deleted the fix-measure-in-window branch April 23, 2026 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. p: Expo Partner: Expo Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants