Merge "Extract ActivityLaunchAnimator into a reusable class." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
ccd86069fc
@@ -27,6 +27,7 @@ java_library {
|
|||||||
|
|
||||||
srcs: [
|
srcs: [
|
||||||
"src/**/*.java",
|
"src/**/*.java",
|
||||||
|
"src/**/*.kt",
|
||||||
"bcsmartspace/src/**/*.java",
|
"bcsmartspace/src/**/*.java",
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import android.app.PendingIntent;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
|
||||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,7 +45,15 @@ public interface ActivityStarter {
|
|||||||
* specifies an associated view that should be used for the activity launch animation.
|
* specifies an associated view that should be used for the activity launch animation.
|
||||||
*/
|
*/
|
||||||
void startPendingIntentDismissingKeyguard(PendingIntent intent,
|
void startPendingIntentDismissingKeyguard(PendingIntent intent,
|
||||||
Runnable intentSentUiThreadCallback, View associatedView);
|
Runnable intentSentUiThreadCallback, @Nullable View associatedView);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but also
|
||||||
|
* specifies an animation controller that should be used for the activity launch animation.
|
||||||
|
*/
|
||||||
|
void startPendingIntentDismissingKeyguard(PendingIntent intent,
|
||||||
|
Runnable intentSentUiThreadCallback,
|
||||||
|
@Nullable ActivityLaunchAnimator.Controller animationController);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The intent flag can be specified in startActivity().
|
* The intent flag can be specified in startActivity().
|
||||||
@@ -55,6 +64,14 @@ public interface ActivityStarter {
|
|||||||
void startActivity(Intent intent, boolean dismissShade, Callback callback);
|
void startActivity(Intent intent, boolean dismissShade, Callback callback);
|
||||||
void postStartActivityDismissingKeyguard(Intent intent, int delay);
|
void postStartActivityDismissingKeyguard(Intent intent, int delay);
|
||||||
void postStartActivityDismissingKeyguard(PendingIntent intent);
|
void postStartActivityDismissingKeyguard(PendingIntent intent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to {@link #postStartActivityDismissingKeyguard(PendingIntent)}, but also specifies an
|
||||||
|
* animation controller that should be used for the activity launch animation.
|
||||||
|
*/
|
||||||
|
void postStartActivityDismissingKeyguard(PendingIntent intent,
|
||||||
|
@Nullable ActivityLaunchAnimator.Controller animationController);
|
||||||
|
|
||||||
void postQSRunnableDismissingKeyguard(Runnable runnable);
|
void postQSRunnableDismissingKeyguard(Runnable runnable);
|
||||||
|
|
||||||
void dismissKeyguardThenExecute(OnDismissAction action, @Nullable Runnable cancel,
|
void dismissKeyguardThenExecute(OnDismissAction action, @Nullable Runnable cancel,
|
||||||
|
|||||||
@@ -0,0 +1,479 @@
|
|||||||
|
package com.android.systemui.plugins.animation
|
||||||
|
|
||||||
|
import android.animation.Animator
|
||||||
|
import android.animation.AnimatorListenerAdapter
|
||||||
|
import android.animation.ValueAnimator
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.graphics.Matrix
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.os.RemoteException
|
||||||
|
import android.util.MathUtils
|
||||||
|
import android.view.IRemoteAnimationFinishedCallback
|
||||||
|
import android.view.IRemoteAnimationRunner
|
||||||
|
import android.view.RemoteAnimationAdapter
|
||||||
|
import android.view.RemoteAnimationTarget
|
||||||
|
import android.view.SyncRtSurfaceTransactionApplier
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.view.animation.LinearInterpolator
|
||||||
|
import android.view.animation.PathInterpolator
|
||||||
|
import com.android.internal.annotations.VisibleForTesting
|
||||||
|
import com.android.internal.policy.ScreenDecorationsUtils
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that allows activities to be started in a seamless way from a view that is transforming
|
||||||
|
* nicely into the starting window.
|
||||||
|
*/
|
||||||
|
class ActivityLaunchAnimator {
|
||||||
|
companion object {
|
||||||
|
const val ANIMATION_DURATION = 400L
|
||||||
|
const val ANIMATION_DURATION_FADE_OUT_CONTENT = 67L
|
||||||
|
const val ANIMATION_DURATION_FADE_IN_WINDOW = 200L
|
||||||
|
private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
|
||||||
|
private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
|
||||||
|
private const val ANIMATION_DELAY_NAV_FADE_IN =
|
||||||
|
ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN
|
||||||
|
private const val LAUNCH_TIMEOUT = 500L
|
||||||
|
|
||||||
|
// TODO(b/184121838): Use android.R.interpolator.fast_out_extra_slow_in instead.
|
||||||
|
// TODO(b/184121838): Move com.android.systemui.Interpolators in an animation library we can
|
||||||
|
// reuse here.
|
||||||
|
private val ANIMATION_INTERPOLATOR = PathInterpolator(0f, 0f, 0.2f, 1f)
|
||||||
|
private val LINEAR_INTERPOLATOR = LinearInterpolator()
|
||||||
|
private val ALPHA_IN_INTERPOLATOR = PathInterpolator(0.4f, 0f, 1f, 1f)
|
||||||
|
private val ALPHA_OUT_INTERPOLATOR = PathInterpolator(0f, 0f, 0.8f, 1f)
|
||||||
|
private val NAV_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0f, 1f)
|
||||||
|
private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the [linearProgress] of a launch animation, return the linear progress of the
|
||||||
|
* sub-animation starting [delay] ms after the launch animation and that lasts [duration].
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun getProgress(linearProgress: Float, delay: Long, duration: Long): Float {
|
||||||
|
return MathUtils.constrain(
|
||||||
|
(linearProgress * ANIMATION_DURATION - delay) / duration,
|
||||||
|
0.0f,
|
||||||
|
1.0f
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start an intent and animate the opening window. The intent will be started by running
|
||||||
|
* [intentStarter], which should use the provided [RemoteAnimationAdapter] and return the launch
|
||||||
|
* result. [controller] is responsible from animating the view from which the intent was started
|
||||||
|
* in [Controller.onLaunchAnimationProgress]. No animation will start if there is no window
|
||||||
|
* opening.
|
||||||
|
*
|
||||||
|
* If [controller] is null, then the intent will be started and no animation will run.
|
||||||
|
*
|
||||||
|
* This method will throw any exception thrown by [intentStarter].
|
||||||
|
*/
|
||||||
|
inline fun startIntentWithAnimation(
|
||||||
|
controller: Controller?,
|
||||||
|
intentStarter: (RemoteAnimationAdapter?) -> Int
|
||||||
|
) {
|
||||||
|
if (controller == null) {
|
||||||
|
intentStarter(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val runner = Runner(controller)
|
||||||
|
val animationAdapter = RemoteAnimationAdapter(
|
||||||
|
runner,
|
||||||
|
ANIMATION_DURATION,
|
||||||
|
ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
|
||||||
|
)
|
||||||
|
val launchResult = intentStarter(animationAdapter)
|
||||||
|
val willAnimate = launchResult == ActivityManager.START_TASK_TO_FRONT ||
|
||||||
|
launchResult == ActivityManager.START_SUCCESS
|
||||||
|
runner.context.mainExecutor.execute { controller.onIntentStarted(willAnimate) }
|
||||||
|
|
||||||
|
// If we expect an animation, post a timeout to cancel it in case the remote animation is
|
||||||
|
// never started.
|
||||||
|
if (willAnimate) {
|
||||||
|
runner.postTimeout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as [startIntentWithAnimation] but allows [intentStarter] to throw a
|
||||||
|
* [PendingIntent.CanceledException] which must then be handled by the caller. This is useful
|
||||||
|
* for Java caller starting a [PendingIntent].
|
||||||
|
*/
|
||||||
|
@Throws(PendingIntent.CanceledException::class)
|
||||||
|
fun startPendingIntentWithAnimation(
|
||||||
|
controller: Controller?,
|
||||||
|
intentStarter: PendingIntentStarter
|
||||||
|
) {
|
||||||
|
startIntentWithAnimation(controller) { intentStarter.startPendingIntent(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PendingIntentStarter {
|
||||||
|
/**
|
||||||
|
* Start a pending intent using the provided [animationAdapter] and return the launch
|
||||||
|
* result.
|
||||||
|
*/
|
||||||
|
@Throws(PendingIntent.CanceledException::class)
|
||||||
|
fun startPendingIntent(animationAdapter: RemoteAnimationAdapter?): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A controller that takes care of applying the animation to an expanding view.
|
||||||
|
*
|
||||||
|
* Note that all callbacks (onXXX methods) are all called on the main thread.
|
||||||
|
*/
|
||||||
|
interface Controller {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Return a [Controller] that will animate and expand [view] into the opening window.
|
||||||
|
*
|
||||||
|
* Important: The view must be attached to the window when calling this function and
|
||||||
|
* during the animation.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun fromView(view: View): Controller = GhostedViewLaunchAnimatorController(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the root [View] that contains the view that started the intent and will be
|
||||||
|
* animating together with the window.
|
||||||
|
*
|
||||||
|
* This view will be used to:
|
||||||
|
* - Get the associated [Context].
|
||||||
|
* - Compute whether we are expanding fully above the current window.
|
||||||
|
* - Apply surface transactions in sync with RenderThread.
|
||||||
|
*/
|
||||||
|
fun getRootView(): View
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the [State] of the view that will be animated. We will animate from this state to
|
||||||
|
* the final window state.
|
||||||
|
*
|
||||||
|
* Note: This state will be mutated and passed to [onLaunchAnimationProgress] during the
|
||||||
|
* animation.
|
||||||
|
*/
|
||||||
|
fun createAnimatorState(): State
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The intent was started. If [willAnimate] is false, nothing else will happen and the
|
||||||
|
* animation will not be started.
|
||||||
|
*/
|
||||||
|
fun onIntentStarted(willAnimate: Boolean) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The animation started. This is typically used to initialize any additional resource
|
||||||
|
* needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding
|
||||||
|
* fully above the [root view][getRootView].
|
||||||
|
*/
|
||||||
|
fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {}
|
||||||
|
|
||||||
|
/** The animation made progress and the expandable view [state] should be updated. */
|
||||||
|
fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The animation ended. This will be called *if and only if* [onLaunchAnimationStart] was
|
||||||
|
* called previously. This is typically used to clean up the resources initialized when the
|
||||||
|
* animation was started.
|
||||||
|
*/
|
||||||
|
fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The animation was cancelled remotely. Note that [onLaunchAnimationEnd] will still be
|
||||||
|
* called after this if the animation was already started, i.e. if [onLaunchAnimationStart]
|
||||||
|
* was called before the cancellation.
|
||||||
|
*/
|
||||||
|
fun onLaunchAnimationCancelled() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The remote animation was not started within the expected time. It timed out and will
|
||||||
|
* never [start][onLaunchAnimationStart].
|
||||||
|
*/
|
||||||
|
fun onLaunchAnimationTimedOut() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The animation was aborted because the opening window was not found. It will never
|
||||||
|
* [start][onLaunchAnimationStart].
|
||||||
|
*/
|
||||||
|
fun onLaunchAnimationAborted() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The state of an expandable view during an [ActivityLaunchAnimator] animation. */
|
||||||
|
open class State(
|
||||||
|
/** The position of the view in screen space coordinates. */
|
||||||
|
var top: Int,
|
||||||
|
var bottom: Int,
|
||||||
|
var left: Int,
|
||||||
|
var right: Int,
|
||||||
|
|
||||||
|
var topCornerRadius: Float = 0f,
|
||||||
|
var bottomCornerRadius: Float = 0f,
|
||||||
|
|
||||||
|
var contentAlpha: Float = 1f,
|
||||||
|
var backgroundAlpha: Float = 1f
|
||||||
|
) {
|
||||||
|
private val startTop = top
|
||||||
|
private val startLeft = left
|
||||||
|
private val startRight = right
|
||||||
|
|
||||||
|
val width: Int
|
||||||
|
get() = right - left
|
||||||
|
|
||||||
|
val height: Int
|
||||||
|
get() = bottom - top
|
||||||
|
|
||||||
|
open val topChange: Int
|
||||||
|
get() = top - startTop
|
||||||
|
|
||||||
|
val leftChange: Int
|
||||||
|
get() = left - startLeft
|
||||||
|
|
||||||
|
val rightChange: Int
|
||||||
|
get() = right - startRight
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() {
|
||||||
|
private val rootView = controller.getRootView()
|
||||||
|
@PublishedApi internal val context = rootView.context
|
||||||
|
private val transactionApplier = SyncRtSurfaceTransactionApplier(rootView)
|
||||||
|
private var animator: ValueAnimator? = null
|
||||||
|
|
||||||
|
private var windowCrop = Rect()
|
||||||
|
private var timedOut = false
|
||||||
|
private var cancelled = false
|
||||||
|
|
||||||
|
// A timeout to cancel the remote animation if it is not started within X milliseconds after
|
||||||
|
// the intent was started.
|
||||||
|
//
|
||||||
|
// Note that this is important to keep this a Runnable (and not a Kotlin lambda), otherwise
|
||||||
|
// it will be automatically converted when posted and we wouldn't be able to remove it after
|
||||||
|
// posting it.
|
||||||
|
private var onTimeout = Runnable { onAnimationTimedOut() }
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal fun postTimeout() {
|
||||||
|
rootView.postDelayed(onTimeout, LAUNCH_TIMEOUT)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeTimeout() {
|
||||||
|
rootView.removeCallbacks(onTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationStart(
|
||||||
|
@WindowManager.TransitionOldType transit: Int,
|
||||||
|
remoteAnimationTargets: Array<out RemoteAnimationTarget>,
|
||||||
|
remoteAnimationWallpaperTargets: Array<out RemoteAnimationTarget>,
|
||||||
|
remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>,
|
||||||
|
iRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback
|
||||||
|
) {
|
||||||
|
removeTimeout()
|
||||||
|
|
||||||
|
// The animation was started too late and we already notified the controller that it
|
||||||
|
// timed out.
|
||||||
|
if (timedOut) {
|
||||||
|
invokeCallback(iRemoteAnimationFinishedCallback)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should not happen, but let's make sure we don't start the animation if it was
|
||||||
|
// cancelled before and we already notified the controller.
|
||||||
|
if (cancelled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
context.mainExecutor.execute {
|
||||||
|
startAnimation(remoteAnimationTargets, iRemoteAnimationFinishedCallback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startAnimation(
|
||||||
|
remoteAnimationTargets: Array<out RemoteAnimationTarget>,
|
||||||
|
iCallback: IRemoteAnimationFinishedCallback
|
||||||
|
) {
|
||||||
|
val window = remoteAnimationTargets.firstOrNull {
|
||||||
|
it.mode == RemoteAnimationTarget.MODE_OPENING
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window == null) {
|
||||||
|
removeTimeout()
|
||||||
|
invokeCallback(iCallback)
|
||||||
|
controller.onLaunchAnimationAborted()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val navigationBar = remoteAnimationTargets.firstOrNull {
|
||||||
|
it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start state.
|
||||||
|
val state = controller.createAnimatorState()
|
||||||
|
|
||||||
|
val startTop = state.top
|
||||||
|
val startBottom = state.bottom
|
||||||
|
val startLeft = state.left
|
||||||
|
val startRight = state.right
|
||||||
|
|
||||||
|
val startTopCornerRadius = state.topCornerRadius
|
||||||
|
val startBottomCornerRadius = state.bottomCornerRadius
|
||||||
|
|
||||||
|
// End state.
|
||||||
|
val windowBounds = window.screenSpaceBounds
|
||||||
|
val endTop = windowBounds.top
|
||||||
|
val endBottom = windowBounds.bottom
|
||||||
|
val endLeft = windowBounds.left
|
||||||
|
val endRight = windowBounds.right
|
||||||
|
|
||||||
|
// TODO(b/184121838): Ensure that we are launching on the same screen.
|
||||||
|
val rootViewLocation = rootView.locationOnScreen
|
||||||
|
val isExpandingFullyAbove = endTop <= rootViewLocation[1] &&
|
||||||
|
endBottom >= rootViewLocation[1] + rootView.height &&
|
||||||
|
endLeft <= rootViewLocation[0] &&
|
||||||
|
endRight >= rootViewLocation[0] + rootView.width
|
||||||
|
|
||||||
|
// TODO(b/184121838): We should somehow get the top and bottom radius of the window.
|
||||||
|
val endRadius = if (isExpandingFullyAbove) {
|
||||||
|
// Most of the time, expanding fully above the root view means expanding in full
|
||||||
|
// screen.
|
||||||
|
ScreenDecorationsUtils.getWindowCornerRadius(context.resources)
|
||||||
|
} else {
|
||||||
|
// This usually means we are in split screen mode, so 2 out of 4 corners will have
|
||||||
|
// a radius of 0.
|
||||||
|
0f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update state.
|
||||||
|
val animator = ValueAnimator.ofFloat(0f, 1f)
|
||||||
|
this.animator = animator
|
||||||
|
animator.duration = ANIMATION_DURATION
|
||||||
|
animator.interpolator = LINEAR_INTERPOLATOR
|
||||||
|
|
||||||
|
animator.addListener(object : AnimatorListenerAdapter() {
|
||||||
|
override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
|
||||||
|
controller.onLaunchAnimationStart(isExpandingFullyAbove)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationEnd(animation: Animator?) {
|
||||||
|
invokeCallback(iCallback)
|
||||||
|
controller.onLaunchAnimationEnd(isExpandingFullyAbove)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
animator.addUpdateListener { animation ->
|
||||||
|
if (cancelled) {
|
||||||
|
return@addUpdateListener
|
||||||
|
}
|
||||||
|
|
||||||
|
val linearProgress = animation.animatedFraction
|
||||||
|
val progress = ANIMATION_INTERPOLATOR.getInterpolation(linearProgress)
|
||||||
|
|
||||||
|
state.top = lerp(startTop, endTop, progress).roundToInt()
|
||||||
|
state.bottom = lerp(startBottom, endBottom, progress).roundToInt()
|
||||||
|
state.left = lerp(startLeft, endLeft, progress).roundToInt()
|
||||||
|
state.right = lerp(startRight, endRight, progress).roundToInt()
|
||||||
|
|
||||||
|
state.topCornerRadius = MathUtils.lerp(startTopCornerRadius, endRadius, progress)
|
||||||
|
state.bottomCornerRadius =
|
||||||
|
MathUtils.lerp(startBottomCornerRadius, endRadius, progress)
|
||||||
|
|
||||||
|
val contentAlphaProgress = getProgress(linearProgress, 0,
|
||||||
|
ANIMATION_DURATION_FADE_OUT_CONTENT)
|
||||||
|
state.contentAlpha =
|
||||||
|
1 - ALPHA_OUT_INTERPOLATOR.getInterpolation(contentAlphaProgress)
|
||||||
|
|
||||||
|
val backgroundAlphaProgress = getProgress(linearProgress,
|
||||||
|
ANIMATION_DURATION_FADE_OUT_CONTENT, ANIMATION_DURATION_FADE_IN_WINDOW)
|
||||||
|
state.backgroundAlpha =
|
||||||
|
1 - ALPHA_IN_INTERPOLATOR.getInterpolation(backgroundAlphaProgress)
|
||||||
|
|
||||||
|
applyStateToWindow(window, state)
|
||||||
|
navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
|
||||||
|
controller.onLaunchAnimationProgress(state, progress, linearProgress)
|
||||||
|
}
|
||||||
|
|
||||||
|
animator.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyStateToWindow(window: RemoteAnimationTarget, state: State) {
|
||||||
|
val m = Matrix()
|
||||||
|
m.postTranslate(0f, (state.top - window.sourceContainerBounds.top).toFloat())
|
||||||
|
windowCrop.set(state.left, 0, state.right, state.height)
|
||||||
|
|
||||||
|
val cornerRadius = minOf(state.topCornerRadius, state.bottomCornerRadius)
|
||||||
|
val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(window.leash)
|
||||||
|
.withAlpha(1f)
|
||||||
|
.withMatrix(m)
|
||||||
|
.withWindowCrop(windowCrop)
|
||||||
|
.withLayer(window.prefixOrderIndex)
|
||||||
|
.withCornerRadius(cornerRadius)
|
||||||
|
.withVisibility(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
transactionApplier.scheduleApply(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyStateToNavigationBar(
|
||||||
|
navigationBar: RemoteAnimationTarget,
|
||||||
|
state: State,
|
||||||
|
linearProgress: Float
|
||||||
|
) {
|
||||||
|
val fadeInProgress = getProgress(linearProgress, ANIMATION_DELAY_NAV_FADE_IN,
|
||||||
|
ANIMATION_DURATION_NAV_FADE_OUT)
|
||||||
|
|
||||||
|
val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(navigationBar.leash)
|
||||||
|
if (fadeInProgress > 0) {
|
||||||
|
val m = Matrix()
|
||||||
|
m.postTranslate(0f, (state.top - navigationBar.sourceContainerBounds.top).toFloat())
|
||||||
|
windowCrop.set(state.left, 0, state.right, state.height)
|
||||||
|
params
|
||||||
|
.withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress))
|
||||||
|
.withMatrix(m)
|
||||||
|
.withWindowCrop(windowCrop)
|
||||||
|
.withVisibility(true)
|
||||||
|
} else {
|
||||||
|
val fadeOutProgress = getProgress(linearProgress, 0,
|
||||||
|
ANIMATION_DURATION_NAV_FADE_OUT)
|
||||||
|
params.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress))
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionApplier.scheduleApply(params.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onAnimationTimedOut() {
|
||||||
|
if (cancelled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
timedOut = true
|
||||||
|
controller.onLaunchAnimationTimedOut()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationCancelled() {
|
||||||
|
if (timedOut) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelled = true
|
||||||
|
removeTimeout()
|
||||||
|
context.mainExecutor.execute {
|
||||||
|
animator?.cancel()
|
||||||
|
controller.onLaunchAnimationCancelled()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun invokeCallback(iCallback: IRemoteAnimationFinishedCallback) {
|
||||||
|
try {
|
||||||
|
iCallback.onAnimationFinished()
|
||||||
|
} catch (e: RemoteException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lerp(start: Int, stop: Int, amount: Float): Float {
|
||||||
|
return MathUtils.lerp(start.toFloat(), stop.toFloat(), amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
package com.android.systemui.plugins.animation
|
||||||
|
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.ColorFilter
|
||||||
|
import android.graphics.PixelFormat
|
||||||
|
import android.graphics.PorterDuff
|
||||||
|
import android.graphics.PorterDuffXfermode
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.view.GhostView
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base implementation of [ActivityLaunchAnimator.Controller] which creates a [ghost][GhostView]
|
||||||
|
* of [ghostedView] as well as an expandable background view, which are drawn and animated instead
|
||||||
|
* of the ghosted view.
|
||||||
|
*
|
||||||
|
* Important: [ghostedView] must be attached to the window when calling this function and during the
|
||||||
|
* animation.
|
||||||
|
*
|
||||||
|
* Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView]
|
||||||
|
* whenever possible instead.
|
||||||
|
*/
|
||||||
|
open class GhostedViewLaunchAnimatorController(
|
||||||
|
/** The view that will be ghosted and from which the background will be extracted. */
|
||||||
|
private val ghostedView: View
|
||||||
|
) : ActivityLaunchAnimator.Controller {
|
||||||
|
/** The root view to which we will add the ghost view and expanding background. */
|
||||||
|
private val rootView = ghostedView.rootView as ViewGroup
|
||||||
|
private val rootViewOverlay = rootView.overlay
|
||||||
|
|
||||||
|
/** The ghost view that is drawn and animated instead of the ghosted view. */
|
||||||
|
private var ghostView: View? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The expanding background view that will be added to [rootView] (below [ghostView]) and
|
||||||
|
* animate.
|
||||||
|
*/
|
||||||
|
private var backgroundView: FrameLayout? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The drawable wrapping the [ghostedView] background and used as background for
|
||||||
|
* [backgroundView].
|
||||||
|
*/
|
||||||
|
private var backgroundDrawable: WrappedDrawable? = null
|
||||||
|
private var startBackgroundAlpha: Int = 0xFF
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the background of the [ghostedView]. This background will be used to draw the
|
||||||
|
* background of the background view that is expanding up to the final animation position. This
|
||||||
|
* is called at the start of the animation.
|
||||||
|
*
|
||||||
|
* Note that during the animation, the alpha value value of this background will be set to 0,
|
||||||
|
* then set back to its initial value at the end of the animation.
|
||||||
|
*/
|
||||||
|
protected open fun getBackground(): Drawable? = ghostedView.background
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the corner radius of [background]. The background is the one that was returned by
|
||||||
|
* [getBackground].
|
||||||
|
*/
|
||||||
|
protected open fun setBackgroundCornerRadius(
|
||||||
|
background: Drawable,
|
||||||
|
topCornerRadius: Float,
|
||||||
|
bottomCornerRadius: Float
|
||||||
|
) {
|
||||||
|
// TODO(b/184121838): Add default support for GradientDrawable and LayerDrawable to make
|
||||||
|
// this work out of the box for common rounded backgrounds.
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return the current top corner radius of the background. */
|
||||||
|
protected open fun getCurrentTopCornerRadius(): Float = 0f
|
||||||
|
|
||||||
|
/** Return the current bottom corner radius of the background. */
|
||||||
|
protected open fun getCurrentBottomCornerRadius(): Float = 0f
|
||||||
|
|
||||||
|
override fun getRootView(): View {
|
||||||
|
return rootView
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createAnimatorState(): ActivityLaunchAnimator.State {
|
||||||
|
val location = ghostedView.locationOnScreen
|
||||||
|
return ActivityLaunchAnimator.State(
|
||||||
|
top = location[1],
|
||||||
|
bottom = location[1] + ghostedView.height,
|
||||||
|
left = location[0],
|
||||||
|
right = location[0] + ghostedView.width,
|
||||||
|
topCornerRadius = getCurrentTopCornerRadius(),
|
||||||
|
bottomCornerRadius = getCurrentBottomCornerRadius()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
|
||||||
|
backgroundView = FrameLayout(rootView.context).apply {
|
||||||
|
forceHasOverlappingRendering(true)
|
||||||
|
}
|
||||||
|
rootViewOverlay.add(backgroundView)
|
||||||
|
|
||||||
|
// We wrap the ghosted view background and use it to draw the expandable background. Its
|
||||||
|
// alpha will be set to 0 as soon as we start drawing the expanding background.
|
||||||
|
val drawable = getBackground()
|
||||||
|
startBackgroundAlpha = drawable?.alpha ?: 0xFF
|
||||||
|
backgroundDrawable = WrappedDrawable(drawable)
|
||||||
|
backgroundView?.background = backgroundDrawable
|
||||||
|
|
||||||
|
// Create a ghost of the view that will be moving and fading out. This allows to fade out
|
||||||
|
// the content before fading out the background.
|
||||||
|
ghostView = GhostView.addGhost(ghostedView, rootView).apply {
|
||||||
|
setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationProgress(
|
||||||
|
state: ActivityLaunchAnimator.State,
|
||||||
|
progress: Float,
|
||||||
|
linearProgress: Float
|
||||||
|
) {
|
||||||
|
val ghostView = this.ghostView!!
|
||||||
|
ghostView.translationX = (state.leftChange + state.rightChange) / 2.toFloat()
|
||||||
|
ghostView.translationY = state.topChange.toFloat()
|
||||||
|
ghostView.alpha = state.contentAlpha
|
||||||
|
|
||||||
|
val backgroundView = this.backgroundView!!
|
||||||
|
backgroundView.top = state.top
|
||||||
|
backgroundView.bottom = state.bottom
|
||||||
|
backgroundView.left = state.left
|
||||||
|
backgroundView.right = state.right
|
||||||
|
|
||||||
|
val backgroundDrawable = backgroundDrawable!!
|
||||||
|
backgroundDrawable.alpha = (0xFF * state.backgroundAlpha).toInt()
|
||||||
|
backgroundDrawable.wrapped?.let {
|
||||||
|
setBackgroundCornerRadius(it, state.topCornerRadius, state.bottomCornerRadius)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
|
||||||
|
backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
|
||||||
|
|
||||||
|
GhostView.removeGhost(ghostedView)
|
||||||
|
rootViewOverlay.remove(backgroundView)
|
||||||
|
ghostedView.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class WrappedDrawable(val wrapped: Drawable?) : Drawable() {
|
||||||
|
companion object {
|
||||||
|
private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var currentAlpha = 0xFF
|
||||||
|
private var previousBounds = Rect()
|
||||||
|
|
||||||
|
override fun draw(canvas: Canvas) {
|
||||||
|
val wrapped = this.wrapped ?: return
|
||||||
|
|
||||||
|
wrapped.copyBounds(previousBounds)
|
||||||
|
|
||||||
|
wrapped.alpha = currentAlpha
|
||||||
|
wrapped.bounds = bounds
|
||||||
|
wrapped.setXfermode(SRC_MODE)
|
||||||
|
|
||||||
|
wrapped.draw(canvas)
|
||||||
|
|
||||||
|
// The background view (and therefore this drawable) is drawn before the ghost view, so
|
||||||
|
// the ghosted view background alpha should always be 0 when it is drawn above the
|
||||||
|
// background.
|
||||||
|
wrapped.alpha = 0
|
||||||
|
wrapped.bounds = previousBounds
|
||||||
|
wrapped.setXfermode(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setAlpha(alpha: Int) {
|
||||||
|
if (alpha != currentAlpha) {
|
||||||
|
currentAlpha = alpha
|
||||||
|
invalidateSelf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAlpha() = currentAlpha
|
||||||
|
|
||||||
|
override fun getOpacity(): Int {
|
||||||
|
val wrapped = this.wrapped ?: return PixelFormat.TRANSPARENT
|
||||||
|
|
||||||
|
val previousAlpha = wrapped.alpha
|
||||||
|
wrapped.alpha = currentAlpha
|
||||||
|
val opacity = wrapped.opacity
|
||||||
|
wrapped.alpha = previousAlpha
|
||||||
|
return opacity
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setColorFilter(filter: ColorFilter?) {
|
||||||
|
wrapped?.colorFilter = filter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import android.view.View;
|
|||||||
|
|
||||||
import com.android.systemui.dagger.SysUISingleton;
|
import com.android.systemui.dagger.SysUISingleton;
|
||||||
import com.android.systemui.plugins.ActivityStarter;
|
import com.android.systemui.plugins.ActivityStarter;
|
||||||
|
import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
|
||||||
import com.android.systemui.statusbar.phone.StatusBar;
|
import com.android.systemui.statusbar.phone.StatusBar;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -51,18 +52,27 @@ public class ActivityStarterDelegate implements ActivityStarter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startPendingIntentDismissingKeyguard(PendingIntent intent,
|
public void startPendingIntentDismissingKeyguard(PendingIntent intent,
|
||||||
Runnable intentSentCallback) {
|
Runnable intentSentUiThreadCallback) {
|
||||||
mActualStarter.ifPresent(
|
mActualStarter.ifPresent(
|
||||||
starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
|
starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
|
||||||
intentSentCallback));
|
intentSentUiThreadCallback));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startPendingIntentDismissingKeyguard(PendingIntent intent,
|
public void startPendingIntentDismissingKeyguard(PendingIntent intent,
|
||||||
Runnable intentSentCallback, View associatedView) {
|
Runnable intentSentUiThreadCallback, View associatedView) {
|
||||||
mActualStarter.ifPresent(
|
mActualStarter.ifPresent(
|
||||||
starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
|
starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
|
||||||
intentSentCallback, associatedView));
|
intentSentUiThreadCallback, associatedView));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startPendingIntentDismissingKeyguard(PendingIntent intent,
|
||||||
|
Runnable intentSentUiThreadCallback,
|
||||||
|
ActivityLaunchAnimator.Controller animationController) {
|
||||||
|
mActualStarter.ifPresent(
|
||||||
|
starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
|
||||||
|
intentSentUiThreadCallback, animationController));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -102,6 +112,13 @@ public class ActivityStarterDelegate implements ActivityStarter {
|
|||||||
starter -> starter.get().postStartActivityDismissingKeyguard(intent));
|
starter -> starter.get().postStartActivityDismissingKeyguard(intent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postStartActivityDismissingKeyguard(PendingIntent intent,
|
||||||
|
ActivityLaunchAnimator.Controller animationController) {
|
||||||
|
mActualStarter.ifPresent(starter ->
|
||||||
|
starter.get().postStartActivityDismissingKeyguard(intent, animationController));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postQSRunnableDismissingKeyguard(Runnable runnable) {
|
public void postQSRunnableDismissingKeyguard(Runnable runnable) {
|
||||||
mActualStarter.ifPresent(
|
mActualStarter.ifPresent(
|
||||||
|
|||||||
@@ -105,9 +105,7 @@ public class CrossFadeHelper {
|
|||||||
}
|
}
|
||||||
} else if (view.getLayerType() == View.LAYER_TYPE_HARDWARE
|
} else if (view.getLayerType() == View.LAYER_TYPE_HARDWARE
|
||||||
&& view.getTag(R.id.cross_fade_layer_type_changed_tag) != null) {
|
&& view.getTag(R.id.cross_fade_layer_type_changed_tag) != null) {
|
||||||
if (view.getTag(R.id.cross_fade_layer_type_changed_tag) != null) {
|
view.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||||
view.setLayerType(View.LAYER_TYPE_NONE, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import com.android.systemui.Interpolators
|
|||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.dump.DumpManager
|
import com.android.systemui.dump.DumpManager
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||||
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator
|
import com.android.systemui.statusbar.notification.ExpandAnimationParameters
|
||||||
import com.android.systemui.statusbar.phone.BiometricUnlockController
|
import com.android.systemui.statusbar.phone.BiometricUnlockController
|
||||||
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
|
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
|
||||||
import com.android.systemui.statusbar.phone.DozeParameters
|
import com.android.systemui.statusbar.phone.DozeParameters
|
||||||
@@ -111,7 +111,7 @@ class NotificationShadeDepthController @Inject constructor(
|
|||||||
* When launching an app from the shade, the animations progress should affect how blurry the
|
* When launching an app from the shade, the animations progress should affect how blurry the
|
||||||
* shade is, overriding the expansion amount.
|
* shade is, overriding the expansion amount.
|
||||||
*/
|
*/
|
||||||
var notificationLaunchAnimationParams: ActivityLaunchAnimator.ExpandAnimationParameters? = null
|
var notificationLaunchAnimationParams: ExpandAnimationParameters? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
|||||||
@@ -90,9 +90,14 @@ public interface NotificationShadeWindowController extends RemoteInputController
|
|||||||
/** Sets the state of whether the user activities are forced or not. */
|
/** Sets the state of whether the user activities are forced or not. */
|
||||||
default void setForceUserActivity(boolean forceUserActivity) {}
|
default void setForceUserActivity(boolean forceUserActivity) {}
|
||||||
|
|
||||||
/** Sets the state of whether the user activities are forced or not. */
|
/** Sets the state of whether an activity is launching or not. */
|
||||||
default void setLaunchingActivity(boolean launching) {}
|
default void setLaunchingActivity(boolean launching) {}
|
||||||
|
|
||||||
|
/** Get whether an activity is launching or not. */
|
||||||
|
default boolean isLaunchingActivity() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** Sets the state of whether the scrim is visible or not. */
|
/** Sets the state of whether the scrim is visible or not. */
|
||||||
default void setScrimsVisibility(int scrimsVisibility) {}
|
default void setScrimsVisibility(int scrimsVisibility) {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,490 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.systemui.statusbar.notification;
|
|
||||||
|
|
||||||
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
|
|
||||||
|
|
||||||
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_APP_START;
|
|
||||||
|
|
||||||
import android.animation.Animator;
|
|
||||||
import android.animation.AnimatorListenerAdapter;
|
|
||||||
import android.animation.ValueAnimator;
|
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.graphics.Matrix;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.MathUtils;
|
|
||||||
import android.view.IRemoteAnimationFinishedCallback;
|
|
||||||
import android.view.IRemoteAnimationRunner;
|
|
||||||
import android.view.RemoteAnimationAdapter;
|
|
||||||
import android.view.RemoteAnimationTarget;
|
|
||||||
import android.view.SyncRtSurfaceTransactionApplier;
|
|
||||||
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.view.animation.Interpolator;
|
|
||||||
import android.view.animation.PathInterpolator;
|
|
||||||
|
|
||||||
import com.android.internal.jank.InteractionJankMonitor;
|
|
||||||
import com.android.internal.policy.ScreenDecorationsUtils;
|
|
||||||
import com.android.systemui.Interpolators;
|
|
||||||
import com.android.systemui.statusbar.NotificationShadeDepthController;
|
|
||||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
|
||||||
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
|
|
||||||
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
|
|
||||||
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
|
|
||||||
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class that allows activities to be launched in a seamless way where the notification
|
|
||||||
* transforms nicely into the starting window.
|
|
||||||
*/
|
|
||||||
public class ActivityLaunchAnimator {
|
|
||||||
|
|
||||||
private static final int ANIMATION_DURATION = 400;
|
|
||||||
public static final long ANIMATION_DURATION_FADE_CONTENT = 67;
|
|
||||||
public static final long ANIMATION_DURATION_FADE_APP = 200;
|
|
||||||
public static final long ANIMATION_DELAY_ICON_FADE_IN = ANIMATION_DURATION -
|
|
||||||
CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY
|
|
||||||
- 16;
|
|
||||||
private static final int ANIMATION_DURATION_NAV_FADE_IN = 266;
|
|
||||||
private static final int ANIMATION_DURATION_NAV_FADE_OUT = 133;
|
|
||||||
private static final long ANIMATION_DELAY_NAV_FADE_IN =
|
|
||||||
ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN;
|
|
||||||
private static final Interpolator NAV_FADE_IN_INTERPOLATOR =
|
|
||||||
new PathInterpolator(0f, 0f, 0f, 1f);
|
|
||||||
private static final Interpolator NAV_FADE_OUT_INTERPOLATOR =
|
|
||||||
new PathInterpolator(0.2f, 0f, 1f, 1f);
|
|
||||||
private static final long LAUNCH_TIMEOUT = 500;
|
|
||||||
private final NotificationPanelViewController mNotificationPanel;
|
|
||||||
private final NotificationListContainer mNotificationContainer;
|
|
||||||
private final float mWindowCornerRadius;
|
|
||||||
private final NotificationShadeWindowViewController mNotificationShadeWindowViewController;
|
|
||||||
private final NotificationShadeDepthController mDepthController;
|
|
||||||
private final Executor mMainExecutor;
|
|
||||||
private Callback mCallback;
|
|
||||||
private final Runnable mTimeoutRunnable = () -> {
|
|
||||||
setAnimationPending(false);
|
|
||||||
mCallback.onExpandAnimationTimedOut();
|
|
||||||
};
|
|
||||||
private boolean mAnimationPending;
|
|
||||||
private boolean mAnimationRunning;
|
|
||||||
private boolean mIsLaunchForActivity;
|
|
||||||
|
|
||||||
public ActivityLaunchAnimator(
|
|
||||||
NotificationShadeWindowViewController notificationShadeWindowViewController,
|
|
||||||
Callback callback,
|
|
||||||
NotificationPanelViewController notificationPanel,
|
|
||||||
NotificationShadeDepthController depthController,
|
|
||||||
NotificationListContainer container,
|
|
||||||
Executor mainExecutor) {
|
|
||||||
mNotificationPanel = notificationPanel;
|
|
||||||
mNotificationContainer = container;
|
|
||||||
mDepthController = depthController;
|
|
||||||
mNotificationShadeWindowViewController = notificationShadeWindowViewController;
|
|
||||||
mCallback = callback;
|
|
||||||
mMainExecutor = mainExecutor;
|
|
||||||
mWindowCornerRadius = ScreenDecorationsUtils
|
|
||||||
.getWindowCornerRadius(mNotificationShadeWindowViewController.getView()
|
|
||||||
.getResources());
|
|
||||||
}
|
|
||||||
|
|
||||||
public RemoteAnimationAdapter getLaunchAnimation(
|
|
||||||
View sourceView, boolean occluded) {
|
|
||||||
if (!(sourceView instanceof ExpandableNotificationRow)
|
|
||||||
|| !mCallback.areLaunchAnimationsEnabled() || occluded) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
AnimationRunner animationRunner = new AnimationRunner(
|
|
||||||
(ExpandableNotificationRow) sourceView);
|
|
||||||
return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION,
|
|
||||||
ANIMATION_DURATION - 150 /* statusBarTransitionDelay */);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAnimationPending() {
|
|
||||||
return mAnimationPending;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the launch result the intent requested
|
|
||||||
*
|
|
||||||
* @param launchResult the launch result
|
|
||||||
* @param wasIntentActivity was this launch for an activity
|
|
||||||
*/
|
|
||||||
public void setLaunchResult(int launchResult, boolean wasIntentActivity) {
|
|
||||||
mIsLaunchForActivity = wasIntentActivity;
|
|
||||||
setAnimationPending((launchResult == ActivityManager.START_TASK_TO_FRONT
|
|
||||||
|| launchResult == ActivityManager.START_SUCCESS)
|
|
||||||
&& mCallback.areLaunchAnimationsEnabled());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLaunchForActivity() {
|
|
||||||
return mIsLaunchForActivity;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setAnimationPending(boolean pending) {
|
|
||||||
mAnimationPending = pending;
|
|
||||||
mNotificationShadeWindowViewController.setExpandAnimationPending(pending);
|
|
||||||
if (pending) {
|
|
||||||
mNotificationShadeWindowViewController.getView().postDelayed(mTimeoutRunnable,
|
|
||||||
LAUNCH_TIMEOUT);
|
|
||||||
} else {
|
|
||||||
mNotificationShadeWindowViewController.getView().removeCallbacks(mTimeoutRunnable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAnimationRunning() {
|
|
||||||
return mAnimationRunning;
|
|
||||||
}
|
|
||||||
|
|
||||||
class AnimationRunner extends IRemoteAnimationRunner.Stub {
|
|
||||||
|
|
||||||
private final ExpandableNotificationRow mSourceNotification;
|
|
||||||
private final ExpandAnimationParameters mParams;
|
|
||||||
private final Rect mWindowCrop = new Rect();
|
|
||||||
private boolean mIsFullScreenLaunch = true;
|
|
||||||
private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
|
|
||||||
|
|
||||||
private final float mNotificationStartTopCornerRadius;
|
|
||||||
private final float mNotificationStartBottomCornerRadius;
|
|
||||||
|
|
||||||
AnimationRunner(ExpandableNotificationRow sourceNotification) {
|
|
||||||
mSourceNotification = sourceNotification;
|
|
||||||
mParams = new ExpandAnimationParameters();
|
|
||||||
mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification);
|
|
||||||
mNotificationStartTopCornerRadius = mSourceNotification.getCurrentBackgroundRadiusTop();
|
|
||||||
mNotificationStartBottomCornerRadius =
|
|
||||||
mSourceNotification.getCurrentBackgroundRadiusBottom();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationStart(@WindowManager.TransitionOldType int transit,
|
|
||||||
RemoteAnimationTarget[] remoteAnimationTargets,
|
|
||||||
RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
|
|
||||||
RemoteAnimationTarget[] remoteAnimationNonAppTargets,
|
|
||||||
IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
|
|
||||||
throws RemoteException {
|
|
||||||
mMainExecutor.execute(() -> {
|
|
||||||
RemoteAnimationTarget primary = getPrimaryRemoteAnimationTarget(
|
|
||||||
remoteAnimationTargets);
|
|
||||||
if (primary == null) {
|
|
||||||
setAnimationPending(false);
|
|
||||||
invokeCallback(iRemoteAnimationFinishedCallback);
|
|
||||||
mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setExpandAnimationRunning(true);
|
|
||||||
mIsFullScreenLaunch = primary.position.y == 0
|
|
||||||
&& primary.sourceContainerBounds.height()
|
|
||||||
>= mNotificationPanel.getHeight();
|
|
||||||
if (!mIsFullScreenLaunch) {
|
|
||||||
mNotificationPanel.collapseWithDuration(ANIMATION_DURATION);
|
|
||||||
}
|
|
||||||
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
|
|
||||||
mParams.startPosition = mSourceNotification.getLocationOnScreen();
|
|
||||||
mParams.startTranslationZ = mSourceNotification.getTranslationZ();
|
|
||||||
mParams.startClipTopAmount = mSourceNotification.getClipTopAmount();
|
|
||||||
if (mSourceNotification.isChildInGroup()) {
|
|
||||||
int parentClip = mSourceNotification
|
|
||||||
.getNotificationParent().getClipTopAmount();
|
|
||||||
mParams.parentStartClipTopAmount = parentClip;
|
|
||||||
// We need to calculate how much the child is clipped by the parent
|
|
||||||
// because children always have 0 clipTopAmount
|
|
||||||
if (parentClip != 0) {
|
|
||||||
float childClip = parentClip
|
|
||||||
- mSourceNotification.getTranslationY();
|
|
||||||
if (childClip > 0.0f) {
|
|
||||||
mParams.startClipTopAmount = (int) Math.ceil(childClip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int targetWidth = primary.sourceContainerBounds.width();
|
|
||||||
// If the notification panel is collapsed, the clip may be larger than the height.
|
|
||||||
int notificationHeight = Math.max(mSourceNotification.getActualHeight()
|
|
||||||
- mSourceNotification.getClipBottomAmount(), 0);
|
|
||||||
int notificationWidth = mSourceNotification.getWidth();
|
|
||||||
final RemoteAnimationTarget navigationBarTarget =
|
|
||||||
getNavBarRemoteAnimationTarget(remoteAnimationNonAppTargets);
|
|
||||||
anim.setDuration(ANIMATION_DURATION);
|
|
||||||
anim.setInterpolator(Interpolators.LINEAR);
|
|
||||||
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationUpdate(ValueAnimator animation) {
|
|
||||||
mParams.linearProgress = animation.getAnimatedFraction();
|
|
||||||
float progress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
|
|
||||||
mParams.linearProgress);
|
|
||||||
int newWidth = (int) MathUtils.lerp(notificationWidth,
|
|
||||||
targetWidth, progress);
|
|
||||||
mParams.left = (int) ((targetWidth - newWidth) / 2.0f);
|
|
||||||
mParams.right = mParams.left + newWidth;
|
|
||||||
mParams.top = (int) MathUtils.lerp(mParams.startPosition[1],
|
|
||||||
primary.position.y, progress);
|
|
||||||
mParams.bottom = (int) MathUtils.lerp(mParams.startPosition[1]
|
|
||||||
+ notificationHeight,
|
|
||||||
primary.position.y + primary.sourceContainerBounds.bottom,
|
|
||||||
progress);
|
|
||||||
mParams.topCornerRadius = MathUtils.lerp(mNotificationStartTopCornerRadius,
|
|
||||||
mWindowCornerRadius, progress);
|
|
||||||
mParams.bottomCornerRadius = MathUtils.lerp(
|
|
||||||
mNotificationStartBottomCornerRadius,
|
|
||||||
mWindowCornerRadius, progress);
|
|
||||||
applyParamsToWindow(primary);
|
|
||||||
applyParamsToNotification(mParams);
|
|
||||||
applyParamsToNotificationShade(mParams);
|
|
||||||
applyNavigationBarParamsToWindow(navigationBarTarget);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
anim.addListener(new AnimatorListenerAdapter() {
|
|
||||||
private boolean mWasCancelled;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationStart(Animator animation) {
|
|
||||||
InteractionJankMonitor.getInstance().begin(mSourceNotification,
|
|
||||||
CUJ_NOTIFICATION_APP_START);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationCancel(Animator animation) {
|
|
||||||
mWasCancelled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation) {
|
|
||||||
setExpandAnimationRunning(false);
|
|
||||||
invokeCallback(iRemoteAnimationFinishedCallback);
|
|
||||||
if (!mWasCancelled) {
|
|
||||||
InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_APP_START);
|
|
||||||
} else {
|
|
||||||
InteractionJankMonitor.getInstance().cancel(CUJ_NOTIFICATION_APP_START);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
anim.start();
|
|
||||||
setAnimationPending(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void invokeCallback(IRemoteAnimationFinishedCallback callback) {
|
|
||||||
try {
|
|
||||||
callback.onAnimationFinished();
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private RemoteAnimationTarget getPrimaryRemoteAnimationTarget(
|
|
||||||
RemoteAnimationTarget[] remoteAnimationTargets) {
|
|
||||||
RemoteAnimationTarget primary = null;
|
|
||||||
for (RemoteAnimationTarget app : remoteAnimationTargets) {
|
|
||||||
if (app.mode == RemoteAnimationTarget.MODE_OPENING) {
|
|
||||||
primary = app;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RemoteAnimationTarget getNavBarRemoteAnimationTarget(
|
|
||||||
RemoteAnimationTarget[] remoteAnimationTargets) {
|
|
||||||
RemoteAnimationTarget navBar = null;
|
|
||||||
for (RemoteAnimationTarget target : remoteAnimationTargets) {
|
|
||||||
if (target.windowType == TYPE_NAVIGATION_BAR) {
|
|
||||||
navBar = target;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return navBar;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setExpandAnimationRunning(boolean running) {
|
|
||||||
mNotificationPanel.setLaunchingNotification(running);
|
|
||||||
mSourceNotification.setExpandAnimationRunning(running);
|
|
||||||
mNotificationShadeWindowViewController.setExpandAnimationRunning(running);
|
|
||||||
mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null);
|
|
||||||
mAnimationRunning = running;
|
|
||||||
if (!running) {
|
|
||||||
mCallback.onExpandAnimationFinished(mIsFullScreenLaunch);
|
|
||||||
applyParamsToNotification(null);
|
|
||||||
applyParamsToNotificationShade(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyParamsToNotificationShade(ExpandAnimationParameters params) {
|
|
||||||
mNotificationContainer.applyExpandAnimationParams(params);
|
|
||||||
mNotificationPanel.applyExpandAnimationParams(params);
|
|
||||||
mDepthController.setNotificationLaunchAnimationParams(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyParamsToNotification(ExpandAnimationParameters params) {
|
|
||||||
mSourceNotification.applyExpandAnimationParams(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyParamsToWindow(RemoteAnimationTarget app) {
|
|
||||||
Matrix m = new Matrix();
|
|
||||||
m.postTranslate(0, (float) (mParams.top - app.position.y));
|
|
||||||
mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
|
|
||||||
float cornerRadius = Math.min(mParams.topCornerRadius, mParams.bottomCornerRadius);
|
|
||||||
SurfaceParams params = new SurfaceParams.Builder(app.leash)
|
|
||||||
.withAlpha(1f)
|
|
||||||
.withMatrix(m)
|
|
||||||
.withWindowCrop(mWindowCrop)
|
|
||||||
.withLayer(app.prefixOrderIndex)
|
|
||||||
.withCornerRadius(cornerRadius)
|
|
||||||
.withVisibility(true)
|
|
||||||
.build();
|
|
||||||
mSyncRtTransactionApplier.scheduleApply(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyNavigationBarParamsToWindow(RemoteAnimationTarget navBarTarget) {
|
|
||||||
if (navBarTarget == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate navigation bar fade-out progress
|
|
||||||
final float fadeOutProgress = mParams.getProgress(0,
|
|
||||||
ANIMATION_DURATION_NAV_FADE_OUT);
|
|
||||||
|
|
||||||
// calculate navigation bar fade-in progress
|
|
||||||
final float fadeInProgress = mParams.getProgress(ANIMATION_DELAY_NAV_FADE_IN,
|
|
||||||
ANIMATION_DURATION_NAV_FADE_OUT);
|
|
||||||
|
|
||||||
final SurfaceParams.Builder builder = new SurfaceParams.Builder(navBarTarget.leash);
|
|
||||||
if (fadeInProgress > 0) {
|
|
||||||
Matrix m = new Matrix();
|
|
||||||
m.postTranslate(0, (float) (mParams.top - navBarTarget.position.y));
|
|
||||||
mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
|
|
||||||
builder.withMatrix(m)
|
|
||||||
.withWindowCrop(mWindowCrop)
|
|
||||||
.withVisibility(true);
|
|
||||||
builder.withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress));
|
|
||||||
} else {
|
|
||||||
builder.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress));
|
|
||||||
}
|
|
||||||
mSyncRtTransactionApplier.scheduleApply(builder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationCancelled() throws RemoteException {
|
|
||||||
mMainExecutor.execute(() -> {
|
|
||||||
setAnimationPending(false);
|
|
||||||
mCallback.onLaunchAnimationCancelled();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static class ExpandAnimationParameters {
|
|
||||||
public float linearProgress;
|
|
||||||
int[] startPosition;
|
|
||||||
float startTranslationZ;
|
|
||||||
int left;
|
|
||||||
int top;
|
|
||||||
int right;
|
|
||||||
int bottom;
|
|
||||||
int startClipTopAmount;
|
|
||||||
int parentStartClipTopAmount;
|
|
||||||
float topCornerRadius;
|
|
||||||
float bottomCornerRadius;
|
|
||||||
|
|
||||||
public ExpandAnimationParameters() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTop() {
|
|
||||||
return top;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBottom() {
|
|
||||||
return bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getWidth() {
|
|
||||||
return right - left;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getHeight() {
|
|
||||||
return bottom - top;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTopChange() {
|
|
||||||
// We need this compensation to ensure that the QS moves in sync.
|
|
||||||
int clipTopAmountCompensation = 0;
|
|
||||||
if (startClipTopAmount != 0.0f) {
|
|
||||||
clipTopAmountCompensation = (int) MathUtils.lerp(0, startClipTopAmount,
|
|
||||||
Interpolators.FAST_OUT_SLOW_IN.getInterpolation(linearProgress));
|
|
||||||
}
|
|
||||||
return Math.min(top - startPosition[1] - clipTopAmountCompensation, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getProgress() {
|
|
||||||
return linearProgress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getProgress(long delay, long duration) {
|
|
||||||
return MathUtils.constrain((linearProgress * ANIMATION_DURATION - delay)
|
|
||||||
/ duration, 0.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getStartClipTopAmount() {
|
|
||||||
return startClipTopAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getParentStartClipTopAmount() {
|
|
||||||
return parentStartClipTopAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getStartTranslationZ() {
|
|
||||||
return startTranslationZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getTopCornerRadius() {
|
|
||||||
return topCornerRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getBottomCornerRadius() {
|
|
||||||
return bottomCornerRadius;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface Callback {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the launch animation was cancelled.
|
|
||||||
*/
|
|
||||||
void onLaunchAnimationCancelled();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the launch animation has timed out without starting an actual animation.
|
|
||||||
*/
|
|
||||||
void onExpandAnimationTimedOut();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the expand animation has finished.
|
|
||||||
*
|
|
||||||
* @param launchIsFullScreen True if this launch was fullscreen, such that now the window
|
|
||||||
* fills the whole screen
|
|
||||||
*/
|
|
||||||
void onExpandAnimationFinished(boolean launchIsFullScreen);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Are animations currently enabled.
|
|
||||||
*/
|
|
||||||
boolean areLaunchAnimationsEnabled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.android.systemui.statusbar.notification
|
||||||
|
|
||||||
|
import android.util.MathUtils
|
||||||
|
import com.android.internal.annotations.VisibleForTesting
|
||||||
|
import com.android.systemui.Interpolators
|
||||||
|
import com.android.systemui.plugins.animation.ActivityLaunchAnimator
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
/** Parameters for the notifications expand animations. */
|
||||||
|
class ExpandAnimationParameters(
|
||||||
|
top: Int,
|
||||||
|
bottom: Int,
|
||||||
|
left: Int,
|
||||||
|
right: Int,
|
||||||
|
|
||||||
|
topCornerRadius: Float = 0f,
|
||||||
|
bottomCornerRadius: Float = 0f
|
||||||
|
) : ActivityLaunchAnimator.State(top, bottom, left, right, topCornerRadius, bottomCornerRadius) {
|
||||||
|
@VisibleForTesting
|
||||||
|
constructor() : this(
|
||||||
|
top = 0, bottom = 0, left = 0, right = 0, topCornerRadius = 0f, bottomCornerRadius = 0f
|
||||||
|
)
|
||||||
|
|
||||||
|
var startTranslationZ = 0f
|
||||||
|
var startClipTopAmount = 0
|
||||||
|
var parentStartClipTopAmount = 0
|
||||||
|
var progress = 0f
|
||||||
|
var linearProgress = 0f
|
||||||
|
|
||||||
|
override val topChange: Int
|
||||||
|
get() {
|
||||||
|
// We need this compensation to ensure that the QS moves in sync.
|
||||||
|
var clipTopAmountCompensation = 0
|
||||||
|
if (startClipTopAmount.toFloat() != 0.0f) {
|
||||||
|
clipTopAmountCompensation = MathUtils.lerp(0f, startClipTopAmount.toFloat(),
|
||||||
|
Interpolators.FAST_OUT_SLOW_IN.getInterpolation(linearProgress)).toInt()
|
||||||
|
}
|
||||||
|
return min(super.topChange - clipTopAmountCompensation, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getProgress(delay: Long, duration: Long): Float {
|
||||||
|
return ActivityLaunchAnimator.getProgress(linearProgress, delay, duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package com.android.systemui.statusbar.notification
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import com.android.internal.jank.InteractionJankMonitor
|
||||||
|
import com.android.systemui.statusbar.NotificationShadeDepthController
|
||||||
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
|
||||||
|
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
|
||||||
|
import com.android.systemui.statusbar.phone.NotificationPanelViewController
|
||||||
|
import com.android.systemui.plugins.animation.ActivityLaunchAnimator
|
||||||
|
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController
|
||||||
|
import kotlin.math.ceil
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
/** A provider of [NotificationLaunchAnimatorController]. */
|
||||||
|
class NotificationLaunchAnimatorControllerProvider(
|
||||||
|
private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
|
||||||
|
private val notificationPanelViewController: NotificationPanelViewController,
|
||||||
|
private val notificationListContainer: NotificationListContainer,
|
||||||
|
private val depthController: NotificationShadeDepthController
|
||||||
|
) {
|
||||||
|
fun getAnimatorController(
|
||||||
|
notification: ExpandableNotificationRow
|
||||||
|
): NotificationLaunchAnimatorController {
|
||||||
|
return NotificationLaunchAnimatorController(
|
||||||
|
notificationShadeWindowViewController,
|
||||||
|
notificationPanelViewController,
|
||||||
|
notificationListContainer,
|
||||||
|
depthController,
|
||||||
|
notification
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An [ActivityLaunchAnimator.Controller] that animates an [ExpandableNotificationRow]. An instance
|
||||||
|
* of this class can be passed to [ActivityLaunchAnimator.startIntentWithAnimation] to animate a
|
||||||
|
* notification expanding into an opening window.
|
||||||
|
*/
|
||||||
|
class NotificationLaunchAnimatorController(
|
||||||
|
private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
|
||||||
|
private val notificationPanelViewController: NotificationPanelViewController,
|
||||||
|
private val notificationListContainer: NotificationListContainer,
|
||||||
|
private val depthController: NotificationShadeDepthController,
|
||||||
|
private val notification: ExpandableNotificationRow
|
||||||
|
) : ActivityLaunchAnimator.Controller {
|
||||||
|
override fun getRootView(): View = notification.rootView
|
||||||
|
|
||||||
|
override fun createAnimatorState(): ActivityLaunchAnimator.State {
|
||||||
|
// If the notification panel is collapsed, the clip may be larger than the height.
|
||||||
|
val height = max(0, notification.actualHeight - notification.clipBottomAmount)
|
||||||
|
val location = notification.locationOnScreen
|
||||||
|
|
||||||
|
val params = ExpandAnimationParameters(
|
||||||
|
top = location[1],
|
||||||
|
bottom = location[1] + height,
|
||||||
|
left = location[0],
|
||||||
|
right = location[0] + notification.width,
|
||||||
|
topCornerRadius = notification.currentBackgroundRadiusTop,
|
||||||
|
bottomCornerRadius = notification.currentBackgroundRadiusBottom
|
||||||
|
)
|
||||||
|
|
||||||
|
params.startTranslationZ = notification.translationZ
|
||||||
|
params.startClipTopAmount = notification.clipTopAmount
|
||||||
|
if (notification.isChildInGroup) {
|
||||||
|
val parentClip = notification.notificationParent.clipTopAmount
|
||||||
|
params.parentStartClipTopAmount = parentClip
|
||||||
|
|
||||||
|
// We need to calculate how much the child is clipped by the parent because children
|
||||||
|
// always have 0 clipTopAmount
|
||||||
|
if (parentClip != 0) {
|
||||||
|
val childClip = parentClip - notification.translationY
|
||||||
|
if (childClip > 0) {
|
||||||
|
params.startClipTopAmount = ceil(childClip.toDouble()).toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onIntentStarted(willAnimate: Boolean) {
|
||||||
|
notificationShadeWindowViewController.setExpandAnimationRunning(willAnimate)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationCancelled() {
|
||||||
|
// TODO(b/184121838): Should we call InteractionJankMonitor.cancel if the animation started
|
||||||
|
// here?
|
||||||
|
notificationShadeWindowViewController.setExpandAnimationRunning(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationTimedOut() {
|
||||||
|
notificationShadeWindowViewController.setExpandAnimationRunning(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationAborted() {
|
||||||
|
notificationShadeWindowViewController.setExpandAnimationRunning(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
|
||||||
|
notificationPanelViewController.setLaunchingNotification(true)
|
||||||
|
notification.isExpandAnimationRunning = true
|
||||||
|
notificationListContainer.setExpandingNotification(notification)
|
||||||
|
|
||||||
|
InteractionJankMonitor.getInstance().begin(notification,
|
||||||
|
InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
|
||||||
|
InteractionJankMonitor.getInstance().end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
|
||||||
|
|
||||||
|
notificationPanelViewController.setLaunchingNotification(false)
|
||||||
|
notification.isExpandAnimationRunning = false
|
||||||
|
notificationShadeWindowViewController.setExpandAnimationRunning(false)
|
||||||
|
notificationListContainer.setExpandingNotification(null)
|
||||||
|
applyParams(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyParams(params: ExpandAnimationParameters?) {
|
||||||
|
notification.applyExpandAnimationParams(params)
|
||||||
|
notificationListContainer.applyExpandAnimationParams(params)
|
||||||
|
notificationPanelViewController.applyExpandAnimationParams(params)
|
||||||
|
depthController.notificationLaunchAnimationParams = params
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationProgress(
|
||||||
|
state: ActivityLaunchAnimator.State,
|
||||||
|
progress: Float,
|
||||||
|
linearProgress: Float
|
||||||
|
) {
|
||||||
|
val params = state as ExpandAnimationParameters
|
||||||
|
params.progress = progress
|
||||||
|
params.linearProgress = linearProgress
|
||||||
|
|
||||||
|
applyParams(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.row;
|
|||||||
import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
|
import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
|
||||||
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
|
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
|
||||||
|
|
||||||
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
|
|
||||||
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
|
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
|
||||||
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
|
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
|
||||||
|
|
||||||
@@ -79,6 +78,7 @@ import com.android.systemui.R;
|
|||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
import com.android.systemui.plugins.FalsingManager;
|
import com.android.systemui.plugins.FalsingManager;
|
||||||
import com.android.systemui.plugins.PluginListener;
|
import com.android.systemui.plugins.PluginListener;
|
||||||
|
import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
|
||||||
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||||
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
|
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
@@ -86,7 +86,7 @@ import com.android.systemui.statusbar.NotificationMediaManager;
|
|||||||
import com.android.systemui.statusbar.RemoteInputController;
|
import com.android.systemui.statusbar.RemoteInputController;
|
||||||
import com.android.systemui.statusbar.StatusBarIconView;
|
import com.android.systemui.statusbar.StatusBarIconView;
|
||||||
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
|
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
|
||||||
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
|
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
|
||||||
import com.android.systemui.statusbar.notification.NotificationUtils;
|
import com.android.systemui.statusbar.notification.NotificationUtils;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||||
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
|
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
|
||||||
@@ -2045,9 +2045,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
float extraWidthForClipping = params.getWidth() - getWidth();
|
float extraWidthForClipping = params.getWidth() - getWidth();
|
||||||
setExtraWidthForClipping(extraWidthForClipping);
|
setExtraWidthForClipping(extraWidthForClipping);
|
||||||
int top = params.getTop();
|
int top = params.getTop();
|
||||||
float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
|
|
||||||
int startClipTopAmount = params.getStartClipTopAmount();
|
int startClipTopAmount = params.getStartClipTopAmount();
|
||||||
int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
|
int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, params.getProgress());
|
||||||
if (mNotificationParent != null) {
|
if (mNotificationParent != null) {
|
||||||
float parentY = mNotificationParent.getTranslationY();
|
float parentY = mNotificationParent.getTranslationY();
|
||||||
top -= parentY;
|
top -= parentY;
|
||||||
@@ -2096,7 +2095,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
if (expandAnimationRunning) {
|
if (expandAnimationRunning) {
|
||||||
contentView.animate()
|
contentView.animate()
|
||||||
.alpha(0f)
|
.alpha(0f)
|
||||||
.setDuration(ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT)
|
.setDuration(ActivityLaunchAnimator.ANIMATION_DURATION_FADE_OUT_CONTENT)
|
||||||
.setInterpolator(Interpolators.ALPHA_OUT);
|
.setInterpolator(Interpolators.ALPHA_OUT);
|
||||||
setAboveShelf(true);
|
setAboveShelf(true);
|
||||||
mExpandAnimationRunning = true;
|
mExpandAnimationRunning = true;
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ import android.view.View;
|
|||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.systemui.Interpolators;
|
import com.android.systemui.Interpolators;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
|
import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
|
||||||
|
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A view that can be used for both the dimmed and normal background of an notification.
|
* A view that can be used for both the dimmed and normal background of an notification.
|
||||||
@@ -277,13 +278,14 @@ public class NotificationBackgroundView extends View {
|
|||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExpandAnimationParams(ActivityLaunchAnimator.ExpandAnimationParameters params) {
|
/** Set the current expand animation parameters. */
|
||||||
|
public void setExpandAnimationParams(ExpandAnimationParameters params) {
|
||||||
mActualHeight = params.getHeight();
|
mActualHeight = params.getHeight();
|
||||||
mActualWidth = params.getWidth();
|
mActualWidth = params.getWidth();
|
||||||
float alphaProgress = Interpolators.ALPHA_IN.getInterpolation(
|
float alphaProgress = Interpolators.ALPHA_IN.getInterpolation(
|
||||||
params.getProgress(
|
params.getProgress(
|
||||||
ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT /* delay */,
|
ActivityLaunchAnimator.ANIMATION_DURATION_FADE_OUT_CONTENT /* delay */,
|
||||||
ActivityLaunchAnimator.ANIMATION_DURATION_FADE_APP /* duration */));
|
ActivityLaunchAnimator.ANIMATION_DURATION_FADE_IN_WINDOW /* duration */));
|
||||||
mBackground.setAlpha((int) (mDrawableAlpha * (1.0f - alphaProgress)));
|
mBackground.setAlpha((int) (mDrawableAlpha * (1.0f - alphaProgress)));
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,13 @@
|
|||||||
|
|
||||||
package com.android.systemui.statusbar.notification.stack;
|
package com.android.systemui.statusbar.notification.stack;
|
||||||
|
|
||||||
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
|
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
|
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
|
||||||
|
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
|
||||||
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
||||||
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
|
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package com.android.systemui.statusbar.notification.stack;
|
package com.android.systemui.statusbar.notification.stack;
|
||||||
|
|
||||||
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
|
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
|
||||||
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
|
|
||||||
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
|
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
|
||||||
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
|
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
|
||||||
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
|
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
|
||||||
@@ -91,6 +90,7 @@ import com.android.systemui.statusbar.NotificationShelfController;
|
|||||||
import com.android.systemui.statusbar.RemoteInputController;
|
import com.android.systemui.statusbar.RemoteInputController;
|
||||||
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.ExpandAnimationParameters;
|
||||||
import com.android.systemui.statusbar.notification.FakeShadowView;
|
import com.android.systemui.statusbar.notification.FakeShadowView;
|
||||||
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
||||||
import com.android.systemui.statusbar.notification.NotificationUtils;
|
import com.android.systemui.statusbar.notification.NotificationUtils;
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ import com.android.systemui.statusbar.NotificationShelfController;
|
|||||||
import com.android.systemui.statusbar.RemoteInputController;
|
import com.android.systemui.statusbar.RemoteInputController;
|
||||||
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.ActivityLaunchAnimator;
|
|
||||||
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
||||||
|
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
|
||||||
import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
|
import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
|
||||||
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
||||||
@@ -1497,8 +1497,7 @@ public class NotificationStackScrollLayoutController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyExpandAnimationParams(
|
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
|
||||||
ActivityLaunchAnimator.ExpandAnimationParameters params) {
|
|
||||||
mView.applyExpandAnimationParams(params);
|
mView.applyExpandAnimationParams(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_
|
|||||||
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
|
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
|
||||||
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
|
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
|
||||||
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
|
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
|
||||||
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
|
|
||||||
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
|
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
|
||||||
|
|
||||||
import static java.lang.Float.isNaN;
|
import static java.lang.Float.isNaN;
|
||||||
@@ -100,6 +99,7 @@ import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
|
|||||||
import com.android.systemui.media.MediaDataManager;
|
import com.android.systemui.media.MediaDataManager;
|
||||||
import com.android.systemui.media.MediaHierarchyManager;
|
import com.android.systemui.media.MediaHierarchyManager;
|
||||||
import com.android.systemui.plugins.FalsingManager;
|
import com.android.systemui.plugins.FalsingManager;
|
||||||
|
import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
|
||||||
import com.android.systemui.plugins.qs.DetailAdapter;
|
import com.android.systemui.plugins.qs.DetailAdapter;
|
||||||
import com.android.systemui.plugins.qs.QS;
|
import com.android.systemui.plugins.qs.QS;
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
@@ -117,10 +117,10 @@ import com.android.systemui.statusbar.RemoteInputController;
|
|||||||
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.VibratorHelper;
|
import com.android.systemui.statusbar.VibratorHelper;
|
||||||
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
|
|
||||||
import com.android.systemui.statusbar.notification.AnimatableProperty;
|
import com.android.systemui.statusbar.notification.AnimatableProperty;
|
||||||
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
|
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
|
||||||
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
||||||
|
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||||
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
|
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
|
||||||
import com.android.systemui.statusbar.notification.PropertyAnimator;
|
import com.android.systemui.statusbar.notification.PropertyAnimator;
|
||||||
@@ -178,6 +178,10 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
* Fling until QS is completely hidden.
|
* Fling until QS is completely hidden.
|
||||||
*/
|
*/
|
||||||
private static final int FLING_HIDE = 2;
|
private static final int FLING_HIDE = 2;
|
||||||
|
private static final long ANIMATION_DELAY_ICON_FADE_IN =
|
||||||
|
ActivityLaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION
|
||||||
|
- CollapsedStatusBarFragment.FADE_IN_DELAY - 16;
|
||||||
|
|
||||||
private final DozeParameters mDozeParameters;
|
private final DozeParameters mDozeParameters;
|
||||||
private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener();
|
private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener();
|
||||||
private final OnClickListener mOnClickListener = new OnClickListener();
|
private final OnClickListener mOnClickListener = new OnClickListener();
|
||||||
@@ -3208,8 +3212,7 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hideIcons = params.getProgress(
|
boolean hideIcons = params.getProgress(ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
|
||||||
ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
|
|
||||||
if (hideIcons != mHideIconsDuringNotificationLaunch) {
|
if (hideIcons != mHideIconsDuringNotificationLaunch) {
|
||||||
mHideIconsDuringNotificationLaunch = hideIcons;
|
mHideIconsDuringNotificationLaunch = hideIcons;
|
||||||
if (!hideIcons) {
|
if (!hideIcons) {
|
||||||
|
|||||||
@@ -526,6 +526,11 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
|
|||||||
apply(mCurrentState);
|
apply(mCurrentState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLaunchingActivity() {
|
||||||
|
return mCurrentState.mLaunchingActivity;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setScrimsVisibility(int scrimsVisibility) {
|
public void setScrimsVisibility(int scrimsVisibility) {
|
||||||
mCurrentState.mScrimsVisibility = scrimsVisibility;
|
mCurrentState.mScrimsVisibility = scrimsVisibility;
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ public class NotificationShadeWindowViewController {
|
|||||||
private View mBrightnessMirror;
|
private View mBrightnessMirror;
|
||||||
private boolean mTouchActive;
|
private boolean mTouchActive;
|
||||||
private boolean mTouchCancelled;
|
private boolean mTouchCancelled;
|
||||||
private boolean mExpandAnimationPending;
|
|
||||||
private boolean mExpandAnimationRunning;
|
private boolean mExpandAnimationRunning;
|
||||||
private NotificationStackScrollLayout mStackScrollLayout;
|
private NotificationStackScrollLayout mStackScrollLayout;
|
||||||
private PhoneStatusBarView mStatusBarView;
|
private PhoneStatusBarView mStatusBarView;
|
||||||
@@ -235,7 +234,7 @@ public class NotificationShadeWindowViewController {
|
|||||||
|| ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
|
|| ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
|
||||||
setTouchActive(false);
|
setTouchActive(false);
|
||||||
}
|
}
|
||||||
if (mTouchCancelled || mExpandAnimationRunning || mExpandAnimationPending) {
|
if (mTouchCancelled || mExpandAnimationRunning) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mFalsingCollector.onTouchEvent(ev);
|
mFalsingCollector.onTouchEvent(ev);
|
||||||
@@ -435,8 +434,6 @@ public class NotificationShadeWindowViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||||
pw.print(" mExpandAnimationPending=");
|
|
||||||
pw.println(mExpandAnimationPending);
|
|
||||||
pw.print(" mExpandAnimationRunning=");
|
pw.print(" mExpandAnimationRunning=");
|
||||||
pw.println(mExpandAnimationRunning);
|
pw.println(mExpandAnimationRunning);
|
||||||
pw.print(" mTouchCancelled=");
|
pw.print(" mTouchCancelled=");
|
||||||
@@ -445,19 +442,10 @@ public class NotificationShadeWindowViewController {
|
|||||||
pw.println(mTouchActive);
|
pw.println(mTouchActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExpandAnimationPending(boolean pending) {
|
|
||||||
if (mExpandAnimationPending != pending) {
|
|
||||||
mExpandAnimationPending = pending;
|
|
||||||
mNotificationShadeWindowController
|
|
||||||
.setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExpandAnimationRunning(boolean running) {
|
public void setExpandAnimationRunning(boolean running) {
|
||||||
if (mExpandAnimationRunning != running) {
|
if (mExpandAnimationRunning != running) {
|
||||||
mExpandAnimationRunning = running;
|
mExpandAnimationRunning = running;
|
||||||
mNotificationShadeWindowController
|
mNotificationShadeWindowController.setLaunchingActivity(mExpandAnimationRunning);
|
||||||
.setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ import com.android.systemui.plugins.FalsingManager;
|
|||||||
import com.android.systemui.plugins.OverlayPlugin;
|
import com.android.systemui.plugins.OverlayPlugin;
|
||||||
import com.android.systemui.plugins.PluginDependencyProvider;
|
import com.android.systemui.plugins.PluginDependencyProvider;
|
||||||
import com.android.systemui.plugins.PluginListener;
|
import com.android.systemui.plugins.PluginListener;
|
||||||
|
import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
|
||||||
import com.android.systemui.plugins.qs.QS;
|
import com.android.systemui.plugins.qs.QS;
|
||||||
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
|
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
@@ -208,9 +209,9 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
|||||||
import com.android.systemui.statusbar.VibratorHelper;
|
import com.android.systemui.statusbar.VibratorHelper;
|
||||||
import com.android.systemui.statusbar.charging.ChargingRippleView;
|
import com.android.systemui.statusbar.charging.ChargingRippleView;
|
||||||
import com.android.systemui.statusbar.charging.WiredChargingRippleController;
|
import com.android.systemui.statusbar.charging.WiredChargingRippleController;
|
||||||
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
|
|
||||||
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
||||||
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
||||||
|
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
|
||||||
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
|
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||||
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
|
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
|
||||||
@@ -263,7 +264,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
ActivityStarter, KeyguardStateController.Callback,
|
ActivityStarter, KeyguardStateController.Callback,
|
||||||
OnHeadsUpChangedListener, CommandQueue.Callbacks,
|
OnHeadsUpChangedListener, CommandQueue.Callbacks,
|
||||||
ColorExtractor.OnColorsChangedListener, ConfigurationListener,
|
ColorExtractor.OnColorsChangedListener, ConfigurationListener,
|
||||||
StatusBarStateController.StateListener, ActivityLaunchAnimator.Callback,
|
StatusBarStateController.StateListener,
|
||||||
LifecycleOwner, BatteryController.BatteryStateChangeCallback {
|
LifecycleOwner, BatteryController.BatteryStateChangeCallback {
|
||||||
public static final boolean MULTIUSER_DEBUG = false;
|
public static final boolean MULTIUSER_DEBUG = false;
|
||||||
|
|
||||||
@@ -692,6 +693,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
private boolean mVibrateOnOpening;
|
private boolean mVibrateOnOpening;
|
||||||
private final VibratorHelper mVibratorHelper;
|
private final VibratorHelper mVibratorHelper;
|
||||||
private ActivityLaunchAnimator mActivityLaunchAnimator;
|
private ActivityLaunchAnimator mActivityLaunchAnimator;
|
||||||
|
private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
|
||||||
protected StatusBarNotificationPresenter mPresenter;
|
protected StatusBarNotificationPresenter mPresenter;
|
||||||
private NotificationActivityStarter mNotificationActivityStarter;
|
private NotificationActivityStarter mNotificationActivityStarter;
|
||||||
private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
|
private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
|
||||||
@@ -1370,16 +1372,18 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
|
|
||||||
private void setUpPresenter() {
|
private void setUpPresenter() {
|
||||||
// Set up the initial notification state.
|
// Set up the initial notification state.
|
||||||
mActivityLaunchAnimator = new ActivityLaunchAnimator(
|
mActivityLaunchAnimator = new ActivityLaunchAnimator();
|
||||||
mNotificationShadeWindowViewController, this, mNotificationPanelViewController,
|
mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
|
||||||
mNotificationShadeDepthControllerLazy.get(),
|
mNotificationShadeWindowViewController,
|
||||||
|
mNotificationPanelViewController,
|
||||||
mStackScrollerController.getNotificationListContainer(),
|
mStackScrollerController.getNotificationListContainer(),
|
||||||
mContext.getMainExecutor());
|
mNotificationShadeDepthControllerLazy.get()
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: inject this.
|
// TODO: inject this.
|
||||||
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
|
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
|
||||||
mHeadsUpManager, mNotificationShadeWindowView, mStackScrollerController,
|
mHeadsUpManager, mNotificationShadeWindowView, mStackScrollerController,
|
||||||
mDozeScrimController, mScrimController, mActivityLaunchAnimator,
|
mDozeScrimController, mScrimController, mNotificationShadeWindowController,
|
||||||
mDynamicPrivacyController, mKeyguardStateController,
|
mDynamicPrivacyController, mKeyguardStateController,
|
||||||
mKeyguardIndicationController,
|
mKeyguardIndicationController,
|
||||||
this /* statusBar */, mShadeController, mCommandQueue, mInitController,
|
this /* statusBar */, mShadeController, mCommandQueue, mInitController,
|
||||||
@@ -1392,6 +1396,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
mStatusBarNotificationActivityStarterBuilder
|
mStatusBarNotificationActivityStarterBuilder
|
||||||
.setStatusBar(this)
|
.setStatusBar(this)
|
||||||
.setActivityLaunchAnimator(mActivityLaunchAnimator)
|
.setActivityLaunchAnimator(mActivityLaunchAnimator)
|
||||||
|
.setNotificationAnimatorControllerProvider(mNotificationAnimationProvider)
|
||||||
.setNotificationPresenter(mPresenter)
|
.setNotificationPresenter(mPresenter)
|
||||||
.setNotificationPanelViewController(mNotificationPanelViewController)
|
.setNotificationPanelViewController(mNotificationPanelViewController)
|
||||||
.build();
|
.build();
|
||||||
@@ -1990,16 +1995,16 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
return mHeadsUpAppearanceController.shouldBeVisible();
|
return mHeadsUpAppearanceController.shouldBeVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A launch animation was cancelled. */
|
||||||
//TODO: These can / should probably be moved to NotificationPresenter or ShadeController
|
//TODO: These can / should probably be moved to NotificationPresenter or ShadeController
|
||||||
@Override
|
|
||||||
public void onLaunchAnimationCancelled() {
|
public void onLaunchAnimationCancelled() {
|
||||||
if (!mPresenter.isCollapsing()) {
|
if (!mPresenter.isCollapsing()) {
|
||||||
onClosingFinished();
|
onClosingFinished();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/** A launch animation ended. */
|
||||||
public void onExpandAnimationFinished(boolean launchIsFullScreen) {
|
public void onLaunchAnimationEnd(boolean launchIsFullScreen) {
|
||||||
if (!mPresenter.isCollapsing()) {
|
if (!mPresenter.isCollapsing()) {
|
||||||
onClosingFinished();
|
onClosingFinished();
|
||||||
}
|
}
|
||||||
@@ -2008,19 +2013,19 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/** A launch animation timed out. */
|
||||||
public void onExpandAnimationTimedOut() {
|
public void onLaunchAnimationTimedOut(boolean isLaunchForActivity) {
|
||||||
if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing()
|
if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing()
|
||||||
&& mActivityLaunchAnimator != null
|
&& isLaunchForActivity) {
|
||||||
&& !mActivityLaunchAnimator.isLaunchForActivity()) {
|
|
||||||
onClosingFinished();
|
onClosingFinished();
|
||||||
} else {
|
} else {
|
||||||
mShadeController.collapsePanel(true /* animate */);
|
mShadeController.collapsePanel(true /* animate */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/** Whether we should animate an activity launch. */
|
||||||
public boolean areLaunchAnimationsEnabled() {
|
public boolean areLaunchAnimationsEnabled() {
|
||||||
|
// TODO(b/184121838): Support lock screen launch animations.
|
||||||
return mState == StatusBarState.SHADE;
|
return mState == StatusBarState.SHADE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3134,8 +3139,15 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postStartActivityDismissingKeyguard(final PendingIntent intent) {
|
public void postStartActivityDismissingKeyguard(PendingIntent intent) {
|
||||||
mHandler.post(() -> startPendingIntentDismissingKeyguard(intent));
|
postStartActivityDismissingKeyguard(intent, null /* animationController */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postStartActivityDismissingKeyguard(final PendingIntent intent,
|
||||||
|
@Nullable ActivityLaunchAnimator.Controller animationController) {
|
||||||
|
mHandler.post(() -> startPendingIntentDismissingKeyguard(intent,
|
||||||
|
null /* intentSentUiThreadCallback */, animationController));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -3618,6 +3630,23 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
mShadeController.runPostCollapseRunnables();
|
mShadeController.runPostCollapseRunnables();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapse the panel directly if we are on the main thread, post the collapsing on the main
|
||||||
|
* thread if we are not.
|
||||||
|
*/
|
||||||
|
void collapsePanelOnMainThread() {
|
||||||
|
if (Looper.getMainLooper().isCurrentThread()) {
|
||||||
|
mShadeController.collapsePanel();
|
||||||
|
} else {
|
||||||
|
mContext.getMainExecutor().execute(mShadeController::collapsePanel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Collapse the panel. The collapsing will be animated for the given {@code duration}. */
|
||||||
|
void collapsePanelWithDuration(int duration) {
|
||||||
|
mNotificationPanelViewController.collapseWithDuration(duration);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStatePreChange(int oldState, int newState) {
|
public void onStatePreChange(int oldState, int newState) {
|
||||||
// If we're visible and switched to SHADE_LOCKED (the user dragged
|
// If we're visible and switched to SHADE_LOCKED (the user dragged
|
||||||
@@ -4415,7 +4444,14 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
KeyboardShortcuts.dismiss();
|
KeyboardShortcuts.dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone) {
|
/**
|
||||||
|
* Dismiss the keyguard then execute an action.
|
||||||
|
*
|
||||||
|
* @param action The action to execute after dismissing the keyguard.
|
||||||
|
* @param collapsePanel Whether we should collapse the panel after dismissing the keyguard.
|
||||||
|
*/
|
||||||
|
private void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone,
|
||||||
|
boolean collapsePanel) {
|
||||||
if (!mDeviceProvisionedController.isDeviceProvisioned()) return;
|
if (!mDeviceProvisionedController.isDeviceProvisioned()) return;
|
||||||
|
|
||||||
dismissKeyguardThenExecute(() -> {
|
dismissKeyguardThenExecute(() -> {
|
||||||
@@ -4431,7 +4467,8 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
action.run();
|
action.run();
|
||||||
}).start();
|
}).start();
|
||||||
|
|
||||||
return mShadeController.collapsePanel();
|
boolean deferred = collapsePanel ? mShadeController.collapsePanel() : false;
|
||||||
|
return deferred;
|
||||||
}, afterKeyguardGone);
|
}, afterKeyguardGone);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4443,27 +4480,55 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
@Override
|
@Override
|
||||||
public void startPendingIntentDismissingKeyguard(
|
public void startPendingIntentDismissingKeyguard(
|
||||||
final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) {
|
final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) {
|
||||||
startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback, null /* row */);
|
startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback,
|
||||||
|
(ActivityLaunchAnimator.Controller) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startPendingIntentDismissingKeyguard(PendingIntent intent,
|
||||||
|
Runnable intentSentUiThreadCallback, View associatedView) {
|
||||||
|
ActivityLaunchAnimator.Controller animationController = null;
|
||||||
|
if (associatedView instanceof ExpandableNotificationRow) {
|
||||||
|
animationController = mNotificationAnimationProvider.getAnimatorController(
|
||||||
|
((ExpandableNotificationRow) associatedView));
|
||||||
|
}
|
||||||
|
|
||||||
|
startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback,
|
||||||
|
animationController);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startPendingIntentDismissingKeyguard(
|
public void startPendingIntentDismissingKeyguard(
|
||||||
final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback,
|
final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback,
|
||||||
View associatedView) {
|
@Nullable ActivityLaunchAnimator.Controller animationController) {
|
||||||
final boolean afterKeyguardGone = intent.isActivity()
|
final boolean afterKeyguardGone = intent.isActivity()
|
||||||
&& mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
|
&& mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
|
||||||
mLockscreenUserManager.getCurrentUserId());
|
mLockscreenUserManager.getCurrentUserId());
|
||||||
|
|
||||||
|
boolean animate =
|
||||||
|
animationController != null && areLaunchAnimationsEnabled() && !isOccluded();
|
||||||
|
boolean collapse = !animate;
|
||||||
executeActionDismissingKeyguard(() -> {
|
executeActionDismissingKeyguard(() -> {
|
||||||
try {
|
try {
|
||||||
intent.send(null, 0, null, null, null, null, getActivityOptions(
|
// We wrap animationCallback with a StatusBarLaunchAnimatorController so that the
|
||||||
mDisplayId,
|
// shade is collapsed after the animation (or when it is cancelled, aborted, etc).
|
||||||
mActivityLaunchAnimator.getLaunchAnimation(associatedView, isOccluded())));
|
ActivityLaunchAnimator.Controller controller =
|
||||||
|
animate ? new StatusBarLaunchAnimatorController(animationController, this,
|
||||||
|
intent.isActivity())
|
||||||
|
: null;
|
||||||
|
|
||||||
|
mActivityLaunchAnimator.startPendingIntentWithAnimation(
|
||||||
|
controller,
|
||||||
|
(animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null,
|
||||||
|
null, getActivityOptions(mDisplayId, animationAdapter)));
|
||||||
} catch (PendingIntent.CanceledException e) {
|
} catch (PendingIntent.CanceledException e) {
|
||||||
// the stack trace isn't very helpful here.
|
// the stack trace isn't very helpful here.
|
||||||
// Just log the exception message.
|
// Just log the exception message.
|
||||||
Log.w(TAG, "Sending intent failed: " + e);
|
Log.w(TAG, "Sending intent failed: " + e);
|
||||||
|
if (!collapse) {
|
||||||
|
// executeActionDismissingKeyguard did not collapse for us already.
|
||||||
|
collapsePanelOnMainThread();
|
||||||
|
}
|
||||||
// TODO: Dismiss Keyguard.
|
// TODO: Dismiss Keyguard.
|
||||||
}
|
}
|
||||||
if (intent.isActivity()) {
|
if (intent.isActivity()) {
|
||||||
@@ -4472,7 +4537,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
if (intentSentUiThreadCallback != null) {
|
if (intentSentUiThreadCallback != null) {
|
||||||
postOnUiThread(intentSentUiThreadCallback);
|
postOnUiThread(intentSentUiThreadCallback);
|
||||||
}
|
}
|
||||||
}, afterKeyguardGone);
|
}, afterKeyguardGone, collapse);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void postOnUiThread(Runnable runnable) {
|
private void postOnUiThread(Runnable runnable) {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.android.systemui.statusbar.phone
|
||||||
|
|
||||||
|
import com.android.systemui.plugins.animation.ActivityLaunchAnimator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [ActivityLaunchAnimator.Controller] that takes care of collapsing the status bar at the right
|
||||||
|
* time.
|
||||||
|
*/
|
||||||
|
class StatusBarLaunchAnimatorController(
|
||||||
|
private val delegate: ActivityLaunchAnimator.Controller,
|
||||||
|
private val statusBar: StatusBar,
|
||||||
|
private val isLaunchForActivity: Boolean = true
|
||||||
|
) : ActivityLaunchAnimator.Controller by delegate {
|
||||||
|
override fun onIntentStarted(willAnimate: Boolean) {
|
||||||
|
delegate.onIntentStarted(willAnimate)
|
||||||
|
if (!willAnimate) {
|
||||||
|
statusBar.collapsePanelOnMainThread()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
|
||||||
|
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
|
||||||
|
if (!isExpandingFullyAbove) {
|
||||||
|
statusBar.collapsePanelWithDuration(ActivityLaunchAnimator.ANIMATION_DURATION.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
|
||||||
|
delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
|
||||||
|
statusBar.onLaunchAnimationEnd(isExpandingFullyAbove)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationCancelled() {
|
||||||
|
delegate.onLaunchAnimationCancelled()
|
||||||
|
statusBar.onLaunchAnimationCancelled()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationTimedOut() {
|
||||||
|
delegate.onLaunchAnimationTimedOut()
|
||||||
|
statusBar.onLaunchAnimationTimedOut(isLaunchForActivity)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationAborted() {
|
||||||
|
delegate.onLaunchAnimationAborted()
|
||||||
|
statusBar.collapsePanelOnMainThread()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,6 @@ import static android.service.notification.NotificationListenerService.REASON_CL
|
|||||||
import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
|
import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
|
||||||
|
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.ActivityTaskManager;
|
|
||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
@@ -40,7 +39,6 @@ import android.service.dreams.IDreamManager;
|
|||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.EventLog;
|
import android.util.EventLog;
|
||||||
import android.view.RemoteAnimationAdapter;
|
|
||||||
|
|
||||||
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.MetricsLogger;
|
||||||
import com.android.internal.statusbar.NotificationVisibility;
|
import com.android.internal.statusbar.NotificationVisibility;
|
||||||
@@ -52,6 +50,7 @@ import com.android.systemui.dagger.SysUISingleton;
|
|||||||
import com.android.systemui.dagger.qualifiers.Main;
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
import com.android.systemui.dagger.qualifiers.UiBackground;
|
import com.android.systemui.dagger.qualifiers.UiBackground;
|
||||||
import com.android.systemui.plugins.ActivityStarter;
|
import com.android.systemui.plugins.ActivityStarter;
|
||||||
|
import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
import com.android.systemui.statusbar.CommandQueue;
|
import com.android.systemui.statusbar.CommandQueue;
|
||||||
import com.android.systemui.statusbar.FeatureFlags;
|
import com.android.systemui.statusbar.FeatureFlags;
|
||||||
@@ -60,11 +59,10 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager;
|
|||||||
import com.android.systemui.statusbar.NotificationPresenter;
|
import com.android.systemui.statusbar.NotificationPresenter;
|
||||||
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||||
import com.android.systemui.statusbar.RemoteInputController;
|
import com.android.systemui.statusbar.RemoteInputController;
|
||||||
import com.android.systemui.statusbar.StatusBarState;
|
|
||||||
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
|
|
||||||
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||||
|
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
|
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
|
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
|
||||||
@@ -124,6 +122,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
private final NotificationPresenter mPresenter;
|
private final NotificationPresenter mPresenter;
|
||||||
private final NotificationPanelViewController mNotificationPanel;
|
private final NotificationPanelViewController mNotificationPanel;
|
||||||
private final ActivityLaunchAnimator mActivityLaunchAnimator;
|
private final ActivityLaunchAnimator mActivityLaunchAnimator;
|
||||||
|
private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
|
||||||
private final OnUserInteractionCallback mOnUserInteractionCallback;
|
private final OnUserInteractionCallback mOnUserInteractionCallback;
|
||||||
|
|
||||||
private boolean mIsCollapsingToShowActivityOverLockscreen;
|
private boolean mIsCollapsingToShowActivityOverLockscreen;
|
||||||
@@ -162,7 +161,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
StatusBar statusBar,
|
StatusBar statusBar,
|
||||||
NotificationPresenter presenter,
|
NotificationPresenter presenter,
|
||||||
NotificationPanelViewController panel,
|
NotificationPanelViewController panel,
|
||||||
ActivityLaunchAnimator activityLaunchAnimator) {
|
ActivityLaunchAnimator activityLaunchAnimator,
|
||||||
|
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mCommandQueue = commandQueue;
|
mCommandQueue = commandQueue;
|
||||||
mMainThreadHandler = mainThreadHandler;
|
mMainThreadHandler = mainThreadHandler;
|
||||||
@@ -198,6 +198,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
mPresenter = presenter;
|
mPresenter = presenter;
|
||||||
mNotificationPanel = panel;
|
mNotificationPanel = panel;
|
||||||
mActivityLaunchAnimator = activityLaunchAnimator;
|
mActivityLaunchAnimator = activityLaunchAnimator;
|
||||||
|
mNotificationAnimationProvider = notificationAnimationProvider;
|
||||||
|
|
||||||
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
|
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
|
||||||
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
|
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
|
||||||
@@ -400,17 +401,15 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Looper.getMainLooper().isCurrentThread()) {
|
if (Looper.getMainLooper().isCurrentThread()) {
|
||||||
mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
|
expandBubbleStack(entry);
|
||||||
} else {
|
} else {
|
||||||
mMainThreadHandler.post(
|
mMainThreadHandler.post(() -> expandBubbleStack(entry));
|
||||||
() -> mBubblesManagerOptional.get().expandStackAndSelectBubble(entry));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// expandStackAndSelectBubble won't affect shouldCollapse, so we can collapse directly even
|
private void expandBubbleStack(NotificationEntry entry) {
|
||||||
// if we are not on the main thread.
|
mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
|
||||||
if (shouldCollapse()) {
|
mShadeController.collapsePanel();
|
||||||
collapseOnMainThread();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startNotificationIntent(
|
private void startNotificationIntent(
|
||||||
@@ -420,32 +419,36 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
ExpandableNotificationRow row,
|
ExpandableNotificationRow row,
|
||||||
boolean wasOccluded,
|
boolean wasOccluded,
|
||||||
boolean isActivityIntent) {
|
boolean isActivityIntent) {
|
||||||
RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row,
|
|
||||||
wasOccluded);
|
|
||||||
mLogger.logStartNotificationIntent(entry.getKey(), intent);
|
mLogger.logStartNotificationIntent(entry.getKey(), intent);
|
||||||
try {
|
try {
|
||||||
if (adapter != null) {
|
ActivityLaunchAnimator.Controller animationController = null;
|
||||||
ActivityTaskManager.getService()
|
if (!wasOccluded && mStatusBar.areLaunchAnimationsEnabled()) {
|
||||||
.registerRemoteAnimationForNextActivityStart(
|
animationController = new StatusBarLaunchAnimatorController(
|
||||||
intent.getCreatorPackage(), adapter);
|
mNotificationAnimationProvider.getAnimatorController(row), mStatusBar,
|
||||||
|
isActivityIntent);
|
||||||
}
|
}
|
||||||
long eventTime = row.getAndResetLastActionUpTime();
|
|
||||||
Bundle options = eventTime > 0
|
mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController,
|
||||||
? getActivityOptions(
|
(adapter) -> {
|
||||||
mStatusBar.getDisplayId(),
|
long eventTime = row.getAndResetLastActionUpTime();
|
||||||
adapter,
|
Bundle options = eventTime > 0
|
||||||
mKeyguardStateController.isShowing(),
|
? getActivityOptions(
|
||||||
eventTime)
|
mStatusBar.getDisplayId(),
|
||||||
: getActivityOptions(mStatusBar.getDisplayId(), adapter);
|
adapter,
|
||||||
int launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
|
mKeyguardStateController.isShowing(),
|
||||||
null, null, options);
|
eventTime)
|
||||||
mMainThreadHandler.post(() -> {
|
: getActivityOptions(mStatusBar.getDisplayId(), adapter);
|
||||||
mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
|
return intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
|
||||||
if (shouldCollapse()) {
|
null, null, options);
|
||||||
collapseOnMainThread();
|
});
|
||||||
}
|
|
||||||
});
|
// Note that other cases when we should still collapse (like activity already on top) is
|
||||||
} catch (RemoteException | PendingIntent.CanceledException e) {
|
// handled by the StatusBarLaunchAnimatorController.
|
||||||
|
boolean shouldCollapse = animationController == null;
|
||||||
|
if (shouldCollapse) {
|
||||||
|
collapseOnMainThread();
|
||||||
|
}
|
||||||
|
} catch (PendingIntent.CanceledException e) {
|
||||||
// the stack trace isn't very helpful here.
|
// the stack trace isn't very helpful here.
|
||||||
// Just log the exception message.
|
// Just log the exception message.
|
||||||
mLogger.logSendingIntentFailed(e);
|
mLogger.logSendingIntentFailed(e);
|
||||||
@@ -458,20 +461,30 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
ExpandableNotificationRow row) {
|
ExpandableNotificationRow row) {
|
||||||
mActivityStarter.dismissKeyguardThenExecute(() -> {
|
mActivityStarter.dismissKeyguardThenExecute(() -> {
|
||||||
AsyncTask.execute(() -> {
|
AsyncTask.execute(() -> {
|
||||||
int launchResult = TaskStackBuilder.create(mContext)
|
ActivityLaunchAnimator.Controller animationController = null;
|
||||||
.addNextIntentWithParentStack(intent)
|
if (!mStatusBar.isOccluded() && mStatusBar.areLaunchAnimationsEnabled()) {
|
||||||
.startActivities(getActivityOptions(
|
animationController = new StatusBarLaunchAnimatorController(
|
||||||
mStatusBar.getDisplayId(),
|
mNotificationAnimationProvider.getAnimatorController(row), mStatusBar,
|
||||||
mActivityLaunchAnimator.getLaunchAnimation(
|
true /* isActivityIntent */);
|
||||||
row, mStatusBar.isOccluded())),
|
}
|
||||||
new UserHandle(UserHandle.getUserId(appUid)));
|
|
||||||
|
mActivityLaunchAnimator.startIntentWithAnimation(
|
||||||
|
animationController,
|
||||||
|
(adapter) -> TaskStackBuilder.create(mContext)
|
||||||
|
.addNextIntentWithParentStack(intent)
|
||||||
|
.startActivities(getActivityOptions(
|
||||||
|
mStatusBar.getDisplayId(),
|
||||||
|
adapter),
|
||||||
|
new UserHandle(UserHandle.getUserId(appUid))));
|
||||||
|
|
||||||
|
// Note that other cases when we should still collapse (like activity already on
|
||||||
|
// top) is handled by the StatusBarLaunchAnimatorController.
|
||||||
|
boolean shouldCollapse = animationController == null;
|
||||||
|
|
||||||
// Putting it back on the main thread, since we're touching views
|
// Putting it back on the main thread, since we're touching views
|
||||||
mMainThreadHandler.post(() -> {
|
mMainThreadHandler.post(() -> {
|
||||||
mActivityLaunchAnimator.setLaunchResult(launchResult,
|
|
||||||
true /* isActivityIntent */);
|
|
||||||
removeHUN(row);
|
removeHUN(row);
|
||||||
if (shouldCollapse()) {
|
if (shouldCollapse) {
|
||||||
mCommandQueue.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
|
mCommandQueue.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
|
||||||
true /* force */);
|
true /* force */);
|
||||||
}
|
}
|
||||||
@@ -494,11 +507,10 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
tsb.addNextIntent(intent);
|
tsb.addNextIntent(intent);
|
||||||
}
|
}
|
||||||
tsb.startActivities(null, UserHandle.CURRENT);
|
tsb.startActivities(null, UserHandle.CURRENT);
|
||||||
if (shouldCollapse()) {
|
|
||||||
// Putting it back on the main thread, since we're touching views
|
// Putting it back on the main thread, since we're touching views
|
||||||
mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
|
mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
|
||||||
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
|
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}, null, false /* afterKeyguardGone */);
|
}, null, false /* afterKeyguardGone */);
|
||||||
@@ -576,11 +588,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldCollapse() {
|
|
||||||
return mStatusBarStateController.getState() != StatusBarState.SHADE
|
|
||||||
|| !mActivityLaunchAnimator.isAnimationPending();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldSuppressFullScreenIntent(NotificationEntry entry) {
|
private boolean shouldSuppressFullScreenIntent(NotificationEntry entry) {
|
||||||
if (mPresenter.isDeviceInVrMode()) {
|
if (mPresenter.isDeviceInVrMode()) {
|
||||||
return true;
|
return true;
|
||||||
@@ -639,6 +646,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
private NotificationPresenter mNotificationPresenter;
|
private NotificationPresenter mNotificationPresenter;
|
||||||
private NotificationPanelViewController mNotificationPanelViewController;
|
private NotificationPanelViewController mNotificationPanelViewController;
|
||||||
private ActivityLaunchAnimator mActivityLaunchAnimator;
|
private ActivityLaunchAnimator mActivityLaunchAnimator;
|
||||||
|
private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Builder(
|
public Builder(
|
||||||
@@ -714,12 +722,20 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set the ActivityLaunchAnimator. */
|
||||||
public Builder setActivityLaunchAnimator(ActivityLaunchAnimator activityLaunchAnimator) {
|
public Builder setActivityLaunchAnimator(ActivityLaunchAnimator activityLaunchAnimator) {
|
||||||
mActivityLaunchAnimator = activityLaunchAnimator;
|
mActivityLaunchAnimator = activityLaunchAnimator;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the NotificationPanelViewController */
|
/** Set the NotificationLaunchAnimatorControllerProvider. */
|
||||||
|
public Builder setNotificationAnimatorControllerProvider(
|
||||||
|
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
|
||||||
|
mNotificationAnimationProvider = notificationAnimationProvider;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the NotificationPanelViewController. */
|
||||||
public Builder setNotificationPanelViewController(
|
public Builder setNotificationPanelViewController(
|
||||||
NotificationPanelViewController notificationPanelViewController) {
|
NotificationPanelViewController notificationPanelViewController) {
|
||||||
mNotificationPanelViewController = notificationPanelViewController;
|
mNotificationPanelViewController = notificationPanelViewController;
|
||||||
@@ -759,7 +775,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
|
|||||||
mStatusBar,
|
mStatusBar,
|
||||||
mNotificationPresenter,
|
mNotificationPresenter,
|
||||||
mNotificationPanelViewController,
|
mNotificationPanelViewController,
|
||||||
mActivityLaunchAnimator);
|
mActivityLaunchAnimator,
|
||||||
|
mNotificationAnimationProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ import com.android.systemui.statusbar.NotificationViewHierarchyManager;
|
|||||||
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.AboveShelfObserver;
|
import com.android.systemui.statusbar.notification.AboveShelfObserver;
|
||||||
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
|
|
||||||
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||||
@@ -117,7 +116,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
|
|||||||
|
|
||||||
private final AccessibilityManager mAccessibilityManager;
|
private final AccessibilityManager mAccessibilityManager;
|
||||||
private final KeyguardManager mKeyguardManager;
|
private final KeyguardManager mKeyguardManager;
|
||||||
private final ActivityLaunchAnimator mActivityLaunchAnimator;
|
private final NotificationShadeWindowController mNotificationShadeWindowController;
|
||||||
private final IStatusBarService mBarService;
|
private final IStatusBarService mBarService;
|
||||||
private final DynamicPrivacyController mDynamicPrivacyController;
|
private final DynamicPrivacyController mDynamicPrivacyController;
|
||||||
private boolean mReinflateNotificationsOnUserSwitched;
|
private boolean mReinflateNotificationsOnUserSwitched;
|
||||||
@@ -133,7 +132,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
|
|||||||
NotificationStackScrollLayoutController stackScrollerController,
|
NotificationStackScrollLayoutController stackScrollerController,
|
||||||
DozeScrimController dozeScrimController,
|
DozeScrimController dozeScrimController,
|
||||||
ScrimController scrimController,
|
ScrimController scrimController,
|
||||||
ActivityLaunchAnimator activityLaunchAnimator,
|
NotificationShadeWindowController notificationShadeWindowController,
|
||||||
DynamicPrivacyController dynamicPrivacyController,
|
DynamicPrivacyController dynamicPrivacyController,
|
||||||
KeyguardStateController keyguardStateController,
|
KeyguardStateController keyguardStateController,
|
||||||
KeyguardIndicationController keyguardIndicationController,
|
KeyguardIndicationController keyguardIndicationController,
|
||||||
@@ -152,7 +151,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
|
|||||||
mShadeController = shadeController;
|
mShadeController = shadeController;
|
||||||
mCommandQueue = commandQueue;
|
mCommandQueue = commandQueue;
|
||||||
mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView());
|
mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView());
|
||||||
mActivityLaunchAnimator = activityLaunchAnimator;
|
mNotificationShadeWindowController = notificationShadeWindowController;
|
||||||
mAboveShelfObserver.setListener(statusBarWindow.findViewById(
|
mAboveShelfObserver.setListener(statusBarWindow.findViewById(
|
||||||
R.id.notification_container_parent));
|
R.id.notification_container_parent));
|
||||||
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
|
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
|
||||||
@@ -272,8 +271,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
|
|||||||
@Override
|
@Override
|
||||||
public boolean isCollapsing() {
|
public boolean isCollapsing() {
|
||||||
return mNotificationPanel.isCollapsing()
|
return mNotificationPanel.isCollapsing()
|
||||||
|| mActivityLaunchAnimator.isAnimationPending()
|
|| mNotificationShadeWindowController.isLaunchingActivity();
|
||||||
|| mActivityLaunchAnimator.isAnimationRunning();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeEndAmbientPulse() {
|
private void maybeEndAmbientPulse() {
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ class TransitionLayout @JvmOverloads constructor(
|
|||||||
|
|
||||||
private var desiredMeasureWidth = 0
|
private var desiredMeasureWidth = 0
|
||||||
private var desiredMeasureHeight = 0
|
private var desiredMeasureHeight = 0
|
||||||
|
private var transitionVisibility = View.VISIBLE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The measured state of this view which is the one we will lay ourselves out with. This
|
* The measured state of this view which is the one we will lay ourselves out with. This
|
||||||
* may differ from the currentState if there is an external animation or transition running.
|
* may differ from the currentState if there is an external animation or transition running.
|
||||||
@@ -81,6 +83,13 @@ class TransitionLayout @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setTransitionVisibility(visibility: Int) {
|
||||||
|
// We store the last transition visibility assigned to this view to restore it later if
|
||||||
|
// necessary.
|
||||||
|
super.setTransitionVisibility(visibility)
|
||||||
|
transitionVisibility = visibility
|
||||||
|
}
|
||||||
|
|
||||||
override fun onFinishInflate() {
|
override fun onFinishInflate() {
|
||||||
super.onFinishInflate()
|
super.onFinishInflate()
|
||||||
val childCount = childCount
|
val childCount = childCount
|
||||||
@@ -162,7 +171,16 @@ class TransitionLayout @JvmOverloads constructor(
|
|||||||
updateBounds()
|
updateBounds()
|
||||||
translationX = currentState.translation.x
|
translationX = currentState.translation.x
|
||||||
translationY = currentState.translation.y
|
translationY = currentState.translation.y
|
||||||
|
|
||||||
CrossFadeHelper.fadeIn(this, currentState.alpha)
|
CrossFadeHelper.fadeIn(this, currentState.alpha)
|
||||||
|
|
||||||
|
// CrossFadeHelper#fadeIn will change this view visibility, which overrides the transition
|
||||||
|
// visibility. We set the transition visibility again to make sure that this view plays well
|
||||||
|
// with GhostView, which sets the transition visibility and is used for activity launch
|
||||||
|
// animations.
|
||||||
|
if (transitionVisibility != View.VISIBLE) {
|
||||||
|
setTransitionVisibility(transitionVisibility)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyCurrentStateOnPredraw() {
|
private fun applyCurrentStateOnPredraw() {
|
||||||
|
|||||||
@@ -0,0 +1,188 @@
|
|||||||
|
package com.android.systemui.plugins.animation
|
||||||
|
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.app.WindowConfiguration
|
||||||
|
import android.graphics.Point
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.os.Looper
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import android.testing.TestableLooper.RunWithLooper
|
||||||
|
import android.view.IRemoteAnimationFinishedCallback
|
||||||
|
import android.view.RemoteAnimationAdapter
|
||||||
|
import android.view.RemoteAnimationTarget
|
||||||
|
import android.view.SurfaceControl
|
||||||
|
import android.view.View
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import junit.framework.Assert.assertFalse
|
||||||
|
import junit.framework.Assert.assertNotNull
|
||||||
|
import junit.framework.Assert.assertNull
|
||||||
|
import junit.framework.Assert.assertTrue
|
||||||
|
import junit.framework.AssertionFailedError
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentCaptor
|
||||||
|
import org.mockito.ArgumentMatchers.anyBoolean
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.never
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.Spy
|
||||||
|
import org.mockito.junit.MockitoJUnit
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
@RunWithLooper
|
||||||
|
class ActivityLaunchAnimatorTest : SysuiTestCase() {
|
||||||
|
private val activityLaunchAnimator = ActivityLaunchAnimator()
|
||||||
|
private val rootView = View(mContext)
|
||||||
|
@Spy private val controller = TestLaunchAnimatorController(rootView)
|
||||||
|
@Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
|
||||||
|
|
||||||
|
@get:Rule val rule = MockitoJUnit.rule()
|
||||||
|
|
||||||
|
private fun startIntentWithAnimation(
|
||||||
|
controller: ActivityLaunchAnimator.Controller? = this.controller,
|
||||||
|
intentStarter: (RemoteAnimationAdapter?) -> Int
|
||||||
|
) {
|
||||||
|
// We start in a new thread so that we can ensure that the callbacks are called in the main
|
||||||
|
// thread.
|
||||||
|
thread {
|
||||||
|
activityLaunchAnimator.startIntentWithAnimation(controller, intentStarter)
|
||||||
|
}.join()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun animationAdapterIsNullIfControllerIsNull() {
|
||||||
|
var startedIntent = false
|
||||||
|
var animationAdapter: RemoteAnimationAdapter? = null
|
||||||
|
|
||||||
|
startIntentWithAnimation(controller = null) { adapter ->
|
||||||
|
startedIntent = true
|
||||||
|
animationAdapter = adapter
|
||||||
|
|
||||||
|
ActivityManager.START_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(startedIntent)
|
||||||
|
assertNull(animationAdapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun animatesIfActivityOpens() {
|
||||||
|
val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
|
||||||
|
var animationAdapter: RemoteAnimationAdapter? = null
|
||||||
|
startIntentWithAnimation { adapter ->
|
||||||
|
animationAdapter = adapter
|
||||||
|
ActivityManager.START_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNotNull(animationAdapter)
|
||||||
|
waitForIdleSync()
|
||||||
|
verify(controller).onIntentStarted(willAnimateCaptor.capture())
|
||||||
|
assertTrue(willAnimateCaptor.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun doesNotAnimateIfActivityIsAlreadyOpen() {
|
||||||
|
val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
|
||||||
|
startIntentWithAnimation { ActivityManager.START_DELIVERED_TO_TOP }
|
||||||
|
|
||||||
|
waitForIdleSync()
|
||||||
|
verify(controller).onIntentStarted(willAnimateCaptor.capture())
|
||||||
|
assertFalse(willAnimateCaptor.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun doesNotStartIfAnimationIsCancelled() {
|
||||||
|
val runner = ActivityLaunchAnimator.Runner(controller)
|
||||||
|
runner.onAnimationCancelled()
|
||||||
|
runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
|
||||||
|
|
||||||
|
waitForIdleSync()
|
||||||
|
verify(controller).onLaunchAnimationCancelled()
|
||||||
|
verify(controller, never()).onLaunchAnimationStart(anyBoolean())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun abortsIfNoOpeningWindowIsFound() {
|
||||||
|
val runner = ActivityLaunchAnimator.Runner(controller)
|
||||||
|
runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
|
||||||
|
|
||||||
|
waitForIdleSync()
|
||||||
|
verify(controller).onLaunchAnimationAborted()
|
||||||
|
verify(controller, never()).onLaunchAnimationStart(anyBoolean())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun startsAnimationIfWindowIsOpening() {
|
||||||
|
val runner = ActivityLaunchAnimator.Runner(controller)
|
||||||
|
runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback)
|
||||||
|
waitForIdleSync()
|
||||||
|
verify(controller).onLaunchAnimationStart(anyBoolean())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fakeWindow() = RemoteAnimationTarget(
|
||||||
|
0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
|
||||||
|
Point(), Rect(), Rect(), WindowConfiguration(), false, SurfaceControl(), Rect(),
|
||||||
|
ActivityManager.RunningTaskInfo()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called
|
||||||
|
* outside of the main thread.
|
||||||
|
*/
|
||||||
|
private class TestLaunchAnimatorController(
|
||||||
|
private val rootView: View
|
||||||
|
) : ActivityLaunchAnimator.Controller {
|
||||||
|
override fun getRootView(): View = rootView
|
||||||
|
|
||||||
|
override fun createAnimatorState() = ActivityLaunchAnimator.State(
|
||||||
|
top = 100,
|
||||||
|
bottom = 200,
|
||||||
|
left = 300,
|
||||||
|
right = 400,
|
||||||
|
topCornerRadius = 10f,
|
||||||
|
bottomCornerRadius = 20f
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun assertOnMainThread() {
|
||||||
|
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||||
|
throw AssertionFailedError("Called outside of main thread.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onIntentStarted(willAnimate: Boolean) {
|
||||||
|
assertOnMainThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
|
||||||
|
assertOnMainThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationProgress(
|
||||||
|
state: ActivityLaunchAnimator.State,
|
||||||
|
progress: Float,
|
||||||
|
linearProgress: Float
|
||||||
|
) {
|
||||||
|
assertOnMainThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
|
||||||
|
assertOnMainThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationCancelled() {
|
||||||
|
assertOnMainThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationTimedOut() {
|
||||||
|
assertOnMainThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLaunchAnimationAborted() {
|
||||||
|
assertOnMainThread()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ import androidx.test.filters.SmallTest
|
|||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.dump.DumpManager
|
import com.android.systemui.dump.DumpManager
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||||
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator
|
import com.android.systemui.statusbar.notification.ExpandAnimationParameters
|
||||||
import com.android.systemui.statusbar.phone.BiometricUnlockController
|
import com.android.systemui.statusbar.phone.BiometricUnlockController
|
||||||
import com.android.systemui.statusbar.phone.DozeParameters
|
import com.android.systemui.statusbar.phone.DozeParameters
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||||
@@ -215,7 +215,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
|
|||||||
fun updateBlurCallback_appLaunchAnimation_overridesZoom() {
|
fun updateBlurCallback_appLaunchAnimation_overridesZoom() {
|
||||||
`when`(shadeSpring.radius).thenReturn(maxBlur)
|
`when`(shadeSpring.radius).thenReturn(maxBlur)
|
||||||
`when`(shadeAnimation.radius).thenReturn(maxBlur)
|
`when`(shadeAnimation.radius).thenReturn(maxBlur)
|
||||||
val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
|
val animProgress = ExpandAnimationParameters()
|
||||||
animProgress.linearProgress = 1f
|
animProgress.linearProgress = 1f
|
||||||
notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
|
notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
|
||||||
notificationShadeDepthController.updateBlurCallback.doFrame(0)
|
notificationShadeDepthController.updateBlurCallback.doFrame(0)
|
||||||
@@ -264,7 +264,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun setNotificationLaunchAnimationParams_schedulesFrame() {
|
fun setNotificationLaunchAnimationParams_schedulesFrame() {
|
||||||
val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
|
val animProgress = ExpandAnimationParameters()
|
||||||
animProgress.linearProgress = 0.5f
|
animProgress.linearProgress = 0.5f
|
||||||
notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
|
notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
|
||||||
verify(choreographer).postFrameCallback(
|
verify(choreographer).postFrameCallback(
|
||||||
@@ -273,7 +273,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun setNotificationLaunchAnimationParams_whennNull_ignoresIfShadeHasNoBlur() {
|
fun setNotificationLaunchAnimationParams_whennNull_ignoresIfShadeHasNoBlur() {
|
||||||
val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
|
val animProgress = ExpandAnimationParameters()
|
||||||
animProgress.linearProgress = 0.5f
|
animProgress.linearProgress = 0.5f
|
||||||
`when`(shadeSpring.radius).thenReturn(0)
|
`when`(shadeSpring.radius).thenReturn(0)
|
||||||
`when`(shadeAnimation.radius).thenReturn(0)
|
`when`(shadeAnimation.radius).thenReturn(0)
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.systemui.statusbar.notification;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
|
||||||
import static org.mockito.Mockito.doAnswer;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
|
||||||
import android.testing.AndroidTestingRunner;
|
|
||||||
import android.testing.TestableLooper;
|
|
||||||
import android.view.RemoteAnimationAdapter;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import com.android.systemui.SysuiTestCase;
|
|
||||||
import com.android.systemui.statusbar.NotificationShadeDepthController;
|
|
||||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
|
||||||
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
|
|
||||||
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
|
|
||||||
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
|
|
||||||
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
|
|
||||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
|
||||||
import com.android.systemui.util.time.FakeSystemClock;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.MockitoJUnit;
|
|
||||||
import org.mockito.junit.MockitoRule;
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
@RunWith(AndroidTestingRunner.class)
|
|
||||||
@TestableLooper.RunWithLooper
|
|
||||||
public class ActivityLaunchAnimatorTest extends SysuiTestCase {
|
|
||||||
|
|
||||||
private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
|
|
||||||
private ActivityLaunchAnimator mLaunchAnimator;
|
|
||||||
@Mock
|
|
||||||
private ActivityLaunchAnimator.Callback mCallback;
|
|
||||||
@Mock
|
|
||||||
private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
|
|
||||||
@Mock
|
|
||||||
private NotificationShadeWindowView mNotificationShadeWindowView;
|
|
||||||
@Mock
|
|
||||||
private NotificationListContainer mNotificationContainer;
|
|
||||||
@Mock
|
|
||||||
private ExpandableNotificationRow mRow;
|
|
||||||
@Mock
|
|
||||||
private NotificationShadeDepthController mNotificationShadeDepthController;
|
|
||||||
@Mock
|
|
||||||
private NotificationPanelViewController mNotificationPanelViewController;
|
|
||||||
@Rule
|
|
||||||
public MockitoRule rule = MockitoJUnit.rule();
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
when(mNotificationShadeWindowViewController.getView())
|
|
||||||
.thenReturn(mNotificationShadeWindowView);
|
|
||||||
when(mNotificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
|
|
||||||
when(mCallback.areLaunchAnimationsEnabled()).thenReturn(true);
|
|
||||||
mLaunchAnimator = new ActivityLaunchAnimator(
|
|
||||||
mNotificationShadeWindowViewController,
|
|
||||||
mCallback,
|
|
||||||
mNotificationPanelViewController,
|
|
||||||
mNotificationShadeDepthController,
|
|
||||||
mNotificationContainer,
|
|
||||||
mExecutor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReturnsNullIfNotEnabled() {
|
|
||||||
when(mCallback.areLaunchAnimationsEnabled()).thenReturn(false);
|
|
||||||
RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
|
|
||||||
false /* occluded */);
|
|
||||||
Assert.assertTrue("The LaunchAnimator generated an animation even though animations are "
|
|
||||||
+ "disabled", launchAnimation == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNotWorkingWhenOccluded() {
|
|
||||||
when(mCallback.areLaunchAnimationsEnabled()).thenReturn(false);
|
|
||||||
RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
|
|
||||||
true /* occluded */);
|
|
||||||
Assert.assertTrue("The LaunchAnimator generated an animation even though we're occluded",
|
|
||||||
launchAnimation == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTimeoutCalled() {
|
|
||||||
RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
|
|
||||||
false /* occluded */);
|
|
||||||
Assert.assertTrue("No animation generated", launchAnimation != null);
|
|
||||||
executePostsImmediately(mNotificationShadeWindowView);
|
|
||||||
mLaunchAnimator.setLaunchResult(ActivityManager.START_SUCCESS,
|
|
||||||
true /* wasIntentActivity */);
|
|
||||||
verify(mCallback).onExpandAnimationTimedOut();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void executePostsImmediately(View view) {
|
|
||||||
doAnswer((i) -> {
|
|
||||||
Runnable run = i.getArgument(0);
|
|
||||||
run.run();
|
|
||||||
return null;
|
|
||||||
}).when(view).post(any());
|
|
||||||
doAnswer((i) -> {
|
|
||||||
Runnable run = i.getArgument(0);
|
|
||||||
run.run();
|
|
||||||
return null;
|
|
||||||
}).when(view).postDelayed(any(), anyLong());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -35,7 +35,6 @@ import static org.mockito.Mockito.when;
|
|||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
@@ -54,6 +53,7 @@ import com.android.systemui.ActivityIntentHelper;
|
|||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
import com.android.systemui.assist.AssistManager;
|
import com.android.systemui.assist.AssistManager;
|
||||||
import com.android.systemui.plugins.ActivityStarter;
|
import com.android.systemui.plugins.ActivityStarter;
|
||||||
|
import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
import com.android.systemui.statusbar.CommandQueue;
|
import com.android.systemui.statusbar.CommandQueue;
|
||||||
import com.android.systemui.statusbar.FeatureFlags;
|
import com.android.systemui.statusbar.FeatureFlags;
|
||||||
@@ -63,9 +63,9 @@ import com.android.systemui.statusbar.NotificationPresenter;
|
|||||||
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||||
import com.android.systemui.statusbar.RemoteInputController;
|
import com.android.systemui.statusbar.RemoteInputController;
|
||||||
import com.android.systemui.statusbar.StatusBarState;
|
import com.android.systemui.statusbar.StatusBarState;
|
||||||
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
|
|
||||||
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||||
|
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
|
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
|
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
|
||||||
@@ -136,6 +136,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
|
|||||||
private OnUserInteractionCallback mOnUserInteractionCallback;
|
private OnUserInteractionCallback mOnUserInteractionCallback;
|
||||||
@Mock
|
@Mock
|
||||||
private NotificationActivityStarter mNotificationActivityStarter;
|
private NotificationActivityStarter mNotificationActivityStarter;
|
||||||
|
@Mock
|
||||||
|
private ActivityLaunchAnimator mActivityLaunchAnimator;
|
||||||
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
|
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
|
||||||
|
|
||||||
private NotificationTestHelper mNotificationTestHelper;
|
private NotificationTestHelper mNotificationTestHelper;
|
||||||
@@ -213,11 +215,14 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
|
|||||||
mock(MetricsLogger.class),
|
mock(MetricsLogger.class),
|
||||||
mock(StatusBarNotificationActivityStarterLogger.class),
|
mock(StatusBarNotificationActivityStarterLogger.class),
|
||||||
mOnUserInteractionCallback)
|
mOnUserInteractionCallback)
|
||||||
.setStatusBar(mStatusBar)
|
.setStatusBar(mStatusBar)
|
||||||
.setNotificationPresenter(mock(NotificationPresenter.class))
|
.setNotificationPresenter(mock(NotificationPresenter.class))
|
||||||
.setNotificationPanelViewController(mock(NotificationPanelViewController.class))
|
.setNotificationPanelViewController(
|
||||||
.setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class))
|
mock(NotificationPanelViewController.class))
|
||||||
.build();
|
.setActivityLaunchAnimator(mActivityLaunchAnimator)
|
||||||
|
.setNotificationAnimatorControllerProvider(
|
||||||
|
mock(NotificationLaunchAnimatorControllerProvider.class))
|
||||||
|
.build();
|
||||||
|
|
||||||
// set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
|
// set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
|
||||||
doAnswer(mCallOnDismiss).when(mActivityStarter).dismissKeyguardThenExecute(
|
doAnswer(mCallOnDismiss).when(mActivityStarter).dismissKeyguardThenExecute(
|
||||||
@@ -254,14 +259,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
|
|||||||
// Then
|
// Then
|
||||||
verify(mShadeController, atLeastOnce()).collapsePanel();
|
verify(mShadeController, atLeastOnce()).collapsePanel();
|
||||||
|
|
||||||
verify(mContentIntent).sendAndReturnResult(
|
verify(mActivityLaunchAnimator).startPendingIntentWithAnimation(eq(null), any());
|
||||||
any(Context.class),
|
|
||||||
anyInt() /* code */,
|
|
||||||
any() /* fillInIntent */,
|
|
||||||
any() /* PendingIntent.OnFinished */,
|
|
||||||
any() /* Handler */,
|
|
||||||
any() /* requiredPermission */,
|
|
||||||
any() /* Bundle options */);
|
|
||||||
|
|
||||||
verify(mAssistManager).hideAssist();
|
verify(mAssistManager).hideAssist();
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
|
|||||||
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
|
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
|
||||||
import com.android.systemui.statusbar.RemoteInputController;
|
import com.android.systemui.statusbar.RemoteInputController;
|
||||||
import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
||||||
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
|
|
||||||
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||||
@@ -125,7 +124,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
|
|||||||
mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class),
|
mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class),
|
||||||
notificationShadeWindowView, stackScrollLayoutController,
|
notificationShadeWindowView, stackScrollLayoutController,
|
||||||
mock(DozeScrimController.class), mock(ScrimController.class),
|
mock(DozeScrimController.class), mock(ScrimController.class),
|
||||||
mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
|
mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class),
|
||||||
mock(KeyguardStateController.class),
|
mock(KeyguardStateController.class),
|
||||||
mock(KeyguardIndicationController.class), mStatusBar,
|
mock(KeyguardIndicationController.class), mStatusBar,
|
||||||
mock(ShadeControllerImpl.class), mCommandQueue, mInitController,
|
mock(ShadeControllerImpl.class), mCommandQueue, mInitController,
|
||||||
|
|||||||
@@ -703,6 +703,10 @@ public class SmartReplyViewTest extends SysuiTestCase {
|
|||||||
// ============================= Smart Action tests ============================================
|
// ============================= Smart Action tests ============================================
|
||||||
// =============================================================================================
|
// =============================================================================================
|
||||||
|
|
||||||
|
private View anyView() {
|
||||||
|
return any();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTapSmartAction_waitsForKeyguard() throws InterruptedException {
|
public void testTapSmartAction_waitsForKeyguard() throws InterruptedException {
|
||||||
setSmartActions(TEST_ACTION_TITLES);
|
setSmartActions(TEST_ACTION_TITLES);
|
||||||
@@ -710,7 +714,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
|
|||||||
mView.getChildAt(2).performClick();
|
mView.getChildAt(2).performClick();
|
||||||
|
|
||||||
verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
|
verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
|
||||||
any());
|
anyView());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -721,7 +725,8 @@ public class SmartReplyViewTest extends SysuiTestCase {
|
|||||||
|
|
||||||
mView.getChildAt(2).performClick();
|
mView.getChildAt(2).performClick();
|
||||||
|
|
||||||
verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any(), any());
|
verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any(),
|
||||||
|
anyView());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -734,7 +739,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
|
|||||||
mView.getChildAt(2).performClick();
|
mView.getChildAt(2).performClick();
|
||||||
|
|
||||||
verify(mActivityStarter, times(1))
|
verify(mActivityStarter, times(1))
|
||||||
.startPendingIntentDismissingKeyguard(any(), any(), any());
|
.startPendingIntentDismissingKeyguard(any(), any(), anyView());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -746,7 +751,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
|
|||||||
mView.getChildAt(2).performClick();
|
mView.getChildAt(2).performClick();
|
||||||
|
|
||||||
verify(mActivityStarter, times(1))
|
verify(mActivityStarter, times(1))
|
||||||
.startPendingIntentDismissingKeyguard(any(), any(), any());
|
.startPendingIntentDismissingKeyguard(any(), any(), anyView());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user