/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.permissioncontroller.safetycenter.ui import android.transition.AutoTransition import android.transition.Transition import android.transition.TransitionListenerAdapter import android.transition.TransitionManager import android.transition.TransitionSet import android.view.View import android.view.ViewGroup import android.view.animation.LinearInterpolator import android.widget.TextView import java.time.Duration /** * An animator which can animate a fade in/fade out of either one textView, or several textViews * that are in the same ViewGroup. */ class TextFadeAnimator @JvmOverloads constructor(targetIds: List, changeDuration: Duration = DEFAULT_TEXT_CHANGE_DURATION) { @JvmOverloads constructor( targetId: Int, changeDuration: Duration = DEFAULT_TEXT_CHANGE_DURATION ) : this(listOf(targetId), changeDuration) private val textChangeTransition: TransitionSet init { var transition = AutoTransition() .setInterpolator(linearInterpolator) .setDuration(changeDuration.toMillis()) for (targetId in targetIds) { transition = transition.addTarget(targetId) } textChangeTransition = transition } @JvmOverloads fun animateChangeText(textView: TextView, text: String, onFinish: Runnable? = null) { animateChangeText(listOf(textView to text), onFinish) } /** Animate changes for a set of textViews under the same parent. */ @JvmOverloads fun animateChangeText(textChanges: List>, onFinish: Runnable? = null) { if (textChanges.isEmpty()) { return } val firstView = textChanges[0].first val parentViewGroup: ViewGroup = firstView.parent as ViewGroup val fadeOutTransition = textChangeTransition .clone() .addListener( object : TransitionListenerAdapter() { override fun onTransitionStart(transition: Transition?) { super.onTransitionStart(transition) } override fun onTransitionEnd(transition: Transition?) { super.onTransitionEnd(transition) fadeTextIn(textChanges, parentViewGroup, onFinish) } }) parentViewGroup.post { TransitionManager.beginDelayedTransition(parentViewGroup, fadeOutTransition) for ((textView, _) in textChanges) { textView.visibility = View.INVISIBLE } } } private fun fadeTextIn( textChanges: List>, parent: ViewGroup, onFinish: Runnable? ) { val fadeInTransition = textChangeTransition .clone() .addListener( object : TransitionListenerAdapter() { override fun onTransitionEnd(transition: Transition?) { super.onTransitionEnd(transition) onFinish?.run() } }) parent.post { TransitionManager.beginDelayedTransition(parent, fadeInTransition) for ((textView, text) in textChanges) { textView.text = text textView.visibility = View.VISIBLE } } } fun cancelTextChangeAnimation(textView: TextView) { TransitionManager.endTransitions(textView.parent as ViewGroup) } companion object { // Duration is for fade-out & fade-in individually, not combined private val DEFAULT_TEXT_CHANGE_DURATION = Duration.ofMillis(167) private val linearInterpolator = LinearInterpolator() } }