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