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 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 fun init(rootView: ViewGroup, translationMax: Float) { 41 this.rootView = rootView 42 this.translationMax = translationMax 43 progressProvider.addCallback(this) 44 } 45 46 override fun onTransitionStarted() { 47 registerViewsForAnimation(rootView, viewsIdToTranslate) 48 } 49 50 override fun onTransitionProgress(progress: Float) { 51 translateViews(progress) 52 } 53 54 override fun onTransitionFinished() { 55 translateViews(progress = 1f) 56 } 57 58 private fun translateViews(progress: Float) { 59 // progress == 0 -> -translationMax 60 // progress == 1 -> 0 61 val xTrans = (progress - 1f) * translationMax 62 val rtlMultiplier = 63 if (rootView.getLayoutDirection() == LAYOUT_DIRECTION_RTL) { 64 -1 65 } else { 66 1 67 } 68 viewsToTranslate.forEach { (view, direction, func) -> 69 view.get()?.let { func(it, xTrans * direction.multiplier * rtlMultiplier) } 70 } 71 } 72 73 /** Finds in [parent] all views specified by [ids] and register them for the animation. */ 74 private fun registerViewsForAnimation(parent: ViewGroup, ids: Set<ViewIdToTranslate>) { 75 viewsToTranslate = 76 ids.asSequence() 77 .filter { it.shouldBeAnimated() } 78 .mapNotNull { 79 parent.findViewById<View>(it.viewId)?.let { view -> 80 ViewToTranslate(WeakReference(view), it.direction, it.translateFunc) 81 } 82 } 83 .toList() 84 } 85 86 /** 87 * Represents a view to animate. [rootView] should contain a view with [viewId] inside. 88 * [shouldBeAnimated] is only evaluated when the viewsToTranslate is registered in 89 * [registerViewsForAnimation]. 90 */ 91 data class ViewIdToTranslate( 92 val viewId: Int, 93 val direction: Direction, 94 val shouldBeAnimated: () -> Boolean = { true }, 95 val translateFunc: (View, Float) -> Unit = { view, value -> view.translationX = value }, 96 ) 97 98 /** 99 * Represents a view whose animation process is in-progress. It should be immutable because the 100 * started animation should be completed. 101 */ 102 private data class ViewToTranslate( 103 val view: WeakReference<View>, 104 val direction: Direction, 105 val translateFunc: (View, Float) -> Unit, 106 ) 107 108 /** Direction of the animation. */ 109 enum class Direction(val multiplier: Float) { 110 START(-1f), 111 END(1f), 112 } 113 } 114