diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java index 4c24d95462..360210b010 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java @@ -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); diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java index 963504f761..8529dcf40a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -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 diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 855ff98062..d161d452b9 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -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 diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java index 8ec97edbd7..c247e66d81 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -292,8 +292,7 @@ public class FallbackRecentsView - remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true)); + mBlurUtils.setDrawLiveTileBelowRecents(true); } } diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java index f956a7cfbb..89f1e33d6e 100644 --- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java +++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java @@ -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. + *

+ * Blur is applied to below {@link #mBaseSurfaceOverride}. When set to {@code null}, blur is + * applied + * to below {@link #mBaseSurface}. + *

+ */ + 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(); } } diff --git a/quickstep/src/com/android/quickstep/views/BlurUtils.kt b/quickstep/src/com/android/quickstep/views/BlurUtils.kt new file mode 100644 index 0000000000..d6b2a055b7 --- /dev/null +++ b/quickstep/src/com/android/quickstep/views/BlurUtils.kt @@ -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? = 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 + } + ) + } + } +} diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index 0f1c2941b3..423a82905e 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -184,10 +184,8 @@ public class LauncherRecentsView extends RecentsView - remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true)); + mBlurUtils.setDrawLiveTileBelowRecents(true); } } @@ -276,7 +274,7 @@ public class LauncherRecentsView extends RecentsView - 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); + } } diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt index 8d95b131bf..be05122764 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.kt +++ b/quickstep/src/com/android/quickstep/views/TaskView.kt @@ -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): 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