Merge "Transitioning media on lockscreen with a fade" into sc-dev
This commit is contained in:
@@ -22,6 +22,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:forceHasOverlappingRendering="false"
|
||||||
android:theme="@style/MediaPlayer">
|
android:theme="@style/MediaPlayer">
|
||||||
<com.android.systemui.media.MediaScrollView
|
<com.android.systemui.media.MediaScrollView
|
||||||
android:id="@+id/media_carousel_scroller"
|
android:id="@+id/media_carousel_scroller"
|
||||||
|
|||||||
@@ -1418,10 +1418,6 @@
|
|||||||
<dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
|
<dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
|
||||||
<dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
|
<dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
|
||||||
|
|
||||||
<!-- Delay after which the media will start transitioning to the full shade on
|
|
||||||
the lockscreen -->
|
|
||||||
<dimen name="lockscreen_shade_media_transition_start_delay">40dp</dimen>
|
|
||||||
|
|
||||||
<!-- Distance that the full shade transition takes in order for qs to fully transition to the
|
<!-- Distance that the full shade transition takes in order for qs to fully transition to the
|
||||||
shade -->
|
shade -->
|
||||||
<dimen name="lockscreen_shade_qs_transition_distance">200dp</dimen>
|
<dimen name="lockscreen_shade_qs_transition_distance">200dp</dimen>
|
||||||
@@ -1430,13 +1426,16 @@
|
|||||||
the shade (in alpha) -->
|
the shade (in alpha) -->
|
||||||
<dimen name="lockscreen_shade_scrim_transition_distance">80dp</dimen>
|
<dimen name="lockscreen_shade_scrim_transition_distance">80dp</dimen>
|
||||||
|
|
||||||
<!-- Extra inset for the notifications when accounting for media during the lockscreen to
|
<!-- Distance that the full shade transition takes in order for media to fully transition to
|
||||||
shade transition to compensate for the disappearing media -->
|
the shade -->
|
||||||
<dimen name="lockscreen_shade_transition_extra_media_inset">-48dp</dimen>
|
<dimen name="lockscreen_shade_media_transition_distance">140dp</dimen>
|
||||||
|
|
||||||
<!-- Maximum overshoot for the topPadding of notifications when transitioning to the full
|
<!-- Maximum overshoot for the topPadding of notifications when transitioning to the full
|
||||||
shade -->
|
shade -->
|
||||||
<dimen name="lockscreen_shade_max_top_overshoot">32dp</dimen>
|
<dimen name="lockscreen_shade_notification_movement">24dp</dimen>
|
||||||
|
|
||||||
|
<!-- Maximum overshoot for the pulse expansion -->
|
||||||
|
<dimen name="pulse_expansion_max_top_overshoot">16dp</dimen>
|
||||||
|
|
||||||
<dimen name="people_space_widget_radius">28dp</dimen>
|
<dimen name="people_space_widget_radius">28dp</dimen>
|
||||||
<dimen name="people_space_image_radius">20dp</dimen>
|
<dimen name="people_space_image_radius">20dp</dimen>
|
||||||
|
|||||||
@@ -26,21 +26,23 @@ import android.util.MathUtils
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewGroupOverlay
|
import android.view.ViewGroupOverlay
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.animation.Interpolators
|
import com.android.systemui.animation.Interpolators
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.keyguard.WakefulnessLifecycle
|
import com.android.systemui.keyguard.WakefulnessLifecycle
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||||
|
import com.android.systemui.statusbar.CrossFadeHelper
|
||||||
import com.android.systemui.statusbar.NotificationLockscreenUserManager
|
import com.android.systemui.statusbar.NotificationLockscreenUserManager
|
||||||
import com.android.systemui.statusbar.StatusBarState
|
import com.android.systemui.statusbar.StatusBarState
|
||||||
import com.android.systemui.statusbar.SysuiStatusBarStateController
|
import com.android.systemui.statusbar.SysuiStatusBarStateController
|
||||||
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
|
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
|
||||||
import com.android.systemui.statusbar.phone.KeyguardBypassController
|
import com.android.systemui.statusbar.phone.KeyguardBypassController
|
||||||
|
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
|
||||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||||
import com.android.systemui.util.animation.UniqueObjectHostView
|
import com.android.systemui.util.animation.UniqueObjectHostView
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similarly to isShown but also excludes views that have 0 alpha
|
* Similarly to isShown but also excludes views that have 0 alpha
|
||||||
@@ -80,6 +82,7 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
wakefulnessLifecycle: WakefulnessLifecycle,
|
wakefulnessLifecycle: WakefulnessLifecycle,
|
||||||
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager
|
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The root overlay of the hierarchy. This is where the media notification is attached to
|
* The root overlay of the hierarchy. This is where the media notification is attached to
|
||||||
* whenever the view is transitioning from one host to another. It also make sure that the
|
* whenever the view is transitioning from one host to another. It also make sure that the
|
||||||
@@ -90,6 +93,30 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
private var rootView: View? = null
|
private var rootView: View? = null
|
||||||
private var currentBounds = Rect()
|
private var currentBounds = Rect()
|
||||||
private var animationStartBounds: Rect = Rect()
|
private var animationStartBounds: Rect = Rect()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cross fade progress at the start of the animation. 0.5f means it's just switching between
|
||||||
|
* the start and the end location and the content is fully faded, while 0.75f means that we're
|
||||||
|
* halfway faded in again in the target state.
|
||||||
|
*/
|
||||||
|
private var animationStartCrossFadeProgress = 0.0f
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The starting alpha of the animation
|
||||||
|
*/
|
||||||
|
private var animationStartAlpha = 0.0f
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The starting location of the cross fade if an animation is running right now.
|
||||||
|
*/
|
||||||
|
@MediaLocation
|
||||||
|
private var crossFadeAnimationStartLocation = -1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The end location of the cross fade if an animation is running right now.
|
||||||
|
*/
|
||||||
|
@MediaLocation
|
||||||
|
private var crossFadeAnimationEndLocation = -1
|
||||||
private var targetBounds: Rect = Rect()
|
private var targetBounds: Rect = Rect()
|
||||||
private val mediaFrame
|
private val mediaFrame
|
||||||
get() = mediaCarouselController.mediaFrame
|
get() = mediaCarouselController.mediaFrame
|
||||||
@@ -98,9 +125,22 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
interpolator = Interpolators.FAST_OUT_SLOW_IN
|
interpolator = Interpolators.FAST_OUT_SLOW_IN
|
||||||
addUpdateListener {
|
addUpdateListener {
|
||||||
updateTargetState()
|
updateTargetState()
|
||||||
interpolateBounds(animationStartBounds, targetBounds, animatedFraction,
|
val currentAlpha: Float
|
||||||
|
var boundsProgress = animatedFraction
|
||||||
|
if (isCrossFadeAnimatorRunning) {
|
||||||
|
animationCrossFadeProgress = MathUtils.lerp(animationStartCrossFadeProgress, 1.0f,
|
||||||
|
animatedFraction)
|
||||||
|
// When crossfading, let's keep the bounds at the right location during fading
|
||||||
|
boundsProgress = if (animationCrossFadeProgress < 0.5f) 0.0f else 1.0f
|
||||||
|
currentAlpha = calculateAlphaFromCrossFade(animationCrossFadeProgress,
|
||||||
|
instantlyShowAtEnd = false)
|
||||||
|
} else {
|
||||||
|
// If we're not crossfading, let's interpolate from the start alpha to 1.0f
|
||||||
|
currentAlpha = MathUtils.lerp(animationStartAlpha, 1.0f, animatedFraction)
|
||||||
|
}
|
||||||
|
interpolateBounds(animationStartBounds, targetBounds, boundsProgress,
|
||||||
result = currentBounds)
|
result = currentBounds)
|
||||||
applyState(currentBounds)
|
applyState(currentBounds, currentAlpha)
|
||||||
}
|
}
|
||||||
addListener(object : AnimatorListenerAdapter() {
|
addListener(object : AnimatorListenerAdapter() {
|
||||||
private var cancelled: Boolean = false
|
private var cancelled: Boolean = false
|
||||||
@@ -112,6 +152,7 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onAnimationEnd(animation: Animator?) {
|
override fun onAnimationEnd(animation: Animator?) {
|
||||||
|
isCrossFadeAnimatorRunning = false
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
applyTargetStateIfNotAnimating()
|
applyTargetStateIfNotAnimating()
|
||||||
}
|
}
|
||||||
@@ -191,11 +232,6 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
*/
|
*/
|
||||||
private var distanceForFullShadeTransition = 0
|
private var distanceForFullShadeTransition = 0
|
||||||
|
|
||||||
/**
|
|
||||||
* Delay after which the media will start transitioning to the full shade on the lockscreen.
|
|
||||||
*/
|
|
||||||
private var fullShadeTransitionDelay = 0
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount of progress we are currently in if we're transitioning to the full shade.
|
* The amount of progress we are currently in if we're transitioning to the full shade.
|
||||||
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
|
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
|
||||||
@@ -207,18 +243,33 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
field = value
|
field = value
|
||||||
if (bypassController.bypassEnabled) {
|
if (bypassController.bypassEnabled || statusbarState != StatusBarState.KEYGUARD) {
|
||||||
|
// No need to do all the calculations / updates below if we're not on the lockscreen
|
||||||
|
// or if we're bypassing.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
updateDesiredLocation()
|
updateDesiredLocation(forceNoAnimation = isCurrentlyFading())
|
||||||
if (value >= 0) {
|
if (value >= 0) {
|
||||||
updateTargetState()
|
updateTargetState()
|
||||||
|
// Setting the alpha directly, as the below call will use it to update the alpha
|
||||||
|
carouselAlpha = calculateAlphaFromCrossFade(field, instantlyShowAtEnd = true)
|
||||||
applyTargetStateIfNotAnimating()
|
applyTargetStateIfNotAnimating()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is there currently a cross-fade animation running driven by an animator?
|
||||||
|
*/
|
||||||
|
private var isCrossFadeAnimatorRunning = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are we currently transitionioning from the lockscreen to the full shade
|
||||||
|
* [StatusBarState.SHADE_LOCKED] or [StatusBarState.SHADE]. Once the user has dragged down and
|
||||||
|
* the transition starts, this will no longer return true.
|
||||||
|
*/
|
||||||
private val isTransitioningToFullShade: Boolean
|
private val isTransitioningToFullShade: Boolean
|
||||||
get() = fullShadeTransitionProgress != 0f && !bypassController.bypassEnabled
|
get() = fullShadeTransitionProgress != 0f && !bypassController.bypassEnabled &&
|
||||||
|
statusbarState == StatusBarState.KEYGUARD
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the amount of pixels we have currently dragged down if we're transitioning to the full
|
* Set the amount of pixels we have currently dragged down if we're transitioning to the full
|
||||||
@@ -227,14 +278,8 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
fun setTransitionToFullShadeAmount(value: Float) {
|
fun setTransitionToFullShadeAmount(value: Float) {
|
||||||
// If we're transitioning starting on the shade_locked, we don't want any delay and rather
|
// If we're transitioning starting on the shade_locked, we don't want any delay and rather
|
||||||
// have it aligned with the rest of the animation
|
// have it aligned with the rest of the animation
|
||||||
val delay = if (statusbarState == StatusBarState.KEYGUARD) {
|
val progress = MathUtils.saturate(value / distanceForFullShadeTransition)
|
||||||
fullShadeTransitionDelay
|
fullShadeTransitionProgress = progress
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
val progress = MathUtils.saturate((value - delay) /
|
|
||||||
(distanceForFullShadeTransition - delay))
|
|
||||||
fullShadeTransitionProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(progress)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -296,6 +341,49 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current cross fade progress. 0.5f means it's just switching
|
||||||
|
* between the start and the end location and the content is fully faded, while 0.75f means
|
||||||
|
* that we're halfway faded in again in the target state.
|
||||||
|
* This is only valid while [isCrossFadeAnimatorRunning] is true.
|
||||||
|
*/
|
||||||
|
private var animationCrossFadeProgress = 1.0f
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current carousel Alpha.
|
||||||
|
*/
|
||||||
|
private var carouselAlpha: Float = 1.0f
|
||||||
|
set(value) {
|
||||||
|
if (field == value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
field = value
|
||||||
|
CrossFadeHelper.fadeIn(mediaFrame, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the alpha of the view when given a cross-fade progress.
|
||||||
|
*
|
||||||
|
* @param crossFadeProgress The current cross fade progress. 0.5f means it's just switching
|
||||||
|
* between the start and the end location and the content is fully faded, while 0.75f means
|
||||||
|
* that we're halfway faded in again in the target state.
|
||||||
|
*
|
||||||
|
* @param instantlyShowAtEnd should the view be instantly shown at the end. This is needed
|
||||||
|
* to avoid fadinging in when the target was hidden anyway.
|
||||||
|
*/
|
||||||
|
private fun calculateAlphaFromCrossFade(
|
||||||
|
crossFadeProgress: Float,
|
||||||
|
instantlyShowAtEnd: Boolean
|
||||||
|
): Float {
|
||||||
|
if (crossFadeProgress <= 0.5f) {
|
||||||
|
return 1.0f - crossFadeProgress / 0.5f
|
||||||
|
} else if (instantlyShowAtEnd) {
|
||||||
|
return 1.0f
|
||||||
|
} else {
|
||||||
|
return (crossFadeProgress - 0.5f) / 0.5f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
updateConfiguration()
|
updateConfiguration()
|
||||||
configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
|
configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
|
||||||
@@ -375,9 +463,7 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
|
|
||||||
private fun updateConfiguration() {
|
private fun updateConfiguration() {
|
||||||
distanceForFullShadeTransition = context.resources.getDimensionPixelSize(
|
distanceForFullShadeTransition = context.resources.getDimensionPixelSize(
|
||||||
R.dimen.lockscreen_shade_qs_transition_distance)
|
R.dimen.lockscreen_shade_media_transition_distance)
|
||||||
fullShadeTransitionDelay = context.resources.getDimensionPixelSize(
|
|
||||||
R.dimen.lockscreen_shade_media_transition_start_delay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -449,8 +535,13 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
shouldAnimateTransition(desiredLocation, previousLocation)
|
shouldAnimateTransition(desiredLocation, previousLocation)
|
||||||
val (animDuration, delay) = getAnimationParams(previousLocation, desiredLocation)
|
val (animDuration, delay) = getAnimationParams(previousLocation, desiredLocation)
|
||||||
val host = getHost(desiredLocation)
|
val host = getHost(desiredLocation)
|
||||||
mediaCarouselController.onDesiredLocationChanged(desiredLocation, host, animate,
|
val willFade = calculateTransformationType() == TRANSFORMATION_TYPE_FADE
|
||||||
animDuration, delay)
|
if (!willFade || isCurrentlyInGuidedTransformation() || !animate) {
|
||||||
|
// if we're fading, we want the desired location / measurement only to change
|
||||||
|
// once fully faded. This is happening in the host attachment
|
||||||
|
mediaCarouselController.onDesiredLocationChanged(desiredLocation, host,
|
||||||
|
animate, animDuration, delay)
|
||||||
|
}
|
||||||
performTransitionToNewLocation(isNewView, animate)
|
performTransitionToNewLocation(isNewView, animate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -470,6 +561,8 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
if (isCurrentlyInGuidedTransformation()) {
|
if (isCurrentlyInGuidedTransformation()) {
|
||||||
applyTargetStateIfNotAnimating()
|
applyTargetStateIfNotAnimating()
|
||||||
} else if (animate) {
|
} else if (animate) {
|
||||||
|
val wasCrossFading = isCrossFadeAnimatorRunning
|
||||||
|
val previewsCrossFadeProgress = animationCrossFadeProgress
|
||||||
animator.cancel()
|
animator.cancel()
|
||||||
if (currentAttachmentLocation != previousLocation ||
|
if (currentAttachmentLocation != previousLocation ||
|
||||||
!previousHost.hostView.isAttachedToWindow) {
|
!previousHost.hostView.isAttachedToWindow) {
|
||||||
@@ -482,6 +575,42 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
// be outdated
|
// be outdated
|
||||||
animationStartBounds.set(previousHost.currentBounds)
|
animationStartBounds.set(previousHost.currentBounds)
|
||||||
}
|
}
|
||||||
|
val transformationType = calculateTransformationType()
|
||||||
|
var needsCrossFade = transformationType == TRANSFORMATION_TYPE_FADE
|
||||||
|
var crossFadeStartProgress = 0.0f
|
||||||
|
// The alpha is only relevant when not cross fading
|
||||||
|
var newCrossFadeStartLocation = previousLocation
|
||||||
|
if (wasCrossFading) {
|
||||||
|
if (currentAttachmentLocation == crossFadeAnimationEndLocation) {
|
||||||
|
if (needsCrossFade) {
|
||||||
|
// We were previously crossFading and we've already reached
|
||||||
|
// the end view, Let's start crossfading from the same position there
|
||||||
|
crossFadeStartProgress = 1.0f - previewsCrossFadeProgress
|
||||||
|
}
|
||||||
|
// Otherwise let's fade in from the current alpha, but not cross fade
|
||||||
|
} else {
|
||||||
|
// We haven't reached the previous location yet, let's still cross fade from
|
||||||
|
// where we were.
|
||||||
|
newCrossFadeStartLocation = crossFadeAnimationStartLocation
|
||||||
|
if (newCrossFadeStartLocation == desiredLocation) {
|
||||||
|
// we're crossFading back to where we were, let's start at the end position
|
||||||
|
crossFadeStartProgress = 1.0f - previewsCrossFadeProgress
|
||||||
|
} else {
|
||||||
|
// Let's start from where we are right now
|
||||||
|
crossFadeStartProgress = previewsCrossFadeProgress
|
||||||
|
// We need to force cross fading as we haven't reached the end location yet
|
||||||
|
needsCrossFade = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (needsCrossFade) {
|
||||||
|
// let's not flicker and start with the same alpha
|
||||||
|
crossFadeStartProgress = (1.0f - carouselAlpha) / 2.0f
|
||||||
|
}
|
||||||
|
isCrossFadeAnimatorRunning = needsCrossFade
|
||||||
|
crossFadeAnimationStartLocation = newCrossFadeStartLocation
|
||||||
|
crossFadeAnimationEndLocation = desiredLocation
|
||||||
|
animationStartAlpha = carouselAlpha
|
||||||
|
animationStartCrossFadeProgress = crossFadeStartProgress
|
||||||
adjustAnimatorForTransition(desiredLocation, previousLocation)
|
adjustAnimatorForTransition(desiredLocation, previousLocation)
|
||||||
if (!animationPending) {
|
if (!animationPending) {
|
||||||
rootView?.let {
|
rootView?.let {
|
||||||
@@ -518,6 +647,17 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
// non-trivial reattaching logic happening that will make the view not-shown earlier
|
// non-trivial reattaching logic happening that will make the view not-shown earlier
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (statusbarState == StatusBarState.KEYGUARD) {
|
||||||
|
if (currentLocation == LOCATION_LOCKSCREEN &&
|
||||||
|
previousLocation == LOCATION_QS ||
|
||||||
|
(currentLocation == LOCATION_QS &&
|
||||||
|
previousLocation == LOCATION_LOCKSCREEN)) {
|
||||||
|
// We're always fading from lockscreen to keyguard in situations where the player
|
||||||
|
// is already fully hidden
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
return mediaFrame.isShownNotFaded || animator.isRunning || animationPending
|
return mediaFrame.isShownNotFaded || animator.isRunning || animationPending
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,7 +678,7 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
keyguardStateController.isKeyguardFadingAway) {
|
keyguardStateController.isKeyguardFadingAway) {
|
||||||
delay = keyguardStateController.keyguardFadingAwayDelay
|
delay = keyguardStateController.keyguardFadingAwayDelay
|
||||||
}
|
}
|
||||||
animDuration = StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE.toLong()
|
animDuration = (StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE / 2f).toLong()
|
||||||
} else if (previousLocation == LOCATION_QQS && desiredLocation == LOCATION_LOCKSCREEN) {
|
} else if (previousLocation == LOCATION_QQS && desiredLocation == LOCATION_LOCKSCREEN) {
|
||||||
animDuration = StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR.toLong()
|
animDuration = StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR.toLong()
|
||||||
}
|
}
|
||||||
@@ -550,7 +690,7 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
// Let's immediately apply the target state (which is interpolated) if there is
|
// Let's immediately apply the target state (which is interpolated) if there is
|
||||||
// no animation running. Otherwise the animation update will already update
|
// no animation running. Otherwise the animation update will already update
|
||||||
// the location
|
// the location
|
||||||
applyState(targetBounds)
|
applyState(targetBounds, carouselAlpha)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,7 +698,7 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
* Updates the bounds that the view wants to be in at the end of the animation.
|
* Updates the bounds that the view wants to be in at the end of the animation.
|
||||||
*/
|
*/
|
||||||
private fun updateTargetState() {
|
private fun updateTargetState() {
|
||||||
if (isCurrentlyInGuidedTransformation()) {
|
if (isCurrentlyInGuidedTransformation() && !isCurrentlyFading()) {
|
||||||
val progress = getTransformationProgress()
|
val progress = getTransformationProgress()
|
||||||
var endHost = getHost(desiredLocation)!!
|
var endHost = getHost(desiredLocation)!!
|
||||||
var starthost = getHost(previousLocation)!!
|
var starthost = getHost(previousLocation)!!
|
||||||
@@ -605,13 +745,34 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
return getTransformationProgress() >= 0
|
return getTransformationProgress() >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the transformation type for the current animation
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
@TransformationType
|
||||||
|
fun calculateTransformationType(): Int {
|
||||||
|
if (isTransitioningToFullShade) {
|
||||||
|
return TRANSFORMATION_TYPE_FADE
|
||||||
|
}
|
||||||
|
if (previousLocation == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QS ||
|
||||||
|
previousLocation == LOCATION_QS && desiredLocation == LOCATION_LOCKSCREEN) {
|
||||||
|
// animating between ls and qs should fade, as QS is clipped.
|
||||||
|
return TRANSFORMATION_TYPE_FADE
|
||||||
|
}
|
||||||
|
if (previousLocation == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QQS) {
|
||||||
|
// animating between ls and qqs should fade when dragging down via e.g. expand button
|
||||||
|
return TRANSFORMATION_TYPE_FADE
|
||||||
|
}
|
||||||
|
return TRANSFORMATION_TYPE_TRANSITION
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the current transformation progress if we're in a guided transformation and -1
|
* @return the current transformation progress if we're in a guided transformation and -1
|
||||||
* otherwise
|
* otherwise
|
||||||
*/
|
*/
|
||||||
private fun getTransformationProgress(): Float {
|
private fun getTransformationProgress(): Float {
|
||||||
val progress = getQSTransformationProgress()
|
val progress = getQSTransformationProgress()
|
||||||
if (progress >= 0) {
|
if (statusbarState != StatusBarState.KEYGUARD && progress >= 0) {
|
||||||
return progress
|
return progress
|
||||||
}
|
}
|
||||||
if (isTransitioningToFullShade) {
|
if (isTransitioningToFullShade) {
|
||||||
@@ -643,19 +804,20 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
private fun cancelAnimationAndApplyDesiredState() {
|
private fun cancelAnimationAndApplyDesiredState() {
|
||||||
animator.cancel()
|
animator.cancel()
|
||||||
getHost(desiredLocation)?.let {
|
getHost(desiredLocation)?.let {
|
||||||
applyState(it.currentBounds, immediately = true)
|
applyState(it.currentBounds, alpha = 1.0f, immediately = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply the current state to the view, updating it's bounds and desired state
|
* Apply the current state to the view, updating it's bounds and desired state
|
||||||
*/
|
*/
|
||||||
private fun applyState(bounds: Rect, immediately: Boolean = false) {
|
private fun applyState(bounds: Rect, alpha: Float, immediately: Boolean = false) {
|
||||||
currentBounds.set(bounds)
|
currentBounds.set(bounds)
|
||||||
val currentlyInGuidedTransformation = isCurrentlyInGuidedTransformation()
|
carouselAlpha = if (isCurrentlyFading()) alpha else 1.0f
|
||||||
val startLocation = if (currentlyInGuidedTransformation) previousLocation else -1
|
val onlyUseEndState = !isCurrentlyInGuidedTransformation() || isCurrentlyFading()
|
||||||
val progress = if (currentlyInGuidedTransformation) getTransformationProgress() else 1.0f
|
val startLocation = if (onlyUseEndState) -1 else previousLocation
|
||||||
val endLocation = desiredLocation
|
val progress = if (onlyUseEndState) 1.0f else getTransformationProgress()
|
||||||
|
val endLocation = resolveLocationForFading()
|
||||||
mediaCarouselController.setCurrentState(startLocation, endLocation, progress, immediately)
|
mediaCarouselController.setCurrentState(startLocation, endLocation, progress, immediately)
|
||||||
updateHostAttachment()
|
updateHostAttachment()
|
||||||
if (currentAttachmentLocation == IN_OVERLAY) {
|
if (currentAttachmentLocation == IN_OVERLAY) {
|
||||||
@@ -668,8 +830,19 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateHostAttachment() {
|
private fun updateHostAttachment() {
|
||||||
val inOverlay = isTransitionRunning() && rootOverlay != null
|
var newLocation = resolveLocationForFading()
|
||||||
val newLocation = if (inOverlay) IN_OVERLAY else desiredLocation
|
var canUseOverlay = !isCurrentlyFading()
|
||||||
|
if (isCrossFadeAnimatorRunning) {
|
||||||
|
if (getHost(newLocation)?.visible == true &&
|
||||||
|
getHost(newLocation)?.hostView?.isShown == false &&
|
||||||
|
newLocation != desiredLocation) {
|
||||||
|
// We're crossfading but the view is already hidden. Let's move to the overlay
|
||||||
|
// instead. This happens when animating to the full shade using a button click.
|
||||||
|
canUseOverlay = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val inOverlay = isTransitionRunning() && rootOverlay != null && canUseOverlay
|
||||||
|
newLocation = if (inOverlay) IN_OVERLAY else newLocation
|
||||||
if (currentAttachmentLocation != newLocation) {
|
if (currentAttachmentLocation != newLocation) {
|
||||||
currentAttachmentLocation = newLocation
|
currentAttachmentLocation = newLocation
|
||||||
|
|
||||||
@@ -677,10 +850,10 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
(mediaFrame.parent as ViewGroup?)?.removeView(mediaFrame)
|
(mediaFrame.parent as ViewGroup?)?.removeView(mediaFrame)
|
||||||
|
|
||||||
// Add it to the new one
|
// Add it to the new one
|
||||||
val targetHost = getHost(desiredLocation)!!.hostView
|
|
||||||
if (inOverlay) {
|
if (inOverlay) {
|
||||||
rootOverlay!!.add(mediaFrame)
|
rootOverlay!!.add(mediaFrame)
|
||||||
} else {
|
} else {
|
||||||
|
val targetHost = getHost(newLocation)!!.hostView
|
||||||
// When adding back to the host, let's make sure to reset the bounds.
|
// When adding back to the host, let's make sure to reset the bounds.
|
||||||
// Usually adding the view will trigger a layout that does this automatically,
|
// Usually adding the view will trigger a layout that does this automatically,
|
||||||
// but we sometimes suppress this.
|
// but we sometimes suppress this.
|
||||||
@@ -693,9 +866,39 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
left + currentBounds.width(),
|
left + currentBounds.width(),
|
||||||
top + currentBounds.height())
|
top + currentBounds.height())
|
||||||
}
|
}
|
||||||
|
if (isCrossFadeAnimatorRunning) {
|
||||||
|
// When cross-fading with an animation, we only notify the media carousel of the
|
||||||
|
// location change, once the view is reattached to the new place and not immediately
|
||||||
|
// when the desired location changes. This callback will update the measurement
|
||||||
|
// of the carousel, only once we've faded out at the old location and then reattach
|
||||||
|
// to fade it in at the new location.
|
||||||
|
mediaCarouselController.onDesiredLocationChanged(
|
||||||
|
newLocation,
|
||||||
|
getHost(newLocation),
|
||||||
|
animate = false
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the location when cross fading between locations. While fading out,
|
||||||
|
* the content should remain in the previous location, while after the switch it should
|
||||||
|
* be at the desired location.
|
||||||
|
*/
|
||||||
|
private fun resolveLocationForFading(): Int {
|
||||||
|
if (isCrossFadeAnimatorRunning) {
|
||||||
|
// When animating between two hosts with a fade, let's keep ourselves in the old
|
||||||
|
// location for the first half, and then switch over to the end location
|
||||||
|
if (animationCrossFadeProgress > 0.5 || previousLocation == -1) {
|
||||||
|
return crossFadeAnimationEndLocation
|
||||||
|
} else {
|
||||||
|
return crossFadeAnimationStartLocation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return desiredLocation
|
||||||
|
}
|
||||||
|
|
||||||
private fun isTransitionRunning(): Boolean {
|
private fun isTransitionRunning(): Boolean {
|
||||||
return isCurrentlyInGuidedTransformation() && getTransformationProgress() != 1.0f ||
|
return isCurrentlyInGuidedTransformation() && getTransformationProgress() != 1.0f ||
|
||||||
animator.isRunning || animationPending
|
animator.isRunning || animationPending
|
||||||
@@ -708,29 +911,29 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
return desiredLocation
|
return desiredLocation
|
||||||
}
|
}
|
||||||
val onLockscreen = (!bypassController.bypassEnabled &&
|
val onLockscreen = (!bypassController.bypassEnabled &&
|
||||||
(statusbarState == StatusBarState.KEYGUARD ||
|
(statusbarState == StatusBarState.KEYGUARD ||
|
||||||
statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
|
statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
|
||||||
val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications()
|
val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications()
|
||||||
val location = when {
|
val location = when {
|
||||||
qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS
|
qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS
|
||||||
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
|
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
|
||||||
onLockscreen && isTransitioningToFullShade -> LOCATION_QQS
|
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
|
||||||
onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN
|
onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN
|
||||||
else -> LOCATION_QQS
|
else -> LOCATION_QQS
|
||||||
}
|
}
|
||||||
// When we're on lock screen and the player is not active, we should keep it in QS.
|
// When we're on lock screen and the player is not active, we should keep it in QS.
|
||||||
// Otherwise it will try to animate a transition that doesn't make sense.
|
// Otherwise it will try to animate a transition that doesn't make sense.
|
||||||
if (location == LOCATION_LOCKSCREEN && getHost(location)?.visible != true &&
|
if (location == LOCATION_LOCKSCREEN && getHost(location)?.visible != true &&
|
||||||
!statusBarStateController.isDozing) {
|
!statusBarStateController.isDozing) {
|
||||||
return LOCATION_QS
|
return LOCATION_QS
|
||||||
}
|
}
|
||||||
if (location == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QS &&
|
if (location == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QS &&
|
||||||
collapsingShadeFromQS) {
|
collapsingShadeFromQS) {
|
||||||
// When collapsing on the lockscreen, we want to remain in QS
|
// When collapsing on the lockscreen, we want to remain in QS
|
||||||
return LOCATION_QS
|
return LOCATION_QS
|
||||||
}
|
}
|
||||||
if (location != LOCATION_LOCKSCREEN && desiredLocation == LOCATION_LOCKSCREEN &&
|
if (location != LOCATION_LOCKSCREEN && desiredLocation == LOCATION_LOCKSCREEN &&
|
||||||
!fullyAwake) {
|
!fullyAwake) {
|
||||||
// When unlocking from dozing / while waking up, the media shouldn't be transitioning
|
// When unlocking from dozing / while waking up, the media shouldn't be transitioning
|
||||||
// in an animated way. Let's keep it in the lockscreen until we're fully awake and
|
// in an animated way. Let's keep it in the lockscreen until we're fully awake and
|
||||||
// reattach it without an animation
|
// reattach it without an animation
|
||||||
@@ -739,6 +942,26 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
return location
|
return location
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are we currently transforming to the full shade and already in QQS
|
||||||
|
*/
|
||||||
|
private fun isTransformingToFullShadeAndInQQS(): Boolean {
|
||||||
|
if (!isTransitioningToFullShade) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return fullShadeTransitionProgress > 0.5f
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the current transformationType fading
|
||||||
|
*/
|
||||||
|
private fun isCurrentlyFading(): Boolean {
|
||||||
|
if (isTransitioningToFullShade) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return isCrossFadeAnimatorRunning
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true when the media card could be visible to the user if existed.
|
* Returns true when the media card could be visible to the user if existed.
|
||||||
*/
|
*/
|
||||||
@@ -789,9 +1012,27 @@ class MediaHierarchyManager @Inject constructor(
|
|||||||
* Attached at the root of the hierarchy in an overlay
|
* Attached at the root of the hierarchy in an overlay
|
||||||
*/
|
*/
|
||||||
const val IN_OVERLAY = -1000
|
const val IN_OVERLAY = -1000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default transformation type where the hosts transform into each other using a direct
|
||||||
|
* transition
|
||||||
|
*/
|
||||||
|
const val TRANSFORMATION_TYPE_TRANSITION = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A transformation type where content fades from one place to another instead of
|
||||||
|
* transitioning
|
||||||
|
*/
|
||||||
|
const val TRANSFORMATION_TYPE_FADE = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IntDef(prefix = ["TRANSFORMATION_TYPE_"], value = [
|
||||||
|
MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION,
|
||||||
|
MediaHierarchyManager.TRANSFORMATION_TYPE_FADE])
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
private annotation class TransformationType
|
||||||
|
|
||||||
@IntDef(prefix = ["LOCATION_"], value = [MediaHierarchyManager.LOCATION_QS,
|
@IntDef(prefix = ["LOCATION_"], value = [MediaHierarchyManager.LOCATION_QS,
|
||||||
MediaHierarchyManager.LOCATION_QQS, MediaHierarchyManager.LOCATION_LOCKSCREEN])
|
MediaHierarchyManager.LOCATION_QQS, MediaHierarchyManager.LOCATION_LOCKSCREEN])
|
||||||
@Retention(AnnotationRetention.SOURCE)
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
private boolean mLastKeyguardAndExpanded;
|
private boolean mLastKeyguardAndExpanded;
|
||||||
/**
|
/**
|
||||||
* The last received state from the controller. This should not be used directly to check if
|
* The last received state from the controller. This should not be used directly to check if
|
||||||
* we're on keyguard but use {@link #isKeyguardShowing()} instead since that is more accurate
|
* we're on keyguard but use {@link #isKeyguardState()} instead since that is more accurate
|
||||||
* during state transitions which often call into us.
|
* during state transitions which often call into us.
|
||||||
*/
|
*/
|
||||||
private int mState;
|
private int mState;
|
||||||
@@ -326,7 +326,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
|| mHeaderAnimating;
|
|| mHeaderAnimating;
|
||||||
mQSPanelController.setExpanded(mQsExpanded);
|
mQSPanelController.setExpanded(mQsExpanded);
|
||||||
mQSDetail.setExpanded(mQsExpanded);
|
mQSDetail.setExpanded(mQsExpanded);
|
||||||
boolean keyguardShowing = isKeyguardShowing();
|
boolean keyguardShowing = isKeyguardState();
|
||||||
mHeader.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating
|
mHeader.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating
|
||||||
|| mShowCollapsedOnKeyguard)
|
|| mShowCollapsedOnKeyguard)
|
||||||
? View.VISIBLE
|
? View.VISIBLE
|
||||||
@@ -344,7 +344,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
!mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
|
!mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isKeyguardShowing() {
|
private boolean isKeyguardState() {
|
||||||
// We want the freshest state here since otherwise we'll have some weirdness if earlier
|
// We want the freshest state here since otherwise we'll have some weirdness if earlier
|
||||||
// listeners trigger updates
|
// listeners trigger updates
|
||||||
return mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
|
return mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
|
||||||
@@ -366,7 +366,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
if (mQSAnimator != null) {
|
if (mQSAnimator != null) {
|
||||||
mQSAnimator.setShowCollapsedOnKeyguard(showCollapsed);
|
mQSAnimator.setShowCollapsedOnKeyguard(showCollapsed);
|
||||||
}
|
}
|
||||||
if (!showCollapsed && isKeyguardShowing()) {
|
if (!showCollapsed && isKeyguardState()) {
|
||||||
setQsExpansion(mLastQSExpansion, 0);
|
setQsExpansion(mLastQSExpansion, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -457,7 +457,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
mContainer.setExpansion(expansion);
|
mContainer.setExpansion(expansion);
|
||||||
final float translationScaleY = (mTranslateWhileExpanding
|
final float translationScaleY = (mTranslateWhileExpanding
|
||||||
? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1);
|
? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1);
|
||||||
boolean onKeyguardAndExpanded = isKeyguardShowing() && !mShowCollapsedOnKeyguard;
|
boolean onKeyguardAndExpanded = isKeyguardState() && !mShowCollapsedOnKeyguard;
|
||||||
if (!mHeaderAnimating && !headerWillBeAnimating()) {
|
if (!mHeaderAnimating && !headerWillBeAnimating()) {
|
||||||
getView().setTranslationY(
|
getView().setTranslationY(
|
||||||
onKeyguardAndExpanded
|
onKeyguardAndExpanded
|
||||||
@@ -531,7 +531,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
// The Media can be scrolled off screen by default, let's offset it
|
// The Media can be scrolled off screen by default, let's offset it
|
||||||
float expandedMediaPosition = absoluteBottomPosition - mQSPanelScrollView.getScrollY()
|
float expandedMediaPosition = absoluteBottomPosition - mQSPanelScrollView.getScrollY()
|
||||||
+ mQSPanelScrollView.getScrollRange();
|
+ mQSPanelScrollView.getScrollRange();
|
||||||
// The expanded media host should never move below the laid out position
|
|
||||||
pinToBottom(expandedMediaPosition, mQsMediaHost, true /* expanded */);
|
pinToBottom(expandedMediaPosition, mQsMediaHost, true /* expanded */);
|
||||||
// The expanded media host should never move above the laid out position
|
// The expanded media host should never move above the laid out position
|
||||||
pinToBottom(absoluteBottomPosition, mQqsMediaHost, false /* expanded */);
|
pinToBottom(absoluteBottomPosition, mQqsMediaHost, false /* expanded */);
|
||||||
@@ -540,7 +539,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
|
|
||||||
private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost, boolean expanded) {
|
private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost, boolean expanded) {
|
||||||
View hostView = mediaHost.getHostView();
|
View hostView = mediaHost.getHostView();
|
||||||
if (mLastQSExpansion > 0) {
|
// on keyguard we cross-fade to expanded, so no need to pin it.
|
||||||
|
if (mLastQSExpansion > 0 && !isKeyguardState()) {
|
||||||
float targetPosition = absoluteBottomPosition - getTotalBottomMargin(hostView)
|
float targetPosition = absoluteBottomPosition - getTotalBottomMargin(hostView)
|
||||||
- hostView.getHeight();
|
- hostView.getHeight();
|
||||||
float currentPosition = mediaHost.getCurrentBounds().top
|
float currentPosition = mediaHost.getCurrentBounds().top
|
||||||
@@ -573,7 +573,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
|||||||
|
|
||||||
private boolean headerWillBeAnimating() {
|
private boolean headerWillBeAnimating() {
|
||||||
return mState == StatusBarState.KEYGUARD && mShowCollapsedOnKeyguard
|
return mState == StatusBarState.KEYGUARD && mShowCollapsedOnKeyguard
|
||||||
&& !isKeyguardShowing();
|
&& !isKeyguardState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -5145,8 +5145,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the extra top inset for the full shade transition. This is needed to compensate for
|
* Sets the extra top inset for the full shade transition. This moves notifications down
|
||||||
* media transitioning to quick settings
|
* during the drag down.
|
||||||
*/
|
*/
|
||||||
public void setExtraTopInsetForFullShadeTransition(float inset) {
|
public void setExtraTopInsetForFullShadeTransition(float inset) {
|
||||||
mExtraTopInsetForFullShadeTransition = inset;
|
mExtraTopInsetForFullShadeTransition = inset;
|
||||||
|
|||||||
@@ -185,8 +185,17 @@ public class NotificationStackScrollLayoutController {
|
|||||||
|
|
||||||
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
|
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total distance in pixels that the full shade transition takes to transition entirely to
|
||||||
|
* the full shade.
|
||||||
|
*/
|
||||||
private int mTotalDistanceForFullShadeTransition;
|
private int mTotalDistanceForFullShadeTransition;
|
||||||
private int mTotalExtraMediaInsetFullShadeTransition;
|
|
||||||
|
/**
|
||||||
|
* The amount of movement the notifications do when transitioning to the full shade before
|
||||||
|
* reaching the overstrech
|
||||||
|
*/
|
||||||
|
private int mNotificationDragDownMovement;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
|
final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
|
||||||
@@ -255,8 +264,8 @@ public class NotificationStackScrollLayoutController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private void updateResources() {
|
private void updateResources() {
|
||||||
mTotalExtraMediaInsetFullShadeTransition = mResources.getDimensionPixelSize(
|
mNotificationDragDownMovement = mResources.getDimensionPixelSize(
|
||||||
R.dimen.lockscreen_shade_transition_extra_media_inset);
|
R.dimen.lockscreen_shade_notification_movement);
|
||||||
mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize(
|
mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize(
|
||||||
R.dimen.lockscreen_shade_qs_transition_distance);
|
R.dimen.lockscreen_shade_qs_transition_distance);
|
||||||
}
|
}
|
||||||
@@ -1410,15 +1419,13 @@ public class NotificationStackScrollLayoutController {
|
|||||||
* shade. 0.0f means we're not transitioning yet.
|
* shade. 0.0f means we're not transitioning yet.
|
||||||
*/
|
*/
|
||||||
public void setTransitionToFullShadeAmount(float amount) {
|
public void setTransitionToFullShadeAmount(float amount) {
|
||||||
float extraTopInset;
|
float extraTopInset = 0.0f;
|
||||||
MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer();
|
if (mStatusBarStateController.getState() == KEYGUARD) {
|
||||||
if (view == null || view.getHeight() == 0
|
float overallProgress = MathUtils.saturate(amount / mView.getHeight());
|
||||||
|| mStatusBarStateController.getState() != KEYGUARD) {
|
float transitionProgress = Interpolators.getOvershootInterpolation(overallProgress,
|
||||||
extraTopInset = 0;
|
0.6f,
|
||||||
} else {
|
(float) mTotalDistanceForFullShadeTransition / (float) mView.getHeight());
|
||||||
extraTopInset = MathUtils.saturate(amount / mTotalDistanceForFullShadeTransition);
|
extraTopInset = transitionProgress * mNotificationDragDownMovement;
|
||||||
extraTopInset = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(extraTopInset);
|
|
||||||
extraTopInset = extraTopInset * mTotalExtraMediaInsetFullShadeTransition;
|
|
||||||
}
|
}
|
||||||
mView.setExtraTopInsetForFullShadeTransition(extraTopInset);
|
mView.setExtraTopInsetForFullShadeTransition(extraTopInset);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -325,7 +325,9 @@ public class KeyguardClockPositionAlgorithm {
|
|||||||
*/
|
*/
|
||||||
private float getClockAlpha(int y) {
|
private float getClockAlpha(int y) {
|
||||||
float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f, mDarkAmount)));
|
float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f, mDarkAmount)));
|
||||||
alphaKeyguard *= (1f - mQsExpansion);
|
float qsAlphaFactor = MathUtils.saturate(mQsExpansion / 0.3f);
|
||||||
|
qsAlphaFactor = 1f - qsAlphaFactor;
|
||||||
|
alphaKeyguard *= qsAlphaFactor;
|
||||||
alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard);
|
alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard);
|
||||||
return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
|
return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -509,6 +509,13 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
private Runnable mExpandAfterLayoutRunnable;
|
private Runnable mExpandAfterLayoutRunnable;
|
||||||
private float mSectionPadding;
|
private float mSectionPadding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The padding between the start of notifications and the qs boundary on the lockscreen.
|
||||||
|
* On lockscreen, notifications aren't inset this extra amount, but we still want the
|
||||||
|
* qs boundary to be padded.
|
||||||
|
*/
|
||||||
|
private int mLockscreenNotificationQSPadding;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount of progress we are currently in if we're transitioning to the full shade.
|
* The amount of progress we are currently in if we're transitioning to the full shade.
|
||||||
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
|
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
|
||||||
@@ -531,7 +538,7 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
/**
|
/**
|
||||||
* The maximum overshoot allowed for the top padding for the full shade transition
|
* The maximum overshoot allowed for the top padding for the full shade transition
|
||||||
*/
|
*/
|
||||||
private int mMaxOverscrollAmountForDragDown;
|
private int mMaxOverscrollAmountForPulse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should we animate the next bounds update
|
* Should we animate the next bounds update
|
||||||
@@ -823,7 +830,7 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
amount -> {
|
amount -> {
|
||||||
float progress = amount / mView.getHeight();
|
float progress = amount / mView.getHeight();
|
||||||
float overstretch = Interpolators.getOvershootInterpolation(progress,
|
float overstretch = Interpolators.getOvershootInterpolation(progress,
|
||||||
(float) mMaxOverscrollAmountForDragDown / mView.getHeight(),
|
(float) mMaxOverscrollAmountForPulse / mView.getHeight(),
|
||||||
0.2f);
|
0.2f);
|
||||||
setOverStrechAmount(overstretch);
|
setOverStrechAmount(overstretch);
|
||||||
});
|
});
|
||||||
@@ -886,14 +893,16 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
R.dimen.heads_up_status_bar_padding);
|
R.dimen.heads_up_status_bar_padding);
|
||||||
mDistanceForQSFullShadeTransition = mResources.getDimensionPixelSize(
|
mDistanceForQSFullShadeTransition = mResources.getDimensionPixelSize(
|
||||||
R.dimen.lockscreen_shade_qs_transition_distance);
|
R.dimen.lockscreen_shade_qs_transition_distance);
|
||||||
mMaxOverscrollAmountForDragDown = mResources.getDimensionPixelSize(
|
mMaxOverscrollAmountForPulse = mResources.getDimensionPixelSize(
|
||||||
R.dimen.lockscreen_shade_max_top_overshoot);
|
R.dimen.pulse_expansion_max_top_overshoot);
|
||||||
mScrimCornerRadius = mResources.getDimensionPixelSize(
|
mScrimCornerRadius = mResources.getDimensionPixelSize(
|
||||||
R.dimen.notification_scrim_corner_radius);
|
R.dimen.notification_scrim_corner_radius);
|
||||||
mScreenCornerRadius = mResources.getDimensionPixelSize(
|
mScreenCornerRadius = mResources.getDimensionPixelSize(
|
||||||
com.android.internal.R.dimen.rounded_corner_radius);
|
com.android.internal.R.dimen.rounded_corner_radius);
|
||||||
mNotificationScrimPadding = mResources.getDimensionPixelSize(
|
mNotificationScrimPadding = mResources.getDimensionPixelSize(
|
||||||
R.dimen.notification_side_paddings);
|
R.dimen.notification_side_paddings);
|
||||||
|
mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize(
|
||||||
|
R.dimen.notification_side_paddings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
|
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
|
||||||
@@ -2380,7 +2389,6 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
public void setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay) {
|
public void setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay) {
|
||||||
mAnimateNextNotificationBounds = animate && !mShouldUseSplitNotificationShade;
|
mAnimateNextNotificationBounds = animate && !mShouldUseSplitNotificationShade;
|
||||||
mNotificationBoundsAnimationDelay = delay;
|
mNotificationBoundsAnimationDelay = delay;
|
||||||
float progress = MathUtils.saturate(pxAmount / mView.getHeight());
|
|
||||||
|
|
||||||
float endPosition = 0;
|
float endPosition = 0;
|
||||||
if (pxAmount > 0.0f) {
|
if (pxAmount > 0.0f) {
|
||||||
@@ -2388,29 +2396,28 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
&& !mMediaDataManager.hasActiveMedia()) {
|
&& !mMediaDataManager.hasActiveMedia()) {
|
||||||
// No notifications are visible, let's animate to the height of qs instead
|
// No notifications are visible, let's animate to the height of qs instead
|
||||||
if (mQs != null) {
|
if (mQs != null) {
|
||||||
// Let's interpolate to the header height
|
// Let's interpolate to the header height instead of the top padding,
|
||||||
endPosition = mQs.getHeader().getHeight();
|
// because the toppadding is way too low because of the large clock.
|
||||||
|
// we still want to take into account the edgePosition though as that nicely
|
||||||
|
// overshoots in the stackscroller
|
||||||
|
endPosition = getQSEdgePosition()
|
||||||
|
- mNotificationStackScrollLayoutController.getTopPadding()
|
||||||
|
+ mQs.getHeader().getHeight();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Interpolating to the new bottom edge position!
|
// Interpolating to the new bottom edge position!
|
||||||
endPosition = getQSEdgePosition() - mOverStretchAmount;
|
endPosition = getQSEdgePosition()
|
||||||
|
+ mNotificationStackScrollLayoutController.getFullShadeTransitionInset();
|
||||||
// If we have media, we need to put the boundary below it, as the media header
|
if (isOnKeyguard()) {
|
||||||
// still uses the space during the transition.
|
endPosition -= mLockscreenNotificationQSPadding;
|
||||||
endPosition +=
|
}
|
||||||
mNotificationStackScrollLayoutController.getFullShadeTransitionInset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the overshoot amount such that we're reaching the target after our desired
|
// Calculate the overshoot amount such that we're reaching the target after our desired
|
||||||
// distance, but only reach it fully once we drag a full shade length.
|
// distance, but only reach it fully once we drag a full shade length.
|
||||||
float transitionProgress = 0;
|
mTransitioningToFullShadeProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
|
||||||
if (endPosition != 0 && progress != 0) {
|
MathUtils.saturate(pxAmount / mDistanceForQSFullShadeTransition));
|
||||||
transitionProgress = Interpolators.getOvershootInterpolation(progress,
|
|
||||||
mMaxOverscrollAmountForDragDown / endPosition,
|
|
||||||
(float) mDistanceForQSFullShadeTransition / (float) mView.getHeight());
|
|
||||||
}
|
|
||||||
mTransitioningToFullShadeProgress = transitionProgress;
|
|
||||||
|
|
||||||
int position = (int) MathUtils.lerp((float) 0, endPosition,
|
int position = (int) MathUtils.lerp((float) 0, endPosition,
|
||||||
mTransitioningToFullShadeProgress);
|
mTransitioningToFullShadeProgress);
|
||||||
@@ -2418,8 +2425,6 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
// we want at least 1 pixel otherwise the panel won't be clipped
|
// we want at least 1 pixel otherwise the panel won't be clipped
|
||||||
position = Math.max(1, position);
|
position = Math.max(1, position);
|
||||||
}
|
}
|
||||||
float overStretchAmount = Math.max(position - endPosition, 0.0f);
|
|
||||||
setOverStrechAmount(overStretchAmount);
|
|
||||||
mTransitionToFullShadeQSPosition = position;
|
mTransitionToFullShadeQSPosition = position;
|
||||||
updateQsExpansion();
|
updateQsExpansion();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.graphics.Rect
|
|||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
import android.testing.TestableLooper
|
import android.testing.TestableLooper
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.FrameLayout
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
|
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
|
||||||
@@ -33,6 +34,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
|
|||||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||||
import com.android.systemui.util.animation.UniqueObjectHostView
|
import com.android.systemui.util.animation.UniqueObjectHostView
|
||||||
|
import junit.framework.Assert
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
@@ -65,8 +67,6 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
|
|||||||
@Mock
|
@Mock
|
||||||
private lateinit var bypassController: KeyguardBypassController
|
private lateinit var bypassController: KeyguardBypassController
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var mediaFrame: ViewGroup
|
|
||||||
@Mock
|
|
||||||
private lateinit var keyguardStateController: KeyguardStateController
|
private lateinit var keyguardStateController: KeyguardStateController
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var statusBarStateController: SysuiStatusBarStateController
|
private lateinit var statusBarStateController: SysuiStatusBarStateController
|
||||||
@@ -90,9 +90,11 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
|
|||||||
@Rule
|
@Rule
|
||||||
val mockito = MockitoJUnit.rule()
|
val mockito = MockitoJUnit.rule()
|
||||||
private lateinit var mediaHiearchyManager: MediaHierarchyManager
|
private lateinit var mediaHiearchyManager: MediaHierarchyManager
|
||||||
|
private lateinit var mediaFrame: ViewGroup
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
|
mediaFrame = FrameLayout(context)
|
||||||
`when`(mediaCarouselController.mediaFrame).thenReturn(mediaFrame)
|
`when`(mediaCarouselController.mediaFrame).thenReturn(mediaFrame)
|
||||||
mediaHiearchyManager = MediaHierarchyManager(
|
mediaHiearchyManager = MediaHierarchyManager(
|
||||||
context,
|
context,
|
||||||
@@ -112,6 +114,9 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
|
|||||||
`when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
|
`when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
|
||||||
`when`(mediaCarouselController.mediaCarouselScrollHandler)
|
`when`(mediaCarouselController.mediaCarouselScrollHandler)
|
||||||
.thenReturn(mediaCarouselScrollHandler)
|
.thenReturn(mediaCarouselScrollHandler)
|
||||||
|
val observer = wakefullnessObserver.value
|
||||||
|
assertNotNull("lifecycle observer wasn't registered", observer)
|
||||||
|
observer.onFinishedWakingUp()
|
||||||
// We'll use the viewmanager to verify a few calls below, let's reset this.
|
// We'll use the viewmanager to verify a few calls below, let's reset this.
|
||||||
clearInvocations(mediaCarouselController)
|
clearInvocations(mediaCarouselController)
|
||||||
}
|
}
|
||||||
@@ -120,6 +125,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
|
|||||||
`when`(host.location).thenReturn(location)
|
`when`(host.location).thenReturn(location)
|
||||||
`when`(host.currentBounds).thenReturn(Rect())
|
`when`(host.currentBounds).thenReturn(Rect())
|
||||||
`when`(host.hostView).thenReturn(UniqueObjectHostView(context))
|
`when`(host.hostView).thenReturn(UniqueObjectHostView(context))
|
||||||
|
`when`(host.visible).thenReturn(true)
|
||||||
mediaHiearchyManager.register(host)
|
mediaHiearchyManager.register(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +165,73 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
|
|||||||
any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
|
any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGoingToFullShade() {
|
||||||
|
// Let's set it onto Lock screen
|
||||||
|
`when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
|
||||||
|
`when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
|
||||||
|
true)
|
||||||
|
statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
|
||||||
|
clearInvocations(mediaCarouselController)
|
||||||
|
|
||||||
|
// Let's transition all the way to full shade
|
||||||
|
mediaHiearchyManager.setTransitionToFullShadeAmount(100000f)
|
||||||
|
verify(mediaCarouselController).onDesiredLocationChanged(
|
||||||
|
eq(MediaHierarchyManager.LOCATION_QQS),
|
||||||
|
any(MediaHostState::class.java),
|
||||||
|
eq(false),
|
||||||
|
anyLong(),
|
||||||
|
anyLong())
|
||||||
|
clearInvocations(mediaCarouselController)
|
||||||
|
|
||||||
|
// Let's go back to the lock screen
|
||||||
|
mediaHiearchyManager.setTransitionToFullShadeAmount(0.0f)
|
||||||
|
verify(mediaCarouselController).onDesiredLocationChanged(
|
||||||
|
eq(MediaHierarchyManager.LOCATION_LOCKSCREEN),
|
||||||
|
any(MediaHostState::class.java),
|
||||||
|
eq(false),
|
||||||
|
anyLong(),
|
||||||
|
anyLong())
|
||||||
|
|
||||||
|
// Let's make sure alpha is set
|
||||||
|
mediaHiearchyManager.setTransitionToFullShadeAmount(2.0f)
|
||||||
|
Assert.assertTrue("alpha should not be 1.0f when cross fading", mediaFrame.alpha != 1.0f)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testTransformationOnLockScreenIsFading() {
|
||||||
|
// Let's set it onto Lock screen
|
||||||
|
`when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
|
||||||
|
`when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
|
||||||
|
true)
|
||||||
|
statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
|
||||||
|
clearInvocations(mediaCarouselController)
|
||||||
|
|
||||||
|
// Let's transition from lockscreen to qs
|
||||||
|
mediaHiearchyManager.qsExpansion = 1.0f
|
||||||
|
val transformType = mediaHiearchyManager.calculateTransformationType()
|
||||||
|
Assert.assertTrue("media isn't transforming to qs with a fade",
|
||||||
|
transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testTransformationOnLockScreenToQQSisFading() {
|
||||||
|
// Let's set it onto Lock screen
|
||||||
|
`when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
|
||||||
|
`when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
|
||||||
|
true)
|
||||||
|
statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
|
||||||
|
clearInvocations(mediaCarouselController)
|
||||||
|
|
||||||
|
// Let's transition from lockscreen to qs
|
||||||
|
`when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
|
||||||
|
statusBarCallback.value.onStatePreChange(StatusBarState.KEYGUARD,
|
||||||
|
StatusBarState.SHADE_LOCKED)
|
||||||
|
val transformType = mediaHiearchyManager.calculateTransformationType()
|
||||||
|
Assert.assertTrue("media isn't transforming to qqswith a fade",
|
||||||
|
transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCloseGutsRelayToCarousel() {
|
fun testCloseGutsRelayToCarousel() {
|
||||||
mediaHiearchyManager.closeGuts()
|
mediaHiearchyManager.closeGuts()
|
||||||
|
|||||||
Reference in New Issue
Block a user