Loading core/java/android/view/View.java +12 −5 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFI import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION; import static com.android.window.flags.Flags.FLAG_DELEGATE_UNHANDLED_DRAGS; import static com.android.window.flags.Flags.FLAG_SUPPORTS_DRAG_ASSISTANT_TO_MULTIWINDOW; import static com.android.window.flags.Flags.reduceChangedExclusionRectsMsgs; import static java.lang.Math.max; Loading Loading @@ -247,6 +248,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; Loading Loading @@ -12888,16 +12890,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (rects.isEmpty() && mListenerInfo == null) return; final ListenerInfo info = getListenerInfo(); final boolean rectsChanged = !reduceChangedExclusionRectsMsgs() || !Objects.equals(info.mSystemGestureExclusionRects, rects); if (info.mSystemGestureExclusionRects != null) { if (rectsChanged) { info.mSystemGestureExclusionRects.clear(); info.mSystemGestureExclusionRects.addAll(rects); } } else { info.mSystemGestureExclusionRects = new ArrayList<>(rects); } if (rectsChanged) { updatePositionUpdateListener(); postUpdate(this::updateSystemGestureExclusionRects); } } private void updatePositionUpdateListener() { final ListenerInfo info = getListenerInfo(); core/java/android/view/ViewRootImpl.java +11 −2 Original line number Diff line number Diff line Loading @@ -136,6 +136,7 @@ import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme; import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay; import static com.android.window.flags.Flags.enableWindowContextResourcesUpdateOnConfigChange; import static com.android.window.flags.Flags.predictiveBackSwipeEdgeNoneApi; import static com.android.window.flags.Flags.reduceChangedExclusionRectsMsgs; import static com.android.window.flags.Flags.setScPropertiesInClient; import android.Manifest; Loading Loading @@ -6074,9 +6075,13 @@ public final class ViewRootImpl implements ViewParent, } void updateSystemGestureExclusionRectsForView(View view) { boolean msgInQueue = reduceChangedExclusionRectsMsgs() && mGestureExclusionTracker.isWaitingForComputeChanges(); mGestureExclusionTracker.updateRectsForView(view); if (!msgInQueue) { mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); } } void systemGestureExclusionChanged() { final List<Rect> rectsForWindowManager = mGestureExclusionTracker.computeChangedRects(); Loading Loading @@ -6119,9 +6124,13 @@ public final class ViewRootImpl implements ViewParent, * the root's view hierarchy. */ public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) { boolean msgInQueue = reduceChangedExclusionRectsMsgs() && mGestureExclusionTracker.isWaitingForComputeChanges(); mGestureExclusionTracker.setRootRects(rects); if (!msgInQueue) { mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); } } /** * Returns the root-level system gesture exclusion rects. These do not include those provided by Loading core/java/android/view/ViewRootRectTracker.java +19 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.lang.ref.WeakReference; Loading @@ -32,8 +33,10 @@ import java.util.function.Function; /** * Abstract class to track a collection of rects reported by the views under the same * {@link ViewRootImpl}. * @hide */ class ViewRootRectTracker { @VisibleForTesting public class ViewRootRectTracker { private final Function<View, List<Rect>> mRectCollector; private boolean mViewsChanged = false; private boolean mRootRectsChanged = false; Loading @@ -41,11 +44,18 @@ class ViewRootRectTracker { private List<ViewInfo> mViewInfos = new ArrayList<>(); private List<Rect> mRects = Collections.emptyList(); // Keeps track of whether updateRectsForView has been called but there was no subsequent call // on computeChanges yet. Since updateRectsForView is called when sending a message and // computeChanges when it is received, this tracks whether such message is in the queue already private boolean mWaitingForComputeChanges = false; /** * @param rectCollector given a view returns a list of the rects of interest for this * ViewRootRectTracker * @hide */ ViewRootRectTracker(Function<View, List<Rect>> rectCollector) { @VisibleForTesting public ViewRootRectTracker(Function<View, List<Rect>> rectCollector) { mRectCollector = rectCollector; } Loading @@ -70,6 +80,7 @@ class ViewRootRectTracker { mViewInfos.add(new ViewInfo(view)); mViewsChanged = true; } mWaitingForComputeChanges = true; } /** Loading @@ -92,6 +103,7 @@ class ViewRootRectTracker { * @return {@code true} if there were changes, {@code false} otherwise. */ public boolean computeChanges() { mWaitingForComputeChanges = false; boolean changed = mRootRectsChanged; final Iterator<ViewInfo> i = mViewInfos.iterator(); final List<Rect> rects = new ArrayList<>(mRootRects); Loading Loading @@ -121,6 +133,10 @@ class ViewRootRectTracker { return false; } public boolean isWaitingForComputeChanges() { return mWaitingForComputeChanges; } /** * Returns a List of all Rects from all visible Views in the global (root) coordinate system. * This list is only updated when calling {@link #computeChanges()} or Loading @@ -140,6 +156,7 @@ class ViewRootRectTracker { Preconditions.checkNotNull(rects, "rects must not be null"); mRootRects = rects; mRootRectsChanged = true; mWaitingForComputeChanges = true; } @NonNull Loading core/java/android/window/flags/windowing_frontend.aconfig +11 −0 Original line number Diff line number Diff line Loading @@ -427,6 +427,17 @@ flag { } } flag { name: "reduce_changed_exclusion_rects_msgs" namespace: "windowing_frontend" description: "Don't send MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED when there is no change" bug: "388231176" is_fixed_read_only: true metadata { purpose: PURPOSE_BUGFIX } } flag { name: "keep_app_window_hide_while_locked" namespace: "windowing_frontend" Loading core/tests/coretests/src/android/view/ViewRootRectTrackerTest.java 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.testng.AssertJUnit.assertEquals; import android.graphics.Rect; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.google.common.collect.Lists; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Collections; import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) public class ViewRootRectTrackerTest { private ViewRootRectTracker mTracker; private final List<Rect> mRects = Lists.newArrayList(new Rect(0, 0, 5, 5), new Rect(5, 5, 10, 10)); @Before public void setUp() { mTracker = new ViewRootRectTracker(v -> Collections.emptyList()); } @Test public void setRootRectsAndComputeTest() { mTracker.setRootRects(mRects); mTracker.computeChanges(); assertEquals(mRects, mTracker.getLastComputedRects()); } @Test public void waitingForComputeChangesTest() { mTracker.setRootRects(mRects); assertTrue(mTracker.isWaitingForComputeChanges()); mTracker.computeChangedRects(); assertFalse(mTracker.isWaitingForComputeChanges()); View mockView = mock(View.class); mTracker.updateRectsForView(mockView); assertTrue(mTracker.isWaitingForComputeChanges()); mTracker.computeChangedRects(); assertFalse(mTracker.isWaitingForComputeChanges()); } } Loading
core/java/android/view/View.java +12 −5 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFI import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION; import static com.android.window.flags.Flags.FLAG_DELEGATE_UNHANDLED_DRAGS; import static com.android.window.flags.Flags.FLAG_SUPPORTS_DRAG_ASSISTANT_TO_MULTIWINDOW; import static com.android.window.flags.Flags.reduceChangedExclusionRectsMsgs; import static java.lang.Math.max; Loading Loading @@ -247,6 +248,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; Loading Loading @@ -12888,16 +12890,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (rects.isEmpty() && mListenerInfo == null) return; final ListenerInfo info = getListenerInfo(); final boolean rectsChanged = !reduceChangedExclusionRectsMsgs() || !Objects.equals(info.mSystemGestureExclusionRects, rects); if (info.mSystemGestureExclusionRects != null) { if (rectsChanged) { info.mSystemGestureExclusionRects.clear(); info.mSystemGestureExclusionRects.addAll(rects); } } else { info.mSystemGestureExclusionRects = new ArrayList<>(rects); } if (rectsChanged) { updatePositionUpdateListener(); postUpdate(this::updateSystemGestureExclusionRects); } } private void updatePositionUpdateListener() { final ListenerInfo info = getListenerInfo();
core/java/android/view/ViewRootImpl.java +11 −2 Original line number Diff line number Diff line Loading @@ -136,6 +136,7 @@ import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme; import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay; import static com.android.window.flags.Flags.enableWindowContextResourcesUpdateOnConfigChange; import static com.android.window.flags.Flags.predictiveBackSwipeEdgeNoneApi; import static com.android.window.flags.Flags.reduceChangedExclusionRectsMsgs; import static com.android.window.flags.Flags.setScPropertiesInClient; import android.Manifest; Loading Loading @@ -6074,9 +6075,13 @@ public final class ViewRootImpl implements ViewParent, } void updateSystemGestureExclusionRectsForView(View view) { boolean msgInQueue = reduceChangedExclusionRectsMsgs() && mGestureExclusionTracker.isWaitingForComputeChanges(); mGestureExclusionTracker.updateRectsForView(view); if (!msgInQueue) { mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); } } void systemGestureExclusionChanged() { final List<Rect> rectsForWindowManager = mGestureExclusionTracker.computeChangedRects(); Loading Loading @@ -6119,9 +6124,13 @@ public final class ViewRootImpl implements ViewParent, * the root's view hierarchy. */ public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) { boolean msgInQueue = reduceChangedExclusionRectsMsgs() && mGestureExclusionTracker.isWaitingForComputeChanges(); mGestureExclusionTracker.setRootRects(rects); if (!msgInQueue) { mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); } } /** * Returns the root-level system gesture exclusion rects. These do not include those provided by Loading
core/java/android/view/ViewRootRectTracker.java +19 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.lang.ref.WeakReference; Loading @@ -32,8 +33,10 @@ import java.util.function.Function; /** * Abstract class to track a collection of rects reported by the views under the same * {@link ViewRootImpl}. * @hide */ class ViewRootRectTracker { @VisibleForTesting public class ViewRootRectTracker { private final Function<View, List<Rect>> mRectCollector; private boolean mViewsChanged = false; private boolean mRootRectsChanged = false; Loading @@ -41,11 +44,18 @@ class ViewRootRectTracker { private List<ViewInfo> mViewInfos = new ArrayList<>(); private List<Rect> mRects = Collections.emptyList(); // Keeps track of whether updateRectsForView has been called but there was no subsequent call // on computeChanges yet. Since updateRectsForView is called when sending a message and // computeChanges when it is received, this tracks whether such message is in the queue already private boolean mWaitingForComputeChanges = false; /** * @param rectCollector given a view returns a list of the rects of interest for this * ViewRootRectTracker * @hide */ ViewRootRectTracker(Function<View, List<Rect>> rectCollector) { @VisibleForTesting public ViewRootRectTracker(Function<View, List<Rect>> rectCollector) { mRectCollector = rectCollector; } Loading @@ -70,6 +80,7 @@ class ViewRootRectTracker { mViewInfos.add(new ViewInfo(view)); mViewsChanged = true; } mWaitingForComputeChanges = true; } /** Loading @@ -92,6 +103,7 @@ class ViewRootRectTracker { * @return {@code true} if there were changes, {@code false} otherwise. */ public boolean computeChanges() { mWaitingForComputeChanges = false; boolean changed = mRootRectsChanged; final Iterator<ViewInfo> i = mViewInfos.iterator(); final List<Rect> rects = new ArrayList<>(mRootRects); Loading Loading @@ -121,6 +133,10 @@ class ViewRootRectTracker { return false; } public boolean isWaitingForComputeChanges() { return mWaitingForComputeChanges; } /** * Returns a List of all Rects from all visible Views in the global (root) coordinate system. * This list is only updated when calling {@link #computeChanges()} or Loading @@ -140,6 +156,7 @@ class ViewRootRectTracker { Preconditions.checkNotNull(rects, "rects must not be null"); mRootRects = rects; mRootRectsChanged = true; mWaitingForComputeChanges = true; } @NonNull Loading
core/java/android/window/flags/windowing_frontend.aconfig +11 −0 Original line number Diff line number Diff line Loading @@ -427,6 +427,17 @@ flag { } } flag { name: "reduce_changed_exclusion_rects_msgs" namespace: "windowing_frontend" description: "Don't send MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED when there is no change" bug: "388231176" is_fixed_read_only: true metadata { purpose: PURPOSE_BUGFIX } } flag { name: "keep_app_window_hide_while_locked" namespace: "windowing_frontend" Loading
core/tests/coretests/src/android/view/ViewRootRectTrackerTest.java 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.testng.AssertJUnit.assertEquals; import android.graphics.Rect; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.google.common.collect.Lists; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Collections; import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) public class ViewRootRectTrackerTest { private ViewRootRectTracker mTracker; private final List<Rect> mRects = Lists.newArrayList(new Rect(0, 0, 5, 5), new Rect(5, 5, 10, 10)); @Before public void setUp() { mTracker = new ViewRootRectTracker(v -> Collections.emptyList()); } @Test public void setRootRectsAndComputeTest() { mTracker.setRootRects(mRects); mTracker.computeChanges(); assertEquals(mRects, mTracker.getLastComputedRects()); } @Test public void waitingForComputeChangesTest() { mTracker.setRootRects(mRects); assertTrue(mTracker.isWaitingForComputeChanges()); mTracker.computeChangedRects(); assertFalse(mTracker.isWaitingForComputeChanges()); View mockView = mock(View.class); mTracker.updateRectsForView(mockView); assertTrue(mTracker.isWaitingForComputeChanges()); mTracker.computeChangedRects(); assertFalse(mTracker.isWaitingForComputeChanges()); } }