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 import android.transition.Transition 26 import android.transition.TransitionValues 27 import android.util.Log 28 import android.view.View 29 import android.view.ViewGroup 30 import android.view.Window 31 import androidx.lifecycle.Lifecycle 32 import androidx.lifecycle.LifecycleObserver 33 import androidx.lifecycle.OnLifecycleEvent 34 import com.android.systemui.res.R 35 import com.android.app.animation.Interpolators 36 import com.android.systemui.controls.ui.ControlsUiController 37 38 object ControlsAnimations { 39 40 private const val ALPHA_EXIT_DURATION = 183L 41 private const val ALPHA_ENTER_DELAY = ALPHA_EXIT_DURATION 42 private const val ALPHA_ENTER_DURATION = 350L - ALPHA_ENTER_DELAY 43 44 private const val Y_TRANSLATION_EXIT_DURATION = 183L 45 private const val Y_TRANSLATION_ENTER_DELAY = Y_TRANSLATION_EXIT_DURATION - ALPHA_ENTER_DELAY 46 private const val Y_TRANSLATION_ENTER_DURATION = 400L - Y_TRANSLATION_EXIT_DURATION 47 private var translationY: Float = -1f 48 49 /** 50 * Setup an activity to handle enter/exit animations. [view] should be the root of the content. 51 * Fade and translate together. 52 */ 53 fun observerForAnimations( 54 view: ViewGroup, 55 window: Window, 56 intent: Intent, 57 animateY: Boolean = true 58 ): LifecycleObserver { 59 return object : LifecycleObserver { 60 var showAnimation = intent.getBooleanExtra(ControlsUiController.EXTRA_ANIMATE, false) 61 62 init { 63 // Must flag the parent group to move it all together, and set the initial 64 // transitionAlpha to 0.0f. This property is reserved for fade animations. 65 view.setTransitionGroup(true) 66 view.transitionAlpha = 0.0f 67 68 if (translationY == -1f) { 69 if (animateY) { 70 translationY = view.context.resources.getDimensionPixelSize( 71 R.dimen.global_actions_controls_y_translation).toFloat() 72 } else { 73 translationY = 0f 74 } 75 } 76 } 77 78 @OnLifecycleEvent(Lifecycle.Event.ON_START) 79 fun setup() { 80 with(window) { 81 allowEnterTransitionOverlap = true 82 enterTransition = enterWindowTransition(view.getId()) 83 exitTransition = exitWindowTransition(view.getId()) 84 reenterTransition = enterWindowTransition(view.getId()) 85 returnTransition = exitWindowTransition(view.getId()) 86 } 87 } 88 89 @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) 90 fun enterAnimation() { 91 if (showAnimation) { 92 ControlsAnimations.enterAnimation(view).start() 93 showAnimation = false 94 } 95 } 96 97 @OnLifecycleEvent(Lifecycle.Event.ON_STOP) 98 fun resetAnimation() { 99 view.translationY = 0f 100 } 101 } 102 } 103 104 fun enterAnimation(view: View): Animator { 105 Log.d(ControlsUiController.TAG, "Enter animation for $view") 106 107 view.transitionAlpha = 0.0f 108 view.alpha = 1.0f 109 110 view.translationY = translationY 111 112 val alphaAnimator = ObjectAnimator.ofFloat(view, "transitionAlpha", 0.0f, 1.0f).apply { 113 interpolator = Interpolators.DECELERATE_QUINT 114 startDelay = ALPHA_ENTER_DELAY 115 duration = ALPHA_ENTER_DURATION 116 } 117 118 val yAnimator = ObjectAnimator.ofFloat(view, "translationY", 0.0f).apply { 119 interpolator = Interpolators.DECELERATE_QUINT 120 startDelay = Y_TRANSLATION_ENTER_DURATION 121 duration = Y_TRANSLATION_ENTER_DURATION 122 } 123 124 return AnimatorSet().apply { 125 playTogether(alphaAnimator, yAnimator) 126 } 127 } 128 129 /** 130 * Properly handle animations originating from dialogs. Activity transitions require 131 * transitioning between two activities, so expose this method for dialogs to animate 132 * on exit. 133 */ 134 @JvmStatic 135 fun exitAnimation(view: View, onEnd: Runnable? = null): Animator { 136 Log.d(ControlsUiController.TAG, "Exit animation for $view") 137 138 val alphaAnimator = ObjectAnimator.ofFloat(view, "transitionAlpha", 0.0f).apply { 139 interpolator = Interpolators.ACCELERATE 140 duration = ALPHA_EXIT_DURATION 141 } 142 143 view.translationY = 0.0f 144 val yAnimator = ObjectAnimator.ofFloat(view, "translationY", -translationY).apply { 145 interpolator = Interpolators.ACCELERATE 146 duration = Y_TRANSLATION_EXIT_DURATION 147 } 148 149 return AnimatorSet().apply { 150 playTogether(alphaAnimator, yAnimator) 151 onEnd?.let { 152 addListener(object : AnimatorListenerAdapter() { 153 override fun onAnimationEnd(animation: Animator) { 154 it.run() 155 } 156 }) 157 } 158 } 159 } 160 161 fun enterWindowTransition(@IdRes id: Int) = 162 WindowTransition({ view: View -> enterAnimation(view) }).apply { 163 addTarget(id) 164 } 165 166 fun exitWindowTransition(@IdRes id: Int) = 167 WindowTransition({ view: View -> exitAnimation(view) }).apply { 168 addTarget(id) 169 } 170 } 171 172 /** 173 * In order to animate, at least one property must be marked on each view that should move. 174 * Setting "item" is just a flag to indicate that it should move by the animator. 175 */ 176 class WindowTransition( 177 val animator: (view: View) -> Animator 178 ) : Transition() { captureStartValuesnull179 override fun captureStartValues(tv: TransitionValues) { 180 tv.values["item"] = 0.0f 181 } 182 captureEndValuesnull183 override fun captureEndValues(tv: TransitionValues) { 184 tv.values["item"] = 1.0f 185 } 186 createAnimatornull187 override fun createAnimator( 188 sceneRoot: ViewGroup, 189 startValues: TransitionValues?, 190 endValues: TransitionValues? 191 ): Animator? = animator(startValues!!.view) 192 } 193