Merge "Add an extra colored layer when animating launches (1/2)" into sc-dev am: d17c68d57c
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14679830 Change-Id: I2b50de4828b797dac706feb5d298030a11c76b59
This commit is contained in:
@@ -135,7 +135,7 @@ public class SplashscreenContentDrawer {
|
|||||||
com.android.wm.shell.R.dimen.starting_surface_exit_animation_window_shift_length);
|
com.android.wm.shell.R.dimen.starting_surface_exit_animation_window_shift_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getSystemBGColor() {
|
private static int getSystemBGColor() {
|
||||||
final Context systemContext = ActivityThread.currentApplication();
|
final Context systemContext = ActivityThread.currentApplication();
|
||||||
if (systemContext == null) {
|
if (systemContext == null) {
|
||||||
Slog.e(TAG, "System context does not exist!");
|
Slog.e(TAG, "System context does not exist!");
|
||||||
@@ -145,17 +145,18 @@ public class SplashscreenContentDrawer {
|
|||||||
return res.getColor(com.android.wm.shell.R.color.splash_window_background_default);
|
return res.getColor(com.android.wm.shell.R.color.splash_window_background_default);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable createDefaultBackgroundDrawable() {
|
private static Drawable createDefaultBackgroundDrawable() {
|
||||||
return new ColorDrawable(getSystemBGColor());
|
return new ColorDrawable(getSystemBGColor());
|
||||||
}
|
}
|
||||||
|
|
||||||
private @ColorInt int peekWindowBGColor(Context context) {
|
/** Extract the window background color from {@code attrs}. */
|
||||||
|
public static int peekWindowBGColor(Context context, SplashScreenWindowAttrs attrs) {
|
||||||
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "peekWindowBGColor");
|
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "peekWindowBGColor");
|
||||||
final Drawable themeBGDrawable;
|
final Drawable themeBGDrawable;
|
||||||
if (mTmpAttrs.mWindowBgColor != 0) {
|
if (attrs.mWindowBgColor != 0) {
|
||||||
themeBGDrawable = new ColorDrawable(mTmpAttrs.mWindowBgColor);
|
themeBGDrawable = new ColorDrawable(attrs.mWindowBgColor);
|
||||||
} else if (mTmpAttrs.mWindowBgResId != 0) {
|
} else if (attrs.mWindowBgResId != 0) {
|
||||||
themeBGDrawable = context.getDrawable(mTmpAttrs.mWindowBgResId);
|
themeBGDrawable = context.getDrawable(attrs.mWindowBgResId);
|
||||||
} else {
|
} else {
|
||||||
themeBGDrawable = createDefaultBackgroundDrawable();
|
themeBGDrawable = createDefaultBackgroundDrawable();
|
||||||
Slog.w(TAG, "Window background does not exist, using " + themeBGDrawable);
|
Slog.w(TAG, "Window background does not exist, using " + themeBGDrawable);
|
||||||
@@ -165,7 +166,7 @@ public class SplashscreenContentDrawer {
|
|||||||
return estimatedWindowBGColor;
|
return estimatedWindowBGColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int estimateWindowBGColor(Drawable themeBGDrawable) {
|
private static int estimateWindowBGColor(Drawable themeBGDrawable) {
|
||||||
final DrawableColorTester themeBGTester =
|
final DrawableColorTester themeBGTester =
|
||||||
new DrawableColorTester(themeBGDrawable, true /* filterTransparent */);
|
new DrawableColorTester(themeBGDrawable, true /* filterTransparent */);
|
||||||
if (themeBGTester.nonTransparentRatio() == 0) {
|
if (themeBGTester.nonTransparentRatio() == 0) {
|
||||||
@@ -183,7 +184,7 @@ public class SplashscreenContentDrawer {
|
|||||||
|
|
||||||
getWindowAttrs(context, mTmpAttrs);
|
getWindowAttrs(context, mTmpAttrs);
|
||||||
final StartingWindowViewBuilder builder = new StartingWindowViewBuilder();
|
final StartingWindowViewBuilder builder = new StartingWindowViewBuilder();
|
||||||
final int themeBGColor = peekWindowBGColor(context);
|
final int themeBGColor = peekWindowBGColor(context, this.mTmpAttrs);
|
||||||
// TODO (b/173975965) Tracking the performance on improved splash screen.
|
// TODO (b/173975965) Tracking the performance on improved splash screen.
|
||||||
return builder
|
return builder
|
||||||
.setContext(context)
|
.setContext(context)
|
||||||
@@ -193,7 +194,11 @@ public class SplashscreenContentDrawer {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void getWindowAttrs(Context context, SplashScreenWindowAttrs attrs) {
|
/**
|
||||||
|
* Get the {@link SplashScreenWindowAttrs} from {@code context} and fill them into
|
||||||
|
* {@code attrs}.
|
||||||
|
*/
|
||||||
|
public static void getWindowAttrs(Context context, SplashScreenWindowAttrs attrs) {
|
||||||
final TypedArray typedArray = context.obtainStyledAttributes(
|
final TypedArray typedArray = context.obtainStyledAttributes(
|
||||||
com.android.internal.R.styleable.Window);
|
com.android.internal.R.styleable.Window);
|
||||||
attrs.mWindowBgResId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
|
attrs.mWindowBgResId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
|
||||||
@@ -216,7 +221,8 @@ public class SplashscreenContentDrawer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SplashScreenWindowAttrs {
|
/** The configuration of the splash screen window. */
|
||||||
|
public static class SplashScreenWindowAttrs {
|
||||||
private int mWindowBgResId = 0;
|
private int mWindowBgResId = 0;
|
||||||
private int mWindowBgColor = Color.TRANSPARENT;
|
private int mWindowBgColor = Color.TRANSPARENT;
|
||||||
private Drawable mReplaceIcon = null;
|
private Drawable mReplaceIcon = null;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ android_library {
|
|||||||
|
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"PluginCoreLib",
|
"PluginCoreLib",
|
||||||
|
"WindowManager-Shell",
|
||||||
],
|
],
|
||||||
|
|
||||||
manifest: "AndroidManifest.xml",
|
manifest: "AndroidManifest.xml",
|
||||||
|
|||||||
@@ -5,13 +5,18 @@ import android.animation.AnimatorListenerAdapter
|
|||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
import android.app.ActivityTaskManager
|
import android.app.ActivityTaskManager
|
||||||
|
import android.app.AppGlobals
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Matrix
|
import android.graphics.Matrix
|
||||||
|
import android.graphics.PorterDuff
|
||||||
|
import android.graphics.PorterDuffXfermode
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
|
import android.graphics.drawable.GradientDrawable
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.os.RemoteException
|
import android.os.RemoteException
|
||||||
|
import android.os.UserHandle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.MathUtils
|
import android.util.MathUtils
|
||||||
import android.view.IRemoteAnimationFinishedCallback
|
import android.view.IRemoteAnimationFinishedCallback
|
||||||
@@ -26,6 +31,8 @@ import android.view.animation.AnimationUtils
|
|||||||
import android.view.animation.PathInterpolator
|
import android.view.animation.PathInterpolator
|
||||||
import com.android.internal.annotations.VisibleForTesting
|
import com.android.internal.annotations.VisibleForTesting
|
||||||
import com.android.internal.policy.ScreenDecorationsUtils
|
import com.android.internal.policy.ScreenDecorationsUtils
|
||||||
|
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer
|
||||||
|
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer.SplashScreenWindowAttrs
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,9 +47,9 @@ class ActivityLaunchAnimator(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val ANIMATION_DURATION = 500L
|
const val ANIMATION_DURATION = 500L
|
||||||
const val ANIMATION_DURATION_FADE_OUT_CONTENT = 183L
|
private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L
|
||||||
const val ANIMATION_DURATION_FADE_IN_WINDOW = 217L
|
private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L
|
||||||
const val ANIMATION_DELAY_FADE_IN_WINDOW = 167L
|
private const val ANIMATION_DELAY_FADE_IN_WINDOW = ANIMATION_DURATION_FADE_OUT_CONTENT
|
||||||
private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
|
private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
|
||||||
private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
|
private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
|
||||||
private const val ANIMATION_DELAY_NAV_FADE_IN =
|
private const val ANIMATION_DELAY_NAV_FADE_IN =
|
||||||
@@ -54,6 +61,8 @@ class ActivityLaunchAnimator(
|
|||||||
private val NAV_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0f, 1f)
|
private val NAV_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0f, 1f)
|
||||||
private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
|
private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
|
||||||
|
|
||||||
|
private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given the [linearProgress] of a launch animation, return the linear progress of the
|
* 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].
|
* sub-animation starting [delay] ms after the launch animation and that lasts [duration].
|
||||||
@@ -68,6 +77,8 @@ class ActivityLaunchAnimator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val packageManager = AppGlobals.getPackageManager()
|
||||||
|
|
||||||
/** The interpolator used for the width, height, Y position and corner radius. */
|
/** The interpolator used for the width, height, Y position and corner radius. */
|
||||||
private val animationInterpolator = AnimationUtils.loadInterpolator(context,
|
private val animationInterpolator = AnimationUtils.loadInterpolator(context,
|
||||||
R.interpolator.launch_animation_interpolator_y)
|
R.interpolator.launch_animation_interpolator_y)
|
||||||
@@ -76,6 +87,8 @@ class ActivityLaunchAnimator(
|
|||||||
private val animationInterpolatorX = AnimationUtils.loadInterpolator(context,
|
private val animationInterpolatorX = AnimationUtils.loadInterpolator(context,
|
||||||
R.interpolator.launch_animation_interpolator_x)
|
R.interpolator.launch_animation_interpolator_x)
|
||||||
|
|
||||||
|
private val cornerRadii = FloatArray(8)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start an intent and animate the opening window. The intent will be started by running
|
* 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
|
* [intentStarter], which should use the provided [RemoteAnimationAdapter] and return the launch
|
||||||
@@ -288,10 +301,7 @@ class ActivityLaunchAnimator(
|
|||||||
var right: Int,
|
var right: Int,
|
||||||
|
|
||||||
var topCornerRadius: Float = 0f,
|
var topCornerRadius: Float = 0f,
|
||||||
var bottomCornerRadius: Float = 0f,
|
var bottomCornerRadius: Float = 0f
|
||||||
|
|
||||||
var contentAlpha: Float = 1f,
|
|
||||||
var backgroundAlpha: Float = 1f
|
|
||||||
) {
|
) {
|
||||||
private val startTop = top
|
private val startTop = top
|
||||||
private val startBottom = bottom
|
private val startBottom = bottom
|
||||||
@@ -331,6 +341,9 @@ class ActivityLaunchAnimator(
|
|||||||
|
|
||||||
val centerY: Float
|
val centerY: Float
|
||||||
get() = top + height / 2f
|
get() = top + height / 2f
|
||||||
|
|
||||||
|
/** Whether the expanded view should be visible or hidden. */
|
||||||
|
var visible: Boolean = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -452,22 +465,39 @@ class ActivityLaunchAnimator(
|
|||||||
0f
|
0f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We add an extra layer with the same color as the app splash screen background color,
|
||||||
|
// which is usually the same color of the app background. We first fade in this layer
|
||||||
|
// to hide the expanding view, then we fade it out with SRC mode to draw a hole in the
|
||||||
|
// launch container and reveal the opening window.
|
||||||
|
val windowBackgroundColor = extractSplashScreenBackgroundColor(window)
|
||||||
|
val windowBackgroundLayer = GradientDrawable().apply {
|
||||||
|
setColor(windowBackgroundColor)
|
||||||
|
alpha = 0
|
||||||
|
}
|
||||||
|
|
||||||
// Update state.
|
// Update state.
|
||||||
val animator = ValueAnimator.ofFloat(0f, 1f)
|
val animator = ValueAnimator.ofFloat(0f, 1f)
|
||||||
this.animator = animator
|
this.animator = animator
|
||||||
animator.duration = ANIMATION_DURATION
|
animator.duration = ANIMATION_DURATION
|
||||||
animator.interpolator = Interpolators.LINEAR
|
animator.interpolator = Interpolators.LINEAR
|
||||||
|
|
||||||
|
val launchContainerOverlay = launchContainer.overlay
|
||||||
animator.addListener(object : AnimatorListenerAdapter() {
|
animator.addListener(object : AnimatorListenerAdapter() {
|
||||||
override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
|
override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
|
||||||
Log.d(TAG, "Animation started")
|
Log.d(TAG, "Animation started")
|
||||||
controller.onLaunchAnimationStart(isExpandingFullyAbove)
|
controller.onLaunchAnimationStart(isExpandingFullyAbove)
|
||||||
|
|
||||||
|
// Add the drawable to the launch container overlay. Overlays always draw
|
||||||
|
// drawables after views, so we know that it will be drawn above any view added
|
||||||
|
// by the controller.
|
||||||
|
launchContainerOverlay.add(windowBackgroundLayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAnimationEnd(animation: Animator?) {
|
override fun onAnimationEnd(animation: Animator?) {
|
||||||
Log.d(TAG, "Animation ended")
|
Log.d(TAG, "Animation ended")
|
||||||
iCallback?.invoke()
|
iCallback?.invoke()
|
||||||
controller.onLaunchAnimationEnd(isExpandingFullyAbove)
|
controller.onLaunchAnimationEnd(isExpandingFullyAbove)
|
||||||
|
launchContainerOverlay.remove(windowBackgroundLayer)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -491,24 +521,61 @@ class ActivityLaunchAnimator(
|
|||||||
state.bottomCornerRadius =
|
state.bottomCornerRadius =
|
||||||
MathUtils.lerp(startBottomCornerRadius, endRadius, progress)
|
MathUtils.lerp(startBottomCornerRadius, endRadius, progress)
|
||||||
|
|
||||||
val contentAlphaProgress = getProgress(linearProgress, 0,
|
// The expanding view can/should be hidden once it is completely coverred by the
|
||||||
ANIMATION_DURATION_FADE_OUT_CONTENT)
|
// windowBackgroundLayer.
|
||||||
state.contentAlpha =
|
state.visible =
|
||||||
1 - CONTENT_FADE_OUT_INTERPOLATOR.getInterpolation(contentAlphaProgress)
|
getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT) < 1
|
||||||
|
|
||||||
val backgroundAlphaProgress = getProgress(linearProgress,
|
|
||||||
ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW)
|
|
||||||
state.backgroundAlpha =
|
|
||||||
1 - WINDOW_FADE_IN_INTERPOLATOR.getInterpolation(backgroundAlphaProgress)
|
|
||||||
|
|
||||||
applyStateToWindow(window, state)
|
applyStateToWindow(window, state)
|
||||||
|
applyStateToWindowBackgroundLayer(windowBackgroundLayer, state, linearProgress)
|
||||||
navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
|
navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
|
||||||
|
|
||||||
|
// If we started expanding the view, we make it 1 pixel smaller on all sides to
|
||||||
|
// avoid artefacts on the corners caused by anti-aliasing of the view background and
|
||||||
|
// the window background layer.
|
||||||
|
if (state.top != startTop && state.left != startLeft &&
|
||||||
|
state.bottom != startBottom && state.right != startRight) {
|
||||||
|
state.top += 1
|
||||||
|
state.left += 1
|
||||||
|
state.right -= 1
|
||||||
|
state.bottom -= 1
|
||||||
|
}
|
||||||
controller.onLaunchAnimationProgress(state, progress, linearProgress)
|
controller.onLaunchAnimationProgress(state, progress, linearProgress)
|
||||||
}
|
}
|
||||||
|
|
||||||
animator.start()
|
animator.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Extract the background color of the app splash screen. */
|
||||||
|
private fun extractSplashScreenBackgroundColor(window: RemoteAnimationTarget): Int {
|
||||||
|
val taskInfo = window.taskInfo
|
||||||
|
val windowPackage = taskInfo.topActivity.packageName
|
||||||
|
val userId = taskInfo.userId
|
||||||
|
val windowContext = context.createPackageContextAsUser(
|
||||||
|
windowPackage, Context.CONTEXT_RESTRICTED, UserHandle.of(userId))
|
||||||
|
val activityInfo = taskInfo.topActivityInfo
|
||||||
|
val splashScreenThemeName = packageManager.getSplashScreenTheme(windowPackage, userId)
|
||||||
|
val splashScreenThemeId = if (splashScreenThemeName != null) {
|
||||||
|
windowContext.resources.getIdentifier(splashScreenThemeName, null, null)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
val themeResId = when {
|
||||||
|
splashScreenThemeId != 0 -> splashScreenThemeId
|
||||||
|
activityInfo.themeResource != 0 -> activityInfo.themeResource
|
||||||
|
else -> com.android.internal.R.style.Theme_DeviceDefault_DayNight
|
||||||
|
}
|
||||||
|
|
||||||
|
if (themeResId != windowContext.themeResId) {
|
||||||
|
windowContext.setTheme(themeResId)
|
||||||
|
}
|
||||||
|
|
||||||
|
val windowAttrs = SplashScreenWindowAttrs()
|
||||||
|
SplashscreenContentDrawer.getWindowAttrs(windowContext, windowAttrs)
|
||||||
|
return SplashscreenContentDrawer.peekWindowBGColor(windowContext, windowAttrs)
|
||||||
|
}
|
||||||
|
|
||||||
private fun applyStateToWindow(window: RemoteAnimationTarget, state: State) {
|
private fun applyStateToWindow(window: RemoteAnimationTarget, state: State) {
|
||||||
val screenBounds = window.screenSpaceBounds
|
val screenBounds = window.screenSpaceBounds
|
||||||
val centerX = (screenBounds.left + screenBounds.right) / 2f
|
val centerX = (screenBounds.left + screenBounds.right) / 2f
|
||||||
@@ -563,6 +630,41 @@ class ActivityLaunchAnimator(
|
|||||||
transactionApplier.scheduleApply(params)
|
transactionApplier.scheduleApply(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun applyStateToWindowBackgroundLayer(
|
||||||
|
drawable: GradientDrawable,
|
||||||
|
state: State,
|
||||||
|
linearProgress: Float
|
||||||
|
) {
|
||||||
|
// Update position.
|
||||||
|
drawable.setBounds(state.left, state.top, state.right, state.bottom)
|
||||||
|
|
||||||
|
// Update radius.
|
||||||
|
cornerRadii[0] = state.topCornerRadius
|
||||||
|
cornerRadii[1] = state.topCornerRadius
|
||||||
|
cornerRadii[2] = state.topCornerRadius
|
||||||
|
cornerRadii[3] = state.topCornerRadius
|
||||||
|
cornerRadii[4] = state.bottomCornerRadius
|
||||||
|
cornerRadii[5] = state.bottomCornerRadius
|
||||||
|
cornerRadii[6] = state.bottomCornerRadius
|
||||||
|
cornerRadii[7] = state.bottomCornerRadius
|
||||||
|
drawable.cornerRadii = cornerRadii
|
||||||
|
|
||||||
|
// We first fade in the background layer to hide the expanding view, then fade it out
|
||||||
|
// with SRC mode to draw a hole punch in the status bar and reveal the opening window.
|
||||||
|
val fadeInProgress = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT)
|
||||||
|
if (fadeInProgress < 1) {
|
||||||
|
val alpha = CONTENT_FADE_OUT_INTERPOLATOR.getInterpolation(fadeInProgress)
|
||||||
|
drawable.alpha = (alpha * 0xFF).roundToInt()
|
||||||
|
drawable.setXfermode(null)
|
||||||
|
} else {
|
||||||
|
val fadeOutProgress = getProgress(linearProgress,
|
||||||
|
ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW)
|
||||||
|
val alpha = 1 - WINDOW_FADE_IN_INTERPOLATOR.getInterpolation(fadeOutProgress)
|
||||||
|
drawable.alpha = (alpha * 0xFF).roundToInt()
|
||||||
|
drawable.setXfermode(SRC_MODE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun applyStateToNavigationBar(
|
private fun applyStateToNavigationBar(
|
||||||
navigationBar: RemoteAnimationTarget,
|
navigationBar: RemoteAnimationTarget,
|
||||||
state: State,
|
state: State,
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import android.graphics.Canvas
|
|||||||
import android.graphics.ColorFilter
|
import android.graphics.ColorFilter
|
||||||
import android.graphics.Matrix
|
import android.graphics.Matrix
|
||||||
import android.graphics.PixelFormat
|
import android.graphics.PixelFormat
|
||||||
import android.graphics.PorterDuff
|
|
||||||
import android.graphics.PorterDuffXfermode
|
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
@@ -111,9 +109,7 @@ open class GhostedViewLaunchAnimatorController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
|
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
|
||||||
backgroundView = FrameLayout(launchContainer.context).apply {
|
backgroundView = FrameLayout(launchContainer.context)
|
||||||
forceHasOverlappingRendering(false)
|
|
||||||
}
|
|
||||||
launchContainerOverlay.add(backgroundView)
|
launchContainerOverlay.add(backgroundView)
|
||||||
|
|
||||||
// We wrap the ghosted view background and use it to draw the expandable background. Its
|
// We wrap the ghosted view background and use it to draw the expandable background. Its
|
||||||
@@ -125,9 +121,7 @@ open class GhostedViewLaunchAnimatorController(
|
|||||||
|
|
||||||
// Create a ghost of the view that will be moving and fading out. This allows to fade out
|
// 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.
|
// the content before fading out the background.
|
||||||
ghostView = GhostView.addGhost(ghostedView, launchContainer).apply {
|
ghostView = GhostView.addGhost(ghostedView, launchContainer)
|
||||||
setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
|
val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
|
||||||
matrix.getValues(initialGhostViewMatrixValues)
|
matrix.getValues(initialGhostViewMatrixValues)
|
||||||
@@ -139,7 +133,18 @@ open class GhostedViewLaunchAnimatorController(
|
|||||||
linearProgress: Float
|
linearProgress: Float
|
||||||
) {
|
) {
|
||||||
val ghostView = this.ghostView!!
|
val ghostView = this.ghostView!!
|
||||||
ghostView.alpha = state.contentAlpha
|
val backgroundView = this.backgroundView!!
|
||||||
|
|
||||||
|
if (!state.visible) {
|
||||||
|
if (ghostView.visibility == View.VISIBLE) {
|
||||||
|
// Making the ghost view invisible will make the ghosted view visible, so order is
|
||||||
|
// important here.
|
||||||
|
ghostView.visibility = View.INVISIBLE
|
||||||
|
ghostedView.visibility = View.INVISIBLE
|
||||||
|
backgroundView.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val scale = min(state.widthRatio, state.heightRatio)
|
val scale = min(state.widthRatio, state.heightRatio)
|
||||||
ghostViewMatrix.setValues(initialGhostViewMatrixValues)
|
ghostViewMatrix.setValues(initialGhostViewMatrixValues)
|
||||||
@@ -150,14 +155,12 @@ open class GhostedViewLaunchAnimatorController(
|
|||||||
)
|
)
|
||||||
ghostView.animationMatrix = ghostViewMatrix
|
ghostView.animationMatrix = ghostViewMatrix
|
||||||
|
|
||||||
val backgroundView = this.backgroundView!!
|
|
||||||
backgroundView.top = state.top
|
backgroundView.top = state.top
|
||||||
backgroundView.bottom = state.bottom
|
backgroundView.bottom = state.bottom
|
||||||
backgroundView.left = state.left
|
backgroundView.left = state.left
|
||||||
backgroundView.right = state.right
|
backgroundView.right = state.right
|
||||||
|
|
||||||
val backgroundDrawable = backgroundDrawable!!
|
val backgroundDrawable = backgroundDrawable!!
|
||||||
backgroundDrawable.alpha = (0xFF * state.backgroundAlpha).toInt()
|
|
||||||
backgroundDrawable.wrapped?.let {
|
backgroundDrawable.wrapped?.let {
|
||||||
setBackgroundCornerRadius(it, state.topCornerRadius, state.bottomCornerRadius)
|
setBackgroundCornerRadius(it, state.topCornerRadius, state.bottomCornerRadius)
|
||||||
}
|
}
|
||||||
@@ -168,6 +171,7 @@ open class GhostedViewLaunchAnimatorController(
|
|||||||
|
|
||||||
GhostView.removeGhost(ghostedView)
|
GhostView.removeGhost(ghostedView)
|
||||||
launchContainerOverlay.remove(backgroundView)
|
launchContainerOverlay.remove(backgroundView)
|
||||||
|
ghostedView.visibility = View.VISIBLE
|
||||||
ghostedView.invalidate()
|
ghostedView.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,10 +207,6 @@ open class GhostedViewLaunchAnimatorController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class WrappedDrawable(val wrapped: Drawable?) : Drawable() {
|
private class WrappedDrawable(val wrapped: Drawable?) : Drawable() {
|
||||||
companion object {
|
|
||||||
private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var currentAlpha = 0xFF
|
private var currentAlpha = 0xFF
|
||||||
private var previousBounds = Rect()
|
private var previousBounds = Rect()
|
||||||
|
|
||||||
@@ -220,7 +220,6 @@ open class GhostedViewLaunchAnimatorController(
|
|||||||
|
|
||||||
wrapped.alpha = currentAlpha
|
wrapped.alpha = currentAlpha
|
||||||
wrapped.bounds = bounds
|
wrapped.bounds = bounds
|
||||||
setXfermode(wrapped, SRC_MODE)
|
|
||||||
applyBackgroundRadii()
|
applyBackgroundRadii()
|
||||||
|
|
||||||
wrapped.draw(canvas)
|
wrapped.draw(canvas)
|
||||||
@@ -230,7 +229,6 @@ open class GhostedViewLaunchAnimatorController(
|
|||||||
// background.
|
// background.
|
||||||
wrapped.alpha = 0
|
wrapped.alpha = 0
|
||||||
wrapped.bounds = previousBounds
|
wrapped.bounds = previousBounds
|
||||||
setXfermode(wrapped, null)
|
|
||||||
restoreBackgroundRadii()
|
restoreBackgroundRadii()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,27 +255,6 @@ open class GhostedViewLaunchAnimatorController(
|
|||||||
wrapped?.colorFilter = filter
|
wrapped?.colorFilter = filter
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setXfermode(background: Drawable, mode: PorterDuffXfermode?) {
|
|
||||||
if (background is InsetDrawable) {
|
|
||||||
background.drawable?.let { setXfermode(it, mode) }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (background !is LayerDrawable) {
|
|
||||||
background.setXfermode(mode)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// We set the xfermode on the first layer that is not a mask. Most of the time it will
|
|
||||||
// be the "background layer".
|
|
||||||
for (i in 0 until background.numberOfLayers) {
|
|
||||||
if (background.getId(i) != android.R.id.mask) {
|
|
||||||
background.getDrawable(i).setXfermode(mode)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setBackgroundRadius(topCornerRadius: Float, bottomCornerRadius: Float) {
|
fun setBackgroundRadius(topCornerRadius: Float, bottomCornerRadius: Float) {
|
||||||
updateRadii(cornerRadii, topCornerRadius, bottomCornerRadius)
|
updateRadii(cornerRadii, topCornerRadius, bottomCornerRadius)
|
||||||
invalidateSelf()
|
invalidateSelf()
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ import com.android.internal.widget.CachingIconView;
|
|||||||
import com.android.internal.widget.CallLayout;
|
import com.android.internal.widget.CallLayout;
|
||||||
import com.android.systemui.Dependency;
|
import com.android.systemui.Dependency;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.animation.ActivityLaunchAnimator;
|
|
||||||
import com.android.systemui.animation.Interpolators;
|
import com.android.systemui.animation.Interpolators;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
import com.android.systemui.plugins.FalsingManager;
|
import com.android.systemui.plugins.FalsingManager;
|
||||||
@@ -2020,6 +2019,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
if (params == null) {
|
if (params == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!params.getVisible()) {
|
||||||
|
if (getVisibility() == View.VISIBLE) {
|
||||||
|
setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
float zProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
|
float zProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
|
||||||
params.getProgress(0, 50));
|
params.getProgress(0, 50));
|
||||||
float translationZ = MathUtils.lerp(params.getStartTranslationZ(),
|
float translationZ = MathUtils.lerp(params.getStartTranslationZ(),
|
||||||
@@ -2077,10 +2084,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
contentView = mGuts;
|
contentView = mGuts;
|
||||||
}
|
}
|
||||||
if (expandAnimationRunning) {
|
if (expandAnimationRunning) {
|
||||||
contentView.animate()
|
|
||||||
.alpha(0f)
|
|
||||||
.setDuration(ActivityLaunchAnimator.ANIMATION_DURATION_FADE_OUT_CONTENT)
|
|
||||||
.setInterpolator(ActivityLaunchAnimator.CONTENT_FADE_OUT_INTERPOLATOR);
|
|
||||||
setAboveShelf(true);
|
setAboveShelf(true);
|
||||||
mExpandAnimationRunning = true;
|
mExpandAnimationRunning = true;
|
||||||
getViewState().cancelAnimations(this);
|
getViewState().cancelAnimations(this);
|
||||||
@@ -2088,6 +2091,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
} else {
|
} else {
|
||||||
mExpandAnimationRunning = false;
|
mExpandAnimationRunning = false;
|
||||||
setAboveShelf(isAboveShelf());
|
setAboveShelf(isAboveShelf());
|
||||||
|
setVisibility(View.VISIBLE);
|
||||||
if (mGuts != null) {
|
if (mGuts != null) {
|
||||||
mGuts.setAlpha(1.0f);
|
mGuts.setAlpha(1.0f);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import android.content.Context;
|
|||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.PorterDuffXfermode;
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.GradientDrawable;
|
import android.graphics.drawable.GradientDrawable;
|
||||||
import android.graphics.drawable.LayerDrawable;
|
import android.graphics.drawable.LayerDrawable;
|
||||||
@@ -30,8 +29,6 @@ import android.view.View;
|
|||||||
|
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.animation.ActivityLaunchAnimator;
|
|
||||||
import com.android.systemui.animation.Interpolators;
|
|
||||||
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
|
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -281,11 +278,6 @@ public class NotificationBackgroundView extends View {
|
|||||||
public void setExpandAnimationParams(ExpandAnimationParameters params) {
|
public void setExpandAnimationParams(ExpandAnimationParameters params) {
|
||||||
mActualHeight = params.getHeight();
|
mActualHeight = params.getHeight();
|
||||||
mActualWidth = params.getWidth();
|
mActualWidth = params.getWidth();
|
||||||
float alphaProgress = Interpolators.ALPHA_IN.getInterpolation(
|
|
||||||
params.getProgress(
|
|
||||||
ActivityLaunchAnimator.ANIMATION_DELAY_FADE_IN_WINDOW /* delay */,
|
|
||||||
ActivityLaunchAnimator.ANIMATION_DURATION_FADE_IN_WINDOW /* duration */));
|
|
||||||
mBackground.setAlpha((int) (mDrawableAlpha * (1.0f - alphaProgress)));
|
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,8 +286,6 @@ public class NotificationBackgroundView extends View {
|
|||||||
if (mBackground instanceof LayerDrawable) {
|
if (mBackground instanceof LayerDrawable) {
|
||||||
GradientDrawable gradientDrawable =
|
GradientDrawable gradientDrawable =
|
||||||
(GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
|
(GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
|
||||||
gradientDrawable.setXfermode(
|
|
||||||
running ? new PorterDuffXfermode(PorterDuff.Mode.SRC) : null);
|
|
||||||
// Speed optimization: disable AA if transfer mode is not SRC_OVER. AA is not easy to
|
// Speed optimization: disable AA if transfer mode is not SRC_OVER. AA is not easy to
|
||||||
// spot during animation anyways.
|
// spot during animation anyways.
|
||||||
gradientDrawable.setAntiAlias(!running);
|
gradientDrawable.setAntiAlias(!running);
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package com.android.systemui.animation
|
|||||||
|
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
import android.app.WindowConfiguration
|
import android.app.WindowConfiguration
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.pm.ActivityInfo
|
||||||
|
import android.content.pm.ApplicationInfo
|
||||||
import android.graphics.Point
|
import android.graphics.Point
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
@@ -167,10 +170,16 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
private fun fakeWindow(): RemoteAnimationTarget {
|
private fun fakeWindow(): RemoteAnimationTarget {
|
||||||
val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */)
|
val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */)
|
||||||
|
val taskInfo = ActivityManager.RunningTaskInfo()
|
||||||
|
taskInfo.topActivity = ComponentName("com.android.systemui", "FakeActivity")
|
||||||
|
taskInfo.topActivityInfo = ActivityInfo().apply {
|
||||||
|
applicationInfo = ApplicationInfo()
|
||||||
|
}
|
||||||
|
|
||||||
return RemoteAnimationTarget(
|
return RemoteAnimationTarget(
|
||||||
0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
|
0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
|
||||||
Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(),
|
Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(),
|
||||||
ActivityManager.RunningTaskInfo()
|
taskInfo
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user