Merge "Fixing surface blur when using multiple surfaces" into tm-qpr-dev am: 0c0fb6bc08

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/19222535

Change-Id: Icbae40f77402af53ae1190f3d6a21bf554483fa7
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Sunny Goyal
2022-08-06 01:49:15 +00:00
committed by Automerger Merge Worker
4 changed files with 223 additions and 255 deletions

View File

@@ -110,7 +110,7 @@ import java.util.stream.Stream;
*/
public abstract class BaseQuickstepLauncher extends Launcher {
private DepthController mDepthController = new DepthController(this);
private DepthController mDepthController;
private QuickstepTransitionManager mAppTransitionManager;
/**
@@ -247,7 +247,6 @@ public abstract class BaseQuickstepLauncher extends Launcher {
@Override
public void onScrollChanged(float progress) {
super.onScrollChanged(progress);
mDepthController.onOverlayScrollChanged(progress);
onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX);
}
@@ -345,6 +344,7 @@ public abstract class BaseQuickstepLauncher extends Launcher {
mAppTransitionManager.registerRemoteTransitions();
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
}
private void onTISConnected(TISBinder binder) {

View File

@@ -78,6 +78,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.util.Pair;
import android.util.Size;
import android.view.CrossWindowBlurListeners;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
@@ -93,6 +94,7 @@ import androidx.core.graphics.ColorUtils;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.shortcuts.DeepShortcutView;
@@ -143,8 +145,6 @@ import java.util.List;
*/
public class QuickstepTransitionManager implements OnDeviceProfileChangeListener {
private static final String TAG = "QuickstepTransition";
private static final boolean ENABLE_SHELL_STARTING_SURFACE =
SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
@@ -1044,54 +1044,37 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
private ObjectAnimator getBackgroundAnimator() {
// When launching an app from overview that doesn't map to a task, we still want to just
// blur the wallpaper instead of the launcher surface as well
boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW;
DepthController depthController = mLauncher.getDepthController();
boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW
&& BlurUtils.supportsBlursOnWindows();
MyDepthController depthController = new MyDepthController(mLauncher);
ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController, DEPTH,
BACKGROUND_APP.getDepth(mLauncher))
BACKGROUND_APP.getDepth(mLauncher))
.setDuration(APP_LAUNCH_DURATION);
if (allowBlurringLauncher) {
final SurfaceControl dimLayer;
if (BlurUtils.supportsBlursOnWindows()) {
// Create a temporary effect layer, that lives on top of launcher, so we can apply
// the blur to it. The EffectLayer will be fullscreen, which will help with caching
// optimizations on the SurfaceFlinger side:
// - Results would be able to be cached as a texture
// - There won't be texture allocation overhead, because EffectLayers don't have
// buffers
ViewRootImpl viewRootImpl = mLauncher.getDragLayer().getViewRootImpl();
SurfaceControl parent = viewRootImpl != null
? viewRootImpl.getSurfaceControl()
: null;
dimLayer = new SurfaceControl.Builder()
.setName("Blur layer")
.setParent(parent)
.setOpaque(false)
.setHidden(false)
.setEffectLayer()
.build();
} else {
dimLayer = null;
}
// Create a temporary effect layer, that lives on top of launcher, so we can apply
// the blur to it. The EffectLayer will be fullscreen, which will help with caching
// optimizations on the SurfaceFlinger side:
// - Results would be able to be cached as a texture
// - There won't be texture allocation overhead, because EffectLayers don't have
// buffers
ViewRootImpl viewRootImpl = mLauncher.getDragLayer().getViewRootImpl();
SurfaceControl parent = viewRootImpl != null
? viewRootImpl.getSurfaceControl()
: null;
SurfaceControl dimLayer = new SurfaceControl.Builder()
.setName("Blur layer")
.setParent(parent)
.setOpaque(false)
.setHidden(false)
.setEffectLayer()
.build();
depthController.setSurface(dimLayer);
backgroundRadiusAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
depthController.setIsInLaunchTransition(true);
}
@Override
public void onAnimationEnd(Animator animation) {
depthController.setIsInLaunchTransition(false);
depthController.setSurface(null);
if (dimLayer != null) {
new SurfaceControl.Transaction()
.remove(dimLayer)
.apply();
}
}
});
backgroundRadiusAnim.addListener(AnimatorListeners.forEndCallback(() ->
new SurfaceControl.Transaction().remove(dimLayer).apply()));
}
return backgroundRadiusAnim;
}
@@ -1936,4 +1919,17 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
}
}
private static class MyDepthController extends DepthController {
MyDepthController(Launcher l) {
super(l);
setCrossWindowBlursEnabled(
CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled());
}
@Override
public void setSurface(SurfaceControl surface) {
super.setSurface(surface);
}
}
}

View File

@@ -23,13 +23,8 @@ import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTR
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.app.WallpaperManager;
import android.os.IBinder;
import android.os.SystemProperties;
import android.util.FloatProperty;
import android.view.AttachedSurfaceControl;
import android.view.CrossWindowBlurListeners;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
@@ -37,12 +32,11 @@ import android.view.ViewTreeObserver;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.systemui.shared.system.BlurUtils;
import com.android.quickstep.util.BaseDepthController;
import java.io.PrintWriter;
import java.util.function.Consumer;
@@ -50,23 +44,9 @@ import java.util.function.Consumer;
/**
* Controls blur and wallpaper zoom, for the Launcher surface only.
*/
public class DepthController implements StateHandler<LauncherState>,
public class DepthController extends BaseDepthController implements StateHandler<LauncherState>,
BaseActivity.MultiWindowModeChangedListener {
private static final boolean OVERLAY_SCROLL_ENABLED = false;
public static final FloatProperty<DepthController> DEPTH =
new FloatProperty<DepthController>("depth") {
@Override
public void setValue(DepthController depthController, float depth) {
depthController.setDepth(depth);
}
@Override
public Float get(DepthController depthController) {
return depthController.mDepth;
}
};
/**
* A property that updates the background blur within a given range of values (ie. even if the
* animator goes beyond 0..1, the interpolated value will still be bounded).
@@ -92,96 +72,46 @@ public class DepthController implements StateHandler<LauncherState>,
}
}
private final ViewTreeObserver.OnDrawListener mOnDrawListener =
new ViewTreeObserver.OnDrawListener() {
@Override
public void onDraw() {
View view = mLauncher.getDragLayer();
ViewRootImpl viewRootImpl = view.getViewRootImpl();
boolean applied = setSurface(
viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
if (!applied) {
dispatchTransactionSurface(mDepth);
}
view.post(() -> view.getViewTreeObserver().removeOnDrawListener(this));
}
};
private final ViewTreeObserver.OnDrawListener mOnDrawListener = this::onLauncherDraw;
private final Consumer<Boolean> mCrossWindowBlurListener = new Consumer<Boolean>() {
@Override
public void accept(Boolean enabled) {
mCrossWindowBlursEnabled = enabled;
dispatchTransactionSurface(mDepth);
}
};
private final Consumer<Boolean> mCrossWindowBlurListener = this::setCrossWindowBlursEnabled;
private final Runnable mOpaquenessListener = new Runnable() {
@Override
public void run() {
dispatchTransactionSurface(mDepth);
}
};
private final Runnable mOpaquenessListener = this::applyDepthAndBlur;
private final Launcher mLauncher;
/**
* Blur radius when completely zoomed out, in pixels.
*/
private int mMaxBlurRadius;
private boolean mCrossWindowBlursEnabled;
private WallpaperManager mWallpaperManager;
private SurfaceControl mSurface;
/**
* How visible the -1 overlay is, from 0 to 1.
*/
private float mOverlayScrollProgress;
/**
* 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;
/**
* Last blur value, in pixels, that was applied.
* For debugging purposes.
*/
private int mCurrentBlur;
/**
* If we're launching and app and should not be blurring the screen for performance reasons.
*/
private boolean mBlurDisabledForAppLaunch;
/**
* If we requested early wake-up offsets to SurfaceFlinger.
*/
private boolean mInEarlyWakeUp;
// Workaround for animating the depth when multiwindow mode changes.
private boolean mIgnoreStateChangesDuringMultiWindowAnimation = false;
// 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.
private boolean mHasContentBehindLauncher;
private View.OnAttachStateChangeListener mOnAttachListener;
public DepthController(Launcher l) {
mLauncher = l;
super(l);
}
private void onLauncherDraw() {
View view = mLauncher.getDragLayer();
ViewRootImpl viewRootImpl = view.getViewRootImpl();
setSurface(viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
view.post(() -> view.getViewTreeObserver().removeOnDrawListener(mOnDrawListener));
}
private void ensureDependencies() {
if (mWallpaperManager == null) {
mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
mWallpaperManager = mLauncher.getSystemService(WallpaperManager.class);
}
if (mLauncher.getRootView() != null && mOnAttachListener == null) {
View rootView = mLauncher.getRootView();
mOnAttachListener = new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(),
mCrossWindowBlurListener);
mLauncher.getScrimView().addOpaquenessListener(mOpaquenessListener);
// To handle the case where window token is invalid during last setDepth call.
IBinder windowToken = mLauncher.getRootView().getWindowToken();
if (windowToken != null) {
mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
}
onAttached();
applyDepthAndBlur();
}
@Override
@@ -190,23 +120,13 @@ public class DepthController implements StateHandler<LauncherState>,
mLauncher.getScrimView().removeOpaquenessListener(mOpaquenessListener);
}
};
mLauncher.getRootView().addOnAttachStateChangeListener(mOnAttachListener);
if (mLauncher.getRootView().isAttachedToWindow()) {
onAttached();
rootView.addOnAttachStateChangeListener(mOnAttachListener);
if (rootView.isAttachedToWindow()) {
mOnAttachListener.onViewAttachedToWindow(rootView);
}
}
}
private void onAttached() {
CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(),
mCrossWindowBlurListener);
mLauncher.getScrimView().addOpaquenessListener(mOpaquenessListener);
}
public void setHasContentBehindLauncher(boolean hasContentBehindLauncher) {
mHasContentBehindLauncher = hasContentBehindLauncher;
}
/**
* Sets if the underlying activity is started or not
*/
@@ -219,26 +139,6 @@ public class DepthController implements StateHandler<LauncherState>,
}
}
/**
* Sets the specified app target surface to apply the blur to.
* @return true when surface was valid and transaction was dispatched.
*/
public boolean setSurface(SurfaceControl surface) {
// Set launcher as the SurfaceControl when we don't need an external target anymore.
if (surface == null) {
ViewRootImpl viewRootImpl = mLauncher.getDragLayer().getViewRootImpl();
surface = viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null;
}
if (mSurface != surface) {
mSurface = surface;
if (surface != null) {
dispatchTransactionSurface(mDepth);
return true;
}
}
return false;
}
@Override
public void setState(LauncherState toState) {
if (mSurface == null || mIgnoreStateChangesDuringMultiWindowAnimation) {
@@ -249,7 +149,7 @@ public class DepthController implements StateHandler<LauncherState>,
if (Float.compare(mDepth, toDepth) != 0) {
setDepth(toDepth);
} else if (toState == LauncherState.OVERVIEW) {
dispatchTransactionSurface(mDepth);
applyDepthAndBlur();
} else if (toState == LauncherState.BACKGROUND_APP) {
mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
}
@@ -269,90 +169,10 @@ public class DepthController implements StateHandler<LauncherState>,
}
}
/**
* If we're launching an app from the home screen.
*/
public void setIsInLaunchTransition(boolean inLaunchTransition) {
boolean blurEnabled = SystemProperties.getBoolean("ro.launcher.blur.appLaunch", true);
mBlurDisabledForAppLaunch = inLaunchTransition && !blurEnabled;
if (!inLaunchTransition) {
// Reset depth at the end of the launch animation, so the wallpaper won't be
// zoomed out if an app crashes.
setDepth(0f);
}
}
private void setDepth(float depth) {
depth = Utilities.boundToRange(depth, 0, 1);
// Round out the depth to dedupe frequent, non-perceptable updates
int depthI = (int) (depth * 256);
float depthF = depthI / 256f;
if (Float.compare(mDepth, depthF) == 0) {
return;
}
dispatchTransactionSurface(depthF);
mDepth = depthF;
}
public void onOverlayScrollChanged(float progress) {
if (!OVERLAY_SCROLL_ENABLED) {
return;
}
// Add some padding to the progress, such we don't change the depth on the last frames of
// the animation. It's possible that a user flinging the feed quickly would scroll
// horizontally by accident, causing the device to enter client composition unnecessarily.
progress = Math.min(progress * 1.1f, 1f);
// Round out the progress to dedupe frequent, non-perceptable updates
int progressI = (int) (progress * 256);
float progressF = Utilities.boundToRange(progressI / 256f, 0f, 1f);
if (Float.compare(mOverlayScrollProgress, progressF) == 0) {
return;
}
mOverlayScrollProgress = progressF;
dispatchTransactionSurface(mDepth);
}
private boolean dispatchTransactionSurface(float depth) {
boolean supportsBlur = BlurUtils.supportsBlursOnWindows();
if (supportsBlur && (mSurface == null || !mSurface.isValid())) {
return false;
}
@Override
protected void applyDepthAndBlur() {
ensureDependencies();
depth = Math.max(depth, mOverlayScrollProgress);
IBinder windowToken = mLauncher.getRootView().getWindowToken();
if (windowToken != null) {
mWallpaperManager.setWallpaperZoomOut(windowToken, depth);
}
if (supportsBlur) {
boolean hasOpaqueBg = mLauncher.getScrimView().isFullyOpaque();
boolean isSurfaceOpaque = !mHasContentBehindLauncher && hasOpaqueBg;
mCurrentBlur = !mCrossWindowBlursEnabled || mBlurDisabledForAppLaunch || hasOpaqueBg
? 0 : (int) (depth * mMaxBlurRadius);
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
.setBackgroundBlurRadius(mSurface, mCurrentBlur)
.setOpaque(mSurface, 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;
if (wantsEarlyWakeUp && !mInEarlyWakeUp) {
transaction.setEarlyWakeupStart();
mInEarlyWakeUp = true;
} else if (!wantsEarlyWakeUp && mInEarlyWakeUp) {
transaction.setEarlyWakeupEnd();
mInEarlyWakeUp = false;
}
AttachedSurfaceControl rootSurfaceControl =
mLauncher.getRootView().getRootSurfaceControl();
if (rootSurfaceControl != null) {
rootSurfaceControl.applyTransactionOnDraw(transaction);
}
}
return true;
super.applyDepthAndBlur();
}
@Override
@@ -377,7 +197,6 @@ public class DepthController implements StateHandler<LauncherState>,
writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius);
writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled);
writer.println(prefix + "\tmSurface=" + mSurface);
writer.println(prefix + "\tmOverlayScrollProgress=" + mOverlayScrollProgress);
writer.println(prefix + "\tmDepth=" + mDepth);
writer.println(prefix + "\tmCurrentBlur=" + mCurrentBlur);
writer.println(prefix + "\tmBlurDisabledForAppLaunch=" + mBlurDisabledForAppLaunch);

View File

@@ -0,0 +1,153 @@
/*
* Copyright (C) 2022 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.util;
import android.app.WallpaperManager;
import android.os.IBinder;
import android.util.FloatProperty;
import android.view.AttachedSurfaceControl;
import android.view.SurfaceControl;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.systemui.shared.system.BlurUtils;
/**
* Utility class for applying depth effect
*/
public class BaseDepthController {
public static final FloatProperty<BaseDepthController> DEPTH =
new FloatProperty<BaseDepthController>("depth") {
@Override
public void setValue(BaseDepthController depthController, float depth) {
depthController.setDepth(depth);
}
@Override
public Float get(BaseDepthController depthController) {
return depthController.mDepth;
}
};
protected final Launcher mLauncher;
/**
* Blur radius when completely zoomed out, in pixels.
*/
protected final int mMaxBlurRadius;
protected final WallpaperManager mWallpaperManager;
protected boolean mCrossWindowBlursEnabled;
/**
* Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
* @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
*/
protected float mDepth;
protected SurfaceControl mSurface;
// 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.
private boolean mHasContentBehindLauncher;
/**
* Last blur value, in pixels, that was applied.
* For debugging purposes.
*/
protected int mCurrentBlur;
/**
* If we requested early wake-up offsets to SurfaceFlinger.
*/
protected boolean mInEarlyWakeUp;
public BaseDepthController(Launcher activity) {
mLauncher = activity;
mMaxBlurRadius = activity.getResources().getInteger(R.integer.max_depth_blur_radius);
mWallpaperManager = activity.getSystemService(WallpaperManager.class);
}
protected void setCrossWindowBlursEnabled(boolean isEnabled) {
mCrossWindowBlursEnabled = isEnabled;
applyDepthAndBlur();
}
public void setHasContentBehindLauncher(boolean hasContentBehindLauncher) {
mHasContentBehindLauncher = hasContentBehindLauncher;
}
protected void applyDepthAndBlur() {
float depth = mDepth;
IBinder windowToken = mLauncher.getRootView().getWindowToken();
if (windowToken != null) {
mWallpaperManager.setWallpaperZoomOut(windowToken, depth);
}
if (!BlurUtils.supportsBlursOnWindows()) {
return;
}
if (mSurface == null || !mSurface.isValid()) {
return;
}
boolean hasOpaqueBg = mLauncher.getScrimView().isFullyOpaque();
boolean isSurfaceOpaque = !mHasContentBehindLauncher && hasOpaqueBg;
mCurrentBlur = !mCrossWindowBlursEnabled || hasOpaqueBg
? 0 : (int) (depth * mMaxBlurRadius);
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
.setBackgroundBlurRadius(mSurface, mCurrentBlur)
.setOpaque(mSurface, 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;
if (wantsEarlyWakeUp && !mInEarlyWakeUp) {
transaction.setEarlyWakeupStart();
mInEarlyWakeUp = true;
} else if (!wantsEarlyWakeUp && mInEarlyWakeUp) {
transaction.setEarlyWakeupEnd();
mInEarlyWakeUp = false;
}
AttachedSurfaceControl rootSurfaceControl =
mLauncher.getRootView().getRootSurfaceControl();
if (rootSurfaceControl != null) {
rootSurfaceControl.applyTransactionOnDraw(transaction);
}
}
protected void setDepth(float depth) {
depth = Utilities.boundToRange(depth, 0, 1);
// Round out the depth to dedupe frequent, non-perceptable updates
int depthI = (int) (depth * 256);
float depthF = depthI / 256f;
if (Float.compare(mDepth, depthF) == 0) {
return;
}
mDepth = depthF;
applyDepthAndBlur();
}
/**
* Sets the specified app target surface to apply the blur to.
*/
protected void setSurface(SurfaceControl surface) {
if (mSurface != surface) {
mSurface = surface;
applyDepthAndBlur();
}
}
}