| /* |
| * Copyright (C) 2018 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.graphics.Point |
| import android.test.suitebuilder.annotation.SmallTest |
| import android.testing.AndroidTestingRunner |
| import android.view.Display |
| import android.view.Surface.ROTATION_0 |
| import android.view.Surface.ROTATION_90 |
| import android.view.View |
| import android.view.WindowManager |
| import com.android.systemui.SysuiTestCase |
| import com.android.systemui.util.mockito.any |
| import com.google.common.truth.Truth.assertThat |
| import org.junit.Before |
| import org.junit.Rule |
| import org.junit.Test |
| import org.junit.runner.RunWith |
| import org.mockito.Mock |
| import org.mockito.Mockito.doAnswer |
| import org.mockito.Mockito.mock |
| import org.mockito.Mockito.spy |
| import org.mockito.junit.MockitoJUnit |
| import org.mockito.Mockito.`when` as whenever |
| |
| @RunWith(AndroidTestingRunner::class) |
| @SmallTest |
| class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() { |
| |
| @Mock |
| private lateinit var windowManager: WindowManager |
| |
| @get:Rule |
| val mockito = MockitoJUnit.rule() |
| |
| private lateinit var animator: UnfoldMoveFromCenterAnimator |
| |
| @Before |
| fun before() { |
| animator = UnfoldMoveFromCenterAnimator(windowManager) |
| } |
| |
| @Test |
| fun testRegisterViewOnTheLeftOfVerticalFold_halfProgress_viewTranslatedToTheRight() { |
| givenScreen(width = 100, height = 100, rotation = ROTATION_0) |
| val view = createView(x = 20, width = 10, height = 10) |
| animator.registerViewForAnimation(view) |
| animator.onTransitionStarted() |
| |
| animator.onTransitionProgress(0.5f) |
| |
| // Positive translationX -> translated to the right |
| // 10x10 view center is 25px from the center, |
| // When progress is 0.5 it should be translated at: |
| // 25 * 0.3 * (1 - 0.5) = 3.75px |
| assertThat(view.translationX).isWithin(0.01f).of(3.75f) |
| } |
| |
| @Test |
| fun testRegisterViewOnTheLeftOfVerticalFold_zeroProgress_viewTranslatedToTheRight() { |
| givenScreen(width = 100, height = 100, rotation = ROTATION_0) |
| val view = createView(x = 20, width = 10, height = 10) |
| animator.registerViewForAnimation(view) |
| animator.onTransitionStarted() |
| |
| animator.onTransitionProgress(0f) |
| |
| // Positive translationX -> translated to the right |
| // 10x10 view center is 25px from the center, |
| // When progress is 0 it should be translated at: |
| // 25 * 0.3 * (1 - 0) = 7.5px |
| assertThat(view.translationX).isWithin(0.01f).of(7.5f) |
| } |
| |
| @Test |
| fun testRegisterViewOnTheLeftOfVerticalFold_fullProgress_viewTranslatedToTheOriginalPosition() { |
| givenScreen(width = 100, height = 100, rotation = ROTATION_0) |
| val view = createView(x = 20, width = 10, height = 10) |
| animator.registerViewForAnimation(view) |
| animator.onTransitionStarted() |
| |
| animator.onTransitionProgress(1f) |
| |
| // Positive translationX -> translated to the right |
| // 10x10 view center is 25px from the center, |
| // When progress is 1 it should be translated at: |
| // 25 * 0.3 * 0 = 0px |
| assertThat(view.translationX).isEqualTo(0f) |
| } |
| |
| @Test |
| fun testViewOnTheLeftOfVerticalFoldWithTranslation_halfProgress_viewTranslatedToTheRight() { |
| givenScreen(width = 100, height = 100, rotation = ROTATION_0) |
| val view = createView(x = 20, width = 10, height = 10, translationX = 100f) |
| animator.registerViewForAnimation(view) |
| animator.onTransitionStarted() |
| |
| animator.onTransitionProgress(0.5f) |
| |
| // Positive translationX -> translated to the right, original translation is ignored |
| // 10x10 view center is 25px from the center, |
| // When progress is 0.5 it should be translated at: |
| // 25 * 0.3 * (1 - 0.5) = 3.75px |
| assertThat(view.translationX).isWithin(0.01f).of(3.75f) |
| } |
| |
| @Test |
| fun testRegisterViewAndUnregister_halfProgress_viewIsNotUpdated() { |
| givenScreen(width = 100, height = 100, rotation = ROTATION_0) |
| val view = createView(x = 20, width = 10, height = 10) |
| animator.registerViewForAnimation(view) |
| animator.onTransitionStarted() |
| animator.clearRegisteredViews() |
| |
| animator.onTransitionProgress(0.5f) |
| |
| assertThat(view.translationX).isEqualTo(0f) |
| } |
| |
| @Test |
| fun testRegisterViewUpdateProgressAndUnregister_halfProgress_viewIsNotUpdated() { |
| givenScreen(width = 100, height = 100, rotation = ROTATION_0) |
| val view = createView(x = 20, width = 10, height = 10) |
| animator.registerViewForAnimation(view) |
| animator.onTransitionStarted() |
| animator.onTransitionProgress(0.2f) |
| animator.clearRegisteredViews() |
| |
| animator.onTransitionProgress(0.5f) |
| |
| assertThat(view.translationX).isEqualTo(0f) |
| } |
| |
| @Test |
| fun testRegisterViewOnTheTopOfHorizontalFold_halfProgress_viewTranslatedToTheBottom() { |
| givenScreen(width = 100, height = 100, rotation = ROTATION_90) |
| val view = createView(y = 20, width = 10, height = 10) |
| animator.registerViewForAnimation(view) |
| animator.onTransitionStarted() |
| |
| animator.onTransitionProgress(0.5f) |
| |
| // Positive translationY -> translated to the bottom |
| assertThat(view.translationY).isWithin(0.01f).of(3.75f) |
| } |
| |
| @Test |
| fun testUpdateViewPositions_viewOnTheLeftAndMovedToTheRight_viewTranslatedToTheLeft() { |
| givenScreen(width = 100, height = 100, rotation = ROTATION_0) |
| val view = createView(x = 20) |
| animator.registerViewForAnimation(view) |
| animator.onTransitionStarted() |
| animator.onTransitionProgress(0.5f) |
| view.updateMock(x = 80) // view moved from the left side to the right |
| |
| animator.updateViewPositions() |
| |
| // Negative translationX -> translated to the left |
| assertThat(view.translationX).isWithin(0.1f).of(-5.25f) |
| } |
| |
| private fun createView( |
| x: Int = 0, |
| y: Int = 0, |
| width: Int = 10, |
| height: Int = 10, |
| translationX: Float = 0f, |
| translationY: Float = 0f |
| ): View { |
| val view = spy(View(context)) |
| doAnswer { |
| val location = (it.arguments[0] as IntArray) |
| location[0] = x |
| location[1] = y |
| Unit |
| }.`when`(view).getLocationOnScreen(any()) |
| |
| whenever(view.width).thenReturn(width) |
| whenever(view.height).thenReturn(height) |
| |
| view.updateMock(x, y, width, height, translationX, translationY) |
| |
| return view |
| } |
| |
| private fun View.updateMock( |
| x: Int = 0, |
| y: Int = 0, |
| width: Int = 10, |
| height: Int = 10, |
| translationX: Float = 0f, |
| translationY: Float = 0f |
| ) { |
| doAnswer { |
| val location = (it.arguments[0] as IntArray) |
| location[0] = x |
| location[1] = y |
| Unit |
| }.`when`(this).getLocationOnScreen(any()) |
| |
| whenever(this.width).thenReturn(width) |
| whenever(this.height).thenReturn(height) |
| |
| this.apply { |
| setTranslationX(translationX) |
| setTranslationY(translationY) |
| } |
| } |
| |
| private fun givenScreen( |
| width: Int = 100, |
| height: Int = 100, |
| rotation: Int = ROTATION_0 |
| ) { |
| val display = mock(Display::class.java) |
| whenever(display.getSize(any())).thenAnswer { |
| val size = (it.arguments[0] as Point) |
| size.set(width, height) |
| Unit |
| } |
| whenever(display.rotation).thenReturn(rotation) |
| whenever(windowManager.defaultDisplay).thenReturn(display) |
| |
| animator.updateDisplayProperties() |
| } |
| } |