Implementation of overview background blur

- Remove overview background scrim color.
- Create blur layer and blur surface to set blur to behind live app tile.
- Handles cases like live tile to screenshot during AiAi and orientation change.

Bug: 377244819
Test: Manual. Enter overview from app/home.
Flag: com.android.launcher3.enable_overview_background_wallpaper_blur
Change-Id: I5dce0268f4421cd0ac29dbb429e5d106da7a0bdc
This commit is contained in:
vinayjoglekar
2025-02-10 13:17:46 +00:00
parent 837e576c56
commit 7d88ef3aae
9 changed files with 179 additions and 59 deletions
@@ -65,7 +65,7 @@ public class DepthController extends BaseDepthController implements StateHandler
private void onLauncherDraw() {
View view = mLauncher.getDragLayer();
ViewRootImpl viewRootImpl = view.getViewRootImpl();
setSurface(viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
setBaseSurface(viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
view.post(() -> view.getViewTreeObserver().removeOnDrawListener(mOnDrawListener));
}
@@ -127,7 +127,7 @@ public class DepthController extends BaseDepthController implements StateHandler
mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
} else {
mLauncher.getDragLayer().getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
setSurface(null);
setBaseSurface(null);
}
}
@@ -189,7 +189,8 @@ public class DepthController extends BaseDepthController implements StateHandler
writer.println(prefix + "DepthController");
writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius);
writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled);
writer.println(prefix + "\tmSurface=" + mSurface);
writer.println(prefix + "\tmBaseSurface=" + mBaseSurface);
writer.println(prefix + "\tmBaseSurfaceOverride=" + mBaseSurfaceOverride);
writer.println(prefix + "\tmStateDepth=" + stateDepth.getValue());
writer.println(prefix + "\tmWidgetDepth=" + widgetDepth.getValue());
writer.println(prefix + "\tmCurrentBlur=" + mCurrentBlur);
@@ -17,10 +17,12 @@ package com.android.launcher3.uioverrides.states;
import static com.android.app.animation.Interpolators.DECELERATE_2;
import static com.android.launcher3.Flags.enableDesktopExplodedView;
import static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.SystemProperties;
@@ -158,7 +160,8 @@ public class OverviewState extends LauncherState {
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
return Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
return enableOverviewBackgroundWallpaperBlur() ? Color.TRANSPARENT
: Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
}
@Override
@@ -433,9 +433,7 @@ public final class TaskViewUtils {
out.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
for (RemoteTargetHandle remoteTargetHandle : remoteTargetHandles) {
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false);
}
recentsView.setDrawBelowRecents(false, remoteTargetHandles);
}
@Override
@@ -292,8 +292,7 @@ public class FallbackRecentsView<CONTAINER_TYPE extends Context & RecentsViewCon
// disabling this so app icons aren't drawn on top of recent tasks.
if (isOverlayEnabled && !RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
mBlurUtils.setDrawLiveTileBelowRecents(true);
}
}
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.util;
import static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import android.app.WallpaperManager;
@@ -24,6 +25,8 @@ import android.util.Log;
import android.view.AttachedSurfaceControl;
import android.view.SurfaceControl;
import androidx.annotation.Nullable;
import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -75,11 +78,14 @@ public class BaseDepthController {
/**
* Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
*
* @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
*/
private float mDepth;
protected SurfaceControl mSurface;
protected SurfaceControl mBaseSurface;
protected SurfaceControl mBaseSurfaceOverride;
// Hints that there is potentially content behind Launcher and that we shouldn't optimize by
// marking the launcher surface as opaque. Only used in certain Launcher states.
@@ -100,6 +106,8 @@ public class BaseDepthController {
protected boolean mWaitingOnSurfaceValidity;
private SurfaceControl mBlurSurface = null;
public BaseDepthController(Launcher activity) {
mLauncher = activity;
if (Flags.allAppsBlur()) {
@@ -114,6 +122,13 @@ public class BaseDepthController {
new MultiPropertyFactory<>(this, DEPTH, DEPTH_INDEX_COUNT, Float::max);
stateDepth = depthProperty.get(DEPTH_INDEX_STATE_TRANSITION);
widgetDepth = depthProperty.get(DEPTH_INDEX_WIDGET);
if (enableOverviewBackgroundWallpaperBlur()) {
mBlurSurface = new SurfaceControl.Builder()
.setName("Overview Blur")
.setHidden(false)
.build();
}
}
protected void setCrossWindowBlursEnabled(boolean isEnabled) {
@@ -151,11 +166,11 @@ public class BaseDepthController {
if (!BlurUtils.supportsBlursOnWindows()) {
return;
}
if (mSurface == null) {
if (mBaseSurface == null) {
Log.d(TAG, "mSurface is null and mCurrentBlur is: " + mCurrentBlur);
return;
}
if (!mSurface.isValid()) {
if (!mBaseSurface.isValid()) {
Log.d(TAG, "mSurface is not valid");
mWaitingOnSurfaceValidity = true;
onInvalidSurface();
@@ -174,10 +189,21 @@ public class BaseDepthController {
mCurrentBlur = !mCrossWindowBlursEnabled || hasOpaqueBg || mPauseBlurs
? 0 : (int) (blurAmount * mMaxBlurRadius);
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
.setBackgroundBlurRadius(mSurface, mCurrentBlur)
.setOpaque(mSurface, isSurfaceOpaque);
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
if (enableOverviewBackgroundWallpaperBlur() && mBlurSurface != null) {
// Reparent to launcher for full screen blur.
transaction.setBackgroundBlurRadius(mBlurSurface, mCurrentBlur)
.reparent(mBlurSurface, mBaseSurface);
// Set mBlurSurface to be 1 layer behind mBaseSurface or mBaseSurfaceOverride.
if (mBaseSurfaceOverride != null && mBaseSurfaceOverride.isValid()) {
transaction.setRelativeLayer(mBlurSurface, mBaseSurfaceOverride, -1);
} else {
transaction.setRelativeLayer(mBlurSurface, mBaseSurface, -1);
}
} else {
transaction.setBackgroundBlurRadius(mBaseSurface, mCurrentBlur);
}
transaction.setOpaque(mBaseSurface, isSurfaceOpaque);
// Set early wake-up flags when we know we're executing an expensive operation, this way
// SurfaceFlinger will adjust its internal offsets to avoid jank.
boolean wantsEarlyWakeUp = depth > 0 && depth < 1;
@@ -208,14 +234,27 @@ public class BaseDepthController {
applyDepthAndBlur();
}
/**
* Sets the lowest surface that should not be blurred.
* <p>
* Blur is applied to below {@link #mBaseSurfaceOverride}. When set to {@code null}, blur is
* applied
* to below {@link #mBaseSurface}.
* </p>
*/
public void setBaseSurfaceOverride(@Nullable SurfaceControl baseSurfaceOverride) {
this.mBaseSurfaceOverride = baseSurfaceOverride;
applyDepthAndBlur();
}
/**
* Sets the specified app target surface to apply the blur to.
*/
protected void setSurface(SurfaceControl surface) {
if (mSurface != surface || mWaitingOnSurfaceValidity) {
mSurface = surface;
protected void setBaseSurface(SurfaceControl baseSurface) {
if (mBaseSurface != baseSurface || mWaitingOnSurfaceValidity) {
mBaseSurface = baseSurface;
Log.d(TAG, "setSurface:\n\tmWaitingOnSurfaceValidity: " + mWaitingOnSurfaceValidity
+ "\n\tmSurface: " + mSurface);
+ "\n\tmBaseSurface: " + mBaseSurface);
applyDepthAndBlur();
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep.views
import com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle
/** Applies blur either behind launcher surface or live tile app. */
class BlurUtils(private val recentsView: RecentsView<*, *>) {
fun setDrawLiveTileBelowRecents(drawBelowRecents: Boolean) {
val liveTileRemoteTargetHandles =
if (
recentsView.remoteTargetHandles != null &&
recentsView.recentsAnimationController != null
)
recentsView.remoteTargetHandles
else null
setDrawBelowRecents(drawBelowRecents, liveTileRemoteTargetHandles)
}
/**
* Set surface in [remoteTargetHandles] to be above or below Recents layer, and update the base
* layer to apply blur to in BaseDepthController.
*/
fun setDrawBelowRecents(
drawBelowRecents: Boolean,
remoteTargetHandles: Array<RemoteTargetHandle>? = null,
) {
remoteTargetHandles?.forEach { it.taskViewSimulator.setDrawsBelowRecents(drawBelowRecents) }
if (enableOverviewBackgroundWallpaperBlur()) {
recentsView.depthController?.setBaseSurfaceOverride(
// Blurs behind launcher layer.
if (!drawBelowRecents || remoteTargetHandles == null) {
null
} else {
// Blurs behind live tile. blur will be applied behind window
// which farthest from user in case of desktop and split apps.
remoteTargetHandles
.maxByOrNull { it.transformParams.targetSet.firstAppTarget.leash.layerId }
?.transformParams
?.targetSet
?.firstAppTarget
?.leash
}
)
}
}
}
@@ -184,10 +184,8 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
if (finalState.isRecentsViewVisible && finalState != OVERVIEW_MODAL_TASK) {
setTaskBorderEnabled(true);
}
if (isOverlayEnabled) {
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
mBlurUtils.setDrawLiveTileBelowRecents(true);
}
}
@@ -276,7 +274,7 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
GestureState.GestureEndTarget endTarget = mCurrentGestureEndTarget;
if (endTarget == GestureState.GestureEndTarget.LAST_TASK
&& desktopVisibilityController.isInDesktopModeAndNotInOverview(
mContainer.getDisplayId())) {
mContainer.getDisplayId())) {
// Recents gesture was cancelled and we are returning to the previous task.
// After super class has handled clean up, show desktop apps on top again
showDesktopApps = true;
@@ -42,6 +42,7 @@ import static com.android.launcher3.Flags.enableDesktopExplodedView;
import static com.android.launcher3.Flags.enableDesktopTaskAlphaAnimation;
import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.Flags.enableLargeDesktopWindowingTile;
import static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
import static com.android.launcher3.Flags.enableSeparateExternalDisplayTasks;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
@@ -142,7 +143,6 @@ import androidx.annotation.UiThread;
import androidx.core.graphics.ColorUtils;
import androidx.dynamicanimation.animation.SpringAnimation;
import com.android.app.tracing.TraceUtilsKt;
import com.android.internal.jank.Cuj;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener;
@@ -868,6 +868,9 @@ public abstract class RecentsView<
private final Matrix mTmpMatrix = new Matrix();
private int mTaskViewCount = 0;
protected final BlurUtils mBlurUtils = new BlurUtils(this);
@Nullable
public TaskView getFirstTaskView() {
return mUtils.getFirstTaskView();
@@ -2747,9 +2750,7 @@ public abstract class RecentsView<
}
setEnableDrawingLiveTile(false);
}
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false));
mBlurUtils.setDrawLiveTileBelowRecents(false);
// These are relatively expensive and don't need to be done this frame (RecentsView isn't
// visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
post(this::onReset);
@@ -5819,12 +5820,10 @@ public abstract class RecentsView<
// above RecentsView to avoid wallpaper blur from being applied to it.
if (!taskView.isRunningTask()) {
runActionOnRemoteHandles(
remoteTargetHandle -> {
remoteTargetHandle.getTaskViewSimulator().setPivotOverride(
mTempPointF);
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(
false);
});
remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator()
.setPivotOverride(mTempPointF));
mBlurUtils.setDrawLiveTileBelowRecents(false);
}
}
@@ -5959,8 +5958,7 @@ public abstract class RecentsView<
mPendingAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false));
mBlurUtils.setDrawLiveTileBelowRecents(false);
}
});
mPendingAnimation.addEndListener(isSuccess -> {
@@ -5998,8 +5996,7 @@ public abstract class RecentsView<
// If launch animation didn't complete i.e. user dragged live tile down and then
// back up and returned to Overview, then we need to ensure we reset the
// view to draw below recents so that it can't be interacted with.
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
mBlurUtils.setDrawLiveTileBelowRecents(true);
redrawLiveTile();
}
return Unit.INSTANCE;
@@ -6078,6 +6075,7 @@ public abstract class RecentsView<
});
}
@Nullable
public RemoteTargetHandle[] getRemoteTargetHandles() {
return mRemoteTargetHandles;
}
@@ -6231,6 +6229,9 @@ public abstract class RecentsView<
mRecentsAnimationController = null;
mSplitSelectStateController.setRecentsAnimationRunning(false);
executeSideTaskLaunchCallback();
if (enableOverviewBackgroundWallpaperBlur()) {
mBlurUtils.setDrawLiveTileBelowRecents(false);
}
}
public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
@@ -7186,4 +7187,15 @@ public abstract class RecentsView<
public interface TaskLaunchListener {
void onTaskLaunched();
}
/**
* Sets whether the remote animation targets should draw below the recents view.
*
* @param drawBelowRecents whether the surface should draw below Recents.
* @param remoteTargetHandles collection of remoteTargetHandles in Recents.
*/
public void setDrawBelowRecents(boolean drawBelowRecents,
RemoteTargetHandle[] remoteTargetHandles) {
mBlurUtils.setDrawBelowRecents(drawBelowRecents, remoteTargetHandles);
}
}
@@ -77,6 +77,7 @@ import com.android.launcher3.util.rects.set
import com.android.quickstep.FullscreenDrawParams
import com.android.quickstep.RecentsModel
import com.android.quickstep.RemoteAnimationTargets
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.TaskViewUtils
import com.android.quickstep.orientation.RecentsPagedOrientationHandler
@@ -1210,15 +1211,14 @@ constructor(
/** Launch of the current task (both live and inactive tasks) with an animation. */
fun launchWithAnimation(): RunnableList? {
return if (isRunningTask && recentsView?.remoteTargetHandles != null) {
launchAsLiveTile()
launchAsLiveTile(recentsView?.remoteTargetHandles!!)
} else {
launchAsStaticTile()
}
}
private fun launchAsLiveTile(): RunnableList? {
private fun launchAsLiveTile(remoteTargetHandles: Array<RemoteTargetHandle>): RunnableList? {
val recentsView = recentsView ?: return null
val remoteTargetHandles = recentsView.remoteTargetHandles
if (!isClickableAsLiveTile) {
Log.e(
TAG,
@@ -1228,21 +1228,27 @@ constructor(
}
isClickableAsLiveTile = false
val targets =
if (remoteTargetHandles.size == 1) {
remoteTargetHandles[0].transformParams.targetSet
if (remoteTargetHandles.isNotEmpty()) {
if (remoteTargetHandles.size == 1) {
remoteTargetHandles[0].transformParams.targetSet
} else {
val apps =
remoteTargetHandles.flatMap {
it.transformParams.targetSet.apps.asIterable()
}
val wallpapers =
remoteTargetHandles.flatMap {
it.transformParams.targetSet.wallpapers.asIterable()
}
RemoteAnimationTargets(
apps.toTypedArray(),
wallpapers.toTypedArray(),
remoteTargetHandles[0].transformParams.targetSet.nonApps,
remoteTargetHandles[0].transformParams.targetSet.targetMode,
)
}
} else {
val apps =
remoteTargetHandles.flatMap { it.transformParams.targetSet.apps.asIterable() }
val wallpapers =
remoteTargetHandles.flatMap {
it.transformParams.targetSet.wallpapers.asIterable()
}
RemoteAnimationTargets(
apps.toTypedArray(),
wallpapers.toTypedArray(),
remoteTargetHandles[0].transformParams.targetSet.nonApps,
remoteTargetHandles[0].transformParams.targetSet.targetMode,
)
null
}
if (targets == null) {
// If the recents animation is cancelled somehow between the parent if block and
@@ -1484,10 +1490,11 @@ constructor(
}
private fun closeTaskMenu(): Boolean {
val floatingView: AbstractFloatingView? = AbstractFloatingView.getTopOpenViewWithType(
container,
AbstractFloatingView.TYPE_TASK_MENU
)
val floatingView: AbstractFloatingView? =
AbstractFloatingView.getTopOpenViewWithType(
container,
AbstractFloatingView.TYPE_TASK_MENU,
)
if (floatingView?.isOpen == true) {
floatingView.close(true)
return true