Merge "[Unfold animation] Disable task animations when folding by default" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
3816d9b959
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.unfold.util
|
||||
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
|
||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
||||
import com.android.systemui.unfold.updates.FoldProvider
|
||||
import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
/**
|
||||
* [UnfoldTransitionProgressProvider] that emits transition progress only when unfolding but not
|
||||
* when folding, so we can play the animation only one way but not the other way.
|
||||
*/
|
||||
class UnfoldOnlyProgressProvider(
|
||||
foldProvider: FoldProvider,
|
||||
@Main private val executor: Executor,
|
||||
private val sourceProvider: UnfoldTransitionProgressProvider,
|
||||
private val scopedProvider: ScopedUnfoldTransitionProgressProvider =
|
||||
ScopedUnfoldTransitionProgressProvider(sourceProvider)
|
||||
) : UnfoldTransitionProgressProvider by scopedProvider {
|
||||
|
||||
private var isFolded = false
|
||||
|
||||
init {
|
||||
foldProvider.registerCallback(FoldListener(), executor)
|
||||
sourceProvider.addCallback(SourceTransitionListener())
|
||||
}
|
||||
|
||||
private inner class SourceTransitionListener : TransitionProgressListener {
|
||||
override fun onTransitionFinished() {
|
||||
// Disable scoped progress provider after the first unfold animation, so fold animation
|
||||
// will not be propagated. It will be re-enabled after folding so we can play
|
||||
// the unfold animation again.
|
||||
if (!isFolded) {
|
||||
scopedProvider.setReadyToHandleTransition(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class FoldListener : FoldCallback {
|
||||
override fun onFoldUpdated(isFolded: Boolean) {
|
||||
if (isFolded) {
|
||||
scopedProvider.setReadyToHandleTransition(true)
|
||||
}
|
||||
|
||||
this@UnfoldOnlyProgressProvider.isFolded = isFolded
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,16 +18,19 @@ package com.android.systemui.unfold
|
||||
|
||||
import android.content.Context
|
||||
import android.hardware.devicestate.DeviceStateManager
|
||||
import android.os.SystemProperties
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
|
||||
import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
||||
import com.android.systemui.unfold.system.SystemUnfoldSharedModule
|
||||
import com.android.systemui.unfold.updates.FoldProvider
|
||||
import com.android.systemui.unfold.updates.FoldStateProvider
|
||||
import com.android.systemui.unfold.updates.RotationChangeProvider
|
||||
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
||||
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
|
||||
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
|
||||
import com.android.systemui.unfold.util.UnfoldOnlyProgressProvider
|
||||
import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
|
||||
import com.android.systemui.util.time.SystemClockImpl
|
||||
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
|
||||
@@ -37,6 +40,7 @@ import dagger.Provides
|
||||
import java.util.Optional
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Named
|
||||
import javax.inject.Provider
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module(includes = [UnfoldSharedModule::class, SystemUnfoldSharedModule::class])
|
||||
@@ -90,6 +94,18 @@ class UnfoldTransitionModule {
|
||||
NaturalRotationUnfoldProgressProvider(context, rotationChangeProvider, provider)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named(UNFOLD_ONLY_PROVIDER)
|
||||
fun provideUnfoldOnlyProvider(
|
||||
foldProvider: FoldProvider,
|
||||
@Main executor: Executor,
|
||||
sourceProvider: Optional<UnfoldTransitionProgressProvider>
|
||||
): Optional<UnfoldTransitionProgressProvider> =
|
||||
sourceProvider.map { provider ->
|
||||
UnfoldOnlyProgressProvider(foldProvider, executor, provider)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named(UNFOLD_STATUS_BAR)
|
||||
@Singleton
|
||||
@@ -102,16 +118,35 @@ class UnfoldTransitionModule {
|
||||
@Singleton
|
||||
fun provideShellProgressProvider(
|
||||
config: UnfoldTransitionConfig,
|
||||
provider: Optional<UnfoldTransitionProgressProvider>
|
||||
): ShellUnfoldProgressProvider =
|
||||
if (config.isEnabled && provider.isPresent) {
|
||||
UnfoldProgressProvider(provider.get())
|
||||
} else {
|
||||
ShellUnfoldProgressProvider.NO_PROVIDER
|
||||
}
|
||||
provider: Provider<Optional<UnfoldTransitionProgressProvider>>,
|
||||
@Named(UNFOLD_ONLY_PROVIDER)
|
||||
unfoldOnlyProvider: Provider<Optional<UnfoldTransitionProgressProvider>>
|
||||
): ShellUnfoldProgressProvider {
|
||||
val resultingProvider =
|
||||
if (config.isEnabled) {
|
||||
// Return unfold only provider to the shell if we don't want to animate tasks during
|
||||
// folding. Shell provider listeners are responsible for animating task bounds.
|
||||
if (ENABLE_FOLD_TASK_ANIMATIONS) {
|
||||
provider
|
||||
} else {
|
||||
unfoldOnlyProvider
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return resultingProvider?.get()?.orElse(null)?.let(::UnfoldProgressProvider)
|
||||
?: ShellUnfoldProgressProvider.NO_PROVIDER
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun screenStatusProvider(impl: LifecycleScreenStatusProvider): ScreenStatusProvider = impl
|
||||
}
|
||||
|
||||
const val UNFOLD_STATUS_BAR = "unfold_status_bar"
|
||||
const val UNFOLD_ONLY_PROVIDER = "unfold_only_provider"
|
||||
|
||||
// TODO: b/265764985 - tracking bug to clean-up the flag
|
||||
// FeatureFlags are not accessible here because it's a global submodule (see GlobalModule.java)
|
||||
private val ENABLE_FOLD_TASK_ANIMATIONS =
|
||||
SystemProperties.getBoolean("persist.unfold.enable_fold_tasks_animation", false)
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionPr
|
||||
|
||||
class TestUnfoldTransitionProvider : UnfoldTransitionProgressProvider, TransitionProgressListener {
|
||||
|
||||
private val listeners = arrayListOf<TransitionProgressListener>()
|
||||
private val listeners = mutableListOf<TransitionProgressListener>()
|
||||
|
||||
override fun destroy() {
|
||||
listeners.clear()
|
||||
|
||||
@@ -20,10 +20,9 @@ import android.os.Vibrator
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.unfold.updates.FoldProvider
|
||||
import com.android.systemui.unfold.util.TestFoldProvider
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import java.util.concurrent.Executor
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@@ -115,20 +114,4 @@ class UnfoldHapticsPlayerTest : SysuiTestCase() {
|
||||
|
||||
verify(vibrator).vibrate(any<VibrationEffect>())
|
||||
}
|
||||
|
||||
private class TestFoldProvider : FoldProvider {
|
||||
private val listeners = arrayListOf<FoldProvider.FoldCallback>()
|
||||
|
||||
override fun registerCallback(callback: FoldProvider.FoldCallback, executor: Executor) {
|
||||
listeners += callback
|
||||
}
|
||||
|
||||
override fun unregisterCallback(callback: FoldProvider.FoldCallback) {
|
||||
listeners -= callback
|
||||
}
|
||||
|
||||
fun onFoldUpdate(isFolded: Boolean) {
|
||||
listeners.forEach { it.onFoldUpdated(isFolded) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,11 @@ class TestUnfoldProgressListener : UnfoldTransitionProgressProvider.TransitionPr
|
||||
currentRecording?.assertLastProgress(progress) ?: error("unfold not in progress.")
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
currentRecording = null
|
||||
recordings.clear()
|
||||
}
|
||||
|
||||
class UnfoldTransitionRecording {
|
||||
private val progressHistory: MutableList<Float> = arrayListOf()
|
||||
private var finishingInvocations: Int = 0
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.unfold.util
|
||||
|
||||
import com.android.systemui.unfold.updates.FoldProvider
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
class TestFoldProvider : FoldProvider {
|
||||
private val listeners = arrayListOf<FoldProvider.FoldCallback>()
|
||||
|
||||
override fun registerCallback(callback: FoldProvider.FoldCallback, executor: Executor) {
|
||||
listeners += callback
|
||||
}
|
||||
|
||||
override fun unregisterCallback(callback: FoldProvider.FoldCallback) {
|
||||
listeners -= callback
|
||||
}
|
||||
|
||||
fun onFoldUpdate(isFolded: Boolean) {
|
||||
listeners.forEach { it.onFoldUpdated(isFolded) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.unfold.util
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.unfold.TestUnfoldTransitionProvider
|
||||
import com.android.systemui.unfold.progress.TestUnfoldProgressListener
|
||||
import com.google.common.util.concurrent.MoreExecutors
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class UnfoldOnlyProgressProviderTest : SysuiTestCase() {
|
||||
|
||||
private val listener = TestUnfoldProgressListener()
|
||||
private val sourceProvider = TestUnfoldTransitionProvider()
|
||||
|
||||
private val foldProvider = TestFoldProvider()
|
||||
|
||||
private lateinit var progressProvider: UnfoldOnlyProgressProvider
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
progressProvider =
|
||||
UnfoldOnlyProgressProvider(foldProvider, MoreExecutors.directExecutor(), sourceProvider)
|
||||
|
||||
progressProvider.addCallback(listener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unfolded_unfoldAnimationFinished_propagatesEvents() {
|
||||
foldProvider.onFoldUpdate(isFolded = true)
|
||||
foldProvider.onFoldUpdate(isFolded = false)
|
||||
|
||||
// Unfold animation
|
||||
sourceProvider.onTransitionStarted()
|
||||
sourceProvider.onTransitionProgress(0.5f)
|
||||
sourceProvider.onTransitionFinished()
|
||||
|
||||
with(listener.ensureTransitionFinished()) {
|
||||
assertLastProgress(0.5f)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unfoldedWithAnimation_foldAnimation_doesNotPropagateEvents() {
|
||||
foldProvider.onFoldUpdate(isFolded = true)
|
||||
foldProvider.onFoldUpdate(isFolded = false)
|
||||
// Unfold animation
|
||||
sourceProvider.onTransitionStarted()
|
||||
sourceProvider.onTransitionProgress(0.5f)
|
||||
sourceProvider.onTransitionFinished()
|
||||
listener.clear()
|
||||
|
||||
// Fold animation
|
||||
sourceProvider.onTransitionStarted()
|
||||
sourceProvider.onTransitionProgress(0.2f)
|
||||
sourceProvider.onTransitionFinished()
|
||||
|
||||
listener.assertNotStarted()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unfoldedWithAnimation_foldAnimationSeveralTimes_doesNotPropagateEvents() {
|
||||
foldProvider.onFoldUpdate(isFolded = true)
|
||||
foldProvider.onFoldUpdate(isFolded = false)
|
||||
// Unfold animation
|
||||
sourceProvider.onTransitionStarted()
|
||||
sourceProvider.onTransitionProgress(0.5f)
|
||||
sourceProvider.onTransitionFinished()
|
||||
listener.clear()
|
||||
|
||||
// Start and stop fold animation several times
|
||||
repeat(3) {
|
||||
sourceProvider.onTransitionStarted()
|
||||
sourceProvider.onTransitionProgress(0.2f)
|
||||
sourceProvider.onTransitionFinished()
|
||||
}
|
||||
|
||||
listener.assertNotStarted()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unfoldedAgainAfterFolding_propagatesEvents() {
|
||||
foldProvider.onFoldUpdate(isFolded = true)
|
||||
foldProvider.onFoldUpdate(isFolded = false)
|
||||
|
||||
// Unfold animation
|
||||
sourceProvider.onTransitionStarted()
|
||||
sourceProvider.onTransitionProgress(0.5f)
|
||||
sourceProvider.onTransitionFinished()
|
||||
|
||||
// Fold animation
|
||||
sourceProvider.onTransitionStarted()
|
||||
sourceProvider.onTransitionProgress(0.2f)
|
||||
sourceProvider.onTransitionFinished()
|
||||
foldProvider.onFoldUpdate(isFolded = true)
|
||||
|
||||
listener.clear()
|
||||
|
||||
// Second unfold animation after folding
|
||||
foldProvider.onFoldUpdate(isFolded = false)
|
||||
sourceProvider.onTransitionStarted()
|
||||
sourceProvider.onTransitionProgress(0.1f)
|
||||
sourceProvider.onTransitionFinished()
|
||||
|
||||
with(listener.ensureTransitionFinished()) {
|
||||
assertLastProgress(0.1f)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user