| /* |
| * 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.systemui.shared.animation |
| |
| import android.testing.AndroidTestingRunner |
| import android.view.View |
| import android.view.ViewGroup |
| import androidx.test.filters.SmallTest |
| import com.android.systemui.SysuiTestCase |
| import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction |
| import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate |
| import com.android.systemui.unfold.TestUnfoldTransitionProvider |
| import org.junit.Assert.assertEquals |
| import org.junit.Before |
| import org.junit.Test |
| import org.junit.runner.RunWith |
| import org.mockito.Mock |
| import org.mockito.Mockito.`when` as whenever |
| import org.mockito.MockitoAnnotations |
| |
| @SmallTest |
| @RunWith(AndroidTestingRunner::class) |
| class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { |
| |
| private val progressProvider = TestUnfoldTransitionProvider() |
| |
| @Mock |
| private lateinit var parent: ViewGroup |
| |
| @Mock |
| private lateinit var shouldBeAnimated: () -> Boolean |
| |
| private lateinit var animator: UnfoldConstantTranslateAnimator |
| |
| private val viewsIdToRegister |
| get() = |
| setOf( |
| ViewIdToTranslate(START_VIEW_ID, Direction.START, shouldBeAnimated), |
| ViewIdToTranslate(END_VIEW_ID, Direction.END, shouldBeAnimated) |
| ) |
| |
| @Before |
| fun setup() { |
| MockitoAnnotations.initMocks(this) |
| whenever(shouldBeAnimated.invoke()).thenReturn(true) |
| animator = UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider) |
| |
| animator.init(parent, MAX_TRANSLATION) |
| } |
| |
| @Test |
| fun onTransition_noMatchingIds() { |
| // GIVEN no views matching any ids |
| // WHEN the transition starts |
| progressProvider.onTransitionStarted() |
| progressProvider.onTransitionProgress(.1f) |
| |
| // THEN nothing... no exceptions |
| } |
| |
| @Test |
| fun onTransition_oneMovesStartWithLTR() { |
| // GIVEN one view with a matching id |
| val view = View(context) |
| whenever(parent.findViewById<View>(START_VIEW_ID)).thenReturn(view) |
| |
| moveAndValidate(listOf(view to START), View.LAYOUT_DIRECTION_LTR) |
| } |
| |
| @Test |
| fun onTransition_oneMovesStartWithRTL() { |
| // GIVEN one view with a matching id |
| val view = View(context) |
| whenever(parent.findViewById<View>(START_VIEW_ID)).thenReturn(view) |
| |
| whenever(parent.getLayoutDirection()).thenReturn(View.LAYOUT_DIRECTION_RTL) |
| moveAndValidate(listOf(view to START), View.LAYOUT_DIRECTION_RTL) |
| } |
| |
| @Test |
| fun onTransition_oneMovesStartAndOneMovesEndMultipleTimes() { |
| // GIVEN two views with a matching id |
| val leftView = View(context) |
| val rightView = View(context) |
| whenever(parent.findViewById<View>(START_VIEW_ID)).thenReturn(leftView) |
| whenever(parent.findViewById<View>(END_VIEW_ID)).thenReturn(rightView) |
| |
| moveAndValidate(listOf(leftView to START, rightView to END), View.LAYOUT_DIRECTION_LTR) |
| moveAndValidate(listOf(leftView to START, rightView to END), View.LAYOUT_DIRECTION_LTR) |
| } |
| |
| @Test |
| fun onTransition_completeStartedTranslation() { |
| // GIVEN |
| val leftView = View(context) |
| whenever(parent.findViewById<View>(START_VIEW_ID)).thenReturn(leftView) |
| // To start animation, shouldBeAnimated should return true. |
| // There is a possibility for shouldBeAnimated to return false during the animation. |
| whenever(shouldBeAnimated.invoke()).thenReturn(true).thenReturn(false) |
| |
| // shouldBeAnimated state may change during the animation. |
| // However, started animation should be completed. |
| moveAndValidate(listOf(leftView to START), View.LAYOUT_DIRECTION_LTR) |
| } |
| |
| private fun moveAndValidate(list: List<Pair<View, Int>>, layoutDirection: Int) { |
| // Compare values as ints because -0f != 0f |
| |
| // WHEN the transition starts |
| progressProvider.onTransitionStarted() |
| progressProvider.onTransitionProgress(0f) |
| |
| val rtlMultiplier = if (layoutDirection == View.LAYOUT_DIRECTION_LTR) { |
| 1 |
| } else { |
| -1 |
| } |
| list.forEach { (view, direction) -> |
| assertEquals( |
| (-MAX_TRANSLATION * direction * rtlMultiplier).toInt(), |
| view.translationX.toInt() |
| ) |
| } |
| |
| // WHEN the transition progresses, translation is updated |
| progressProvider.onTransitionProgress(.5f) |
| list.forEach { (view, direction) -> |
| assertEquals( |
| (-MAX_TRANSLATION / 2f * direction * rtlMultiplier).toInt(), |
| view.translationX.toInt() |
| ) |
| } |
| |
| // WHEN the transition ends, translation is completed |
| progressProvider.onTransitionProgress(1f) |
| progressProvider.onTransitionFinished() |
| list.forEach { (view, _) -> assertEquals(0, view.translationX.toInt()) } |
| } |
| |
| companion object { |
| private val START = Direction.START.multiplier.toInt() |
| private val END = Direction.END.multiplier.toInt() |
| |
| private const val MAX_TRANSLATION = 42f |
| |
| private const val START_VIEW_ID = 1 |
| private const val END_VIEW_ID = 2 |
| } |
| } |