Merge "[Unfold animation] Adjust animation parameters" into tm-qpr-dev am: dd5d243b29
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21873248 Change-Id: Ia5d88943a350b54b13a780c90357fc4d3e7743df Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -192,4 +192,4 @@ class UnfoldMoveFromCenterAnimator @JvmOverloads constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val TRANSLATION_PERCENTAGE = 0.3f
|
private const val TRANSLATION_PERCENTAGE = 0.08f
|
||||||
|
|||||||
@@ -694,6 +694,12 @@ object Flags {
|
|||||||
val ENABLE_DARK_VIGNETTE_WHEN_FOLDING =
|
val ENABLE_DARK_VIGNETTE_WHEN_FOLDING =
|
||||||
unreleasedFlag(2700, "enable_dark_vignette_when_folding")
|
unreleasedFlag(2700, "enable_dark_vignette_when_folding")
|
||||||
|
|
||||||
|
// TODO(b/265764985): Tracking Bug
|
||||||
|
@Keep
|
||||||
|
@JvmField
|
||||||
|
val ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS =
|
||||||
|
unreleasedFlag(2701, "enable_unfold_status_bar_animations")
|
||||||
|
|
||||||
// TODO(b259590361): Tracking bug
|
// TODO(b259590361): Tracking bug
|
||||||
val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
|
val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import android.view.ViewGroup
|
|||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import com.android.systemui.Gefingerpoken
|
import com.android.systemui.Gefingerpoken
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.flags.FeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags
|
||||||
import com.android.systemui.shade.ShadeController
|
import com.android.systemui.shade.ShadeController
|
||||||
import com.android.systemui.shade.ShadeLogger
|
import com.android.systemui.shade.ShadeLogger
|
||||||
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
|
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
|
||||||
@@ -215,6 +217,7 @@ class PhoneStatusBarViewController private constructor(
|
|||||||
private val unfoldComponent: Optional<SysUIUnfoldComponent>,
|
private val unfoldComponent: Optional<SysUIUnfoldComponent>,
|
||||||
@Named(UNFOLD_STATUS_BAR)
|
@Named(UNFOLD_STATUS_BAR)
|
||||||
private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
|
private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
|
||||||
|
private val featureFlags: FeatureFlags,
|
||||||
private val userChipViewModel: StatusBarUserChipViewModel,
|
private val userChipViewModel: StatusBarUserChipViewModel,
|
||||||
private val centralSurfaces: CentralSurfaces,
|
private val centralSurfaces: CentralSurfaces,
|
||||||
private val shadeController: ShadeController,
|
private val shadeController: ShadeController,
|
||||||
@@ -224,17 +227,25 @@ class PhoneStatusBarViewController private constructor(
|
|||||||
) {
|
) {
|
||||||
fun create(
|
fun create(
|
||||||
view: PhoneStatusBarView
|
view: PhoneStatusBarView
|
||||||
) =
|
): PhoneStatusBarViewController {
|
||||||
PhoneStatusBarViewController(
|
val statusBarMoveFromCenterAnimationController =
|
||||||
view,
|
if (featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) {
|
||||||
progressProvider.getOrNull(),
|
unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController()
|
||||||
centralSurfaces,
|
} else {
|
||||||
shadeController,
|
null
|
||||||
shadeLogger,
|
}
|
||||||
unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController(),
|
|
||||||
userChipViewModel,
|
return PhoneStatusBarViewController(
|
||||||
viewUtil,
|
view,
|
||||||
configurationController
|
progressProvider.getOrNull(),
|
||||||
|
centralSurfaces,
|
||||||
|
shadeController,
|
||||||
|
shadeLogger,
|
||||||
|
statusBarMoveFromCenterAnimationController,
|
||||||
|
userChipViewModel,
|
||||||
|
viewUtil,
|
||||||
|
configurationController
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ constructor(
|
|||||||
private var isFolded: Boolean = false
|
private var isFolded: Boolean = false
|
||||||
private var isUnfoldHandled: Boolean = true
|
private var isUnfoldHandled: Boolean = true
|
||||||
private var overlayAddReason: AddOverlayReason? = null
|
private var overlayAddReason: AddOverlayReason? = null
|
||||||
|
private var isTouchBlocked: Boolean = true
|
||||||
|
|
||||||
private var currentRotation: Int = context.display!!.rotation
|
private var currentRotation: Int = context.display!!.rotation
|
||||||
|
|
||||||
@@ -253,7 +254,15 @@ constructor(
|
|||||||
params.layoutInDisplayCutoutMode =
|
params.layoutInDisplayCutoutMode =
|
||||||
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
|
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
|
||||||
params.fitInsetsTypes = 0
|
params.fitInsetsTypes = 0
|
||||||
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
|
||||||
|
val touchFlags =
|
||||||
|
if (isTouchBlocked) {
|
||||||
|
// Touchable by default, so it will block the touches
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
||||||
|
}
|
||||||
|
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or touchFlags
|
||||||
params.setTrustedOverlay()
|
params.setTrustedOverlay()
|
||||||
|
|
||||||
val packageName: String = context.opPackageName
|
val packageName: String = context.opPackageName
|
||||||
@@ -262,6 +271,24 @@ constructor(
|
|||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateTouchBlockIfNeeded(progress: Float) {
|
||||||
|
// When unfolding unblock touches a bit earlier than the animation end as the
|
||||||
|
// interpolation has a long tail of very slight movement at the end which should not
|
||||||
|
// affect much the usage of the device
|
||||||
|
val shouldBlockTouches =
|
||||||
|
if (overlayAddReason == UNFOLD) {
|
||||||
|
progress < UNFOLD_BLOCK_TOUCHES_UNTIL_PROGRESS
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTouchBlocked != shouldBlockTouches) {
|
||||||
|
isTouchBlocked = shouldBlockTouches
|
||||||
|
|
||||||
|
traceSection("$TAG#relayoutToUpdateTouch") { root?.relayout(getLayoutParams()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createLightRevealEffect(): LightRevealEffect {
|
private fun createLightRevealEffect(): LightRevealEffect {
|
||||||
val isVerticalFold =
|
val isVerticalFold =
|
||||||
currentRotation == Surface.ROTATION_0 || currentRotation == Surface.ROTATION_180
|
currentRotation == Surface.ROTATION_0 || currentRotation == Surface.ROTATION_180
|
||||||
@@ -288,7 +315,10 @@ constructor(
|
|||||||
private inner class TransitionListener : TransitionProgressListener {
|
private inner class TransitionListener : TransitionProgressListener {
|
||||||
|
|
||||||
override fun onTransitionProgress(progress: Float) {
|
override fun onTransitionProgress(progress: Float) {
|
||||||
executeInBackground { scrimView?.revealAmount = calculateRevealAmount(progress) }
|
executeInBackground {
|
||||||
|
scrimView?.revealAmount = calculateRevealAmount(progress)
|
||||||
|
updateTouchBlockIfNeeded(progress)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTransitionFinished() {
|
override fun onTransitionFinished() {
|
||||||
@@ -360,5 +390,7 @@ constructor(
|
|||||||
// constants for revealAmount.
|
// constants for revealAmount.
|
||||||
const val TRANSPARENT = 1f
|
const val TRANSPARENT = 1f
|
||||||
const val BLACK = 0f
|
const val BLACK = 0f
|
||||||
|
|
||||||
|
private const val UNFOLD_BLOCK_TOUCHES_UNTIL_PROGRESS = 0.8f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() {
|
|||||||
// Positive translationX -> translated to the right
|
// Positive translationX -> translated to the right
|
||||||
// 10x10 view center is 25px from the center,
|
// 10x10 view center is 25px from the center,
|
||||||
// When progress is 0.5 it should be translated at:
|
// When progress is 0.5 it should be translated at:
|
||||||
// 25 * 0.3 * (1 - 0.5) = 3.75px
|
// 25 * 0.08 * (1 - 0.5) = 1px
|
||||||
assertThat(view.translationX).isWithin(0.01f).of(3.75f)
|
assertThat(view.translationX).isWithin(0.01f).of(1.0f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -81,8 +81,8 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() {
|
|||||||
// Positive translationX -> translated to the right
|
// Positive translationX -> translated to the right
|
||||||
// 10x10 view center is 25px from the center,
|
// 10x10 view center is 25px from the center,
|
||||||
// When progress is 0 it should be translated at:
|
// When progress is 0 it should be translated at:
|
||||||
// 25 * 0.3 * (1 - 0) = 7.5px
|
// 25 * 0.08 * (1 - 0) = 7.5px
|
||||||
assertThat(view.translationX).isWithin(0.01f).of(7.5f)
|
assertThat(view.translationX).isWithin(0.01f).of(2f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -97,7 +97,7 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() {
|
|||||||
// Positive translationX -> translated to the right
|
// Positive translationX -> translated to the right
|
||||||
// 10x10 view center is 25px from the center,
|
// 10x10 view center is 25px from the center,
|
||||||
// When progress is 1 it should be translated at:
|
// When progress is 1 it should be translated at:
|
||||||
// 25 * 0.3 * 0 = 0px
|
// 25 * 0.08 * 0 = 0px
|
||||||
assertThat(view.translationX).isEqualTo(0f)
|
assertThat(view.translationX).isEqualTo(0f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,8 +113,8 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() {
|
|||||||
// Positive translationX -> translated to the right, original translation is ignored
|
// Positive translationX -> translated to the right, original translation is ignored
|
||||||
// 10x10 view center is 25px from the center,
|
// 10x10 view center is 25px from the center,
|
||||||
// When progress is 0.5 it should be translated at:
|
// When progress is 0.5 it should be translated at:
|
||||||
// 25 * 0.3 * (1 - 0.5) = 3.75px
|
// 25 * 0.08 * (1 - 0.5) = 1px
|
||||||
assertThat(view.translationX).isWithin(0.01f).of(3.75f)
|
assertThat(view.translationX).isWithin(0.01f).of(1.0f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -154,7 +154,7 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() {
|
|||||||
animator.onTransitionProgress(0.5f)
|
animator.onTransitionProgress(0.5f)
|
||||||
|
|
||||||
// Positive translationY -> translated to the bottom
|
// Positive translationY -> translated to the bottom
|
||||||
assertThat(view.translationY).isWithin(0.01f).of(3.75f)
|
assertThat(view.translationY).isWithin(0.01f).of(1f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -169,7 +169,7 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() {
|
|||||||
animator.updateViewPositions()
|
animator.updateViewPositions()
|
||||||
|
|
||||||
// Negative translationX -> translated to the left
|
// Negative translationX -> translated to the left
|
||||||
assertThat(view.translationX).isWithin(0.1f).of(-5.25f)
|
assertThat(view.translationX).isWithin(0.1f).of(-1.4f)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createView(
|
private fun createView(
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import androidx.test.filters.SmallTest
|
|||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.flags.FeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags
|
||||||
import com.android.systemui.shade.NotificationPanelViewController
|
import com.android.systemui.shade.NotificationPanelViewController
|
||||||
import com.android.systemui.shade.ShadeControllerImpl
|
import com.android.systemui.shade.ShadeControllerImpl
|
||||||
import com.android.systemui.shade.ShadeLogger
|
import com.android.systemui.shade.ShadeLogger
|
||||||
@@ -34,6 +36,7 @@ import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
|||||||
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
|
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
|
||||||
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel
|
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel
|
||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
import com.android.systemui.util.view.ViewUtil
|
import com.android.systemui.util.view.ViewUtil
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@@ -54,6 +57,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
|
|||||||
@Mock
|
@Mock
|
||||||
private lateinit var notificationPanelViewController: NotificationPanelViewController
|
private lateinit var notificationPanelViewController: NotificationPanelViewController
|
||||||
@Mock
|
@Mock
|
||||||
|
private lateinit var featureFlags: FeatureFlags
|
||||||
|
@Mock
|
||||||
private lateinit var moveFromCenterAnimation: StatusBarMoveFromCenterAnimationController
|
private lateinit var moveFromCenterAnimation: StatusBarMoveFromCenterAnimationController
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var sysuiUnfoldComponent: SysUIUnfoldComponent
|
private lateinit var sysuiUnfoldComponent: SysUIUnfoldComponent
|
||||||
@@ -93,6 +98,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onViewAttachedAndDrawn_moveFromCenterAnimationEnabled_moveFromCenterAnimationInitialized() {
|
fun onViewAttachedAndDrawn_moveFromCenterAnimationEnabled_moveFromCenterAnimationInitialized() {
|
||||||
|
whenever(featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS))
|
||||||
|
.thenReturn(true)
|
||||||
val view = createViewMock()
|
val view = createViewMock()
|
||||||
val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java)
|
val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java)
|
||||||
unfoldConfig.isEnabled = true
|
unfoldConfig.isEnabled = true
|
||||||
@@ -107,6 +114,20 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
|
|||||||
verify(moveFromCenterAnimation).onViewsReady(any())
|
verify(moveFromCenterAnimation).onViewsReady(any())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onViewAttachedAndDrawn_statusBarAnimationDisabled_animationNotInitialized() {
|
||||||
|
whenever(featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS))
|
||||||
|
.thenReturn(false)
|
||||||
|
val view = createViewMock()
|
||||||
|
unfoldConfig.isEnabled = true
|
||||||
|
// create the controller on main thread as it requires main looper
|
||||||
|
InstrumentationRegistry.getInstrumentation().runOnMainSync {
|
||||||
|
controller = createAndInitController(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(moveFromCenterAnimation, never()).onViewsReady(any())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() {
|
fun handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() {
|
||||||
`when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(false)
|
`when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(false)
|
||||||
@@ -179,6 +200,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
|
|||||||
return PhoneStatusBarViewController.Factory(
|
return PhoneStatusBarViewController.Factory(
|
||||||
Optional.of(sysuiUnfoldComponent),
|
Optional.of(sysuiUnfoldComponent),
|
||||||
Optional.of(progressProvider),
|
Optional.of(progressProvider),
|
||||||
|
featureFlags,
|
||||||
userChipViewModel,
|
userChipViewModel,
|
||||||
centralSurfacesImpl,
|
centralSurfacesImpl,
|
||||||
shadeControllerImpl,
|
shadeControllerImpl,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
progressProvider = PhysicsBasedUnfoldTransitionProgressProvider(foldStateProvider)
|
progressProvider = PhysicsBasedUnfoldTransitionProgressProvider(context, foldStateProvider)
|
||||||
progressProvider.addCallback(listener)
|
progressProvider.addCallback(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.systemui.unfold.progress
|
package com.android.systemui.unfold.progress
|
||||||
|
|
||||||
|
import android.animation.Animator
|
||||||
|
import android.animation.AnimatorListenerAdapter
|
||||||
|
import android.animation.ObjectAnimator
|
||||||
|
import android.animation.ValueAnimator
|
||||||
|
import android.content.Context
|
||||||
import android.os.Trace
|
import android.os.Trace
|
||||||
|
import android.util.FloatProperty
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.animation.AnimationUtils.loadInterpolator
|
||||||
import androidx.dynamicanimation.animation.DynamicAnimation
|
import androidx.dynamicanimation.animation.DynamicAnimation
|
||||||
import androidx.dynamicanimation.animation.FloatPropertyCompat
|
import androidx.dynamicanimation.animation.FloatPropertyCompat
|
||||||
import androidx.dynamicanimation.animation.SpringAnimation
|
import androidx.dynamicanimation.animation.SpringAnimation
|
||||||
@@ -34,14 +41,22 @@ import com.android.systemui.unfold.updates.name
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/** Maps fold updates to unfold transition progress using DynamicAnimation. */
|
/** Maps fold updates to unfold transition progress using DynamicAnimation. */
|
||||||
class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor(
|
class PhysicsBasedUnfoldTransitionProgressProvider
|
||||||
private val foldStateProvider: FoldStateProvider
|
@Inject
|
||||||
) : UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener {
|
constructor(context: Context, private val foldStateProvider: FoldStateProvider) :
|
||||||
|
UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener {
|
||||||
|
|
||||||
|
private val emphasizedInterpolator =
|
||||||
|
loadInterpolator(context, android.R.interpolator.fast_out_extra_slow_in)
|
||||||
|
|
||||||
|
private var cannedAnimator: ValueAnimator? = null
|
||||||
|
|
||||||
private val springAnimation =
|
private val springAnimation =
|
||||||
SpringAnimation(this, AnimationProgressProperty).apply {
|
SpringAnimation(
|
||||||
addEndListener(this@PhysicsBasedUnfoldTransitionProgressProvider)
|
this,
|
||||||
}
|
FloatPropertyCompat.createFloatPropertyCompat(AnimationProgressProperty)
|
||||||
|
)
|
||||||
|
.apply { addEndListener(this@PhysicsBasedUnfoldTransitionProgressProvider) }
|
||||||
|
|
||||||
private var isTransitionRunning = false
|
private var isTransitionRunning = false
|
||||||
private var isAnimatedCancelRunning = false
|
private var isAnimatedCancelRunning = false
|
||||||
@@ -76,7 +91,8 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor(
|
|||||||
|
|
||||||
override fun onFoldUpdate(@FoldUpdate update: Int) {
|
override fun onFoldUpdate(@FoldUpdate update: Int) {
|
||||||
when (update) {
|
when (update) {
|
||||||
FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_FINISH_HALF_OPEN -> {
|
FOLD_UPDATE_FINISH_FULL_OPEN,
|
||||||
|
FOLD_UPDATE_FINISH_HALF_OPEN -> {
|
||||||
// Do not cancel if we haven't started the transition yet.
|
// Do not cancel if we haven't started the transition yet.
|
||||||
// This could happen when we fully unfolded the device before the screen
|
// This could happen when we fully unfolded the device before the screen
|
||||||
// became available. In this case we start and immediately cancel the animation
|
// became available. In this case we start and immediately cancel the animation
|
||||||
@@ -100,6 +116,14 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor(
|
|||||||
// the transition continues running.
|
// the transition continues running.
|
||||||
if (isAnimatedCancelRunning) {
|
if (isAnimatedCancelRunning) {
|
||||||
isAnimatedCancelRunning = false
|
isAnimatedCancelRunning = false
|
||||||
|
|
||||||
|
// Switching to spring animation, start the animation if it
|
||||||
|
// is not running already
|
||||||
|
springAnimation.animateToFinalPosition(1.0f)
|
||||||
|
|
||||||
|
cannedAnimator?.removeAllListeners()
|
||||||
|
cannedAnimator?.cancel()
|
||||||
|
cannedAnimator = null
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
startTransition(startValue = 1f)
|
startTransition(startValue = 1f)
|
||||||
@@ -130,13 +154,22 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
isAnimatedCancelRunning = true
|
isAnimatedCancelRunning = true
|
||||||
springAnimation.animateToFinalPosition(endValue)
|
|
||||||
|
if (USE_CANNED_ANIMATION) {
|
||||||
|
startCannedCancelAnimation()
|
||||||
|
} else {
|
||||||
|
springAnimation.animateToFinalPosition(endValue)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
transitionProgress = endValue
|
transitionProgress = endValue
|
||||||
isAnimatedCancelRunning = false
|
isAnimatedCancelRunning = false
|
||||||
isTransitionRunning = false
|
isTransitionRunning = false
|
||||||
springAnimation.cancel()
|
springAnimation.cancel()
|
||||||
|
|
||||||
|
cannedAnimator?.removeAllListeners()
|
||||||
|
cannedAnimator?.cancel()
|
||||||
|
cannedAnimator = null
|
||||||
|
|
||||||
listeners.forEach { it.onTransitionFinished() }
|
listeners.forEach { it.onTransitionFinished() }
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -157,7 +190,7 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onStartTransition() {
|
private fun onStartTransition() {
|
||||||
Trace.beginSection( "$TAG#onStartTransition")
|
Trace.beginSection("$TAG#onStartTransition")
|
||||||
listeners.forEach { it.onTransitionStarted() }
|
listeners.forEach { it.onTransitionStarted() }
|
||||||
Trace.endSection()
|
Trace.endSection()
|
||||||
|
|
||||||
@@ -195,8 +228,39 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor(
|
|||||||
listeners.remove(listener)
|
listeners.remove(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startCannedCancelAnimation() {
|
||||||
|
cannedAnimator?.cancel()
|
||||||
|
cannedAnimator = null
|
||||||
|
|
||||||
|
// Temporary remove listener to cancel the spring animation without
|
||||||
|
// finishing the transition
|
||||||
|
springAnimation.removeEndListener(this)
|
||||||
|
springAnimation.cancel()
|
||||||
|
springAnimation.addEndListener(this)
|
||||||
|
|
||||||
|
cannedAnimator =
|
||||||
|
ObjectAnimator.ofFloat(this, AnimationProgressProperty, transitionProgress, 1f).apply {
|
||||||
|
addListener(CannedAnimationListener())
|
||||||
|
duration =
|
||||||
|
(CANNED_ANIMATION_DURATION_MS.toFloat() * (1f - transitionProgress)).toLong()
|
||||||
|
interpolator = emphasizedInterpolator
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class CannedAnimationListener : AnimatorListenerAdapter() {
|
||||||
|
override fun onAnimationStart(animator: Animator) {
|
||||||
|
Trace.beginAsyncSection("$TAG#cannedAnimatorRunning", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationEnd(animator: Animator) {
|
||||||
|
cancelTransition(1f, animate = false)
|
||||||
|
Trace.endAsyncSection("$TAG#cannedAnimatorRunning", 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private object AnimationProgressProperty :
|
private object AnimationProgressProperty :
|
||||||
FloatPropertyCompat<PhysicsBasedUnfoldTransitionProgressProvider>("animation_progress") {
|
FloatProperty<PhysicsBasedUnfoldTransitionProgressProvider>("animation_progress") {
|
||||||
|
|
||||||
override fun setValue(
|
override fun setValue(
|
||||||
provider: PhysicsBasedUnfoldTransitionProgressProvider,
|
provider: PhysicsBasedUnfoldTransitionProgressProvider,
|
||||||
@@ -205,7 +269,7 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor(
|
|||||||
provider.transitionProgress = value
|
provider.transitionProgress = value
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getValue(provider: PhysicsBasedUnfoldTransitionProgressProvider): Float =
|
override fun get(provider: PhysicsBasedUnfoldTransitionProgressProvider): Float =
|
||||||
provider.transitionProgress
|
provider.transitionProgress
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,6 +277,8 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor(
|
|||||||
private const val TAG = "PhysicsBasedUnfoldTransitionProgressProvider"
|
private const val TAG = "PhysicsBasedUnfoldTransitionProgressProvider"
|
||||||
private const val DEBUG = true
|
private const val DEBUG = true
|
||||||
|
|
||||||
|
private const val USE_CANNED_ANIMATION = true
|
||||||
|
private const val CANNED_ANIMATION_DURATION_MS = 1000
|
||||||
private const val SPRING_STIFFNESS = 600.0f
|
private const val SPRING_STIFFNESS = 600.0f
|
||||||
private const val MINIMAL_VISIBLE_CHANGE = 0.001f
|
private const val MINIMAL_VISIBLE_CHANGE = 0.001f
|
||||||
private const val FINAL_HINGE_ANGLE_POSITION = 165f
|
private const val FINAL_HINGE_ANGLE_POSITION = 165f
|
||||||
|
|||||||
Reference in New Issue
Block a user