mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-03 09:26:51 +00:00
Implement spring animations for running state changes.
Flag: com.android.window.flags.enable_taskbar_recents_layout_transition Fix: 402683858 Test: go/testedequals Change-Id: Icc4a3a61b6c0997564a4dcdd8eb7768459d5f2ef
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.launcher3.taskbar
|
||||
|
||||
import android.animation.AnimatorTestRule
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import com.android.launcher3.BubbleTextView
|
||||
import com.android.launcher3.BubbleTextView.RunningAppState
|
||||
import com.android.launcher3.BubbleTextView.RunningAppState.MINIMIZED
|
||||
import com.android.launcher3.BubbleTextView.RunningAppState.NOT_RUNNING
|
||||
import com.android.launcher3.BubbleTextView.RunningAppState.RUNNING
|
||||
import com.android.launcher3.model.data.TaskItemInfo
|
||||
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
|
||||
import com.android.launcher3.taskbar.TaskbarRunningAppStateAnimationController.Companion.LINE_ANIM_DURATION
|
||||
import com.android.launcher3.taskbar.TaskbarRunningAppStateAnimationController.Companion.UNPINNED_APP_LINE_ANIM_DELAY
|
||||
import com.android.launcher3.util.ActivityContextWrapper
|
||||
import com.android.launcher3.util.LauncherMultivalentJUnit
|
||||
import com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_APP_RUNNING_STATE_ANIM
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
private const val FRAME_TIME_MS = 16L // Simulates 60 Hz.
|
||||
private val PINNED_APP = TaskItemInfo(0, TaskbarViewTestUtil.createHotseatWorkspaceItem(0))
|
||||
private val UNPINNED_APP = TaskbarViewTestUtil.createRecentTask(1)
|
||||
|
||||
@RunWith(LauncherMultivalentJUnit::class)
|
||||
class TaskbarRunningAppStateAnimationControllerTest {
|
||||
|
||||
@get:Rule val animatorTestRule = AnimatorTestRule(this)
|
||||
|
||||
private val context = ActivityContextWrapper(getInstrumentation().targetContext)
|
||||
private val btv = BubbleTextView(context)
|
||||
private val controller = TaskbarRunningAppStateAnimationController(context)
|
||||
|
||||
@Test
|
||||
fun updateRunningState_minimizeApp_verifySpringEndState() {
|
||||
startStateChange(start = RUNNING, end = MINIMIZED)
|
||||
verifySpringAnimationEnd(MINIMIZED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateRunningState_minimizeApp_verifyCancelEndState() {
|
||||
startStateChange(start = RUNNING, end = MINIMIZED)
|
||||
runOnMainSync { controller.onDestroy() }
|
||||
verifyStateSettled(state = MINIMIZED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateRunningState_restoreApp_verifySpringEndState() {
|
||||
startStateChange(start = MINIMIZED, end = RUNNING)
|
||||
verifySpringAnimationEnd(RUNNING)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateRunningState_openPinnedApp_verifySpringEndState() {
|
||||
btv.tag = PINNED_APP
|
||||
startStateChange(start = NOT_RUNNING, end = RUNNING)
|
||||
verifySpringAnimationEnd(RUNNING)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateRunningState_openUnpinnedApp_verifyStartDelay() {
|
||||
btv.tag = UNPINNED_APP
|
||||
startStateChange(start = NOT_RUNNING, end = RUNNING)
|
||||
runOnMainSync { animatorTestRule.advanceTimeBy(UNPINNED_APP_LINE_ANIM_DELAY) }
|
||||
verifyLineIndicator(state = NOT_RUNNING)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateRunningState_openUnpinnedApp_verifyEndState() {
|
||||
btv.tag = UNPINNED_APP
|
||||
startStateChange(start = NOT_RUNNING, end = RUNNING)
|
||||
runOnMainSync {
|
||||
animatorTestRule.advanceTimeBy(UNPINNED_APP_LINE_ANIM_DELAY + LINE_ANIM_DURATION)
|
||||
}
|
||||
|
||||
verifyStateSettled(state = RUNNING)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateRunningState_openUnpinnedApp_verifyCancelEndState() {
|
||||
btv.tag = UNPINNED_APP
|
||||
startStateChange(start = NOT_RUNNING, end = RUNNING)
|
||||
runOnMainSync { controller.onDestroy() }
|
||||
verifyStateSettled(state = RUNNING)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateRunningState_closeApp_verifyEndState() {
|
||||
startStateChange(start = RUNNING, end = NOT_RUNNING)
|
||||
runOnMainSync { animatorTestRule.advanceTimeBy(LINE_ANIM_DURATION) }
|
||||
verifyStateSettled(state = NOT_RUNNING)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateRunningState_repeatUpdateDuringAnimation_animationNotCanceled() {
|
||||
startStateChange(start = MINIMIZED, end = RUNNING)
|
||||
runOnMainSync {
|
||||
animatorTestRule.advanceTimeBy(FRAME_TIME_MS)
|
||||
controller.updateRunningState(btv, RUNNING, animate = false)
|
||||
}
|
||||
assertThat(controller.isAnimationRunning(btv)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateRunningState_minimizedDuringOpen_verifyMinimizedEndState() {
|
||||
startStateChange(start = NOT_RUNNING, end = RUNNING)
|
||||
runOnMainSync { controller.updateRunningState(btv, MINIMIZED, animate = true) }
|
||||
verifySpringAnimationEnd(MINIMIZED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onDestroy_multipleAnimations_cancelsAll() {
|
||||
startStateChange(start = RUNNING, end = MINIMIZED)
|
||||
val btv2 = BubbleTextView(context)
|
||||
startStateChange(btv = btv2, start = RUNNING, end = MINIMIZED)
|
||||
|
||||
runOnMainSync { controller.onDestroy() }
|
||||
verifyStateSettled(state = MINIMIZED)
|
||||
verifyStateSettled(btv = btv2, state = MINIMIZED)
|
||||
}
|
||||
|
||||
private fun startStateChange(
|
||||
btv: BubbleTextView = this.btv,
|
||||
start: RunningAppState,
|
||||
end: RunningAppState,
|
||||
) {
|
||||
runOnMainSync {
|
||||
controller.updateRunningState(btv, start, animate = false)
|
||||
controller.updateRunningState(btv, end, animate = true)
|
||||
}
|
||||
verifyLineIndicator(btv, start)
|
||||
assertThat(controller.isAnimationRunning(btv)).isTrue()
|
||||
}
|
||||
|
||||
/** Verifies [btv] spring animation ends at [state]. */
|
||||
private fun verifySpringAnimationEnd(state: RunningAppState) {
|
||||
while (controller.isAnimationRunning(btv)) {
|
||||
runOnMainSync { animatorTestRule.advanceTimeBy(FRAME_TIME_MS) }
|
||||
}
|
||||
verifyStateSettled(state = state)
|
||||
}
|
||||
|
||||
private fun verifyStateSettled(btv: BubbleTextView = this.btv, state: RunningAppState) {
|
||||
assertThat(controller.isAnimationRunning(btv)).isFalse()
|
||||
verifyLineIndicator(btv, state)
|
||||
|
||||
val translateYProp =
|
||||
btv.translateDelegate.getTranslationY(INDEX_TASKBAR_APP_RUNNING_STATE_ANIM)
|
||||
assertThat(translateYProp.value).isZero()
|
||||
}
|
||||
|
||||
private fun verifyLineIndicator(btv: BubbleTextView = this.btv, state: RunningAppState) {
|
||||
controller.run {
|
||||
assertThat(btv.lineIndicatorWidth).isEqualTo(state.lineWidth)
|
||||
assertThat(btv.lineIndicatorColor).isEqualTo(state.lineColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,21 +83,23 @@ object TaskbarViewTestUtil {
|
||||
|
||||
/** Creates a list of fake recent tasks. */
|
||||
fun createRecents(size: Int): List<GroupTask> {
|
||||
return List(size) {
|
||||
SingleTask(
|
||||
Task().apply {
|
||||
key =
|
||||
TaskKey(
|
||||
it,
|
||||
5,
|
||||
TEST_INTENT,
|
||||
TEST_COMPONENT,
|
||||
Process.myUserHandle().identifier,
|
||||
System.currentTimeMillis(),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
return List(size) { createRecentTask(it) }
|
||||
}
|
||||
|
||||
fun createRecentTask(id: Int = 0): GroupTask {
|
||||
return SingleTask(
|
||||
Task().apply {
|
||||
key =
|
||||
TaskKey(
|
||||
id,
|
||||
5,
|
||||
TEST_INTENT,
|
||||
TEST_COMPONENT,
|
||||
Process.myUserHandle().identifier,
|
||||
System.currentTimeMillis(),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user