mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-19 02:38:20 +00:00
Merge "Adding CannedAnimationCoordinator to enable running a predefined animation in Launcher" into udc-qpr-dev am: 1d235fca54
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/24487141 Change-Id: I996475b1a4c5bb628aa30c9a42562cb45ebf6efd Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -198,6 +198,7 @@ import com.android.launcher3.touch.ItemLongClickListener;
|
||||
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
|
||||
import com.android.launcher3.util.ActivityResultInfo;
|
||||
import com.android.launcher3.util.ActivityTracker;
|
||||
import com.android.launcher3.util.CannedAnimationCoordinator;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.IntSet;
|
||||
@@ -421,6 +422,9 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
private StartupLatencyLogger mStartupLatencyLogger;
|
||||
private CellPosMapper mCellPosMapper = CellPosMapper.DEFAULT;
|
||||
|
||||
private final CannedAnimationCoordinator mAnimationCoordinator =
|
||||
new CannedAnimationCoordinator(this);
|
||||
|
||||
@Override
|
||||
@TargetApi(Build.VERSION_CODES.S)
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -3422,4 +3426,11 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
public void launchAppPair(WorkspaceItemInfo app1, WorkspaceItemInfo app2) {
|
||||
// Overridden
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the animation coordinator for playing one-off animations
|
||||
*/
|
||||
public CannedAnimationCoordinator getAnimationCoordinator() {
|
||||
return mAnimationCoordinator;
|
||||
}
|
||||
}
|
||||
|
||||
164
src/com/android/launcher3/util/CannedAnimationCoordinator.kt
Normal file
164
src/com/android/launcher3/util/CannedAnimationCoordinator.kt
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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.launcher3.util
|
||||
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ValueAnimator
|
||||
import android.util.Log
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener
|
||||
import androidx.core.view.OneShotPreDrawListener
|
||||
import com.android.app.animation.Interpolators.LINEAR
|
||||
import com.android.launcher3.anim.AnimatorListeners
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController
|
||||
import com.android.launcher3.anim.PendingAnimation
|
||||
import com.android.launcher3.statemanager.StatefulActivity
|
||||
import java.util.function.Consumer
|
||||
|
||||
private const val TAG = "CannedAnimCoordinator"
|
||||
|
||||
/**
|
||||
* Utility class to run a canned animation on Launcher.
|
||||
*
|
||||
* This class takes care to registering animations with stateManager and ensures that only one
|
||||
* animation is playing at a time.
|
||||
*/
|
||||
class CannedAnimationCoordinator(private val activity: StatefulActivity<*>) {
|
||||
|
||||
private val launcherLayoutListener = OnGlobalLayoutListener { scheduleRecreateAnimOnPreDraw() }
|
||||
private var recreatePending = false
|
||||
|
||||
private var animationProvider: Any? = null
|
||||
|
||||
private var animationDuration: Long = 0L
|
||||
private var animationFactory: Consumer<PendingAnimation>? = null
|
||||
private var animationController: AnimatorPlaybackController? = null
|
||||
|
||||
private var currentAnim: AnimatorPlaybackController? = null
|
||||
|
||||
/**
|
||||
* Sets the current animation cancelling any previously set animation.
|
||||
*
|
||||
* Callers can control the animation using {@link #getPlaybackController}. The state is
|
||||
* automatically cleared when the playback controller ends. The animation is automatically
|
||||
* recreated when any layout change happens. Callers can also ask for recreation by calling
|
||||
* {@link #recreateAnimation}
|
||||
*/
|
||||
fun setAnimation(provider: Any, factory: Consumer<PendingAnimation>, duration: Long) {
|
||||
if (provider != animationProvider) {
|
||||
Log.e(TAG, "Trying to play two animations together, $provider and $animationProvider")
|
||||
}
|
||||
|
||||
// Cancel any previously running animation
|
||||
endCurrentAnimation(false)
|
||||
animationController?.dispatchOnCancel()?.dispatchOnEnd()
|
||||
|
||||
animationProvider = provider
|
||||
animationFactory = factory
|
||||
animationDuration = duration
|
||||
|
||||
// Setup a new controller and link it with launcher state animation
|
||||
val anim = AnimatorSet()
|
||||
anim.play(
|
||||
ValueAnimator.ofFloat(0f, 1f).apply {
|
||||
interpolator = LINEAR
|
||||
this.duration = duration
|
||||
addUpdateListener { anim -> currentAnim?.setPlayFraction(anim.animatedFraction) }
|
||||
}
|
||||
)
|
||||
val controller = AnimatorPlaybackController.wrap(anim, duration)
|
||||
anim.addListener(
|
||||
AnimatorListeners.forEndCallback { success ->
|
||||
if (animationController != controller) {
|
||||
return@forEndCallback
|
||||
}
|
||||
|
||||
endCurrentAnimation(success)
|
||||
animationController = null
|
||||
animationFactory = null
|
||||
animationProvider = null
|
||||
|
||||
activity.rootView.viewTreeObserver.apply {
|
||||
if (isAlive) {
|
||||
removeOnGlobalLayoutListener(launcherLayoutListener)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// Recreate animation whenever layout happens in case transforms change during layout
|
||||
activity.rootView.viewTreeObserver.apply {
|
||||
if (isAlive) {
|
||||
addOnGlobalLayoutListener(launcherLayoutListener)
|
||||
}
|
||||
}
|
||||
// Link this to the state manager so that it auto-cancels when state changes
|
||||
recreatePending = false
|
||||
animationController =
|
||||
controller.apply { activity.stateManager.setCurrentUserControlledAnimation(this) }
|
||||
recreateAnimation(provider)
|
||||
}
|
||||
|
||||
private fun endCurrentAnimation(success: Boolean) {
|
||||
currentAnim?.apply {
|
||||
// When cancelling an animation, apply final progress so that all transformations
|
||||
// are restored
|
||||
setPlayFraction(1f)
|
||||
if (!success) dispatchOnCancel()
|
||||
dispatchOnEnd()
|
||||
}
|
||||
currentAnim = null
|
||||
}
|
||||
|
||||
/** Returns the current animation controller to control the animation */
|
||||
fun getPlaybackController(provider: Any): AnimatorPlaybackController? {
|
||||
return if (provider == animationProvider) animationController
|
||||
else {
|
||||
Log.d(TAG, "Wrong controller access from $provider, actual provider $animationProvider")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun scheduleRecreateAnimOnPreDraw() {
|
||||
if (!recreatePending) {
|
||||
recreatePending = true
|
||||
OneShotPreDrawListener.add(activity.rootView) {
|
||||
if (recreatePending) {
|
||||
recreatePending = false
|
||||
animationProvider?.apply { recreateAnimation(this) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Notify the controller to recreate the animation. The animation progress is preserved */
|
||||
fun recreateAnimation(provider: Any) {
|
||||
if (provider != animationProvider) {
|
||||
Log.e(TAG, "Ignore recreate request from $provider, actual provider $animationProvider")
|
||||
return
|
||||
}
|
||||
endCurrentAnimation(false /* success */)
|
||||
|
||||
if (animationFactory == null || animationController == null) {
|
||||
return
|
||||
}
|
||||
currentAnim =
|
||||
PendingAnimation(animationDuration)
|
||||
.apply { animationFactory?.accept(this) }
|
||||
.createPlaybackController()
|
||||
.apply { setPlayFraction(animationController!!.progressFraction) }
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,7 @@ public class MultiScalePropertyFactory<T extends View> {
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "MultiScaleProperty";
|
||||
private final String mName;
|
||||
private final ArrayMap<Integer, MultiScaleProperty> mProperties =
|
||||
new ArrayMap<Integer, MultiScaleProperty>();
|
||||
private final ArrayMap<Integer, MultiScaleProperty> mProperties = new ArrayMap<>();
|
||||
|
||||
// This is an optimization for cases when set is called repeatedly with the same setterIndex.
|
||||
private float mMinOfOthers = 0;
|
||||
@@ -55,7 +54,7 @@ public class MultiScalePropertyFactory<T extends View> {
|
||||
}
|
||||
|
||||
/** Returns the [MultiFloatProperty] associated with [inx], creating it if not present. */
|
||||
public MultiScaleProperty get(Integer index) {
|
||||
public FloatProperty<T> get(Integer index) {
|
||||
return mProperties.computeIfAbsent(index,
|
||||
(k) -> new MultiScaleProperty(index, mName + "_" + index));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user