1 /* 2 * Copyright (C) 2024 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.shared.clocks 18 19 import android.animation.Animator 20 import android.animation.AnimatorListenerAdapter 21 import android.animation.TimeInterpolator 22 import android.animation.ValueAnimator 23 import android.graphics.Point 24 25 class DigitTranslateAnimator(val updateCallback: () -> Unit) { 26 val DEFAULT_ANIMATION_DURATION = 500L 27 val updatedTranslate = Point(0, 0) 28 29 val baseTranslation = Point(0, 0) 30 var targetTranslation: Point? = null 31 val bounceAnimator: ValueAnimator = <lambda>null32 ValueAnimator.ofFloat(1f).apply { 33 duration = DEFAULT_ANIMATION_DURATION 34 addUpdateListener { 35 updateTranslation(it.animatedFraction, updatedTranslate) 36 updateCallback() 37 } 38 addListener( 39 object : AnimatorListenerAdapter() { 40 override fun onAnimationEnd(animation: Animator) { 41 rebase() 42 } 43 44 override fun onAnimationCancel(animation: Animator) { 45 rebase() 46 } 47 } 48 ) 49 } 50 rebasenull51 fun rebase() { 52 baseTranslation.x = updatedTranslate.x 53 baseTranslation.y = updatedTranslate.y 54 } 55 animatePositionnull56 fun animatePosition( 57 animate: Boolean = true, 58 delay: Long = 0, 59 duration: Long = -1L, 60 interpolator: TimeInterpolator? = null, 61 targetTranslation: Point? = null, 62 onAnimationEnd: Runnable? = null, 63 ) { 64 this.targetTranslation = targetTranslation ?: Point(0, 0) 65 if (animate) { 66 bounceAnimator.cancel() 67 bounceAnimator.startDelay = delay 68 bounceAnimator.duration = 69 if (duration == -1L) { 70 DEFAULT_ANIMATION_DURATION 71 } else { 72 duration 73 } 74 interpolator?.let { bounceAnimator.interpolator = it } 75 if (onAnimationEnd != null) { 76 val listener = 77 object : AnimatorListenerAdapter() { 78 override fun onAnimationEnd(animation: Animator) { 79 onAnimationEnd.run() 80 bounceAnimator.removeListener(this) 81 } 82 83 override fun onAnimationCancel(animation: Animator) { 84 bounceAnimator.removeListener(this) 85 } 86 } 87 bounceAnimator.addListener(listener) 88 } 89 bounceAnimator.start() 90 } else { 91 // No animation is requested, thus set base and target state to the same state. 92 updateTranslation(1F, updatedTranslate) 93 rebase() 94 updateCallback() 95 } 96 } 97 updateTranslationnull98 fun updateTranslation(progress: Float, outPoint: Point) { 99 outPoint.x = 100 (baseTranslation.x + progress * (targetTranslation!!.x - baseTranslation.x)).toInt() 101 outPoint.y = 102 (baseTranslation.y + progress * (targetTranslation!!.y - baseTranslation.y)).toInt() 103 } 104 } 105