1 /* 2 * Copyright (C) 2022 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.permissioncontroller.safetycenter.ui 18 19 import android.transition.AutoTransition 20 import android.transition.Transition 21 import android.transition.TransitionListenerAdapter 22 import android.transition.TransitionManager 23 import android.transition.TransitionSet 24 import android.view.View 25 import android.view.ViewGroup 26 import android.view.animation.LinearInterpolator 27 import android.widget.TextView 28 import java.time.Duration 29 30 /** 31 * An animator which can animate a fade in/fade out of either one textView, or several textViews 32 * that are in the same ViewGroup. 33 */ 34 class TextFadeAnimator 35 @JvmOverloads 36 constructor(targetIds: List<Int>, changeDuration: Duration = DEFAULT_TEXT_CHANGE_DURATION) { 37 38 @JvmOverloads 39 constructor( 40 targetId: Int, 41 changeDuration: Duration = DEFAULT_TEXT_CHANGE_DURATION 42 ) : this(listOf(targetId), changeDuration) 43 44 private val textChangeTransition: TransitionSet 45 init { 46 var transition = 47 AutoTransition() 48 .setInterpolator(linearInterpolator) 49 .setDuration(changeDuration.toMillis()) 50 for (targetId in targetIds) { 51 transition = transition.addTarget(targetId) 52 } 53 textChangeTransition = transition 54 } 55 56 @JvmOverloads animateChangeTextnull57 fun animateChangeText(textView: TextView, text: String, onFinish: Runnable? = null) { 58 animateChangeText(listOf(textView to text), onFinish) 59 } 60 61 /** Animate changes for a set of textViews under the same parent. */ 62 @JvmOverloads animateChangeTextnull63 fun animateChangeText(textChanges: List<Pair<TextView, String>>, onFinish: Runnable? = null) { 64 if (textChanges.isEmpty()) { 65 return 66 } 67 val firstView = textChanges[0].first 68 val parentViewGroup: ViewGroup = firstView.parent as ViewGroup 69 val fadeOutTransition = 70 textChangeTransition 71 .clone() 72 .addListener( 73 object : TransitionListenerAdapter() { 74 override fun onTransitionStart(transition: Transition?) { 75 super.onTransitionStart(transition) 76 } 77 override fun onTransitionEnd(transition: Transition?) { 78 super.onTransitionEnd(transition) 79 fadeTextIn(textChanges, parentViewGroup, onFinish) 80 } 81 }) 82 parentViewGroup.post { 83 TransitionManager.beginDelayedTransition(parentViewGroup, fadeOutTransition) 84 for ((textView, _) in textChanges) { 85 textView.visibility = View.INVISIBLE 86 } 87 } 88 } 89 fadeTextInnull90 private fun fadeTextIn( 91 textChanges: List<Pair<TextView, String>>, 92 parent: ViewGroup, 93 onFinish: Runnable? 94 ) { 95 val fadeInTransition = 96 textChangeTransition 97 .clone() 98 .addListener( 99 object : TransitionListenerAdapter() { 100 override fun onTransitionEnd(transition: Transition?) { 101 super.onTransitionEnd(transition) 102 onFinish?.run() 103 } 104 }) 105 106 parent.post { 107 TransitionManager.beginDelayedTransition(parent, fadeInTransition) 108 for ((textView, text) in textChanges) { 109 textView.text = text 110 textView.visibility = View.VISIBLE 111 } 112 } 113 } 114 cancelTextChangeAnimationnull115 fun cancelTextChangeAnimation(textView: TextView) { 116 TransitionManager.endTransitions(textView.parent as ViewGroup) 117 } 118 119 companion object { 120 // Duration is for fade-out & fade-in individually, not combined 121 private val DEFAULT_TEXT_CHANGE_DURATION = Duration.ofMillis(167) 122 private val linearInterpolator = LinearInterpolator() 123 } 124 } 125