Implementing an unfold animation using CannedAnimationController
Bug: 297057373 Flag: aconfig enable_unfold_state_animation DISABLED Test: Manual Change-Id: Iaa352133ebf06ab74b9ba4b8c4d3523f27666089
This commit is contained in:
@@ -127,3 +127,10 @@ flag {
|
||||
description: "Enables asnc inflation of workspace icons"
|
||||
bug: "318539160"
|
||||
}
|
||||
|
||||
flag {
|
||||
name: "enable_unfold_state_animation"
|
||||
namespace: "launcher"
|
||||
description: "Tie unfold animation with state animation"
|
||||
bug: "297057373"
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
|
||||
|
||||
import static com.android.launcher3.BaseActivity.EVENT_DESTROYED;
|
||||
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
|
||||
import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate;
|
||||
@@ -405,8 +406,12 @@ public class TaskbarManager {
|
||||
*/
|
||||
private UnfoldTransitionProgressProvider getUnfoldTransitionProgressProviderForActivity(
|
||||
StatefulActivity activity) {
|
||||
if (activity instanceof QuickstepLauncher) {
|
||||
return ((QuickstepLauncher) activity).getUnfoldTransitionProgressProvider();
|
||||
if (!enableUnfoldStateAnimation()) {
|
||||
if (activity instanceof QuickstepLauncher ql) {
|
||||
return ql.getUnfoldTransitionProgressProvider();
|
||||
}
|
||||
} else {
|
||||
return SystemUiProxy.INSTANCE.get(mContext).getUnfoldTransitionProvider();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEAS
|
||||
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
|
||||
|
||||
import static com.android.app.animation.Interpolators.EMPHASIZED;
|
||||
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
|
||||
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO;
|
||||
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE;
|
||||
import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON;
|
||||
@@ -170,6 +171,8 @@ import com.android.quickstep.util.SplitSelectStateController;
|
||||
import com.android.quickstep.util.SplitToWorkspaceController;
|
||||
import com.android.quickstep.util.SplitWithKeyboardShortcutController;
|
||||
import com.android.quickstep.util.TISBindHelper;
|
||||
import com.android.quickstep.util.unfold.LauncherUnfoldTransitionController;
|
||||
import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider;
|
||||
import com.android.quickstep.views.FloatingTaskView;
|
||||
import com.android.quickstep.views.OverviewActionsView;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
@@ -955,9 +958,17 @@ public class QuickstepLauncher extends Launcher {
|
||||
}
|
||||
|
||||
private void initUnfoldTransitionProgressProvider() {
|
||||
final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig();
|
||||
if (config.isEnabled()) {
|
||||
initRemotelyCalculatedUnfoldAnimation(config);
|
||||
if (!enableUnfoldStateAnimation()) {
|
||||
final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig();
|
||||
if (config.isEnabled()) {
|
||||
initRemotelyCalculatedUnfoldAnimation(config);
|
||||
}
|
||||
} else {
|
||||
ProxyUnfoldTransitionProvider provider =
|
||||
SystemUiProxy.INSTANCE.get(this).getUnfoldTransitionProvider();
|
||||
if (provider != null) {
|
||||
new LauncherUnfoldTransitionController(this, provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.android.quickstep;
|
||||
|
||||
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
|
||||
|
||||
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
|
||||
@@ -67,6 +68,7 @@ import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.quickstep.util.AssistUtils;
|
||||
import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider;
|
||||
import com.android.systemui.shared.recents.ISystemUiProxy;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
|
||||
@@ -74,6 +76,7 @@ import com.android.systemui.shared.system.RecentsAnimationListener;
|
||||
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
|
||||
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
|
||||
import com.android.systemui.shared.system.smartspace.SmartspaceState;
|
||||
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
|
||||
import com.android.systemui.unfold.progress.IUnfoldAnimation;
|
||||
import com.android.systemui.unfold.progress.IUnfoldTransitionListener;
|
||||
import com.android.wm.shell.back.IBackAnimation;
|
||||
@@ -177,7 +180,10 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
|
||||
*/
|
||||
private final PendingIntent mRecentsPendingIntent;
|
||||
|
||||
public SystemUiProxy(Context context) {
|
||||
@Nullable
|
||||
private final ProxyUnfoldTransitionProvider mUnfoldTransitionProvider;
|
||||
|
||||
private SystemUiProxy(Context context) {
|
||||
mContext = context;
|
||||
mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync);
|
||||
final Intent baseIntent = new Intent().setPackage(mContext.getPackageName());
|
||||
@@ -187,6 +193,10 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
|
||||
mRecentsPendingIntent = PendingIntent.getActivity(mContext, 0, baseIntent,
|
||||
PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
|
||||
| Intent.FILL_IN_COMPONENT, options.toBundle());
|
||||
|
||||
mUnfoldTransitionProvider =
|
||||
(enableUnfoldStateAnimation() && new ResourceUnfoldTransitionConfig().isEnabled())
|
||||
? new ProxyUnfoldTransitionProvider() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -251,7 +261,7 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
|
||||
mRecentTasks = recentTasks;
|
||||
mBackAnimation = backAnimation;
|
||||
mDesktopMode = desktopMode;
|
||||
mUnfoldAnimation = unfoldAnimation;
|
||||
mUnfoldAnimation = enableUnfoldStateAnimation() ? null : unfoldAnimation;
|
||||
mDragAndDrop = dragAndDrop;
|
||||
linkToDeath();
|
||||
// re-attach the listeners once missing due to setProxy has not been initialized yet.
|
||||
@@ -272,6 +282,19 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
|
||||
setAssistantOverridesRequested(
|
||||
AssistUtils.newInstance(mContext).getSysUiAssistOverrideInvocationTypes());
|
||||
mStateChangeCallbacks.forEach(Runnable::run);
|
||||
|
||||
if (mUnfoldTransitionProvider != null) {
|
||||
if (unfoldAnimation != null) {
|
||||
try {
|
||||
unfoldAnimation.setListener(mUnfoldTransitionProvider);
|
||||
mUnfoldTransitionProvider.setActive(true);
|
||||
} catch (RemoteException e) {
|
||||
// Ignore
|
||||
}
|
||||
} else {
|
||||
mUnfoldTransitionProvider.setActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1451,6 +1474,11 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ProxyUnfoldTransitionProvider getUnfoldTransitionProvider() {
|
||||
return mUnfoldTransitionProvider;
|
||||
}
|
||||
|
||||
//
|
||||
// Recents
|
||||
//
|
||||
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.unfold
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Trace
|
||||
import android.view.Surface
|
||||
import com.android.launcher3.Alarm
|
||||
import com.android.launcher3.DeviceProfile
|
||||
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener
|
||||
import com.android.launcher3.Launcher
|
||||
import com.android.launcher3.anim.PendingAnimation
|
||||
import com.android.launcher3.config.FeatureFlags
|
||||
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter
|
||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
||||
|
||||
/** Controls animations that are happening during unfolding foldable devices */
|
||||
class LauncherUnfoldTransitionController(
|
||||
private val launcher: Launcher,
|
||||
private val progressProvider: ProxyUnfoldTransitionProvider
|
||||
) : OnDeviceProfileChangeListener, ActivityLifecycleCallbacksAdapter, TransitionProgressListener {
|
||||
|
||||
private var isTablet: Boolean? = null
|
||||
private var hasUnfoldTransitionStarted = false
|
||||
private val timeoutAlarm =
|
||||
Alarm().apply {
|
||||
setOnAlarmListener {
|
||||
onTransitionFinished()
|
||||
Trace.endAsyncSection("$TAG#startedPreemptively", 0)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
launcher.addOnDeviceProfileChangeListener(this)
|
||||
launcher.registerActivityLifecycleCallbacks(this)
|
||||
}
|
||||
|
||||
override fun onActivityPaused(activity: Activity) {
|
||||
progressProvider.removeCallback(this)
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
progressProvider.addCallback(this)
|
||||
}
|
||||
|
||||
override fun onDeviceProfileChanged(dp: DeviceProfile) {
|
||||
if (!FeatureFlags.PREEMPTIVE_UNFOLD_ANIMATION_START.get()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (isTablet != null && dp.isTablet != isTablet) {
|
||||
// We should preemptively start the animation only if:
|
||||
// - We changed to the unfolded screen
|
||||
// - SystemUI IPC connection is alive, so we won't end up in a situation that we won't
|
||||
// receive transition progress events from SystemUI later because there was no
|
||||
// IPC connection established (e.g. because of SystemUI crash)
|
||||
// - SystemUI has not already sent unfold animation progress events. This might happen
|
||||
// if Launcher was not open during unfold, in this case we receive the configuration
|
||||
// change only after we went back to home screen and we don't want to start the
|
||||
// animation in this case.
|
||||
if (dp.isTablet && progressProvider.isActive && !hasUnfoldTransitionStarted) {
|
||||
// Preemptively start the unfold animation to make sure that we have drawn
|
||||
// the first frame of the animation before the screen gets unblocked
|
||||
onTransitionStarted()
|
||||
Trace.beginAsyncSection("$TAG#startedPreemptively", 0)
|
||||
timeoutAlarm.setAlarm(PREEMPTIVE_UNFOLD_TIMEOUT_MS)
|
||||
}
|
||||
if (!dp.isTablet) {
|
||||
// Reset unfold transition status when folded
|
||||
hasUnfoldTransitionStarted = false
|
||||
}
|
||||
}
|
||||
|
||||
isTablet = dp.isTablet
|
||||
}
|
||||
|
||||
override fun onTransitionStarted() {
|
||||
hasUnfoldTransitionStarted = true
|
||||
launcher.animationCoordinator.setAnimation(
|
||||
provider = this,
|
||||
factory = this::onPrepareUnfoldAnimation,
|
||||
duration =
|
||||
1000L // The expected duration for the animation. Then only comes to play if we have
|
||||
// to run the animation ourselves in case sysui misses the end signal
|
||||
)
|
||||
timeoutAlarm.cancelAlarm()
|
||||
}
|
||||
|
||||
override fun onTransitionProgress(progress: Float) {
|
||||
hasUnfoldTransitionStarted = true
|
||||
launcher.animationCoordinator.getPlaybackController(this)?.setPlayFraction(progress)
|
||||
}
|
||||
|
||||
override fun onTransitionFinished() {
|
||||
// Run the animation to end the animation in case it is not already at end progress. It
|
||||
// will scale the duration to the remaining progress
|
||||
launcher.animationCoordinator.getPlaybackController(this)?.start()
|
||||
timeoutAlarm.cancelAlarm()
|
||||
}
|
||||
|
||||
private fun onPrepareUnfoldAnimation(anim: PendingAnimation) {
|
||||
val dp = launcher.deviceProfile
|
||||
val rotation = dp.displayInfo.rotation
|
||||
val isVertical = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
|
||||
UnfoldAnimationBuilder.buildUnfoldAnimation(
|
||||
launcher,
|
||||
isVertical,
|
||||
dp.displayInfo.currentSize,
|
||||
anim
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "LauncherUnfoldTransitionController"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.unfold
|
||||
|
||||
import androidx.annotation.AnyThread
|
||||
import androidx.annotation.FloatRange
|
||||
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
|
||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
|
||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
||||
import com.android.systemui.unfold.progress.IUnfoldTransitionListener
|
||||
import com.android.systemui.unfold.progress.UnfoldRemoteFilter
|
||||
|
||||
/** Receives unfold events from remote senders (System UI). */
|
||||
class ProxyUnfoldTransitionProvider :
|
||||
UnfoldTransitionProgressProvider, IUnfoldTransitionListener.Stub() {
|
||||
|
||||
private val listeners: MutableSet<TransitionProgressListener> = mutableSetOf()
|
||||
private val delegate = UnfoldRemoteFilter(ProcessedProgressListener())
|
||||
|
||||
private var transitionStarted = false
|
||||
var isActive = false
|
||||
set(value) {
|
||||
field = value
|
||||
if (!value) {
|
||||
// Finish any active transition
|
||||
onTransitionFinished()
|
||||
}
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
override fun onTransitionStarted() {
|
||||
MAIN_EXECUTOR.execute(delegate::onTransitionStarted)
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
override fun onTransitionProgress(progress: Float) {
|
||||
MAIN_EXECUTOR.execute { delegate.onTransitionProgress(progress) }
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
override fun onTransitionFinished() {
|
||||
MAIN_EXECUTOR.execute(delegate::onTransitionFinished)
|
||||
}
|
||||
|
||||
override fun addCallback(listener: TransitionProgressListener) {
|
||||
listeners += listener
|
||||
if (transitionStarted) {
|
||||
// Update the listener in case there was is an active transition
|
||||
listener.onTransitionStarted()
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeCallback(listener: TransitionProgressListener) {
|
||||
listeners -= listener
|
||||
if (transitionStarted) {
|
||||
// Finish the transition if it was already running
|
||||
listener.onTransitionFinished()
|
||||
}
|
||||
}
|
||||
|
||||
override fun destroy() {
|
||||
listeners.clear()
|
||||
}
|
||||
|
||||
private inner class ProcessedProgressListener : TransitionProgressListener {
|
||||
override fun onTransitionStarted() {
|
||||
if (!transitionStarted) {
|
||||
transitionStarted = true
|
||||
listeners.forEach(TransitionProgressListener::onTransitionStarted)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {
|
||||
listeners.forEach { it.onTransitionProgress(progress) }
|
||||
}
|
||||
|
||||
override fun onTransitionFinished() {
|
||||
if (transitionStarted) {
|
||||
transitionStarted = false
|
||||
listeners.forEach(TransitionProgressListener::onTransitionFinished)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.unfold
|
||||
|
||||
import android.graphics.Point
|
||||
import android.view.ViewGroup
|
||||
import com.android.app.animation.Interpolators.LINEAR
|
||||
import com.android.app.animation.Interpolators.clampToProgress
|
||||
import com.android.launcher3.CellLayout
|
||||
import com.android.launcher3.Launcher
|
||||
import com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY
|
||||
import com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_UNFOLD_ANIMATION
|
||||
import com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X
|
||||
import com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y
|
||||
import com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY
|
||||
import com.android.launcher3.Workspace
|
||||
import com.android.launcher3.anim.PendingAnimation
|
||||
import com.android.launcher3.util.HorizontalInsettableView
|
||||
|
||||
private typealias ViewGroupAction = (ViewGroup, Boolean) -> Unit
|
||||
|
||||
object UnfoldAnimationBuilder {
|
||||
|
||||
private val CLIP_CHILDREN: ViewGroupAction = ViewGroup::setClipChildren
|
||||
private val CLIP_TO_PADDING: ViewGroupAction = ViewGroup::setClipToPadding
|
||||
|
||||
data class RestoreInfo(val action: ViewGroupAction, var target: ViewGroup, var value: Boolean)
|
||||
|
||||
// Percentage of the width of the quick search bar that will be reduced
|
||||
// from the both sides of the bar when progress is 0
|
||||
private const val MAX_WIDTH_INSET_FRACTION = 0.04f
|
||||
|
||||
// Scale factor for the whole workspace and hotseat
|
||||
private const val SCALE_LAUNCHER_FROM = 0.92f
|
||||
|
||||
// Translation factor for all the items on the homescreen
|
||||
private const val TRANSLATION_PERCENTAGE = 0.08f
|
||||
|
||||
private fun setClipChildren(
|
||||
target: ViewGroup,
|
||||
value: Boolean,
|
||||
restoreList: MutableList<RestoreInfo>
|
||||
) {
|
||||
val originalValue = target.clipChildren
|
||||
if (originalValue != value) {
|
||||
target.clipChildren = value
|
||||
restoreList.add(RestoreInfo(CLIP_CHILDREN, target, originalValue))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setClipToPadding(
|
||||
target: ViewGroup,
|
||||
value: Boolean,
|
||||
restoreList: MutableList<RestoreInfo>
|
||||
) {
|
||||
val originalValue = target.clipToPadding
|
||||
if (originalValue != value) {
|
||||
target.clipToPadding = value
|
||||
restoreList.add(RestoreInfo(CLIP_TO_PADDING, target, originalValue))
|
||||
}
|
||||
}
|
||||
|
||||
private fun addChildrenAnimation(
|
||||
itemsContainer: ViewGroup,
|
||||
isVerticalFold: Boolean,
|
||||
screenSize: Point,
|
||||
anim: PendingAnimation
|
||||
) {
|
||||
val tempLocation = IntArray(2)
|
||||
for (i in 0 until itemsContainer.childCount) {
|
||||
val child = itemsContainer.getChildAt(i)
|
||||
|
||||
child.getLocationOnScreen(tempLocation)
|
||||
if (isVerticalFold) {
|
||||
val viewCenterX = tempLocation[0] + child.width / 2
|
||||
val distanceFromScreenCenterToViewCenter = screenSize.x / 2 - viewCenterX
|
||||
anim.addFloat(
|
||||
child,
|
||||
VIEW_TRANSLATE_X,
|
||||
distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE,
|
||||
0f,
|
||||
LINEAR
|
||||
)
|
||||
} else {
|
||||
val viewCenterY = tempLocation[1] + child.height / 2
|
||||
val distanceFromScreenCenterToViewCenter = screenSize.y / 2 - viewCenterY
|
||||
anim.addFloat(
|
||||
child,
|
||||
VIEW_TRANSLATE_Y,
|
||||
distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE,
|
||||
0f,
|
||||
LINEAR
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an animation for the unfold experience and adds it to the provided PendingAnimation
|
||||
*/
|
||||
fun buildUnfoldAnimation(
|
||||
launcher: Launcher,
|
||||
isVerticalFold: Boolean,
|
||||
screenSize: Point,
|
||||
anim: PendingAnimation
|
||||
) {
|
||||
val restoreList = ArrayList<RestoreInfo>()
|
||||
val registerViews: (CellLayout) -> Unit = { cellLayout ->
|
||||
setClipChildren(cellLayout, false, restoreList)
|
||||
setClipToPadding(cellLayout, false, restoreList)
|
||||
addChildrenAnimation(cellLayout.shortcutsAndWidgets, isVerticalFold, screenSize, anim)
|
||||
}
|
||||
|
||||
val workspace: Workspace<*> = launcher.workspace
|
||||
val hotseat = launcher.hotseat
|
||||
|
||||
// Animation icons from workspace for all orientations
|
||||
workspace.forEachVisiblePage { registerViews(it as CellLayout) }
|
||||
setClipChildren(workspace, false, restoreList)
|
||||
setClipToPadding(workspace, true, restoreList)
|
||||
|
||||
// Workspace scale
|
||||
launcher.workspace.setPivotToScaleWithSelf(launcher.hotseat)
|
||||
val interpolator = clampToProgress(LINEAR, 0f, 1f)
|
||||
anim.addFloat(
|
||||
workspace,
|
||||
WORKSPACE_SCALE_PROPERTY_FACTORY[SCALE_INDEX_UNFOLD_ANIMATION],
|
||||
SCALE_LAUNCHER_FROM,
|
||||
1f,
|
||||
interpolator
|
||||
)
|
||||
anim.addFloat(
|
||||
hotseat,
|
||||
HOTSEAT_SCALE_PROPERTY_FACTORY[SCALE_INDEX_UNFOLD_ANIMATION],
|
||||
SCALE_LAUNCHER_FROM,
|
||||
1f,
|
||||
interpolator
|
||||
)
|
||||
|
||||
if (isVerticalFold) {
|
||||
if (hotseat.qsb is HorizontalInsettableView) {
|
||||
anim.addFloat(
|
||||
hotseat.qsb as HorizontalInsettableView,
|
||||
HorizontalInsettableView.HORIZONTAL_INSETS,
|
||||
MAX_WIDTH_INSET_FRACTION,
|
||||
0f,
|
||||
LINEAR
|
||||
)
|
||||
}
|
||||
registerViews(hotseat)
|
||||
}
|
||||
anim.addEndListener { restoreList.forEach { it.action(it.target, it.value) } }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user