1 /* <lambda>null2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.controls.management 18 19 import android.animation.Animator 20 import android.animation.AnimatorListenerAdapter 21 import android.animation.AnimatorSet 22 import android.animation.ObjectAnimator 23 import android.annotation.IdRes 24 import android.content.Intent 25 26 import android.transition.Transition 27 import android.transition.TransitionValues 28 import android.util.Log 29 import android.view.View 30 import android.view.ViewGroup 31 import android.view.Window 32 33 import androidx.lifecycle.Lifecycle 34 import androidx.lifecycle.LifecycleObserver 35 import androidx.lifecycle.OnLifecycleEvent 36 37 import com.android.systemui.Interpolators 38 import com.android.systemui.R 39 40 import com.android.systemui.controls.ui.ControlsUiController 41 42 object ControlsAnimations { 43 44 private const val ALPHA_EXIT_DURATION = 167L 45 private const val ALPHA_ENTER_DELAY = ALPHA_EXIT_DURATION 46 private const val ALPHA_ENTER_DURATION = 350L - ALPHA_ENTER_DELAY 47 48 private const val Y_TRANSLATION_EXIT_DURATION = 183L 49 private const val Y_TRANSLATION_ENTER_DELAY = Y_TRANSLATION_EXIT_DURATION - ALPHA_ENTER_DELAY 50 private const val Y_TRANSLATION_ENTER_DURATION = 400L - Y_TRANSLATION_EXIT_DURATION 51 private var translationY: Float = -1f 52 53 /** 54 * Setup an activity to handle enter/exit animations. [view] should be the root of the content. 55 * Fade and translate together. 56 */ 57 fun observerForAnimations(view: ViewGroup, window: Window, intent: Intent): LifecycleObserver { 58 return object : LifecycleObserver { 59 var showAnimation = intent.getBooleanExtra(ControlsUiController.EXTRA_ANIMATE, false) 60 61 init { 62 // Must flag the parent group to move it all together, and set the initial 63 // transitionAlpha to 0.0f. This property is reserved for fade animations. 64 view.setTransitionGroup(true) 65 view.transitionAlpha = 0.0f 66 67 if (translationY == -1f) { 68 translationY = view.context.resources.getDimensionPixelSize( 69 R.dimen.global_actions_controls_y_translation).toFloat() 70 } 71 } 72 73 @OnLifecycleEvent(Lifecycle.Event.ON_START) 74 fun setup() { 75 with(window) { 76 allowEnterTransitionOverlap = true 77 enterTransition = enterWindowTransition(view.getId()) 78 exitTransition = exitWindowTransition(view.getId()) 79 reenterTransition = enterWindowTransition(view.getId()) 80 returnTransition = exitWindowTransition(view.getId()) 81 } 82 } 83 84 @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) 85 fun enterAnimation() { 86 if (showAnimation) { 87 ControlsAnimations.enterAnimation(view).start() 88 showAnimation = false 89 } 90 } 91 92 @OnLifecycleEvent(Lifecycle.Event.ON_STOP) 93 fun resetAnimation() { 94 view.translationY = 0f 95 } 96 } 97 } 98 99 fun enterAnimation(view: View): Animator { 100 Log.d(ControlsUiController.TAG, "Enter animation for $view") 101 102 view.transitionAlpha = 0.0f 103 view.alpha = 1.0f 104 105 view.translationY = translationY 106 107 val alphaAnimator = ObjectAnimator.ofFloat(view, "transitionAlpha", 0.0f, 1.0f).apply { 108 interpolator = Interpolators.DECELERATE_QUINT 109 startDelay = ALPHA_ENTER_DELAY 110 duration = ALPHA_ENTER_DURATION 111 } 112 113 val yAnimator = ObjectAnimator.ofFloat(view, "translationY", 0.0f).apply { 114 interpolator = Interpolators.DECELERATE_QUINT 115 startDelay = Y_TRANSLATION_ENTER_DURATION 116 duration = Y_TRANSLATION_ENTER_DURATION 117 } 118 119 return AnimatorSet().apply { 120 playTogether(alphaAnimator, yAnimator) 121 } 122 } 123 124 /** 125 * Properly handle animations originating from dialogs. Activity transitions require 126 * transitioning between two activities, so expose this method for dialogs to animate 127 * on exit. 128 */ 129 @JvmStatic 130 fun exitAnimation(view: View, onEnd: Runnable? = null): Animator { 131 Log.d(ControlsUiController.TAG, "Exit animation for $view") 132 133 val alphaAnimator = ObjectAnimator.ofFloat(view, "transitionAlpha", 0.0f).apply { 134 interpolator = Interpolators.ACCELERATE 135 duration = ALPHA_EXIT_DURATION 136 } 137 138 view.translationY = 0.0f 139 val yAnimator = ObjectAnimator.ofFloat(view, "translationY", -translationY).apply { 140 interpolator = Interpolators.ACCELERATE 141 duration = Y_TRANSLATION_EXIT_DURATION 142 } 143 144 return AnimatorSet().apply { 145 playTogether(alphaAnimator, yAnimator) 146 onEnd?.let { 147 addListener(object : AnimatorListenerAdapter() { 148 override fun onAnimationEnd(animation: Animator) { 149 it.run() 150 } 151 }) 152 } 153 } 154 } 155 156 fun enterWindowTransition(@IdRes id: Int) = 157 WindowTransition({ view: View -> enterAnimation(view) }).apply { 158 addTarget(id) 159 } 160 161 fun exitWindowTransition(@IdRes id: Int) = 162 WindowTransition({ view: View -> exitAnimation(view) }).apply { 163 addTarget(id) 164 } 165 } 166 167 /** 168 * In order to animate, at least one property must be marked on each view that should move. 169 * Setting "item" is just a flag to indicate that it should move by the animator. 170 */ 171 class WindowTransition( 172 val animator: (view: View) -> Animator 173 ) : Transition() { captureStartValuesnull174 override fun captureStartValues(tv: TransitionValues) { 175 tv.values["item"] = 0.0f 176 } 177 captureEndValuesnull178 override fun captureEndValues(tv: TransitionValues) { 179 tv.values["item"] = 1.0f 180 } 181 createAnimatornull182 override fun createAnimator( 183 sceneRoot: ViewGroup, 184 startValues: TransitionValues?, 185 endValues: TransitionValues? 186 ): Animator? = animator(startValues!!.view) 187 } 188