summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java159
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java40
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java159
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java43
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java16
-rw-r--r--libs/hwui/Android.bp2
-rw-r--r--libs/hwui/WebViewFunctorManager.cpp19
-rw-r--r--libs/hwui/WebViewFunctorManager.h3
-rw-r--r--libs/hwui/platform/android/thread/CommonPoolBase.h57
-rw-r--r--libs/hwui/platform/host/thread/CommonPoolBase.h56
-rw-r--r--libs/hwui/private/hwui/WebViewFunctor.h5
-rw-r--r--libs/hwui/thread/CommonPool.cpp27
-rw-r--r--libs/hwui/thread/CommonPool.h13
22 files changed, 587 insertions, 141 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
new file mode 100644
index 000000000000..100185b84b77
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2024 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 androidx.window.extensions.embedding;
+
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+
+import static androidx.window.extensions.embedding.DividerAttributes.RATIO_UNSET;
+import static androidx.window.extensions.embedding.DividerAttributes.WIDTH_UNSET;
+import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_BOTTOM;
+import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_LEFT;
+import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_RIGHT;
+import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_TOP;
+
+import android.annotation.Nullable;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.util.TypedValue;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
+
+/**
+ * Manages the rendering and interaction of the divider.
+ */
+class DividerPresenter {
+ // TODO(b/327067596) Update based on UX guidance.
+ @VisibleForTesting static final float DEFAULT_MIN_RATIO = 0.35f;
+ @VisibleForTesting static final float DEFAULT_MAX_RATIO = 0.65f;
+ @VisibleForTesting static final int DEFAULT_DIVIDER_WIDTH_DP = 24;
+
+ static int getDividerWidthPx(@NonNull DividerAttributes dividerAttributes) {
+ int dividerWidthDp = dividerAttributes.getWidthDp();
+
+ // TODO(b/329193115) support divider on secondary display
+ final Context applicationContext = ActivityThread.currentActivityThread().getApplication();
+
+ return (int) TypedValue.applyDimension(
+ COMPLEX_UNIT_DIP,
+ dividerWidthDp,
+ applicationContext.getResources().getDisplayMetrics());
+ }
+
+ /**
+ * Returns the container bound offset that is a result of the presence of a divider.
+ *
+ * The offset is the relative position change for the container edge that is next to the divider
+ * due to the presence of the divider. The value could be negative or positive depending on the
+ * container position. Positive values indicate that the edge is shifting towards the right
+ * (or bottom) and negative values indicate that the edge is shifting towards the left (or top).
+ *
+ * @param splitAttributes the {@link SplitAttributes} of the split container that we want to
+ * compute bounds offset.
+ * @param position the position of the container in the split that we want to compute
+ * bounds offset for.
+ * @return the bounds offset in pixels.
+ */
+ static int getBoundsOffsetForDivider(
+ @NonNull SplitAttributes splitAttributes,
+ @SplitPresenter.ContainerPosition int position) {
+ if (!Flags.activityEmbeddingInteractiveDividerFlag()) {
+ return 0;
+ }
+ final DividerAttributes dividerAttributes = splitAttributes.getDividerAttributes();
+ if (dividerAttributes == null) {
+ return 0;
+ }
+ final int dividerWidthPx = getDividerWidthPx(dividerAttributes);
+ return getBoundsOffsetForDivider(
+ dividerWidthPx,
+ splitAttributes.getSplitType(),
+ position);
+ }
+
+ @VisibleForTesting
+ static int getBoundsOffsetForDivider(
+ int dividerWidthPx,
+ @NonNull SplitAttributes.SplitType splitType,
+ @SplitPresenter.ContainerPosition int position) {
+ if (splitType instanceof SplitAttributes.SplitType.ExpandContainersSplitType) {
+ // No divider is needed for the ExpandContainersSplitType.
+ return 0;
+ }
+ int primaryOffset;
+ if (splitType instanceof final SplitAttributes.SplitType.RatioSplitType splitRatio) {
+ // When a divider is present, both containers shrink by an amount proportional to their
+ // split ratio and sum to the width of the divider, so that the ending sizing of the
+ // containers still maintain the same ratio.
+ primaryOffset = (int) (dividerWidthPx * splitRatio.getRatio());
+ } else {
+ // Hinge split type (and other future split types) will have the divider width equally
+ // distributed to both containers.
+ primaryOffset = dividerWidthPx / 2;
+ }
+ final int secondaryOffset = dividerWidthPx - primaryOffset;
+ switch (position) {
+ case CONTAINER_POSITION_LEFT:
+ case CONTAINER_POSITION_TOP:
+ return -primaryOffset;
+ case CONTAINER_POSITION_RIGHT:
+ case CONTAINER_POSITION_BOTTOM:
+ return secondaryOffset;
+ default:
+ throw new IllegalArgumentException("Unknown position:" + position);
+ }
+ }
+
+ /**
+ * Sanitizes and sets default values in the {@link DividerAttributes}.
+ *
+ * Unset values will be set with system default values. See
+ * {@link DividerAttributes#WIDTH_UNSET} and {@link DividerAttributes#RATIO_UNSET}.
+ *
+ * @param dividerAttributes input {@link DividerAttributes}
+ * @return a {@link DividerAttributes} that has all values properly set.
+ */
+ @Nullable
+ static DividerAttributes sanitizeDividerAttributes(
+ @Nullable DividerAttributes dividerAttributes) {
+ if (dividerAttributes == null) {
+ return null;
+ }
+ int widthDp = dividerAttributes.getWidthDp();
+ if (widthDp == WIDTH_UNSET) {
+ widthDp = DEFAULT_DIVIDER_WIDTH_DP;
+ }
+
+ float minRatio = dividerAttributes.getPrimaryMinRatio();
+ if (minRatio == RATIO_UNSET) {
+ minRatio = DEFAULT_MIN_RATIO;
+ }
+
+ float maxRatio = dividerAttributes.getPrimaryMaxRatio();
+ if (maxRatio == RATIO_UNSET) {
+ maxRatio = DEFAULT_MAX_RATIO;
+ }
+
+ return new DividerAttributes.Builder(dividerAttributes)
+ .setWidthDp(widthDp)
+ .setPrimaryMinRatio(minRatio)
+ .setPrimaryMaxRatio(maxRatio)
+ .build();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index b53b9c519cb6..f680694c3af9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -18,6 +18,7 @@ package androidx.window.extensions.embedding;
import static android.content.pm.PackageManager.MATCH_ALL;
+import static androidx.window.extensions.embedding.DividerPresenter.getBoundsOffsetForDivider;
import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK;
import android.app.Activity;
@@ -85,10 +86,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
})
private @interface Position {}
- private static final int CONTAINER_POSITION_LEFT = 0;
- private static final int CONTAINER_POSITION_TOP = 1;
- private static final int CONTAINER_POSITION_RIGHT = 2;
- private static final int CONTAINER_POSITION_BOTTOM = 3;
+ static final int CONTAINER_POSITION_LEFT = 0;
+ static final int CONTAINER_POSITION_TOP = 1;
+ static final int CONTAINER_POSITION_RIGHT = 2;
+ static final int CONTAINER_POSITION_BOTTOM = 3;
@IntDef(value = {
CONTAINER_POSITION_LEFT,
@@ -96,7 +97,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
CONTAINER_POSITION_RIGHT,
CONTAINER_POSITION_BOTTOM,
})
- private @interface ContainerPosition {}
+ @interface ContainerPosition {}
/**
* Result of {@link #expandSplitContainerIfNeeded(WindowContainerTransaction, SplitContainer,
@@ -738,6 +739,15 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
private SplitAttributes sanitizeSplitAttributes(@NonNull TaskProperties taskProperties,
@NonNull SplitAttributes splitAttributes,
@Nullable Pair<Size, Size> minDimensionsPair) {
+ // Sanitize the DividerAttributes and set default values.
+ if (splitAttributes.getDividerAttributes() != null) {
+ splitAttributes = new SplitAttributes.Builder(splitAttributes)
+ .setDividerAttributes(
+ DividerPresenter.sanitizeDividerAttributes(
+ splitAttributes.getDividerAttributes())
+ ).build();
+ }
+
if (minDimensionsPair == null) {
return splitAttributes;
}
@@ -930,18 +940,18 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
*/
private static SplitAttributes updateSplitAttributesType(
@NonNull SplitAttributes splitAttributes, @NonNull SplitType splitTypeToUpdate) {
- return new SplitAttributes.Builder()
+ return new SplitAttributes.Builder(splitAttributes)
.setSplitType(splitTypeToUpdate)
- .setLayoutDirection(splitAttributes.getLayoutDirection())
- .setAnimationBackground(splitAttributes.getAnimationBackground())
.build();
}
@NonNull
private Rect getLeftContainerBounds(@NonNull Configuration taskConfiguration,
@NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) {
+ final int dividerOffset = getBoundsOffsetForDivider(
+ splitAttributes, CONTAINER_POSITION_LEFT);
final int right = computeBoundaryBetweenContainers(taskConfiguration, splitAttributes,
- CONTAINER_POSITION_LEFT, foldingFeature);
+ CONTAINER_POSITION_LEFT, foldingFeature) + dividerOffset;
final Rect taskBounds = taskConfiguration.windowConfiguration.getBounds();
return new Rect(taskBounds.left, taskBounds.top, right, taskBounds.bottom);
}
@@ -949,8 +959,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull
private Rect getRightContainerBounds(@NonNull Configuration taskConfiguration,
@NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) {
+ final int dividerOffset = getBoundsOffsetForDivider(
+ splitAttributes, CONTAINER_POSITION_RIGHT);
final int left = computeBoundaryBetweenContainers(taskConfiguration, splitAttributes,
- CONTAINER_POSITION_RIGHT, foldingFeature);
+ CONTAINER_POSITION_RIGHT, foldingFeature) + dividerOffset;
final Rect parentBounds = taskConfiguration.windowConfiguration.getBounds();
return new Rect(left, parentBounds.top, parentBounds.right, parentBounds.bottom);
}
@@ -958,8 +970,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull
private Rect getTopContainerBounds(@NonNull Configuration taskConfiguration,
@NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) {
+ final int dividerOffset = getBoundsOffsetForDivider(
+ splitAttributes, CONTAINER_POSITION_TOP);
final int bottom = computeBoundaryBetweenContainers(taskConfiguration, splitAttributes,
- CONTAINER_POSITION_TOP, foldingFeature);
+ CONTAINER_POSITION_TOP, foldingFeature) + dividerOffset;
final Rect parentBounds = taskConfiguration.windowConfiguration.getBounds();
return new Rect(parentBounds.left, parentBounds.top, parentBounds.right, bottom);
}
@@ -967,8 +981,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull
private Rect getBottomContainerBounds(@NonNull Configuration taskConfiguration,
@NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) {
+ final int dividerOffset = getBoundsOffsetForDivider(
+ splitAttributes, CONTAINER_POSITION_BOTTOM);
final int top = computeBoundaryBetweenContainers(taskConfiguration, splitAttributes,
- CONTAINER_POSITION_BOTTOM, foldingFeature);
+ CONTAINER_POSITION_BOTTOM, foldingFeature) + dividerOffset;
final Rect parentBounds = taskConfiguration.windowConfiguration.getBounds();
return new Rect(parentBounds.left, top, parentBounds.right, parentBounds.bottom);
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
new file mode 100644
index 000000000000..2a277f4c9619
--- /dev/null
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2024 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 androidx.window.extensions.embedding;
+
+import static androidx.window.extensions.embedding.DividerPresenter.getBoundsOffsetForDivider;
+import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_BOTTOM;
+import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_LEFT;
+import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_RIGHT;
+import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_TOP;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test class for {@link DividerPresenter}.
+ *
+ * Build/Install/Run:
+ * atest WMJetpackUnitTests:DividerPresenterTest
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DividerPresenterTest {
+ @Test
+ public void testSanitizeDividerAttributes_setDefaultValues() {
+ DividerAttributes attributes =
+ new DividerAttributes.Builder(DividerAttributes.DIVIDER_TYPE_DRAGGABLE).build();
+ DividerAttributes sanitized = DividerPresenter.sanitizeDividerAttributes(attributes);
+
+ assertEquals(DividerAttributes.DIVIDER_TYPE_DRAGGABLE, sanitized.getDividerType());
+ assertEquals(DividerPresenter.DEFAULT_DIVIDER_WIDTH_DP, sanitized.getWidthDp());
+ assertEquals(DividerPresenter.DEFAULT_MIN_RATIO, sanitized.getPrimaryMinRatio(),
+ 0.0f /* delta */);
+ assertEquals(DividerPresenter.DEFAULT_MAX_RATIO, sanitized.getPrimaryMaxRatio(),
+ 0.0f /* delta */);
+ }
+
+ @Test
+ public void testSanitizeDividerAttributes_notChangingValidValues() {
+ DividerAttributes attributes =
+ new DividerAttributes.Builder(DividerAttributes.DIVIDER_TYPE_DRAGGABLE)
+ .setWidthDp(10)
+ .setPrimaryMinRatio(0.3f)
+ .setPrimaryMaxRatio(0.7f)
+ .build();
+ DividerAttributes sanitized = DividerPresenter.sanitizeDividerAttributes(attributes);
+
+ assertEquals(attributes, sanitized);
+ }
+
+ @Test
+ public void testGetBoundsOffsetForDivider_ratioSplitType() {
+ final int dividerWidthPx = 100;
+ final float splitRatio = 0.25f;
+ final SplitAttributes.SplitType splitType =
+ new SplitAttributes.SplitType.RatioSplitType(splitRatio);
+ final int expectedTopLeftOffset = 25;
+ final int expectedBottomRightOffset = 75;
+
+ assertDividerOffsetEquals(
+ dividerWidthPx, splitType, expectedTopLeftOffset, expectedBottomRightOffset);
+ }
+
+ @Test
+ public void testGetBoundsOffsetForDivider_ratioSplitType_withRounding() {
+ final int dividerWidthPx = 101;
+ final float splitRatio = 0.25f;
+ final SplitAttributes.SplitType splitType =
+ new SplitAttributes.SplitType.RatioSplitType(splitRatio);
+ final int expectedTopLeftOffset = 25;
+ final int expectedBottomRightOffset = 76;
+
+ assertDividerOffsetEquals(
+ dividerWidthPx, splitType, expectedTopLeftOffset, expectedBottomRightOffset);
+ }
+
+ @Test
+ public void testGetBoundsOffsetForDivider_hingeSplitType() {
+ final int dividerWidthPx = 100;
+ final SplitAttributes.SplitType splitType =
+ new SplitAttributes.SplitType.HingeSplitType(
+ new SplitAttributes.SplitType.RatioSplitType(0.5f));
+
+ final int expectedTopLeftOffset = 50;
+ final int expectedBottomRightOffset = 50;
+
+ assertDividerOffsetEquals(
+ dividerWidthPx, splitType, expectedTopLeftOffset, expectedBottomRightOffset);
+ }
+
+ @Test
+ public void testGetBoundsOffsetForDivider_expandContainersSplitType() {
+ final int dividerWidthPx = 100;
+ final SplitAttributes.SplitType splitType =
+ new SplitAttributes.SplitType.ExpandContainersSplitType();
+ // Always return 0 for ExpandContainersSplitType as divider is not needed.
+ final int expectedTopLeftOffset = 0;
+ final int expectedBottomRightOffset = 0;
+
+ assertDividerOffsetEquals(
+ dividerWidthPx, splitType, expectedTopLeftOffset, expectedBottomRightOffset);
+ }
+
+ private void assertDividerOffsetEquals(
+ int dividerWidthPx,
+ @NonNull SplitAttributes.SplitType splitType,
+ int expectedTopLeftOffset,
+ int expectedBottomRightOffset) {
+ int offset = getBoundsOffsetForDivider(
+ dividerWidthPx,
+ splitType,
+ CONTAINER_POSITION_LEFT
+ );
+ assertEquals(-expectedTopLeftOffset, offset);
+
+ offset = getBoundsOffsetForDivider(
+ dividerWidthPx,
+ splitType,
+ CONTAINER_POSITION_RIGHT
+ );
+ assertEquals(expectedBottomRightOffset, offset);
+
+ offset = getBoundsOffsetForDivider(
+ dividerWidthPx,
+ splitType,
+ CONTAINER_POSITION_TOP
+ );
+ assertEquals(-expectedTopLeftOffset, offset);
+
+ offset = getBoundsOffsetForDivider(
+ dividerWidthPx,
+ splitType,
+ CONTAINER_POSITION_BOTTOM
+ );
+ assertEquals(expectedBottomRightOffset, offset);
+ }
+}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index bdeeb7304b12..cdb37acfc0c2 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -294,7 +294,10 @@ public class SplitControllerTest {
doReturn(tf).when(splitContainer).getPrimaryContainer();
doReturn(tf).when(splitContainer).getSecondaryContainer();
doReturn(createTestTaskContainer()).when(splitContainer).getTaskContainer();
- doReturn(createSplitRule(mActivity, mActivity)).when(splitContainer).getSplitRule();
+ final SplitRule splitRule = createSplitRule(mActivity, mActivity);
+ doReturn(splitRule).when(splitContainer).getSplitRule();
+ doReturn(splitRule.getDefaultSplitAttributes())
+ .when(splitContainer).getDefaultSplitAttributes();
taskContainer = mSplitController.getTaskContainer(TASK_ID);
taskContainer.addSplitContainer(splitContainer);
// Add a mock SplitContainer on top of splitContainer
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index 6a5f785504c0..42de401d9db9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -24,6 +24,7 @@ import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
import android.animation.ArgbEvaluator;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -74,7 +75,7 @@ public class BubbleFlyoutView extends FrameLayout {
private final int mFlyoutElevation;
private final int mBubbleElevation;
- private final int mFloatingBackgroundColor;
+ private int mFloatingBackgroundColor;
private final float mCornerRadius;
private final ViewGroup mFlyoutTextContainer;
@@ -107,6 +108,9 @@ public class BubbleFlyoutView extends FrameLayout {
/** Color of the 'new' dot that the flyout will transform into. */
private int mDotColor;
+ /** Keeps last used night mode flags **/
+ private int mNightModeFlags;
+
/** The outline of the triangle, used for elevation shadows. */
private final Outline mTriangleOutline = new Outline();
@@ -176,11 +180,8 @@ public class BubbleFlyoutView extends FrameLayout {
mFlyoutElevation = res.getDimensionPixelSize(R.dimen.bubble_flyout_elevation);
final TypedArray ta = mContext.obtainStyledAttributes(
- new int[] {
- com.android.internal.R.attr.materialColorSurfaceContainer,
- android.R.attr.dialogCornerRadius});
- mFloatingBackgroundColor = ta.getColor(0, Color.WHITE);
- mCornerRadius = ta.getDimensionPixelSize(1, 0);
+ new int[] {android.R.attr.dialogCornerRadius});
+ mCornerRadius = ta.getDimensionPixelSize(0, 0);
ta.recycle();
// Add padding for the pointer on either side, onDraw will draw it in this space.
@@ -198,19 +199,17 @@ public class BubbleFlyoutView extends FrameLayout {
// Use locale direction so the text is aligned correctly.
setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
- mBgPaint.setColor(mFloatingBackgroundColor);
-
mLeftTriangleShape =
new ShapeDrawable(TriangleShape.createHorizontal(
mPointerSize, mPointerSize, true /* isPointingLeft */));
mLeftTriangleShape.setBounds(0, 0, mPointerSize, mPointerSize);
- mLeftTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
mRightTriangleShape =
new ShapeDrawable(TriangleShape.createHorizontal(
mPointerSize, mPointerSize, false /* isPointingLeft */));
mRightTriangleShape.setBounds(0, 0, mPointerSize, mPointerSize);
- mRightTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+
+ applyConfigurationColors(getResources().getConfiguration());
}
@Override
@@ -244,6 +243,13 @@ public class BubbleFlyoutView extends FrameLayout {
fade(false /* in */, stackPos, hideDot, afterFadeOut);
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ if (applyColorsAccordingToConfiguration(newConfig)) {
+ invalidate();
+ }
+ }
+
/*
* Fade-out above or fade-in from below.
*/
@@ -424,6 +430,42 @@ public class BubbleFlyoutView extends FrameLayout {
}
/**
+ * Resolving and applying colors according to the ui mode, remembering most recent mode.
+ *
+ * @return {@code true} if night mode setting has changed since the last invocation,
+ * {@code false} otherwise
+ */
+ boolean applyColorsAccordingToConfiguration(Configuration configuration) {
+ int nightModeFlags = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ boolean flagsChanged = nightModeFlags != mNightModeFlags;
+ if (flagsChanged) {
+ mNightModeFlags = nightModeFlags;
+ applyConfigurationColors(configuration);
+ }
+ return flagsChanged;
+ }
+
+ private void applyConfigurationColors(Configuration configuration) {
+ int nightModeFlags = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ boolean isNightModeOn = nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
+ try (TypedArray ta = mContext.obtainStyledAttributes(
+ new int[]{
+ com.android.internal.R.attr.materialColorSurfaceContainer,
+ com.android.internal.R.attr.materialColorOnSurface,
+ com.android.internal.R.attr.materialColorOnSurfaceVariant})) {
+ mFloatingBackgroundColor = ta.getColor(0,
+ isNightModeOn ? Color.BLACK : Color.WHITE);
+ mSenderText.setTextColor(ta.getColor(1,
+ isNightModeOn ? Color.WHITE : Color.BLACK));
+ mMessageText.setTextColor(ta.getColor(2,
+ isNightModeOn ? Color.WHITE : Color.BLACK));
+ mBgPaint.setColor(mFloatingBackgroundColor);
+ mLeftTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+ mRightTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+ }
+ }
+
+ /**
* Renders the background, which is either the rounded 'chat bubble' flyout, or some state
* between that and the 'new' dot over the bubbles.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 6524c96fb21a..690868208b91 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1025,6 +1025,7 @@ public class BubbleStackView extends FrameLayout
WindowManager.class)));
onDisplaySizeChanged();
mExpandedAnimationController.updateResources();
+ mExpandedAnimationController.onOrientationChanged();
mStackAnimationController.updateResources();
mBubbleOverflow.updateResources();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index 7798aa753aa2..512c9d133d08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -614,6 +614,14 @@ public class ExpandedAnimationController
}
}
+ /**
+ * Call to update the bubble positions after an orientation change.
+ */
+ public void onOrientationChanged() {
+ if (mLayout == null) return;
+ updateBubblePositions();
+ }
+
private void updateBubblePositions() {
if (mAnimatingExpand || mAnimatingCollapse) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java
index ed00da848a14..bfddff0f72e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java
@@ -378,6 +378,8 @@ public class PhysicsAnimationLayout extends FrameLayout {
}
final int oldIndex = indexOfChild(view);
+ if (oldIndex == index) return;
+
super.removeView(view);
if (view.getParent() != null) {
// View still has a parent. This could have been added as a transient view.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
index 8826141fb406..31214eba8dd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
@@ -17,6 +17,8 @@ package com.android.wm.shell.draganddrop
import android.app.ActivityManager
import android.os.RemoteException
+import android.os.Trace
+import android.os.Trace.TRACE_TAG_WINDOW_MANAGER
import android.util.Log
import android.view.DragEvent
import android.view.IWindowManager
@@ -27,6 +29,7 @@ import com.android.internal.protolog.common.ProtoLog
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.protolog.ShellProtoLogGroup
import java.util.function.Consumer
+import kotlin.random.Random
/**
* Manages the listener and callbacks for unhandled global drags.
@@ -101,10 +104,15 @@ class GlobalDragListener(
@VisibleForTesting
fun onUnhandledDrop(dragEvent: DragEvent, wmCallback: IUnhandledDragCallback) {
+ val traceCookie = Random.nextInt()
+ Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "GlobalDragListener.onUnhandledDrop",
+ traceCookie);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"onUnhandledDrop: %s", dragEvent)
if (callback == null) {
wmCallback.notifyUnhandledDropComplete(false)
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "GlobalDragListener.onUnhandledDrop",
+ traceCookie);
return
}
@@ -112,6 +120,8 @@ class GlobalDragListener(
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"Notifying onUnhandledDrop complete: %b", it)
wmCallback.notifyUnhandledDropComplete(it)
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "GlobalDragListener.onUnhandledDrop",
+ traceCookie);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index b99e94399046..2b433e9c4227 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -64,9 +64,7 @@ public interface SplitScreen {
default void onSplitVisibilityChanged(boolean visible) {}
}
- /**
- * Callback interface for listening to requests to enter split select. Used for desktop -> split
- */
+ /** Callback interface for listening to requests to enter split select */
interface SplitSelectListener {
default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
int splitPosition, Rect taskBounds) {
@@ -74,15 +72,6 @@ public interface SplitScreen {
}
}
- interface SplitInvocationListener {
- /**
- * Called whenever shell starts or stops the split screen animation
- * @param animationRunning if {@code true} the animation has begun, if {@code false} the
- * animation has finished
- */
- default void onSplitAnimationInvoked(boolean animationRunning) { }
- }
-
/** Registers listener that gets split screen callback. */
void registerSplitScreenListener(@NonNull SplitScreenListener listener,
@NonNull Executor executor);
@@ -90,15 +79,6 @@ public interface SplitScreen {
/** Unregisters listener that gets split screen callback. */
void unregisterSplitScreenListener(@NonNull SplitScreenListener listener);
- /**
- * Registers a {@link SplitInvocationListener} to notify when the animation to enter split
- * screen has started and stopped
- *
- * @param executor callbacks to the listener will be executed on this executor
- */
- void registerSplitAnimationListener(@NonNull SplitInvocationListener listener,
- @NonNull Executor executor);
-
/** Called when device waking up finished. */
void onFinishedWakingUp();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 5fbb15216eea..3e34c303e161 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -1144,12 +1144,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
@Override
- public void registerSplitAnimationListener(@NonNull SplitInvocationListener listener,
- @NonNull Executor executor) {
- mStageCoordinator.registerSplitAnimationListener(listener, executor);
- }
-
- @Override
public void onFinishedWakingUp() {
mMainExecutor.execute(SplitScreenController.this::onFinishedWakingUp);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index e69ff70e5273..1a53a1d10dd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -55,7 +55,6 @@ import com.android.wm.shell.transition.OneShotRemoteHandler;
import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
-import java.util.concurrent.Executor;
/** Manages transition animations for split-screen. */
class SplitScreenTransitions {
@@ -80,8 +79,6 @@ class SplitScreenTransitions {
private Transitions.TransitionFinishCallback mFinishCallback = null;
private SurfaceControl.Transaction mFinishTransaction;
- private SplitScreen.SplitInvocationListener mSplitInvocationListener;
- private Executor mSplitInvocationListenerExecutor;
SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions,
@NonNull Runnable onFinishCallback, StageCoordinator stageCoordinator) {
@@ -356,10 +353,6 @@ class SplitScreenTransitions {
+ " skip to start enter split transition since it already exist. ");
return null;
}
- if (mSplitInvocationListenerExecutor != null && mSplitInvocationListener != null) {
- mSplitInvocationListenerExecutor.execute(() -> mSplitInvocationListener
- .onSplitAnimationInvoked(true /*animationRunning*/));
- }
final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
setEnterTransition(transition, remoteTransition, extraTransitType, resizeAnim);
return transition;
@@ -536,12 +529,6 @@ class SplitScreenTransitions {
mTransitions.getAnimExecutor().execute(va::start);
}
- public void registerSplitAnimListener(@NonNull SplitScreen.SplitInvocationListener listener,
- @NonNull Executor executor) {
- mSplitInvocationListener = listener;
- mSplitInvocationListenerExecutor = executor;
- }
-
/** Calls when the transition got consumed. */
interface TransitionConsumedCallback {
void onConsumed(boolean aborted);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 661faf5b4f28..41890df9a4ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -156,7 +156,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
-import java.util.concurrent.Executor;
/**
* Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
@@ -237,9 +236,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private DefaultMixedHandler mMixedHandler;
private final Toast mSplitUnsupportedToast;
private SplitRequest mSplitRequest;
- /** Used to notify others of when shell is animating into split screen */
- private SplitScreen.SplitInvocationListener mSplitInvocationListener;
- private Executor mSplitInvocationListenerExecutor;
/**
* Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support
@@ -250,14 +246,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return false;
}
- /** NOTE: Will overwrite any previously set {@link #mSplitInvocationListener} */
- public void registerSplitAnimationListener(
- @NonNull SplitScreen.SplitInvocationListener listener, @NonNull Executor executor) {
- mSplitInvocationListener = listener;
- mSplitInvocationListenerExecutor = executor;
- mSplitTransitions.registerSplitAnimListener(listener, executor);
- }
-
class SplitRequest {
@SplitPosition
int mActivatePosition;
@@ -541,7 +529,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
null /* childrenToTop */, EXIT_REASON_UNKNOWN));
Log.w(TAG, splitFailureMessage("startShortcut",
"side stage was not populated"));
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
}
if (finishedCallback != null) {
@@ -672,7 +660,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
null /* childrenToTop */, EXIT_REASON_UNKNOWN));
Log.w(TAG, splitFailureMessage("startIntentLegacy",
"side stage was not populated"));
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
}
if (apps != null) {
@@ -1225,7 +1213,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
Log.w(TAG, splitFailureMessage("onRemoteAnimationFinishedOrCancelled",
"main or side stage was not populated."));
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
} else {
mSyncQueue.queue(evictWct);
mSyncQueue.runInSync(t -> {
@@ -1246,7 +1234,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
Log.w(TAG, splitFailureMessage("onRemoteAnimationFinished",
"main or side stage was not populated"));
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
return;
}
@@ -2818,7 +2806,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (hasEnteringPip) {
mMixedHandler.animatePendingEnterPipFromSplit(transition, info,
startTransaction, finishTransaction, finishCallback);
- notifySplitAnimationFinished();
return true;
}
@@ -2853,7 +2840,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// the transition, or synchronize task-org callbacks.
}
// Use normal animations.
- notifySplitAnimationFinished();
return false;
} else if (mMixedHandler != null && TransitionUtil.hasDisplayChange(info)) {
// A display-change has been un-expectedly inserted into the transition. Redirect
@@ -2867,7 +2853,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.update(startTransaction, true /* resetImePosition */);
startTransaction.apply();
}
- notifySplitAnimationFinished();
return true;
}
}
@@ -3041,7 +3026,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
pendingEnter.mRemoteHandler.onTransitionConsumed(transition,
false /*aborted*/, finishT);
}
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
return true;
}
}
@@ -3070,7 +3055,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final TransitionInfo.Change finalMainChild = mainChild;
final TransitionInfo.Change finalSideChild = sideChild;
enterTransition.setFinishedCallback((callbackWct, callbackT) -> {
- notifySplitAnimationFinished();
if (finalMainChild != null) {
if (!mainNotContainOpenTask) {
mMainStage.evictOtherChildren(callbackWct, finalMainChild.getTaskInfo().taskId);
@@ -3487,19 +3471,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.isLeftRightSplit());
}
- private void handleUnsupportedSplitStart() {
- mSplitUnsupportedToast.show();
- notifySplitAnimationFinished();
- }
-
- private void notifySplitAnimationFinished() {
- if (mSplitInvocationListener == null || mSplitInvocationListenerExecutor == null) {
- return;
- }
- mSplitInvocationListenerExecutor.execute(() ->
- mSplitInvocationListener.onSplitAnimationInvoked(false /*animationRunning*/));
- }
-
/**
* Logs the exit of splitscreen to a specific stage. This must be called before the exit is
* executed.
@@ -3562,7 +3533,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (!ENABLE_SHELL_TRANSITIONS) {
StageCoordinator.this.exitSplitScreen(isMainStage ? mMainStage : mSideStage,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
return;
}
@@ -3582,7 +3553,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
"app package " + taskInfo.baseActivity.getPackageName()
+ " does not support splitscreen, or is a controlled activity type"));
if (splitScreenVisible) {
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
}
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 34b2eebb15a1..befc702b01aa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -39,13 +39,10 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -66,7 +63,6 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -109,8 +105,6 @@ public class SplitTransitionTests extends ShellTestCase {
@Mock private ShellExecutor mMainExecutor;
@Mock private LaunchAdjacentController mLaunchAdjacentController;
@Mock private DefaultMixedHandler mMixedHandler;
- @Mock private SplitScreen.SplitInvocationListener mInvocationListener;
- private final TestShellExecutor mTestShellExecutor = new TestShellExecutor();
private SplitLayout mSplitLayout;
private MainStage mMainStage;
private SideStage mSideStage;
@@ -153,7 +147,6 @@ public class SplitTransitionTests extends ShellTestCase {
.setParentTaskId(mSideStage.mRootTaskInfo.taskId).build();
doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager();
doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager();
- mStageCoordinator.registerSplitAnimationListener(mInvocationListener, mTestShellExecutor);
}
@Test
@@ -459,15 +452,6 @@ public class SplitTransitionTests extends ShellTestCase {
mMainStage.activate(new WindowContainerTransaction(), true /* includingTopTask */);
}
- @Test
- @UiThreadTest
- public void testSplitInvocationCallback() {
- enterSplit();
- mTestShellExecutor.flushAll();
- verify(mInvocationListener, times(1))
- .onSplitAnimationInvoked(eq(true));
- }
-
private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) {
for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 33830f154835..014b8413bb10 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -547,6 +547,7 @@ cc_defaults {
"hwui/MinikinUtils.cpp",
"hwui/PaintImpl.cpp",
"hwui/Typeface.cpp",
+ "thread/CommonPool.cpp",
"utils/Blur.cpp",
"utils/Color.cpp",
"utils/LinearAllocator.cpp",
@@ -623,7 +624,6 @@ cc_defaults {
"renderthread/RenderThread.cpp",
"renderthread/HintSessionWrapper.cpp",
"service/GraphicsStatsService.cpp",
- "thread/CommonPool.cpp",
"utils/GLUtils.cpp",
"utils/NdkUtils.cpp",
"AutoBackendTextureRelease.cpp",
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 6fc251dc815c..5b4ab5f2d3b1 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -86,6 +86,10 @@ void WebViewFunctor_release(int functor) {
WebViewFunctorManager::instance().releaseFunctor(functor);
}
+void WebViewFunctor_reportRenderingThreads(int functor, const int32_t* thread_ids, size_t size) {
+ WebViewFunctorManager::instance().reportRenderingThreads(functor, thread_ids, size);
+}
+
static std::atomic_int sNextId{1};
WebViewFunctor::WebViewFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
@@ -260,6 +264,10 @@ void WebViewFunctor::reparentSurfaceControl(ASurfaceControl* parent) {
funcs.transactionDeleteFunc(transaction);
}
+void WebViewFunctor::reportRenderingThreads(const int32_t* thread_ids, size_t size) {
+ // TODO(b/329219352): Pass the threads to HWUI and update the ADPF session.
+}
+
WebViewFunctorManager& WebViewFunctorManager::instance() {
static WebViewFunctorManager sInstance;
return sInstance;
@@ -346,6 +354,17 @@ void WebViewFunctorManager::destroyFunctor(int functor) {
}
}
+void WebViewFunctorManager::reportRenderingThreads(int functor, const int32_t* thread_ids,
+ size_t size) {
+ std::lock_guard _lock{mLock};
+ for (auto& iter : mFunctors) {
+ if (iter->id() == functor) {
+ iter->reportRenderingThreads(thread_ids, size);
+ break;
+ }
+ }
+}
+
sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) {
std::lock_guard _lock{mLock};
for (auto& iter : mActiveFunctors) {
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index 0a02f2d4b720..1bf2c1f9c4ef 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -81,6 +81,8 @@ public:
ASurfaceControl* getSurfaceControl();
void mergeTransaction(ASurfaceTransaction* transaction);
+ void reportRenderingThreads(const int32_t* thread_ids, size_t size);
+
sp<Handle> createHandle() {
LOG_ALWAYS_FATAL_IF(mCreatedHandle);
mCreatedHandle = true;
@@ -110,6 +112,7 @@ public:
void releaseFunctor(int functor);
void onContextDestroyed();
void destroyFunctor(int functor);
+ void reportRenderingThreads(int functor, const int32_t* thread_ids, size_t size);
sp<WebViewFunctor::Handle> handleFor(int functor);
diff --git a/libs/hwui/platform/android/thread/CommonPoolBase.h b/libs/hwui/platform/android/thread/CommonPoolBase.h
new file mode 100644
index 000000000000..8f836b612440
--- /dev/null
+++ b/libs/hwui/platform/android/thread/CommonPoolBase.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#ifndef FRAMEWORKS_BASE_COMMONPOOLBASE_H
+#define FRAMEWORKS_BASE_COMMONPOOLBASE_H
+
+#include <sys/resource.h>
+
+#include "renderthread/RenderThread.h"
+
+namespace android {
+namespace uirenderer {
+
+class CommonPoolBase {
+ PREVENT_COPY_AND_ASSIGN(CommonPoolBase);
+
+protected:
+ CommonPoolBase() {}
+
+ void setupThread(int i, std::mutex& mLock, std::vector<int>& tids,
+ std::vector<std::condition_variable>& tidConditionVars) {
+ std::array<char, 20> name{"hwuiTask"};
+ snprintf(name.data(), name.size(), "hwuiTask%d", i);
+ auto self = pthread_self();
+ pthread_setname_np(self, name.data());
+ {
+ std::unique_lock lock(mLock);
+ tids[i] = pthread_gettid_np(self);
+ tidConditionVars[i].notify_one();
+ }
+ setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
+ auto startHook = renderthread::RenderThread::getOnStartHook();
+ if (startHook) {
+ startHook(name.data());
+ }
+ }
+
+ bool supportsTid() { return true; }
+};
+
+} // namespace uirenderer
+} // namespace android
+
+#endif // FRAMEWORKS_BASE_COMMONPOOLBASE_H
diff --git a/libs/hwui/platform/host/thread/CommonPoolBase.h b/libs/hwui/platform/host/thread/CommonPoolBase.h
new file mode 100644
index 000000000000..cd091013ce0c
--- /dev/null
+++ b/libs/hwui/platform/host/thread/CommonPoolBase.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#ifndef FRAMEWORKS_BASE_COMMONPOOLBASE_H
+#define FRAMEWORKS_BASE_COMMONPOOLBASE_H
+
+#include <condition_variable>
+#include <mutex>
+#include <vector>
+
+#include "renderthread/RenderThread.h"
+
+namespace android {
+namespace uirenderer {
+
+class CommonPoolBase {
+ PREVENT_COPY_AND_ASSIGN(CommonPoolBase);
+
+protected:
+ CommonPoolBase() {}
+
+ void setupThread(int i, std::mutex& mLock, std::vector<int>& tids,
+ std::vector<std::condition_variable>& tidConditionVars) {
+ std::array<char, 20> name{"hwuiTask"};
+ snprintf(name.data(), name.size(), "hwuiTask%d", i);
+ {
+ std::unique_lock lock(mLock);
+ tids[i] = -1;
+ tidConditionVars[i].notify_one();
+ }
+ auto startHook = renderthread::RenderThread::getOnStartHook();
+ if (startHook) {
+ startHook(name.data());
+ }
+ }
+
+ bool supportsTid() { return false; }
+};
+
+} // namespace uirenderer
+} // namespace android
+
+#endif // FRAMEWORKS_BASE_COMMONPOOLBASE_H
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
index 493c943079ab..dbd8a16dfcfc 100644
--- a/libs/hwui/private/hwui/WebViewFunctor.h
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -106,6 +106,11 @@ ANDROID_API int WebViewFunctor_create(void* data, const WebViewFunctorCallbacks&
// and it should be considered alive & active until that point.
ANDROID_API void WebViewFunctor_release(int functor);
+// Reports the list of threads critical for frame production for the given
+// functor. Must be called on render thread.
+ANDROID_API void WebViewFunctor_reportRenderingThreads(int functor, const int32_t* thread_ids,
+ size_t size);
+
} // namespace android::uirenderer
#endif // FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
diff --git a/libs/hwui/thread/CommonPool.cpp b/libs/hwui/thread/CommonPool.cpp
index dc92f9f0d39a..6c0c30f95955 100644
--- a/libs/hwui/thread/CommonPool.cpp
+++ b/libs/hwui/thread/CommonPool.cpp
@@ -16,16 +16,14 @@
#include "CommonPool.h"
-#include <sys/resource.h>
#include <utils/Trace.h>
-#include "renderthread/RenderThread.h"
#include <array>
namespace android {
namespace uirenderer {
-CommonPool::CommonPool() {
+CommonPool::CommonPool() : CommonPoolBase() {
ATRACE_CALL();
CommonPool* pool = this;
@@ -36,22 +34,7 @@ CommonPool::CommonPool() {
// Create 2 workers
for (int i = 0; i < THREAD_COUNT; i++) {
std::thread worker([pool, i, &mLock, &tids, &tidConditionVars] {
- {
- std::array<char, 20> name{"hwuiTask"};
- snprintf(name.data(), name.size(), "hwuiTask%d", i);
- auto self = pthread_self();
- pthread_setname_np(self, name.data());
- {
- std::unique_lock lock(mLock);
- tids[i] = pthread_gettid_np(self);
- tidConditionVars[i].notify_one();
- }
- setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
- auto startHook = renderthread::RenderThread::getOnStartHook();
- if (startHook) {
- startHook(name.data());
- }
- }
+ pool->setupThread(i, mLock, tids, tidConditionVars);
pool->workerLoop();
});
worker.detach();
@@ -64,7 +47,9 @@ CommonPool::CommonPool() {
}
}
}
- mWorkerThreadIds = std::move(tids);
+ if (pool->supportsTid()) {
+ mWorkerThreadIds = std::move(tids);
+ }
}
CommonPool& CommonPool::instance() {
@@ -95,7 +80,7 @@ void CommonPool::enqueue(Task&& task) {
void CommonPool::workerLoop() {
std::unique_lock lock(mLock);
- while (true) {
+ while (!mIsStopping) {
if (!mWorkQueue.hasWork()) {
mWaitingThreads++;
mCondition.wait(lock);
diff --git a/libs/hwui/thread/CommonPool.h b/libs/hwui/thread/CommonPool.h
index 74f852bd1413..0c025b4f0ee7 100644
--- a/libs/hwui/thread/CommonPool.h
+++ b/libs/hwui/thread/CommonPool.h
@@ -17,8 +17,6 @@
#ifndef FRAMEWORKS_BASE_COMMONPOOL_H
#define FRAMEWORKS_BASE_COMMONPOOL_H
-#include "utils/Macros.h"
-
#include <log/log.h>
#include <condition_variable>
@@ -27,6 +25,9 @@
#include <mutex>
#include <vector>
+#include "thread/CommonPoolBase.h"
+#include "utils/Macros.h"
+
namespace android {
namespace uirenderer {
@@ -73,7 +74,7 @@ private:
int mTail = 0;
};
-class CommonPool {
+class CommonPool : private CommonPoolBase {
PREVENT_COPY_AND_ASSIGN(CommonPool);
public:
@@ -107,7 +108,10 @@ private:
static CommonPool& instance();
CommonPool();
- ~CommonPool() {}
+ ~CommonPool() {
+ mIsStopping = true;
+ mCondition.notify_all();
+ }
void enqueue(Task&&);
void doWaitForIdle();
@@ -120,6 +124,7 @@ private:
std::condition_variable mCondition;
int mWaitingThreads = 0;
ArrayQueue<Task, QUEUE_SIZE> mWorkQueue;
+ std::atomic_bool mIsStopping = false;
};
} // namespace uirenderer