1 /* <lambda>null2 * 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 package com.android.systemui.shared.animation 17 18 import android.view.View 19 import android.view.View.LAYOUT_DIRECTION_RTL 20 import android.view.ViewGroup 21 import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate 22 import com.android.systemui.unfold.UnfoldTransitionProgressProvider 23 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener 24 import java.lang.ref.WeakReference 25 26 /** 27 * Translates items away/towards the hinge when the device is opened/closed, according to the 28 * direction specified in [ViewIdToTranslate.direction], for a maximum of [translationMax] when 29 * progresses are 0. 30 */ 31 class UnfoldConstantTranslateAnimator( 32 private val viewsIdToTranslate: Set<ViewIdToTranslate>, 33 private val progressProvider: UnfoldTransitionProgressProvider 34 ) : TransitionProgressListener { 35 36 private var viewsToTranslate = listOf<ViewToTranslate>() 37 private lateinit var rootView: ViewGroup 38 private var translationMax = 0f 39 40 /** 41 * Initializes the animator, it is allowed to call this method multiple times, for example 42 * to update the rootView or maximum translation 43 */ 44 fun init(rootView: ViewGroup, translationMax: Float) { 45 if (!::rootView.isInitialized) { 46 progressProvider.addCallback(this) 47 } 48 49 this.rootView = rootView 50 this.translationMax = translationMax 51 } 52 53 override fun onTransitionStarted() { 54 registerViewsForAnimation(rootView, viewsIdToTranslate) 55 } 56 57 override fun onTransitionProgress(progress: Float) { 58 translateViews(progress) 59 } 60 61 override fun onTransitionFinished() { 62 translateViews(progress = 1f) 63 } 64 65 private fun translateViews(progress: Float) { 66 // progress == 0 -> -translationMax 67 // progress == 1 -> 0 68 val xTrans = (progress - 1f) * translationMax 69 val rtlMultiplier = 70 if (rootView.getLayoutDirection() == LAYOUT_DIRECTION_RTL) { 71 -1 72 } else { 73 1 74 } 75 viewsToTranslate.forEach { (view, direction, func) -> 76 view.get()?.let { func(it, xTrans * direction.multiplier * rtlMultiplier) } 77 } 78 } 79 80 /** Finds in [parent] all views specified by [ids] and register them for the animation. */ 81 private fun registerViewsForAnimation(parent: ViewGroup, ids: Set<ViewIdToTranslate>) { 82 viewsToTranslate = 83 ids.asSequence() 84 .filter { it.shouldBeAnimated() } 85 .mapNotNull { 86 parent.findViewById<View>(it.viewId)?.let { view -> 87 ViewToTranslate(WeakReference(view), it.direction, it.translateFunc) 88 } 89 } 90 .toList() 91 } 92 93 /** 94 * Represents a view to animate. [rootView] should contain a view with [viewId] inside. 95 * [shouldBeAnimated] is only evaluated when the viewsToTranslate is registered in 96 * [registerViewsForAnimation]. 97 */ 98 data class ViewIdToTranslate( 99 val viewId: Int, 100 val direction: Direction, 101 val shouldBeAnimated: () -> Boolean = { true }, 102 val translateFunc: (View, Float) -> Unit = { view, value -> view.translationX = value }, 103 ) 104 105 /** 106 * Represents a view whose animation process is in-progress. It should be immutable because the 107 * started animation should be completed. 108 */ 109 private data class ViewToTranslate( 110 val view: WeakReference<View>, 111 val direction: Direction, 112 val translateFunc: (View, Float) -> Unit, 113 ) 114 115 /** Direction of the animation. */ 116 enum class Direction(val multiplier: Float) { 117 START(-1f), 118 END(1f), 119 } 120 } 121