Merge "Simplify IMMS#updateInputMethodsFromSettingsLocked() a bit" into main
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 2d78317..73ac263 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -199,3 +199,11 @@
description: "redacts notifications on the lockscreen if they have the 'sensitiveContent' flag"
bug: "343631648"
}
+
+flag {
+ name: "api_rich_ongoing"
+ is_exported: true
+ namespace: "systemui"
+ description: "Guards new android.app.richongoingnotification api"
+ bug: "337261753"
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index 579a794..a09720d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -22,6 +22,7 @@
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
+import android.graphics.Rect
import android.os.RemoteException
import android.os.SystemProperties
import android.util.DisplayMetrics
@@ -138,6 +139,30 @@
}
}
+
+ /**
+ * Returns a fake source rect hint for animation purposes when app-provided one is invalid.
+ * Resulting adjusted source rect hint lets the app icon in the content overlay to stay visible.
+ */
+ @JvmStatic
+ fun getEnterPipWithOverlaySrcRectHint(appBounds: Rect, aspectRatio: Float): Rect {
+ val appBoundsAspRatio = appBounds.width().toFloat() / appBounds.height()
+ val width: Int
+ val height: Int
+ var left = 0
+ var top = 0
+ if (appBoundsAspRatio < aspectRatio) {
+ width = appBounds.width()
+ height = Math.round(width / aspectRatio)
+ top = (appBounds.height() - height) / 2
+ } else {
+ height = appBounds.height()
+ width = Math.round(height * aspectRatio)
+ left = (appBounds.width() - width) / 2
+ }
+ return Rect(left, top, left + width, top + height)
+ }
+
private var isPip2ExperimentEnabled: Boolean? = null
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 57c0732..0a3c15b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -40,6 +40,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.Transitions;
@@ -619,19 +620,8 @@
// This is done for entering case only.
if (isInPipDirection(direction)) {
final float aspectRatio = endValue.width() / (float) endValue.height();
- if ((startValue.width() / (float) startValue.height()) > aspectRatio) {
- // use the full height.
- adjustedSourceRectHint.set(0, 0,
- (int) (startValue.height() * aspectRatio), startValue.height());
- adjustedSourceRectHint.offset(
- (startValue.width() - adjustedSourceRectHint.width()) / 2, 0);
- } else {
- // use the full width.
- adjustedSourceRectHint.set(0, 0,
- startValue.width(), (int) (startValue.width() / aspectRatio));
- adjustedSourceRectHint.offset(
- 0, (startValue.height() - adjustedSourceRectHint.height()) / 2);
- }
+ adjustedSourceRectHint.set(PipUtils.getEnterPipWithOverlaySrcRectHint(
+ startValue, aspectRatio));
}
} else {
adjustedSourceRectHint.set(sourceRectHint);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 8c9206c..e4420d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -999,7 +999,6 @@
private void onEndOfSwipePipToHomeTransition() {
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mPipTransitionController.setEnterAnimationType(ANIM_TYPE_BOUNDS);
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index dbf18fa..3cae72d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -1083,6 +1083,8 @@
mSurfaceTransactionHelper
.crop(finishTransaction, leash, destinationBounds)
.round(finishTransaction, leash, true /* applyCornerRadius */);
+ // Always reset to bounds animation type afterwards.
+ setEnterAnimationType(ANIM_TYPE_BOUNDS);
} else {
throw new RuntimeException("Unrecognized animation type: " + enterAnimationType);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
index 3e298e5..895c2ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
@@ -19,11 +19,13 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.content.Context;
import android.view.SurfaceControl;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.wm.shell.R;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import java.lang.annotation.Retention;
@@ -45,6 +47,7 @@
public static final int FADE_IN = 0;
public static final int FADE_OUT = 1;
+ private final int mEnterAnimationDuration;
private final SurfaceControl mLeash;
private final SurfaceControl.Transaction mStartTransaction;
@@ -55,7 +58,8 @@
private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
- public PipAlphaAnimator(SurfaceControl leash,
+ public PipAlphaAnimator(Context context,
+ SurfaceControl leash,
SurfaceControl.Transaction tx,
@Fade int direction) {
mLeash = leash;
@@ -67,6 +71,9 @@
}
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+ mEnterAnimationDuration = context.getResources()
+ .getInteger(R.integer.config_pipEnterAnimationDuration);
+ setDuration(mEnterAnimationDuration);
addListener(this);
addUpdateListener(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 3e215d9..0b2db6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -293,37 +293,32 @@
return false;
}
+ SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay();
PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams;
- Rect srcRectHint = params.getSourceRectHint();
- Rect startBounds = pipChange.getStartAbsBounds();
+
+ Rect appBounds = mPipTransitionState.getSwipePipToHomeAppBounds();
Rect destinationBounds = pipChange.getEndAbsBounds();
+ float aspectRatio = pipChange.getTaskInfo().pictureInPictureParams.getAspectRatioFloat();
+
+ // We fake the source rect hint when the one prvided by the app is invalid for
+ // the animation with an app icon overlay.
+ Rect animationSrcRectHint = overlayLeash == null ? params.getSourceRectHint()
+ : PipUtils.getEnterPipWithOverlaySrcRectHint(appBounds, aspectRatio);
+
WindowContainerTransaction finishWct = new WindowContainerTransaction();
SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- if (PipBoundsAlgorithm.isSourceRectHintValidForEnterPip(srcRectHint, destinationBounds)) {
- final float scale = (float) destinationBounds.width() / srcRectHint.width();
- startTransaction.setWindowCrop(pipLeash, srcRectHint);
- startTransaction.setPosition(pipLeash,
- destinationBounds.left - srcRectHint.left * scale,
- destinationBounds.top - srcRectHint.top * scale);
+ final float scale = (float) destinationBounds.width() / animationSrcRectHint.width();
+ startTransaction.setWindowCrop(pipLeash, animationSrcRectHint);
+ startTransaction.setPosition(pipLeash,
+ destinationBounds.left - animationSrcRectHint.left * scale,
+ destinationBounds.top - animationSrcRectHint.top * scale);
+ startTransaction.setScale(pipLeash, scale, scale);
- // Reset the scale in case we are in the multi-activity case.
- // TO_FRONT transition already scales down the task in single-activity case, but
- // in multi-activity case, reparenting yields new reset scales coming from pinned task.
- startTransaction.setScale(pipLeash, scale, scale);
- } else {
- final float scaleX = (float) destinationBounds.width() / startBounds.width();
- final float scaleY = (float) destinationBounds.height() / startBounds.height();
+ if (overlayLeash != null) {
final int overlaySize = PipContentOverlay.PipAppIconOverlay.getOverlaySize(
mPipTransitionState.getSwipePipToHomeAppBounds(), destinationBounds);
- SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay();
-
- startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
- .setScale(pipLeash, scaleX, scaleY)
- .setWindowCrop(pipLeash, startBounds)
- .reparent(overlayLeash, pipLeash)
- .setLayer(overlayLeash, Integer.MAX_VALUE);
// Overlay needs to be adjusted once a new draw comes in resetting surface transform.
tx.setScale(overlayLeash, 1f, 1f);
@@ -390,15 +385,23 @@
if (pipChange == null) {
return false;
}
- // cache the PiP task token and leash
- WindowContainerToken pipTaskToken = pipChange.getContainer();
- Preconditions.checkNotNull(mPipLeash, "Leash is null for alpha transition.");
- // start transition with 0 alpha
- startTransaction.setAlpha(mPipLeash, 0f);
- PipAlphaAnimator animator = new PipAlphaAnimator(mPipLeash,
- startTransaction, PipAlphaAnimator.FADE_IN);
- animator.setAnimationEndCallback(() -> finishCallback.onTransitionFinished(null));
+ Rect destinationBounds = pipChange.getEndAbsBounds();
+ SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+ Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition.");
+
+ // Start transition with 0 alpha at the entry bounds.
+ startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
+ .setWindowCrop(pipLeash, destinationBounds.width(), destinationBounds.height())
+ .setAlpha(pipLeash, 0f);
+
+ PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction,
+ PipAlphaAnimator.FADE_IN);
+ animator.setAnimationEndCallback(() -> {
+ finishCallback.onTransitionFinished(null);
+ // This should update the pip transition state accordingly after we stop playing.
+ onClientDrawAtTransitionEnd();
+ });
animator.start();
return true;
@@ -480,10 +483,10 @@
private boolean isLegacyEnter(@NonNull TransitionInfo info) {
TransitionInfo.Change pipChange = getPipChange(info);
- // If the only change in the changes list is a TO_FRONT mode PiP task,
+ // If the only change in the changes list is a opening type PiP task,
// then this is legacy-enter PiP.
- return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT
- && info.getChanges().size() == 1;
+ return pipChange != null && info.getChanges().size() == 1
+ && (pipChange.getMode() == TRANSIT_TO_FRONT || pipChange.getMode() == TRANSIT_OPEN);
}
private boolean isRemovePipTransition(@NonNull TransitionInfo info) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 01a479f..3dcdc0b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -831,12 +831,12 @@
// We want handle to remain pressed if the pointer moves outside of it during a drag.
handle.setPressed((inHandle && action == ACTION_DOWN)
|| (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL));
- if (isHandleMenuActive() && !isMenuAboveStatusBar()) {
+ if (isHandleMenuActive() && !isHandleMenuAboveStatusBar()) {
mHandleMenu.checkMotionEvent(ev);
}
}
- private boolean isMenuAboveStatusBar() {
+ private boolean isHandleMenuAboveStatusBar() {
return Flags.enableAdditionalWindowsAboveStatusBar() && !mTaskInfo.isFreeform();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index bfc4e0d..df0836c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -24,6 +24,7 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,6 +36,7 @@
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.MotionEvent;
@@ -70,8 +72,14 @@
private final DesktopModeWindowDecoration mParentDecor;
@VisibleForTesting
AdditionalViewContainer mHandleMenuViewContainer;
+ // Position of the handle menu used for laying out the handle view.
@VisibleForTesting
final PointF mHandleMenuPosition = new PointF();
+ // With the introduction of {@link AdditionalSystemViewContainer}, {@link mHandleMenuPosition}
+ // may be in a different coordinate space than the input coordinates. Therefore, we still care
+ // about the menu's coordinates relative to the display as a whole, so we need to maintain
+ // those as well.
+ final Point mGlobalMenuPosition = new Point();
private final boolean mShouldShowWindowingPill;
private final Bitmap mAppIconBitmap;
private final CharSequence mAppName;
@@ -244,39 +252,23 @@
private void updateHandleMenuPillPositions() {
int menuX;
final int menuY;
+ final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
+ updateGlobalMenuPosition(taskBounds);
if (mLayoutResId == R.layout.desktop_mode_app_header) {
// Align the handle menu to the left side of the caption.
menuX = mMarginMenuStart;
menuY = mMarginMenuTop;
} else {
- final int handleWidth = loadDimensionPixelSize(mContext.getResources(),
- R.dimen.desktop_mode_fullscreen_decor_caption_width);
- final int handleOffset = (mMenuWidth / 2) - (handleWidth / 2);
- final int captionX = mParentDecor.getCaptionX();
- // TODO(b/343561161): This needs to be calculated differently if the task is in
- // top/bottom split.
if (Flags.enableAdditionalWindowsAboveStatusBar()) {
- final Rect leftOrTopStageBounds = new Rect();
- if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
- == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
- mSplitScreenController.getStageBounds(leftOrTopStageBounds, new Rect());
- }
// In a focused decor, we use global coordinates for handle menu. Therefore we
// need to account for other factors like split stage and menu/handle width to
// center the menu.
final DisplayLayout layout = mDisplayController
.getDisplayLayout(mTaskInfo.displayId);
- menuX = captionX + handleOffset - (layout.width() / 2);
- if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
- == SPLIT_POSITION_BOTTOM_OR_RIGHT && layout.isLandscape()) {
- // If this task in the right stage, we need to offset by left stage's width
- menuX += leftOrTopStageBounds.width();
- }
- menuY = mMarginMenuStart - ((layout.height() - mMenuHeight) / 2);
+ menuX = mGlobalMenuPosition.x + ((mMenuWidth - layout.width()) / 2);
+ menuY = mGlobalMenuPosition.y + ((mMenuHeight - layout.height()) / 2);
} else {
- final int captionWidth = mTaskInfo.getConfiguration()
- .windowConfiguration.getBounds().width();
- menuX = (captionWidth / 2) - (mMenuWidth / 2);
+ menuX = (taskBounds.width() / 2) - (mMenuWidth / 2);
menuY = mMarginMenuTop;
}
}
@@ -284,6 +276,36 @@
mHandleMenuPosition.set(menuX, menuY);
}
+ private void updateGlobalMenuPosition(Rect taskBounds) {
+ if (mTaskInfo.isFreeform()) {
+ mGlobalMenuPosition.set(taskBounds.left + mMarginMenuStart,
+ taskBounds.top + mMarginMenuTop);
+ } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ mGlobalMenuPosition.set(
+ (taskBounds.width() / 2) - (mMenuWidth / 2) + mMarginMenuStart,
+ mMarginMenuTop
+ );
+ } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+ final int splitPosition = mSplitScreenController.getSplitPosition(mTaskInfo.taskId);
+ final Rect leftOrTopStageBounds = new Rect();
+ final Rect rightOrBottomStageBounds = new Rect();
+ mSplitScreenController.getStageBounds(leftOrTopStageBounds,
+ rightOrBottomStageBounds);
+ // TODO(b/343561161): This needs to be calculated differently if the task is in
+ // top/bottom split.
+ if (splitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ mGlobalMenuPosition.set(leftOrTopStageBounds.width()
+ + (rightOrBottomStageBounds.width() / 2)
+ - (mMenuWidth / 2) + mMarginMenuStart,
+ mMarginMenuTop);
+ } else if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ mGlobalMenuPosition.set((leftOrTopStageBounds.width() / 2)
+ - (mMenuWidth / 2) + mMarginMenuStart,
+ mMarginMenuTop);
+ }
+ }
+ }
+
/**
* Update pill layout, in case task changes have caused positioning to change.
*/
@@ -302,6 +324,8 @@
* @param ev the MotionEvent to compare against.
*/
void checkMotionEvent(MotionEvent ev) {
+ // If the menu view is above status bar, we can let the views handle input directly.
+ if (isViewAboveStatusBar()) return;
final View handleMenu = mHandleMenuViewContainer.getView();
final HandleMenuImageButton collapse = handleMenu.findViewById(R.id.collapse_menu_button);
final PointF inputPoint = translateInputToLocalSpace(ev);
@@ -314,6 +338,11 @@
}
}
+ private boolean isViewAboveStatusBar() {
+ return Flags.enableAdditionalWindowsAboveStatusBar()
+ && !mTaskInfo.isFreeform();
+ }
+
// Translate the input point from display coordinates to the same space as the handle menu.
private PointF translateInputToLocalSpace(MotionEvent ev) {
return new PointF(ev.getX() - mHandleMenuPosition.x,
@@ -329,10 +358,33 @@
*/
boolean isValidMenuInput(PointF inputPoint) {
if (!viewsLaidOut()) return true;
- return pointInView(
- mHandleMenuViewContainer.getView(),
- inputPoint.x - mHandleMenuPosition.x,
- inputPoint.y - mHandleMenuPosition.y);
+ if (!isViewAboveStatusBar()) {
+ return pointInView(
+ mHandleMenuViewContainer.getView(),
+ inputPoint.x - mHandleMenuPosition.x,
+ inputPoint.y - mHandleMenuPosition.y);
+ } else {
+ // Handle menu exists in a different coordinate space when added to WindowManager.
+ // Therefore we must compare the provided input coordinates to global menu coordinates.
+ // This includes factoring for split stage as input coordinates are relative to split
+ // stage position, not relative to the display as a whole.
+ PointF inputRelativeToMenu = new PointF(
+ inputPoint.x - mGlobalMenuPosition.x,
+ inputPoint.y - mGlobalMenuPosition.y
+ );
+ if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
+ == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ // TODO(b/343561161): This also needs to be calculated differently if
+ // the task is in top/bottom split.
+ Rect leftStageBounds = new Rect();
+ mSplitScreenController.getStageBounds(leftStageBounds, new Rect());
+ inputRelativeToMenu.x += leftStageBounds.width();
+ }
+ return pointInView(
+ mHandleMenuViewContainer.getView(),
+ inputRelativeToMenu.x,
+ inputRelativeToMenu.y);
+ }
}
private boolean pointInView(View v, float x, float y) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index 9891b5b..3295dde 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.spatialaudio.ui.composable
import android.view.Gravity
+import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.basicMarquee
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
@@ -71,7 +72,8 @@
}
@Composable
- private fun Content(dialog: SystemUIDialog) {
+ @VisibleForTesting
+ fun Content(dialog: SystemUIDialog) {
val isAvailable by viewModel.isAvailable.collectAsStateWithLifecycle()
if (!isAvailable) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
index 04b930e..07d8890 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
@@ -25,12 +25,15 @@
import static org.mockito.Mockito.when;
import android.app.DreamManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.view.GestureDetector;
import android.view.MotionEvent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shared.system.InputChannelCompat;
@@ -87,7 +90,7 @@
assertThat(captured).isTrue();
}
- // Verifies that a swipe in the upward direction is not catpured.
+ // Verifies that a swipe in the upward direction is not captured.
@Test
public void testSwipeUp_notCaptured() {
final boolean captured = swipe(Direction.UP);
@@ -98,34 +101,58 @@
// Verifies that a swipe down forwards captured touches to central surfaces for handling.
@Test
- public void testSwipeDown_sentToCentralSurfaces() {
+ @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+ public void testSwipeDown_communalEnabled_sentToCentralSurfaces() {
swipe(Direction.DOWN);
- // Both motion events are sent for the shade window to process.
+ // Both motion events are sent for central surfaces to process.
verify(mCentralSurfaces, times(2)).handleExternalShadeWindowTouch(any());
}
- // Verifies that a swipe down forwards captured touches to central surfaces for handling.
+ // Verifies that a swipe down forwards captured touches to the shade view for handling.
+ @Test
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+ public void testSwipeDown_communalDisabled_sentToShadeView() {
+ swipe(Direction.DOWN);
+
+ // Both motion events are sent for the shade view to process.
+ verify(mShadeViewController, times(2)).handleExternalTouch(any());
+ }
+
+ // Verifies that a swipe down while dreaming forwards captured touches to the shade view for
+ // handling.
@Test
public void testSwipeDown_dreaming_sentToShadeView() {
when(mDreamManager.isDreaming()).thenReturn(true);
swipe(Direction.DOWN);
- // Both motion events are sent for the shade window to process.
+ // Both motion events are sent for the shade view to process.
verify(mShadeViewController, times(2)).handleExternalTouch(any());
}
- // Verifies that a swipe down is not forwarded to the shade window.
+ // Verifies that a swipe up is not forwarded to central surfaces.
@Test
- public void testSwipeUp_touchesNotSent() {
+ @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+ public void testSwipeUp_communalEnabled_touchesNotSent() {
swipe(Direction.UP);
- // Motion events are not sent for the shade window to process as the swipe is going in the
+ // Motion events are not sent for central surfaces to process as the swipe is going in the
// wrong direction.
verify(mCentralSurfaces, never()).handleExternalShadeWindowTouch(any());
}
+ // Verifies that a swipe up is not forwarded to the shade view.
+ @Test
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+ public void testSwipeUp_communalDisabled_touchesNotSent() {
+ swipe(Direction.UP);
+
+ // Motion events are not sent for the shade view to process as the swipe is going in the
+ // wrong direction.
+ verify(mShadeViewController, never()).handleExternalTouch(any());
+ }
+
/**
* Simulates a swipe in the given direction and returns true if the touch was intercepted by the
* touch handler's gesture listener.
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
index 6c6a1cc..324579d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
@@ -57,7 +57,7 @@
.toViewModel(
isChecked = isEnabled is SpatialAudioEnabledModel.SpatialAudioEnabled,
isHeadTrackingAvailable =
- isAvailable is SpatialAudioAvailabilityModel.SpatialAudio,
+ isAvailable is SpatialAudioAvailabilityModel.HeadTracking,
)
.copy(label = context.getString(R.string.volume_panel_spatial_audio_title))
}
@@ -69,7 +69,7 @@
// head tracking availability means there are three possible states for the spatial
// audio: disabled, enabled regular, enabled with head tracking.
// Show popup in this case instead of a togglealbe button.
- it is SpatialAudioAvailabilityModel.SpatialAudio
+ it is SpatialAudioAvailabilityModel.HeadTracking
}
.stateIn(scope, SharingStarted.Eagerly, false)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
index 5a092f3..62e56be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
@@ -16,16 +16,16 @@
package com.android.systemui.animation
+import android.content.applicationContext
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.kosmos.testCase
val Kosmos.dialogTransitionAnimator by Fixture {
fakeDialogTransitionAnimator(
// The main thread is checked in a bunch of places inside the different transitions
// animators, so we have to pass the real main executor here.
- mainExecutor = testCase.context.mainExecutor,
+ mainExecutor = applicationContext.mainExecutor,
interactionJankMonitor = interactionJankMonitor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt
new file mode 100644
index 0000000..49170d8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * 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 com.android.systemui.volume.panel.component.popup.ui.composable
+
+import com.android.systemui.animation.dialogTransitionAnimator
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.systemUIDialogFactory
+
+val Kosmos.volumePanelPopup: VolumePanelPopup by
+ Kosmos.Fixture { VolumePanelPopup(systemUIDialogFactory, dialogTransitionAnimator) }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 167c384..53730e3 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -227,6 +227,7 @@
"connectivity_flags_lib",
"dreams_flags_lib",
"aconfig_new_storage_flags_lib",
+ "powerstats_flags_lib",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 60290bc..e424ffa 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3892,9 +3892,10 @@
return;
}
- final long lastTopTime = sr.app.mState.getLastTopTime();
- final long constantTimeLimit = getTimeLimitForFgsType(fgsType);
+ final boolean currentlyTop = sr.app.mState.getCurProcState() <= PROCESS_STATE_TOP;
final long nowUptime = SystemClock.uptimeMillis();
+ final long lastTopTime = currentlyTop ? nowUptime : sr.app.mState.getLastTopTime();
+ final long constantTimeLimit = getTimeLimitForFgsType(fgsType);
if (lastTopTime != Long.MIN_VALUE && constantTimeLimit > (nowUptime - lastTopTime)) {
// Discard any other messages for this service
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8764f51..fd8f0fa 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -382,12 +382,6 @@
@MultiUserUnawareField
private HardwareKeyboardShortcutController mHardwareKeyboardShortcutController;
- /**
- * Tracks how many times {@link #mSettings} was updated.
- */
- @GuardedBy("ImfLock.class")
- private int mMethodMapUpdateCount = 0;
-
@GuardedBy("ImfLock.class")
@MultiUserUnawareField
private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
@@ -5246,7 +5240,6 @@
Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready");
return;
}
- mMethodMapUpdateCount++;
final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
@@ -6072,7 +6065,7 @@
p.println("Current Input Method Manager state:");
final List<InputMethodInfo> methodList = settings.getMethodList();
int numImes = methodList.size();
- p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
+ p.println(" Input Methods:");
for (int i = 0; i < numImes; i++) {
InputMethodInfo info = methodList.get(i);
p.println(" InputMethod #" + i + ":");
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 2081f73..0acadb1 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -16,7 +16,12 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -25,6 +30,7 @@
import android.app.ActivityManager;
import android.app.admin.SecurityLog;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.Flags;
import android.content.pm.PackageManager;
@@ -376,7 +382,30 @@
mCallingUid = callingUid;
}
- public boolean isSameComponent(ActivityInfo activityInfo) {
+ public boolean isLauncherActivity(@NonNull Computer computer, @UserIdInt int userId) {
+ if (mIsForWholeApp) {
+ return false;
+ }
+ // Query the launcher activities with the package name.
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.setPackage(mPackageName);
+ List<ResolveInfo> launcherActivities = computer.queryIntentActivitiesInternal(
+ intent, null,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | GET_RESOLVED_FILTER
+ | MATCH_DISABLED_COMPONENTS, SYSTEM_UID, userId);
+ final int launcherActivitiesSize =
+ launcherActivities != null ? launcherActivities.size() : 0;
+ for (int i = 0; i < launcherActivitiesSize; i++) {
+ ResolveInfo resolveInfo = launcherActivities.get(i);
+ if (isSameComponent(resolveInfo.activityInfo)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isSameComponent(ActivityInfo activityInfo) {
if (activityInfo == null) {
return false;
}
@@ -395,25 +424,13 @@
Slog.d(TAG, "Fail to report component state due to metrics is empty");
return;
}
- boolean isLauncher = false;
- final List<ResolveInfo> resolveInfosForLauncher = getHomeActivitiesResolveInfoAsUser(
- computer, userId);
- final int resolveInfosForLauncherSize =
- resolveInfosForLauncher != null ? resolveInfosForLauncher.size() : 0;
final int metricsSize = componentStateMetricsList.size();
for (int i = 0; i < metricsSize; i++) {
final ComponentStateMetrics componentStateMetrics = componentStateMetricsList.get(i);
- for (int j = 0; j < resolveInfosForLauncherSize; j++) {
- ResolveInfo resolveInfo = resolveInfosForLauncher.get(j);
- if (componentStateMetrics.isSameComponent(resolveInfo.activityInfo)) {
- isLauncher = true;
- break;
- }
- }
reportComponentStateChanged(componentStateMetrics.mUid,
componentStateMetrics.mComponentOldState,
componentStateMetrics.mComponentNewState,
- isLauncher,
+ componentStateMetrics.isLauncherActivity(computer, userId),
componentStateMetrics.mIsForWholeApp,
componentStateMetrics.mCallingUid);
}
@@ -424,10 +441,4 @@
FrameworkStatsLog.write(FrameworkStatsLog.COMPONENT_STATE_CHANGED_REPORTED,
uid, componentOldState, componentNewState, isLauncher, isForWholeApp, callingUid);
}
-
- private static List<ResolveInfo> getHomeActivitiesResolveInfoAsUser(@NonNull Computer computer,
- @UserIdInt int userId) {
- return computer.queryIntentActivitiesInternal(computer.getHomeIntent(), /* resolvedType */
- null, /* flags */ 0, userId);
- }
}
diff --git a/services/core/java/com/android/server/powerstats/Android.bp b/services/core/java/com/android/server/powerstats/Android.bp
new file mode 100644
index 0000000..7f3b091
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/Android.bp
@@ -0,0 +1,11 @@
+aconfig_declarations {
+ name: "powerstats_flags",
+ package: "com.android.server.powerstats",
+ container: "system",
+ srcs: ["*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "powerstats_flags_lib",
+ aconfig_declarations: "powerstats_flags",
+}
diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java
index f8a4135..817a40d 100644
--- a/services/core/java/com/android/server/powerstats/TimerTrigger.java
+++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java
@@ -16,8 +16,10 @@
package com.android.server.powerstats;
+import android.app.AlarmManager;
import android.content.Context;
import android.os.Handler;
+import android.os.SystemClock;
import android.util.Slog;
/**
@@ -33,37 +35,53 @@
private static final long LOG_PERIOD_MS_HIGH_FREQUENCY = 2 * 60 * 1000; // 2 minutes
private final Handler mHandler;
+ private final AlarmManager mAlarmManager;
- private Runnable mLogDataLowFrequency = new Runnable() {
+ class PeriodicTimer implements Runnable, AlarmManager.OnAlarmListener {
+ private final String mName;
+ private final long mPeriodMs;
+ private final int mMsgType;
+
+ PeriodicTimer(String name, long periodMs, int msgType) {
+ mName = name;
+ mPeriodMs = periodMs;
+ mMsgType = msgType;
+ }
+
+ @Override
+ public void onAlarm() {
+ run();
+ }
+
@Override
public void run() {
- // Do not wake the device for these messages. Opportunistically log rail data every
- // LOG_PERIOD_MS_LOW_FREQUENCY.
- mHandler.postDelayed(mLogDataLowFrequency, LOG_PERIOD_MS_LOW_FREQUENCY);
- if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data low frequency");
- logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY);
+ if (Flags.alarmBasedPowerstatsLogging()) {
+ final long nextAlarmMs = SystemClock.elapsedRealtime() + mPeriodMs;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextAlarmMs,
+ AlarmManager.WINDOW_EXACT, 0, mName, this, mHandler, null);
+ } else {
+ mHandler.postDelayed(this, mPeriodMs);
+ }
+ if (DEBUG) Slog.d(TAG, "Received delayed message (" + mName + "). Logging rail data");
+ logPowerStatsData(mMsgType);
}
- };
-
- private Runnable mLogDataHighFrequency = new Runnable() {
- @Override
- public void run() {
- // Do not wake the device for these messages. Opportunistically log rail data every
- // LOG_PERIOD_MS_HIGH_FREQUENCY.
- mHandler.postDelayed(mLogDataHighFrequency, LOG_PERIOD_MS_HIGH_FREQUENCY);
- if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data high frequency");
- logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY);
- }
- };
+ }
public TimerTrigger(Context context, PowerStatsLogger powerStatsLogger,
boolean triggerEnabled) {
super(context, powerStatsLogger);
mHandler = mContext.getMainThreadHandler();
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
if (triggerEnabled) {
- mLogDataLowFrequency.run();
- mLogDataHighFrequency.run();
+ final PeriodicTimer logDataLowFrequency = new PeriodicTimer("PowerStatsLowFreqLog",
+ LOG_PERIOD_MS_LOW_FREQUENCY,
+ PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY);
+ final PeriodicTimer logDataHighFrequency = new PeriodicTimer("PowerStatsHighFreqLog",
+ LOG_PERIOD_MS_HIGH_FREQUENCY,
+ PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY);
+ logDataLowFrequency.run();
+ logDataHighFrequency.run();
}
}
}
diff --git a/services/core/java/com/android/server/powerstats/flags.aconfig b/services/core/java/com/android/server/powerstats/flags.aconfig
new file mode 100644
index 0000000..0a4a751
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/flags.aconfig
@@ -0,0 +1,13 @@
+
+package: "com.android.server.powerstats"
+container: "system"
+
+flag {
+ name: "alarm_based_powerstats_logging"
+ namespace: "backstage_power"
+ description: "Utilize new OomAdjuster implementation"
+ bug: "294598168"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file