diff options
Diffstat (limited to 'libs')
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 |