Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c0fb6bc authored by Sunny Goyal's avatar Sunny Goyal Committed by Android (Google) Code Review
Browse files

Merge "Fixing surface blur when using multiple surfaces" into tm-qpr-dev

parents 67fe05be ced15907
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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) {
+42 −46
Original line number Diff line number Diff line
@@ -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,14 +1044,15 @@ 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))
                .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:
@@ -1062,36 +1063,18 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
            SurfaceControl parent = viewRootImpl != null
                    ? viewRootImpl.getSurfaceControl()
                    : null;
                dimLayer = new SurfaceControl.Builder()
            SurfaceControl dimLayer = new SurfaceControl.Builder()
                    .setName("Blur layer")
                    .setParent(parent)
                    .setOpaque(false)
                    .setHidden(false)
                    .setEffectLayer()
                    .build();
            } else {
                dimLayer = null;
            }

            depthController.setSurface(dimLayer);
            backgroundRadiusAnim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    depthController.setIsInLaunchTransition(true);
            backgroundRadiusAnim.addListener(AnimatorListeners.forEndCallback(() ->
                    new SurfaceControl.Transaction().remove(dimLayer).apply()));
        }

                @Override
                public void onAnimationEnd(Animator animation) {
                    depthController.setIsInLaunchTransition(false);
                    depthController.setSurface(null);
                    if (dimLayer != null) {
                        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);
        }
    }
}
+27 −208
Original line number Diff line number Diff line
@@ -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 ensureDependencies() {
        if (mWallpaperManager == null) {
            mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
            mWallpaperManager = mLauncher.getSystemService(WallpaperManager.class);
    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 (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,21 +120,11 @@ 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;
    }

    /**
@@ -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);
+153 −0
Original line number Diff line number Diff line
/*
 * 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();
        }
    }
}