Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +9 −10 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSuppl import com.android.wm.shell.windowdecor.extension.InsetsStateKt; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.BiFunction; import java.util.function.Supplier; Loading Loading @@ -483,29 +484,26 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> // Caption bounding rectangles: these are optional, and are used to present finer // insets than traditional |Insets| to apps about where their content is occluded. // These are also in absolute coordinates. final Rect[] boundingRects; final List<Rect> boundingRects = new ArrayList<>(); final int numOfElements = params.mOccludingCaptionElements.size(); if (numOfElements == 0) { boundingRects = null; } else { if (numOfElements != 0) { // The customizable region can at most be equal to the caption bar. if (params.hasInputFeatureSpy()) { outResult.mCustomizableCaptionRegion.set(captionInsetsRect); } final Resources resources = mDecorWindowContext.getResources(); boundingRects = new Rect[numOfElements]; for (int i = 0; i < numOfElements; i++) { final OccludingCaptionElement element = params.mOccludingCaptionElements.get(i); final int elementWidthPx = resources.getDimensionPixelSize(element.mWidthResId); boundingRects[i] = final Rect boundingRect = calculateBoundingRectLocal(element, elementWidthPx, captionInsetsRect); boundingRects.add(boundingRect); // Subtract the regions used by the caption elements, the rest is // customizable. if (params.hasInputFeatureSpy()) { outResult.mCustomizableCaptionRegion.op(boundingRects[i], Region.Op.DIFFERENCE); outResult.mCustomizableCaptionRegion.op(boundingRect, Region.Op.DIFFERENCE); } } } Loading Loading @@ -889,8 +887,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final Rect captionInsets = new Rect(0, 0, 0, captionHeight); final WindowDecorationInsets newInsets = new WindowDecorationInsets(mTaskInfo.token, mOwner, captionInsets, null /* taskFrame */, null /* boundingRects */, 0 /* flags */, true /* shouldAddCaptionInset */, false /* excludedFromAppBounds */); mOwner, captionInsets, null /* taskFrame */, Collections.emptyList() /* boundingRects */, 0 /* flags */, true /* shouldAddCaptionInset */, false /* excludedFromAppBounds */); if (!newInsets.equals(mWindowDecorationInsets)) { mWindowDecorationInsets = newInsets; mWindowDecorationInsets.update(wct); Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorationInsets.kt +97 −48 Original line number Diff line number Diff line Loading @@ -23,31 +23,71 @@ import android.view.InsetsSource import android.view.WindowInsets import android.window.WindowContainerToken import android.window.WindowContainerTransaction import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_WINDOW_DECORATION /** Adds, removes, and updates caption insets. */ data class WindowDecorationInsets( data class WindowDecorationInsets private constructor( private val token: WindowContainerToken, private val owner: Binder, private val frame: Rect, private val taskFrame: Rect? = null, private val boundingRects: Array<Rect>? = null, private val frame: Frame, private val boundingRects: List<Rect> = emptyList(), @InsetsSource.Flags private val flags: Int = 0, private val shouldAddCaptionInset: Boolean = false, private val excludedFromAppBounds: Boolean = false, private val appBoundsExclusion: AppBoundsExclusion? = null, ) { private sealed class Frame { abstract val height: Int data class Absolute(val rect: Rect, override val height: Int = rect.height()) : Frame() data class Relative(override val height: Int) : Frame() } private data class AppBoundsExclusion(val taskFrame: Rect) constructor( token: WindowContainerToken, owner: Binder, frame: Rect, taskFrame: Rect? = null, boundingRects: List<Rect> = emptyList(), @InsetsSource.Flags flags: Int = 0, shouldAddCaptionInset: Boolean = false, excludedFromAppBounds: Boolean = false, ) : this( token, owner, if (Flags.relativeInsets()) { Frame.Relative(frame.height()) } else { Frame.Absolute(frame) }, boundingRects, flags, shouldAddCaptionInset, if (excludedFromAppBounds) AppBoundsExclusion(checkNotNull(taskFrame)) else null ) /** Updates the caption insets. */ fun update(wct: WindowContainerTransaction) { if (!shouldAddCaptionInset) return if (com.android.window.flags.Flags.relativeInsets()) { val insets = Insets.of(0, frame.height(), 0, 0) logD( "update insets for wc=%s with frame=%s, rects=%s, appBoundsExclusion=%s", token, frame, boundingRects, appBoundsExclusion, ) val rects = if (boundingRects.isEmpty()) null else boundingRects.toTypedArray() when (frame) { is Frame.Absolute -> { wct.addInsetsSource( token, owner, INDEX, WindowInsets.Type.captionBar(), insets, boundingRects, frame.rect, rects, flags, ) wct.addInsetsSource( Loading @@ -55,18 +95,20 @@ data class WindowDecorationInsets( owner, INDEX, WindowInsets.Type.mandatorySystemGestures(), insets, boundingRects, frame.rect, rects, /* flags= */ 0, ) } else { } is Frame.Relative -> { val insets = Insets.of(0, frame.height, 0, 0) wct.addInsetsSource( token, owner, INDEX, WindowInsets.Type.captionBar(), frame, boundingRects, insets, rects, flags, ) wct.addInsetsSource( Loading @@ -74,14 +116,16 @@ data class WindowDecorationInsets( owner, INDEX, WindowInsets.Type.mandatorySystemGestures(), frame, boundingRects, insets, rects, /* flags= */ 0, ) } if (excludedFromAppBounds) { val appBounds = Rect(taskFrame) appBounds.top += frame.height() } appBoundsExclusion?.let { exclusion -> val appBounds = Rect(exclusion.taskFrame).apply { top += frame.height } wct.setAppBounds(token, appBounds) } } Loading @@ -95,12 +139,17 @@ data class WindowDecorationInsets( INDEX, WindowInsets.Type.mandatorySystemGestures() ) if (excludedFromAppBounds) { appBoundsExclusion?.let { wct.setAppBounds(token, Rect()) } } private fun logD(msg: String, vararg arguments: Any?) { ProtoLog.d(WM_SHELL_WINDOW_DECORATION, "%s: $msg", TAG, *arguments) } companion object { private const val TAG = "WindowDecorationInsets" /** Index for caption insets source. */ private const val INDEX = 0 } Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/caption/CaptionController.kt +10 −17 Original line number Diff line number Diff line Loading @@ -355,7 +355,7 @@ abstract class CaptionController<T>( owner = insetsOwner, frame = captionInsets, taskFrame = null, boundingRects = null, boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false Loading Loading @@ -388,35 +388,28 @@ abstract class CaptionController<T>( // These are also in absolute coordinates. val numOfElements = params.occludingCaptionElements.size val customizableCaptionRegion = Region.obtain() val boundingRects: Array<Rect>? if (numOfElements == 0) { boundingRects = null } else { val boundingRects = mutableListOf<Rect>() if (numOfElements != 0) { // The customizable region can at most be equal to the caption bar. if (params.hasInputFeatureSpy()) { customizableCaptionRegion.set(captionInsetsRect) } val resources = decorWindowContext.resources boundingRects = Array(numOfElements) { Rect() } for (i in 0 until numOfElements) { val element = params.occludingCaptionElements[i] val elementWidthPx = resources.getDimensionPixelSize(element.widthResId) boundingRects[i].set( calculateBoundingRectLocal( val boundingRect = calculateBoundingRectLocal( element, elementWidthPx, captionInsetsRect, decorWindowContext ) ) boundingRects.add(boundingRect) // Subtract the regions used by the caption elements, the rest is // customizable. if (params.hasInputFeatureSpy()) { customizableCaptionRegion.op( boundingRects[i], Region.Op.DIFFERENCE ) customizableCaptionRegion.op(boundingRect, Region.Op.DIFFERENCE) } } } Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationInsetsTest.kt 0 → 100644 +157 −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 com.android.wm.shell.windowdecor import android.graphics.Rect import android.os.Binder import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.window.WindowContainerToken import androidx.test.filters.SmallTest import com.android.window.flags.Flags import com.google.common.truth.Truth.assertThat import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock /** * Tests for [WindowDecorationInsets]. * * Build/Install/Run: * atest WMShellUnitTests:WindowDecorationInsetsTest */ @SmallTest @RunWith(AndroidTestingRunner::class) class WindowDecorationInsetsTest { @JvmField @Rule val setFlagsRule = SetFlagsRule() private val token: WindowContainerToken = mock() private val owner = Binder() @Test fun `equals`() { val frame = Rect(0, 0, 1000, 80) val taskFrame = Rect(0, 0, 1000, 600) val insets1 = WindowDecorationInsets( token = token, owner = owner, frame = frame, taskFrame = taskFrame, boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) val insets2 = WindowDecorationInsets( token = token, owner = owner, frame = frame, taskFrame = taskFrame, boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) assertThat(insets1).isEqualTo(insets2) } @Test fun `equals with bounding rects`() { val frame = Rect(0, 0, 1000, 80) val taskFrame = Rect(0, 0, 1000, 600) val rects = listOf(Rect(0, 0, 300, 80), Rect(800, 0, 1000, 80)) val insets1 = WindowDecorationInsets( token = token, owner = owner, frame = frame, taskFrame = taskFrame, boundingRects = rects, flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) val insets2 = WindowDecorationInsets( token = token, owner = owner, frame = frame, taskFrame = taskFrame, boundingRects = rects, flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) assertThat(insets1).isEqualTo(insets2) } @Test @EnableFlags(Flags.FLAG_RELATIVE_INSETS) fun `equals with different frame but same height`() { val taskFrame = Rect(0, 0, 1000, 600) val insets1 = WindowDecorationInsets( token = token, owner = owner, frame = Rect(0, 0, 1000, 80), taskFrame = taskFrame, boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) val insets2 = WindowDecorationInsets( token = token, owner = owner, frame = Rect(100, 0, 1000, 80), taskFrame = taskFrame, boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) assertThat(insets1).isEqualTo(insets2) } @Test fun `equals with different task frame but no app bounds exclusion`() { val frame = Rect(0, 0, 1000, 80) val insets1 = WindowDecorationInsets( token = token, owner = owner, frame = frame, taskFrame = Rect(0, 0, 1000, 600), boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) val insets2 = WindowDecorationInsets( token = token, owner = owner, frame = frame, taskFrame = Rect(10, 0, 1010, 600), boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) assertThat(insets1).isEqualTo(insets2) } } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +33 −1 Original line number Diff line number Diff line Loading @@ -62,6 +62,8 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.LocaleList; import android.os.Looper; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.UsesFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.util.DisplayMetrics; Loading Loading @@ -907,7 +909,8 @@ public class WindowDecorationTests extends ShellTestCase { } @Test public void testRelayout_captionFrameChanged_insetsReapplied() { @DisableFlags(Flags.FLAG_RELATIVE_INSETS) public void testRelayout_taskFrameChanged_insetsReapplied() { final Display defaultDisplay = mock(Display.class); doReturn(defaultDisplay).when(mMockDisplayController) .getDisplay(Display.DEFAULT_DISPLAY); Loading @@ -932,6 +935,35 @@ public class WindowDecorationTests extends ShellTestCase { verifyAddedInsets(2 /* times */, token, 0 /* index */, mandatorySystemGestures()); } @Test @EnableFlags(Flags.FLAG_RELATIVE_INSETS) public void testRelayout_captionFrameChanged_insetsReapplied() { final Display defaultDisplay = mock(Display.class); doReturn(defaultDisplay).when(mMockDisplayController) .getDisplay(Display.DEFAULT_DISPLAY); mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, captionBar()).setVisible(true); final WindowContainerToken token = new MockToken().token(); final TestRunningTaskInfoBuilder builder = new TestRunningTaskInfoBuilder() .setDisplayId(Display.DEFAULT_DISPLAY) .setVisible(true); mRelayoutParams.mIsCaptionVisible = true; // Relayout twice with different caption heights. final ActivityManager.RunningTaskInfo firstTaskInfo = builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build(); final TestWindowDecoration windowDecor = createWindowDecoration(firstTaskInfo); mRelayoutParams.mCaptionHeightCalculator = (context, display) -> 80; windowDecor.relayout(firstTaskInfo, true /* hasGlobalFocus */); final ActivityManager.RunningTaskInfo secondTaskInfo = builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build(); mRelayoutParams.mCaptionHeightCalculator = (context, display) -> 100; windowDecor.relayout(secondTaskInfo, true /* hasGlobalFocus */); // Insets should be applied twice. verifyAddedInsets(2 /* times */, token, 0 /* index */, captionBar()); verifyAddedInsets(2 /* times */, token, 0 /* index */, mandatorySystemGestures()); } @Test public void testRelayout_captionFrameUnchanged_insetsNotApplied() { final Display defaultDisplay = mock(Display.class); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +9 −10 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSuppl import com.android.wm.shell.windowdecor.extension.InsetsStateKt; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.BiFunction; import java.util.function.Supplier; Loading Loading @@ -483,29 +484,26 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> // Caption bounding rectangles: these are optional, and are used to present finer // insets than traditional |Insets| to apps about where their content is occluded. // These are also in absolute coordinates. final Rect[] boundingRects; final List<Rect> boundingRects = new ArrayList<>(); final int numOfElements = params.mOccludingCaptionElements.size(); if (numOfElements == 0) { boundingRects = null; } else { if (numOfElements != 0) { // The customizable region can at most be equal to the caption bar. if (params.hasInputFeatureSpy()) { outResult.mCustomizableCaptionRegion.set(captionInsetsRect); } final Resources resources = mDecorWindowContext.getResources(); boundingRects = new Rect[numOfElements]; for (int i = 0; i < numOfElements; i++) { final OccludingCaptionElement element = params.mOccludingCaptionElements.get(i); final int elementWidthPx = resources.getDimensionPixelSize(element.mWidthResId); boundingRects[i] = final Rect boundingRect = calculateBoundingRectLocal(element, elementWidthPx, captionInsetsRect); boundingRects.add(boundingRect); // Subtract the regions used by the caption elements, the rest is // customizable. if (params.hasInputFeatureSpy()) { outResult.mCustomizableCaptionRegion.op(boundingRects[i], Region.Op.DIFFERENCE); outResult.mCustomizableCaptionRegion.op(boundingRect, Region.Op.DIFFERENCE); } } } Loading Loading @@ -889,8 +887,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final Rect captionInsets = new Rect(0, 0, 0, captionHeight); final WindowDecorationInsets newInsets = new WindowDecorationInsets(mTaskInfo.token, mOwner, captionInsets, null /* taskFrame */, null /* boundingRects */, 0 /* flags */, true /* shouldAddCaptionInset */, false /* excludedFromAppBounds */); mOwner, captionInsets, null /* taskFrame */, Collections.emptyList() /* boundingRects */, 0 /* flags */, true /* shouldAddCaptionInset */, false /* excludedFromAppBounds */); if (!newInsets.equals(mWindowDecorationInsets)) { mWindowDecorationInsets = newInsets; mWindowDecorationInsets.update(wct); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorationInsets.kt +97 −48 Original line number Diff line number Diff line Loading @@ -23,31 +23,71 @@ import android.view.InsetsSource import android.view.WindowInsets import android.window.WindowContainerToken import android.window.WindowContainerTransaction import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_WINDOW_DECORATION /** Adds, removes, and updates caption insets. */ data class WindowDecorationInsets( data class WindowDecorationInsets private constructor( private val token: WindowContainerToken, private val owner: Binder, private val frame: Rect, private val taskFrame: Rect? = null, private val boundingRects: Array<Rect>? = null, private val frame: Frame, private val boundingRects: List<Rect> = emptyList(), @InsetsSource.Flags private val flags: Int = 0, private val shouldAddCaptionInset: Boolean = false, private val excludedFromAppBounds: Boolean = false, private val appBoundsExclusion: AppBoundsExclusion? = null, ) { private sealed class Frame { abstract val height: Int data class Absolute(val rect: Rect, override val height: Int = rect.height()) : Frame() data class Relative(override val height: Int) : Frame() } private data class AppBoundsExclusion(val taskFrame: Rect) constructor( token: WindowContainerToken, owner: Binder, frame: Rect, taskFrame: Rect? = null, boundingRects: List<Rect> = emptyList(), @InsetsSource.Flags flags: Int = 0, shouldAddCaptionInset: Boolean = false, excludedFromAppBounds: Boolean = false, ) : this( token, owner, if (Flags.relativeInsets()) { Frame.Relative(frame.height()) } else { Frame.Absolute(frame) }, boundingRects, flags, shouldAddCaptionInset, if (excludedFromAppBounds) AppBoundsExclusion(checkNotNull(taskFrame)) else null ) /** Updates the caption insets. */ fun update(wct: WindowContainerTransaction) { if (!shouldAddCaptionInset) return if (com.android.window.flags.Flags.relativeInsets()) { val insets = Insets.of(0, frame.height(), 0, 0) logD( "update insets for wc=%s with frame=%s, rects=%s, appBoundsExclusion=%s", token, frame, boundingRects, appBoundsExclusion, ) val rects = if (boundingRects.isEmpty()) null else boundingRects.toTypedArray() when (frame) { is Frame.Absolute -> { wct.addInsetsSource( token, owner, INDEX, WindowInsets.Type.captionBar(), insets, boundingRects, frame.rect, rects, flags, ) wct.addInsetsSource( Loading @@ -55,18 +95,20 @@ data class WindowDecorationInsets( owner, INDEX, WindowInsets.Type.mandatorySystemGestures(), insets, boundingRects, frame.rect, rects, /* flags= */ 0, ) } else { } is Frame.Relative -> { val insets = Insets.of(0, frame.height, 0, 0) wct.addInsetsSource( token, owner, INDEX, WindowInsets.Type.captionBar(), frame, boundingRects, insets, rects, flags, ) wct.addInsetsSource( Loading @@ -74,14 +116,16 @@ data class WindowDecorationInsets( owner, INDEX, WindowInsets.Type.mandatorySystemGestures(), frame, boundingRects, insets, rects, /* flags= */ 0, ) } if (excludedFromAppBounds) { val appBounds = Rect(taskFrame) appBounds.top += frame.height() } appBoundsExclusion?.let { exclusion -> val appBounds = Rect(exclusion.taskFrame).apply { top += frame.height } wct.setAppBounds(token, appBounds) } } Loading @@ -95,12 +139,17 @@ data class WindowDecorationInsets( INDEX, WindowInsets.Type.mandatorySystemGestures() ) if (excludedFromAppBounds) { appBoundsExclusion?.let { wct.setAppBounds(token, Rect()) } } private fun logD(msg: String, vararg arguments: Any?) { ProtoLog.d(WM_SHELL_WINDOW_DECORATION, "%s: $msg", TAG, *arguments) } companion object { private const val TAG = "WindowDecorationInsets" /** Index for caption insets source. */ private const val INDEX = 0 } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/caption/CaptionController.kt +10 −17 Original line number Diff line number Diff line Loading @@ -355,7 +355,7 @@ abstract class CaptionController<T>( owner = insetsOwner, frame = captionInsets, taskFrame = null, boundingRects = null, boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false Loading Loading @@ -388,35 +388,28 @@ abstract class CaptionController<T>( // These are also in absolute coordinates. val numOfElements = params.occludingCaptionElements.size val customizableCaptionRegion = Region.obtain() val boundingRects: Array<Rect>? if (numOfElements == 0) { boundingRects = null } else { val boundingRects = mutableListOf<Rect>() if (numOfElements != 0) { // The customizable region can at most be equal to the caption bar. if (params.hasInputFeatureSpy()) { customizableCaptionRegion.set(captionInsetsRect) } val resources = decorWindowContext.resources boundingRects = Array(numOfElements) { Rect() } for (i in 0 until numOfElements) { val element = params.occludingCaptionElements[i] val elementWidthPx = resources.getDimensionPixelSize(element.widthResId) boundingRects[i].set( calculateBoundingRectLocal( val boundingRect = calculateBoundingRectLocal( element, elementWidthPx, captionInsetsRect, decorWindowContext ) ) boundingRects.add(boundingRect) // Subtract the regions used by the caption elements, the rest is // customizable. if (params.hasInputFeatureSpy()) { customizableCaptionRegion.op( boundingRects[i], Region.Op.DIFFERENCE ) customizableCaptionRegion.op(boundingRect, Region.Op.DIFFERENCE) } } } Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationInsetsTest.kt 0 → 100644 +157 −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 com.android.wm.shell.windowdecor import android.graphics.Rect import android.os.Binder import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.window.WindowContainerToken import androidx.test.filters.SmallTest import com.android.window.flags.Flags import com.google.common.truth.Truth.assertThat import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock /** * Tests for [WindowDecorationInsets]. * * Build/Install/Run: * atest WMShellUnitTests:WindowDecorationInsetsTest */ @SmallTest @RunWith(AndroidTestingRunner::class) class WindowDecorationInsetsTest { @JvmField @Rule val setFlagsRule = SetFlagsRule() private val token: WindowContainerToken = mock() private val owner = Binder() @Test fun `equals`() { val frame = Rect(0, 0, 1000, 80) val taskFrame = Rect(0, 0, 1000, 600) val insets1 = WindowDecorationInsets( token = token, owner = owner, frame = frame, taskFrame = taskFrame, boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) val insets2 = WindowDecorationInsets( token = token, owner = owner, frame = frame, taskFrame = taskFrame, boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) assertThat(insets1).isEqualTo(insets2) } @Test fun `equals with bounding rects`() { val frame = Rect(0, 0, 1000, 80) val taskFrame = Rect(0, 0, 1000, 600) val rects = listOf(Rect(0, 0, 300, 80), Rect(800, 0, 1000, 80)) val insets1 = WindowDecorationInsets( token = token, owner = owner, frame = frame, taskFrame = taskFrame, boundingRects = rects, flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) val insets2 = WindowDecorationInsets( token = token, owner = owner, frame = frame, taskFrame = taskFrame, boundingRects = rects, flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) assertThat(insets1).isEqualTo(insets2) } @Test @EnableFlags(Flags.FLAG_RELATIVE_INSETS) fun `equals with different frame but same height`() { val taskFrame = Rect(0, 0, 1000, 600) val insets1 = WindowDecorationInsets( token = token, owner = owner, frame = Rect(0, 0, 1000, 80), taskFrame = taskFrame, boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) val insets2 = WindowDecorationInsets( token = token, owner = owner, frame = Rect(100, 0, 1000, 80), taskFrame = taskFrame, boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) assertThat(insets1).isEqualTo(insets2) } @Test fun `equals with different task frame but no app bounds exclusion`() { val frame = Rect(0, 0, 1000, 80) val insets1 = WindowDecorationInsets( token = token, owner = owner, frame = frame, taskFrame = Rect(0, 0, 1000, 600), boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) val insets2 = WindowDecorationInsets( token = token, owner = owner, frame = frame, taskFrame = Rect(10, 0, 1010, 600), boundingRects = emptyList(), flags = 0, shouldAddCaptionInset = true, excludedFromAppBounds = false ) assertThat(insets1).isEqualTo(insets2) } }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +33 −1 Original line number Diff line number Diff line Loading @@ -62,6 +62,8 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.LocaleList; import android.os.Looper; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.UsesFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.util.DisplayMetrics; Loading Loading @@ -907,7 +909,8 @@ public class WindowDecorationTests extends ShellTestCase { } @Test public void testRelayout_captionFrameChanged_insetsReapplied() { @DisableFlags(Flags.FLAG_RELATIVE_INSETS) public void testRelayout_taskFrameChanged_insetsReapplied() { final Display defaultDisplay = mock(Display.class); doReturn(defaultDisplay).when(mMockDisplayController) .getDisplay(Display.DEFAULT_DISPLAY); Loading @@ -932,6 +935,35 @@ public class WindowDecorationTests extends ShellTestCase { verifyAddedInsets(2 /* times */, token, 0 /* index */, mandatorySystemGestures()); } @Test @EnableFlags(Flags.FLAG_RELATIVE_INSETS) public void testRelayout_captionFrameChanged_insetsReapplied() { final Display defaultDisplay = mock(Display.class); doReturn(defaultDisplay).when(mMockDisplayController) .getDisplay(Display.DEFAULT_DISPLAY); mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, captionBar()).setVisible(true); final WindowContainerToken token = new MockToken().token(); final TestRunningTaskInfoBuilder builder = new TestRunningTaskInfoBuilder() .setDisplayId(Display.DEFAULT_DISPLAY) .setVisible(true); mRelayoutParams.mIsCaptionVisible = true; // Relayout twice with different caption heights. final ActivityManager.RunningTaskInfo firstTaskInfo = builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build(); final TestWindowDecoration windowDecor = createWindowDecoration(firstTaskInfo); mRelayoutParams.mCaptionHeightCalculator = (context, display) -> 80; windowDecor.relayout(firstTaskInfo, true /* hasGlobalFocus */); final ActivityManager.RunningTaskInfo secondTaskInfo = builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build(); mRelayoutParams.mCaptionHeightCalculator = (context, display) -> 100; windowDecor.relayout(secondTaskInfo, true /* hasGlobalFocus */); // Insets should be applied twice. verifyAddedInsets(2 /* times */, token, 0 /* index */, captionBar()); verifyAddedInsets(2 /* times */, token, 0 /* index */, mandatorySystemGestures()); } @Test public void testRelayout_captionFrameUnchanged_insetsNotApplied() { final Display defaultDisplay = mock(Display.class); Loading