Add functionality to DepthController to control wallpaper zoom indepedently of blur
Also add wallpaper zoom to folder motion Bug: b/387543793 Flag: com.android.launcher3.enable_expressive_folder_expansion Test: manually and DepthControllerTest Change-Id: I94142806ffb6dfb93d363017ef4d13b3a5e74681
This commit is contained in:
1 parent
0d40f78616
commit
e9c655dbf6
8 files changed
+200
-16
No files matched your search
@@ -39,7 +39,7 @@ import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.statemanager.StateManager.StateHandler;
|
||||
import com.android.launcher3.states.StateAnimationConfig;
|
||||
import com.android.launcher3.uioverrides.QuickstepLauncher;
|
||||
import com.android.quickstep.util.BaseDepthController;
|
||||
import com.android.quickstep.util.BaseDepthControllerImpl;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.function.Consumer;
|
||||
@@ -51,7 +51,7 @@ import app.lawnchair.preferences2.PreferenceManager2;
|
||||
/**
|
||||
* Controls blur and wallpaper zoom, for the Launcher surface only.
|
||||
*/
|
||||
public class DepthController extends BaseDepthController implements StateHandler<LauncherState>,
|
||||
public class DepthController extends BaseDepthControllerImpl implements StateHandler<LauncherState>,
|
||||
BaseActivity.MultiWindowModeChangedListener {
|
||||
@VisibleForTesting
|
||||
final ViewTreeObserver.OnDrawListener mOnDrawListener = this::onLauncherDraw;
|
||||
|
||||
@@ -1335,6 +1335,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
|
||||
out.add(new RecentsViewStateController(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DepthController getDepthController() {
|
||||
return mDepthController;
|
||||
}
|
||||
|
||||
+51
-12
@@ -43,6 +43,8 @@ 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.AnimatedFloat;
|
||||
import com.android.launcher3.statehandlers.BaseDepthController;
|
||||
import com.android.launcher3.statemanager.StateManager;
|
||||
import com.android.launcher3.uioverrides.QuickstepLauncher;
|
||||
import com.android.launcher3.util.MultiPropertyFactory;
|
||||
@@ -52,20 +54,21 @@ import com.android.systemui.shared.system.BlurUtils;
|
||||
/**
|
||||
* Utility class for applying depth effect
|
||||
*/
|
||||
public class BaseDepthController {
|
||||
public class BaseDepthControllerImpl implements BaseDepthController {
|
||||
public static final float DEPTH_0_PERCENT = 0f;
|
||||
public static final float DEPTH_60_PERCENT = 0.6f;
|
||||
public static final float DEPTH_70_PERCENT = 0.7f;
|
||||
|
||||
private static final FloatProperty<BaseDepthController> DEPTH =
|
||||
new FloatProperty<BaseDepthController>("depth") {
|
||||
// Includes both Wallpaper Zoom and Blur
|
||||
private static final FloatProperty<BaseDepthControllerImpl> DEPTH =
|
||||
new FloatProperty<BaseDepthControllerImpl>("depth") {
|
||||
@Override
|
||||
public void setValue(BaseDepthController depthController, float depth) {
|
||||
public void setValue(BaseDepthControllerImpl depthController, float depth) {
|
||||
depthController.setDepth(depth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(BaseDepthController depthController) {
|
||||
public Float get(BaseDepthControllerImpl depthController) {
|
||||
return depthController.mDepth;
|
||||
}
|
||||
};
|
||||
@@ -74,6 +77,9 @@ public class BaseDepthController {
|
||||
private static final int DEPTH_INDEX_WIDGET = 1;
|
||||
private static final int DEPTH_INDEX_COUNT = 2;
|
||||
|
||||
private static final int ZOOM_INDEX_FOLDER = 0;
|
||||
private static final int ZOOM_INDEX_COUNT = 1;
|
||||
|
||||
// b/291401432
|
||||
private static final String TAG = "BaseDepthController";
|
||||
|
||||
@@ -82,6 +88,8 @@ public class BaseDepthController {
|
||||
public final MultiProperty stateDepth;
|
||||
/** Property to set the depth for widget picker. */
|
||||
public final MultiProperty widgetDepth;
|
||||
/** Property to set just the wallpaper zoom for folder expansion. */
|
||||
public final MultiProperty folderZoom;
|
||||
|
||||
/**
|
||||
* Blur radius when completely zoomed out, in pixels.
|
||||
@@ -96,6 +104,8 @@ public class BaseDepthController {
|
||||
* @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
|
||||
*/
|
||||
private float mDepth;
|
||||
// Used just for animating wallpaper zoom without affecting blur.
|
||||
private final AnimatedFloat mWallpaperZoomOnly = new AnimatedFloat(this::applyDepthAndBlur);
|
||||
|
||||
protected SurfaceControl mBaseSurface;
|
||||
|
||||
@@ -125,7 +135,7 @@ public class BaseDepthController {
|
||||
*/
|
||||
// private EarlyWakeupInfo mEarlyWakeupInfo = new EarlyWakeupInfo();
|
||||
|
||||
public BaseDepthController(QuickstepLauncher activity) {
|
||||
public BaseDepthControllerImpl(QuickstepLauncher activity) {
|
||||
mLauncher = activity;
|
||||
if (Flags.allAppsBlur() || enableOverviewBackgroundWallpaperBlur()) {
|
||||
if (Utilities.ATLEAST_S) {
|
||||
@@ -141,12 +151,38 @@ public class BaseDepthController {
|
||||
}
|
||||
mWallpaperManager = activity.getSystemService(WallpaperManager.class);
|
||||
|
||||
MultiPropertyFactory<BaseDepthController> depthProperty =
|
||||
MultiPropertyFactory<BaseDepthControllerImpl> depthProperty =
|
||||
new MultiPropertyFactory<>(this, DEPTH, DEPTH_INDEX_COUNT, Float::max);
|
||||
stateDepth = depthProperty.get(DEPTH_INDEX_STATE_TRANSITION);
|
||||
widgetDepth = depthProperty.get(DEPTH_INDEX_WIDGET);
|
||||
|
||||
if (Flags.enableExpressiveFolderExpansion()) {
|
||||
MultiPropertyFactory<AnimatedFloat> folderZoomFactory =
|
||||
new MultiPropertyFactory<>(mWallpaperZoomOnly, AnimatedFloat.VALUE,
|
||||
ZOOM_INDEX_COUNT, Float::max);
|
||||
folderZoom = folderZoomFactory.get(ZOOM_INDEX_FOLDER);
|
||||
} else {
|
||||
folderZoom = null;
|
||||
}
|
||||
|
||||
// mEarlyWakeupInfo.token = new Binder();
|
||||
// mEarlyWakeupInfo.trace = BaseDepthController.class.getName();
|
||||
// mEarlyWakeupInfo.trace = BaseDepthControllerImpl.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiProperty getStateDepth() {
|
||||
return stateDepth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiProperty getWidgetDepth() {
|
||||
return widgetDepth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiProperty getFolderZoom() {
|
||||
return folderZoom;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,16 +236,20 @@ public class BaseDepthController {
|
||||
private void applyDepthAndBlurInternal(SurfaceControl.Transaction transaction,
|
||||
boolean applyImmediately, boolean skipSimilarBlur) {
|
||||
float depth = mDepth;
|
||||
float wallpaperZoom = depth;
|
||||
if (Flags.enableExpressiveFolderExpansion()) {
|
||||
wallpaperZoom = Math.max(wallpaperZoom, mWallpaperZoomOnly.value);
|
||||
}
|
||||
IBinder windowToken = mLauncher.getRootView().getWindowToken();
|
||||
if (windowToken != null && Utilities.ATLEAST_R) {
|
||||
if (enableScalingRevealHomeAnimation()) {
|
||||
mWallpaperManager.setWallpaperZoomOut(windowToken, depth);
|
||||
mWallpaperManager.setWallpaperZoomOut(windowToken, wallpaperZoom);
|
||||
} else {
|
||||
// The API's full zoom-out is three times larger than the zoom-out we apply
|
||||
// to the
|
||||
// icons. To keep the two consistent throughout the animation while keeping
|
||||
// Launcher's concept of full depth unchanged, we divide the depth by 3 here.
|
||||
mWallpaperManager.setWallpaperZoomOut(windowToken, depth / 3);
|
||||
mWallpaperManager.setWallpaperZoomOut(windowToken, wallpaperZoom / 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +278,7 @@ public class BaseDepthController {
|
||||
mCurrentBlur = newBlur;
|
||||
blurWorkspaceDepthTargets();
|
||||
}
|
||||
}
|
||||
|
||||
if (!BlurUtils.supportsBlursOnWindows()) {
|
||||
return;
|
||||
@@ -253,7 +294,6 @@ public class BaseDepthController {
|
||||
return;
|
||||
}
|
||||
mWaitingOnSurfaceValidity = false;
|
||||
|
||||
SurfaceControl blurSurface =
|
||||
enableOverviewBackgroundWallpaperBlur() && mBlurSurface != null ? mBlurSurface
|
||||
: mBaseSurface;
|
||||
@@ -477,7 +517,6 @@ public class BaseDepthController {
|
||||
private static float mapDepthToBlur(float depth) {
|
||||
return Interpolators.clampToProgress(depth, 0, 0.3f);
|
||||
}
|
||||
|
||||
private SurfaceControl.Transaction createTransaction() {
|
||||
return new SurfaceControl.Transaction();
|
||||
}
|
||||
@@ -16,35 +16,47 @@
|
||||
|
||||
package com.android.launcher3.statehandlers
|
||||
|
||||
import android.app.WallpaperManager
|
||||
import android.content.res.Resources
|
||||
import android.os.IBinder
|
||||
import android.platform.test.annotations.DisableFlags
|
||||
import android.platform.test.annotations.EnableFlags
|
||||
import android.platform.test.flag.junit.SetFlagsRule
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.launcher3.Flags
|
||||
import com.android.launcher3.Flags.FLAG_ENABLE_EXPRESSIVE_FOLDER_EXPANSION
|
||||
import com.android.launcher3.Launcher
|
||||
import com.android.launcher3.LauncherRootView
|
||||
import com.android.launcher3.LauncherState
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.dragndrop.DragLayer
|
||||
import com.android.launcher3.statemanager.StateManager
|
||||
import com.android.launcher3.uioverrides.QuickstepLauncher
|
||||
import java.util.Collections
|
||||
import com.android.launcher3.views.ScrimView
|
||||
import junit.framework.TestCase.assertFalse
|
||||
import junit.framework.TestCase.assertNull
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.anyFloat
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.atLeastOnce
|
||||
import org.mockito.Mockito.reset
|
||||
import org.mockito.Mockito.same
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.kotlin.any
|
||||
import java.util.Collections
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class DepthControllerTest {
|
||||
@get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
|
||||
|
||||
private lateinit var underTest: DepthController
|
||||
@Mock private lateinit var launcher: QuickstepLauncher
|
||||
@@ -52,6 +64,10 @@ class DepthControllerTest {
|
||||
@Mock private lateinit var resource: Resources
|
||||
@Mock private lateinit var dragLayer: DragLayer
|
||||
@Mock private lateinit var viewTreeObserver: ViewTreeObserver
|
||||
@Mock private lateinit var wallpaperManager: WallpaperManager
|
||||
@Mock private lateinit var rootView: LauncherRootView
|
||||
@Mock private lateinit var windowToken: IBinder
|
||||
@Mock private lateinit var scrimView: ScrimView
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@@ -62,6 +78,13 @@ class DepthControllerTest {
|
||||
`when`(dragLayer.viewTreeObserver).thenReturn(viewTreeObserver)
|
||||
`when`(launcher.stateManager).thenReturn(stateManager)
|
||||
`when`(launcher.depthBlurTargets).thenReturn(Collections.emptyList())
|
||||
`when`(launcher.getSystemService(WallpaperManager::class.java)).thenReturn(wallpaperManager)
|
||||
`when`(wallpaperManager.setWallpaperZoomOut(any(), anyFloat())).then {
|
||||
// do nothing
|
||||
}
|
||||
`when`(launcher.rootView).thenReturn(rootView)
|
||||
`when`(rootView.windowToken).thenReturn(windowToken)
|
||||
`when`(launcher.scrimView).thenReturn(scrimView)
|
||||
|
||||
underTest = DepthController(launcher)
|
||||
}
|
||||
@@ -205,4 +228,43 @@ class DepthControllerTest {
|
||||
`when`(stateManager.targetState).thenReturn(LauncherState.OVERVIEW)
|
||||
assertFalse(underTest.blurWorkspaceDepthTargets())
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFlags(FLAG_ENABLE_EXPRESSIVE_FOLDER_EXPANSION)
|
||||
fun whenExpressiveFolderExpansionFalse_onlySetDepth() {
|
||||
`when`(stateManager.state).thenReturn(LauncherState.NORMAL)
|
||||
assertNull(underTest.folderZoom)
|
||||
underTest.stateDepth.value = 1f
|
||||
verify(wallpaperManager).setWallpaperZoomOut(windowToken, 1f)
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(FLAG_ENABLE_EXPRESSIVE_FOLDER_EXPANSION)
|
||||
fun wallpaperZoom_setsWallpaperZoom() {
|
||||
val zoom = 0.75f
|
||||
underTest.folderZoom.value = zoom
|
||||
verify(wallpaperManager).setWallpaperZoomOut(windowToken, zoom)
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(FLAG_ENABLE_EXPRESSIVE_FOLDER_EXPANSION)
|
||||
fun wallpaperZoom_andStateDepth_setsMaxWallpaperZoom() {
|
||||
`when`(stateManager.state).thenReturn(LauncherState.NORMAL)
|
||||
val folderZoom = 0.8f
|
||||
val stateDepth = 0.5f
|
||||
underTest.stateDepth.value = stateDepth
|
||||
underTest.folderZoom.value = folderZoom
|
||||
|
||||
// wallpaperZoom should be max of folderZoom and stateDepth
|
||||
verify(wallpaperManager, atLeastOnce()).setWallpaperZoomOut(windowToken, folderZoom)
|
||||
|
||||
reset(wallpaperManager)
|
||||
|
||||
val folderZoom2 = 0.6f
|
||||
val stateDepth2 = 1f
|
||||
underTest.folderZoom.value = folderZoom2
|
||||
underTest.stateDepth.value = stateDepth2
|
||||
|
||||
verify(wallpaperManager, atLeastOnce()).setWallpaperZoomOut(windowToken, stateDepth2)
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,8 @@ class FolderAnimationSpringBuilderManager(
|
||||
workspace.scaleY = 1f
|
||||
hotseat.scaleX = 1f
|
||||
hotseat.scaleY = 1f
|
||||
|
||||
launcherDelegate.launcher?.depthController?.folderZoom?.value = 0f
|
||||
scrim.alpha = 1f
|
||||
scrim.setBackgroundColor(Color.TRANSPARENT)
|
||||
if (Utilities.ATLEAST_S && launcher.stateManager.state.getDepth(launcher) == 0f) {
|
||||
|
||||
@@ -37,6 +37,8 @@ import com.android.launcher3.Utilities.isDarkTheme
|
||||
import com.android.launcher3.anim.SpringAnimationBuilder
|
||||
import com.android.launcher3.apppairs.AppPairIcon
|
||||
import com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW
|
||||
import com.android.launcher3.util.MultiPropertyFactory
|
||||
import com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE
|
||||
|
||||
/** Holder for Animators created from [FolderAnimationSpringBuilderManager] */
|
||||
class FolderSpringAnimatorSet(val animatorSet: AnimatorSet) {
|
||||
@@ -55,6 +57,7 @@ class FolderSpringAnimatorSet(val animatorSet: AnimatorSet) {
|
||||
private const val DAMPING_ALPHA = 0.9f
|
||||
private const val STIFFNESS_LAUNCHER_SCRIM = 380f
|
||||
private const val DAMPING_LAUNCHER_SCRIM = 0.98f
|
||||
private const val WALLPAPER_ZOOM = 0.125f
|
||||
|
||||
/**
|
||||
* Factory method to take data calculated from [FolderAnimationSpringBuilderManager], and
|
||||
@@ -78,6 +81,14 @@ class FolderSpringAnimatorSet(val animatorSet: AnimatorSet) {
|
||||
folderAnimData.isOpening,
|
||||
launcherDelegate,
|
||||
)
|
||||
launcherDelegate.launcher?.depthController?.folderZoom?.let {
|
||||
addWallpaperZoomAnimator(
|
||||
folder.context,
|
||||
animatorSet,
|
||||
folderAnimData.isOpening,
|
||||
it,
|
||||
)
|
||||
}
|
||||
iconAnimData.forEach { addContentIconAnimators(folder.context, animatorSet, it) }
|
||||
return FolderSpringAnimatorSet(animatorSet)
|
||||
}
|
||||
@@ -224,6 +235,27 @@ class FolderSpringAnimatorSet(val animatorSet: AnimatorSet) {
|
||||
)
|
||||
}
|
||||
|
||||
private fun addWallpaperZoomAnimator(
|
||||
context: Context,
|
||||
animatorSet: AnimatorSet,
|
||||
isOpening: Boolean,
|
||||
property: MultiPropertyFactory<*>.MultiProperty,
|
||||
) {
|
||||
playSpringAnimation(
|
||||
context = context,
|
||||
animatorSet = animatorSet,
|
||||
isOpening = isOpening,
|
||||
startDelay = 0,
|
||||
stiffness = STIFFNESS_SHAPE_POSITION,
|
||||
damping = DAMPING_SHAPE_POSITION,
|
||||
startValue = 0f,
|
||||
endValue = WALLPAPER_ZOOM,
|
||||
minVisibleChange = MIN_VISIBLE_CHANGE_SCALE,
|
||||
property = MULTI_PROPERTY_VALUE,
|
||||
view = property,
|
||||
)
|
||||
}
|
||||
|
||||
private fun addAlphaAndColorAnimators(
|
||||
folder: Folder,
|
||||
animatorSet: AnimatorSet,
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.launcher3.statehandlers
|
||||
|
||||
import android.content.Context
|
||||
import com.android.launcher3.anim.AnimatedFloat
|
||||
import com.android.launcher3.statemanager.BaseState
|
||||
import com.android.launcher3.statemanager.StatefulContainer
|
||||
import com.android.launcher3.util.MultiPropertyFactory
|
||||
|
||||
/**
|
||||
* Base interface for controlling depth effects from Launcher3.
|
||||
*/
|
||||
interface BaseDepthController {
|
||||
|
||||
/**
|
||||
* Property for state transitions depth.
|
||||
*/
|
||||
val stateDepth: MultiPropertyFactory<*>.MultiProperty?
|
||||
|
||||
/**
|
||||
* Property for the widget picker depth.
|
||||
*/
|
||||
val widgetDepth: MultiPropertyFactory<*>.MultiProperty?
|
||||
|
||||
/**
|
||||
* Property for folder expansion wallpaper zoom.
|
||||
*/
|
||||
val folderZoom: MultiPropertyFactory<*>.MultiProperty?
|
||||
}
|
||||
@@ -83,6 +83,7 @@ import com.android.launcher3.model.StringCache;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.popup.PopupDataProvider;
|
||||
import com.android.launcher3.statehandlers.BaseDepthController;
|
||||
import com.android.launcher3.util.ActivityOptionsWrapper;
|
||||
import com.android.launcher3.util.ApplicationInfoWrapper;
|
||||
import com.android.launcher3.util.LauncherBindableItemsContainer;
|
||||
@@ -215,6 +216,10 @@ public interface ActivityContext extends SavedStateRegistryOwner {
|
||||
return null;
|
||||
}
|
||||
|
||||
default BaseDepthController getDepthController() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return {@code true} if all apps background blur is enabled */
|
||||
default boolean isAllAppsBackgroundBlurEnabled() {
|
||||
return false;
|
||||
|
||||
Reference in new issue
Block a user