Merge changes from topic "qs-animation" into sc-dev

* changes:
  Animate QuickSettings long press (1/2).
  Add generic support for corner radius in launch animations.
  Animate the media activity launch.
This commit is contained in:
Jordan Demeulenaere
2021-04-07 14:53:19 +00:00
committed by Android (Google) Code Review
15 changed files with 370 additions and 86 deletions

View File

@@ -63,6 +63,8 @@ public interface ActivityStarter {
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
void startActivity(Intent intent, boolean dismissShade, Callback callback);
void postStartActivityDismissingKeyguard(Intent intent, int delay);
void postStartActivityDismissingKeyguard(Intent intent, int delay,
@Nullable ActivityLaunchAnimator.Controller animationController);
void postStartActivityDismissingKeyguard(PendingIntent intent);
/**

View File

@@ -35,12 +35,12 @@ class ActivityLaunchAnimator {
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
private const val LAUNCH_TIMEOUT = 1000L
// 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 ANIMATION_INTERPOLATOR = PathInterpolator(0.4f, 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)

View File

@@ -7,6 +7,8 @@ import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.LayerDrawable
import android.view.GhostView
import android.view.View
import android.view.ViewGroup
@@ -66,15 +68,28 @@ open class GhostedViewLaunchAnimatorController(
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.
// By default, we rely on WrappedDrawable to set/restore the background radii before/after
// each draw.
backgroundDrawable?.setBackgroundRadius(topCornerRadius, bottomCornerRadius)
}
/** Return the current top corner radius of the background. */
protected open fun getCurrentTopCornerRadius(): Float = 0f
protected open fun getCurrentTopCornerRadius(): Float {
val drawable = getBackground() ?: return 0f
val gradient = findGradientDrawable(drawable) ?: return 0f
// TODO(b/184121838): Support more than symmetric top & bottom radius.
return gradient.cornerRadii?.get(CORNER_RADIUS_TOP_INDEX) ?: gradient.cornerRadius
}
/** Return the current bottom corner radius of the background. */
protected open fun getCurrentBottomCornerRadius(): Float = 0f
protected open fun getCurrentBottomCornerRadius(): Float {
val drawable = getBackground() ?: return 0f
val gradient = findGradientDrawable(drawable) ?: return 0f
// TODO(b/184121838): Support more than symmetric top & bottom radius.
return gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius
}
override fun getRootView(): View {
return rootView
@@ -94,7 +109,7 @@ open class GhostedViewLaunchAnimatorController(
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
backgroundView = FrameLayout(rootView.context).apply {
forceHasOverlappingRendering(true)
forceHasOverlappingRendering(false)
}
rootViewOverlay.add(backgroundView)
@@ -143,6 +158,33 @@ open class GhostedViewLaunchAnimatorController(
ghostedView.invalidate()
}
companion object {
private const val CORNER_RADIUS_TOP_INDEX = 0
private const val CORNER_RADIUS_BOTTOM_INDEX = 4
/**
* Return the first [GradientDrawable] found in [drawable], or null if none is found. If
* [drawable] is a [LayerDrawable], this will return the first layer that is a
* [GradientDrawable].
*/
private fun findGradientDrawable(drawable: Drawable): GradientDrawable? {
if (drawable is GradientDrawable) {
return drawable
}
if (drawable is LayerDrawable) {
for (i in 0 until drawable.numberOfLayers) {
val maybeGradient = drawable.getDrawable(i)
if (maybeGradient is GradientDrawable) {
return maybeGradient
}
}
}
return null
}
}
private class WrappedDrawable(val wrapped: Drawable?) : Drawable() {
companion object {
private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
@@ -151,6 +193,9 @@ open class GhostedViewLaunchAnimatorController(
private var currentAlpha = 0xFF
private var previousBounds = Rect()
private var cornerRadii = FloatArray(8) { -1f }
private var previousCornerRadii = FloatArray(8)
override fun draw(canvas: Canvas) {
val wrapped = this.wrapped ?: return
@@ -158,7 +203,8 @@ open class GhostedViewLaunchAnimatorController(
wrapped.alpha = currentAlpha
wrapped.bounds = bounds
wrapped.setXfermode(SRC_MODE)
setXfermode(wrapped, SRC_MODE)
applyBackgroundRadii()
wrapped.draw(canvas)
@@ -167,7 +213,8 @@ open class GhostedViewLaunchAnimatorController(
// background.
wrapped.alpha = 0
wrapped.bounds = previousBounds
wrapped.setXfermode(null)
setXfermode(wrapped, null)
restoreBackgroundRadii()
}
override fun setAlpha(alpha: Int) {
@@ -192,5 +239,91 @@ open class GhostedViewLaunchAnimatorController(
override fun setColorFilter(filter: ColorFilter?) {
wrapped?.colorFilter = filter
}
private fun setXfermode(background: Drawable, mode: PorterDuffXfermode?) {
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) {
updateRadii(cornerRadii, topCornerRadius, bottomCornerRadius)
invalidateSelf()
}
private fun updateRadii(
radii: FloatArray,
topCornerRadius: Float,
bottomCornerRadius: Float
) {
radii[0] = topCornerRadius
radii[1] = topCornerRadius
radii[2] = topCornerRadius
radii[3] = topCornerRadius
radii[4] = bottomCornerRadius
radii[5] = bottomCornerRadius
radii[6] = bottomCornerRadius
radii[7] = bottomCornerRadius
}
private fun applyBackgroundRadii() {
if (cornerRadii[0] < 0 || wrapped == null) {
return
}
savePreviousBackgroundRadii(wrapped)
applyBackgroundRadii(wrapped, cornerRadii)
}
private fun savePreviousBackgroundRadii(background: Drawable) {
// TODO(b/184121838): This method assumes that all GradientDrawable in background will
// have the same radius. Should we save/restore the radii for each layer instead?
val gradient = findGradientDrawable(background) ?: return
// TODO(b/184121838): GradientDrawable#getCornerRadii clones its radii array. Should we
// try to avoid that?
val radii = gradient.cornerRadii
if (radii != null) {
radii.copyInto(previousCornerRadii)
} else {
// Copy the cornerRadius into previousCornerRadii.
val radius = gradient.cornerRadius
updateRadii(previousCornerRadii, radius, radius)
}
}
private fun applyBackgroundRadii(drawable: Drawable, radii: FloatArray) {
if (drawable is GradientDrawable) {
drawable.cornerRadii = radii
return
}
if (drawable !is LayerDrawable) {
return
}
for (i in 0 until drawable.numberOfLayers) {
(drawable.getDrawable(i) as? GradientDrawable)?.cornerRadii = radii
}
}
private fun restoreBackgroundRadii() {
if (cornerRadii[0] < 0 || wrapped == null) {
return
}
applyBackgroundRadii(wrapped, previousCornerRadii)
}
}
}

View File

@@ -15,10 +15,12 @@
package com.android.systemui.plugins.qs;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.metrics.LogMaker;
import android.service.quicksettings.Tile;
import android.view.View;
import com.android.internal.logging.InstanceId;
import com.android.systemui.plugins.annotations.DependsOn;
@@ -53,10 +55,16 @@ public interface QSTile {
void removeCallbacks();
QSIconView createTileView(Context context);
void click();
void secondaryClick();
void longClick();
/**
* The tile was long clicked.
*
* @param view The view that was clicked.
*/
void longClick(@Nullable View view);
void userSwitch(int currentUser);
int getMetricsCategory();

View File

@@ -18,6 +18,8 @@ import android.app.PendingIntent;
import android.content.Intent;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
@@ -106,6 +108,14 @@ public class ActivityStarterDelegate implements ActivityStarter {
starter -> starter.get().postStartActivityDismissingKeyguard(intent, delay));
}
@Override
public void postStartActivityDismissingKeyguard(Intent intent, int delay,
@Nullable ActivityLaunchAnimator.Controller animationController) {
mActualStarter.ifPresent(
starter -> starter.get().postStartActivityDismissingKeyguard(intent, delay,
animationController));
}
@Override
public void postStartActivityDismissingKeyguard(PendingIntent intent) {
mActualStarter.ifPresent(

View File

@@ -28,6 +28,7 @@ import android.graphics.ColorFilter
import android.graphics.Outline
import android.graphics.Paint
import android.graphics.PixelFormat
import android.graphics.Xfermode
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.util.MathUtils
@@ -48,7 +49,15 @@ private const val BACKGROUND_ANIM_DURATION = 370L
class IlluminationDrawable : Drawable() {
private var themeAttrs: IntArray? = null
private var cornerRadius = 0f
private var cornerRadiusOverride = -1f
var cornerRadius = 0f
get() {
return if (cornerRadiusOverride >= 0) {
cornerRadiusOverride
} else {
field
}
}
private var highlightColor = Color.TRANSPARENT
private var tmpHsl = floatArrayOf(0f, 0f, 0f)
private var paint = Paint()
@@ -122,8 +131,28 @@ class IlluminationDrawable : Drawable() {
throw UnsupportedOperationException("Color filters are not supported")
}
override fun setAlpha(value: Int) {
throw UnsupportedOperationException("Alpha is not supported")
override fun setAlpha(alpha: Int) {
if (alpha == paint.alpha) {
return
}
paint.alpha = alpha
invalidateSelf()
lightSources.forEach { it.alpha = alpha }
}
override fun getAlpha(): Int {
return paint.alpha
}
override fun setXfermode(mode: Xfermode?) {
if (mode == paint.xfermode) {
return
}
paint.xfermode = mode
invalidateSelf()
}
/**
@@ -171,9 +200,19 @@ class IlluminationDrawable : Drawable() {
fun registerLightSource(lightSource: View) {
if (lightSource.background is LightSourceDrawable) {
lightSources.add(lightSource.background as LightSourceDrawable)
registerLightSource(lightSource.background as LightSourceDrawable)
} else if (lightSource.foreground is LightSourceDrawable) {
lightSources.add(lightSource.foreground as LightSourceDrawable)
registerLightSource(lightSource.foreground as LightSourceDrawable)
}
}
private fun registerLightSource(lightSource: LightSourceDrawable) {
lightSource.alpha = paint.alpha
lightSources.add(lightSource)
}
/** Set or remove the corner radius override. This is typically set during animations. */
fun setCornerRadiusOverride(cornerRadius: Float?) {
cornerRadiusOverride = cornerRadius ?: -1f
}
}

View File

@@ -184,8 +184,13 @@ class LightSourceDrawable : Drawable() {
throw UnsupportedOperationException("Color filters are not supported")
}
override fun setAlpha(value: Int) {
throw UnsupportedOperationException("Alpha is not supported")
override fun setAlpha(alpha: Int) {
if (alpha == paint.alpha) {
return
}
paint.alpha = alpha
invalidateSelf()
}
/**

View File

@@ -49,6 +49,8 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
import com.android.systemui.plugins.animation.GhostedViewLaunchAnimatorController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.util.animation.TransitionLayout;
@@ -101,11 +103,12 @@ public class MediaControlPanel {
// This will provide the corners for the album art.
private final ViewOutlineProvider mViewOutlineProvider;
private final MediaOutputDialogFactory mMediaOutputDialogFactory;
/**
* Initialize a new control panel
* @param context
*
* @param backgroundExecutor background executor, used for processing artwork
* @param activityStarter activity starter
* @param activityStarter activity starter
*/
@Inject
public MediaControlPanel(Context context, @Background Executor backgroundExecutor,
@@ -147,6 +150,7 @@ public class MediaControlPanel {
/**
* Get the view holder used to display media controls
*
* @return the view holder
*/
@Nullable
@@ -156,6 +160,7 @@ public class MediaControlPanel {
/**
* Get the view controller used to display media controls
*
* @return the media view controller
*/
@NonNull
@@ -165,7 +170,7 @@ public class MediaControlPanel {
/**
* Sets the listening state of the player.
*
* <p>
* Should be set to true when the QS panel is open. Otherwise, false. This is a signal to avoid
* unnecessary work when the QS panel is closed.
*
@@ -177,6 +182,7 @@ public class MediaControlPanel {
/**
* Get the context
*
* @return context
*/
public Context getContext() {
@@ -244,7 +250,8 @@ public class MediaControlPanel {
if (clickIntent != null) {
mViewHolder.getPlayer().setOnClickListener(v -> {
if (mMediaViewController.isGutsVisible()) return;
mActivityStarter.postStartActivityDismissingKeyguard(clickIntent);
mActivityStarter.postStartActivityDismissingKeyguard(clickIntent,
buildLaunchAnimatorController(mViewHolder.getPlayer()));
});
}
@@ -396,8 +403,42 @@ public class MediaControlPanel {
mMediaViewController.refreshState();
}
@Nullable
private ActivityLaunchAnimator.Controller buildLaunchAnimatorController(
TransitionLayout player) {
// TODO(b/174236650): Make sure that the carousel indicator also fades out.
// TODO(b/174236650): Instrument the animation to measure jank.
return new GhostedViewLaunchAnimatorController(player) {
@Override
protected float getCurrentTopCornerRadius() {
return ((IlluminationDrawable) player.getBackground()).getCornerRadius();
}
@Override
protected float getCurrentBottomCornerRadius() {
// TODO(b/184121838): Make IlluminationDrawable support top and bottom radius.
return getCurrentTopCornerRadius();
}
@Override
protected void setBackgroundCornerRadius(Drawable background, float topCornerRadius,
float bottomCornerRadius) {
// TODO(b/184121838): Make IlluminationDrawable support top and bottom radius.
float radius = Math.min(topCornerRadius, bottomCornerRadius);
((IlluminationDrawable) background).setCornerRadiusOverride(radius);
}
@Override
public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) {
super.onLaunchAnimationEnd(isExpandingFullyAbove);
((IlluminationDrawable) player.getBackground()).setCornerRadiusOverride(null);
}
};
}
/**
* Close the guts for this player.
*
* @param immediate {@code true} if it should be closed without animation
*/
public void closeGuts(boolean immediate) {
@@ -427,7 +468,7 @@ public class MediaControlPanel {
if (bounds.width() > mAlbumArtSize || bounds.height() > mAlbumArtSize) {
float offsetX = (bounds.width() - mAlbumArtSize) / 2.0f;
float offsetY = (bounds.height() - mAlbumArtSize) / 2.0f;
bounds.offset((int) -offsetX,(int) -offsetY);
bounds.offset((int) -offsetX, (int) -offsetY);
}
drawable.setBounds(bounds);
return drawable;
@@ -435,6 +476,7 @@ public class MediaControlPanel {
/**
* Get the current media controller
*
* @return the controller
*/
public MediaController getController() {
@@ -443,6 +485,7 @@ public class MediaControlPanel {
/**
* Check whether the media controlled by this player is currently playing
*
* @return whether it is playing, or false if no controller information
*/
public boolean isPlaying() {
@@ -451,6 +494,7 @@ public class MediaControlPanel {
/**
* Check whether the given controller is currently playing
*
* @param controller media controller to check
* @return whether it is playing, or false if no controller information
*/
@@ -468,7 +512,7 @@ public class MediaControlPanel {
}
private void setVisibleAndAlpha(ConstraintSet set, int actionId, boolean visible) {
set.setVisibility(actionId, visible? ConstraintSet.VISIBLE : ConstraintSet.GONE);
set.setVisibility(actionId, visible ? ConstraintSet.VISIBLE : ConstraintSet.GONE);
set.setAlpha(actionId, visible ? 1.0f : 0.0f);
}
}

View File

@@ -173,7 +173,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
@Override
public void init(QSTile tile) {
init(v -> tile.click(), v -> tile.secondaryClick(), view -> {
tile.longClick();
tile.longClick(this);
return true;
});
}

View File

@@ -31,6 +31,7 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
@@ -43,6 +44,7 @@ import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
@@ -58,6 +60,7 @@ import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.plugins.ActivityStarter;
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.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
@@ -292,14 +295,15 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
}
public void longClick() {
@Override
public void longClick(@Nullable View view) {
mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)
.addTaggedData(FIELD_STATUS_BAR_STATE,
mStatusBarStateController.getState())));
mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_LONG_PRESS, 0, getMetricsSpec(),
getInstanceId());
mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
mHandler.sendEmptyMessage(H.LONG_CLICK);
mHandler.obtainMessage(H.LONG_CLICK, view).sendToTarget();
}
public LogMaker populate(LogMaker logMaker) {
@@ -374,10 +378,15 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
/**
* Handles long click on the tile by launching the {@link Intent} defined in
* {@link QSTileImpl#getLongClickIntent}
* {@link QSTileImpl#getLongClickIntent}.
*
* @param view The view from which the opening window will be animated.
*/
protected void handleLongClick() {
mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0);
protected void handleLongClick(@Nullable View view) {
ActivityLaunchAnimator.Controller animationController =
view != null ? ActivityLaunchAnimator.Controller.fromView(view) : null;
mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0,
animationController);
}
/**
@@ -614,7 +623,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
handleSecondaryClick();
} else if (msg.what == LONG_CLICK) {
name = "handleLongClick";
handleLongClick();
handleLongClick((View) msg.obj);
} else if (msg.what == REFRESH_STATE) {
name = "handleRefreshState";
handleRefreshState(msg.obj);

View File

@@ -142,7 +142,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
}
@Override
protected void handleLongClick() {
protected void handleLongClick(View view) {
handleClick();
}

View File

@@ -22,6 +22,7 @@ import android.os.Handler;
import android.os.Looper;
import android.provider.MediaStore;
import android.service.quicksettings.Tile;
import android.view.View;
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
@@ -107,7 +108,7 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements
}
@Override
protected void handleLongClick() {
protected void handleLongClick(View view) {
handleClick();
}

View File

@@ -1802,7 +1802,8 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
startActivityDismissingKeyguard(intent, false, dismissShade,
false /* disallowEnterPictureInPictureWhileLaunching */, callback, 0);
false /* disallowEnterPictureInPictureWhileLaunching */, callback, 0,
null /* animationController */);
}
public void setQsExpanded(boolean expanded) {
@@ -2025,7 +2026,7 @@ public class StatusBar extends SystemUI implements DemoMode,
/** Whether we should animate an activity launch. */
public boolean areLaunchAnimationsEnabled() {
// TODO(b/184121838): Support lock screen launch animations.
return mState == StatusBarState.SHADE;
return mState == StatusBarState.SHADE && !isOccluded();
}
public boolean isDeviceInVrMode() {
@@ -2729,7 +2730,7 @@ public class StatusBar extends SystemUI implements DemoMode,
boolean dismissShade, int flags) {
startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade,
false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
flags);
flags, null /* animationController */);
}
public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
@@ -2737,55 +2738,75 @@ public class StatusBar extends SystemUI implements DemoMode,
startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, 0);
}
public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
private void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching,
final Callback callback, int flags) {
final Callback callback, int flags,
@Nullable ActivityLaunchAnimator.Controller animationController) {
if (onlyProvisioned && !mDeviceProvisionedController.isDeviceProvisioned()) return;
final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity(
intent, mLockscreenUserManager.getCurrentUserId());
ActivityLaunchAnimator.Controller animController = null;
if (animationController != null && areLaunchAnimationsEnabled()) {
animController = dismissShade ? new StatusBarLaunchAnimatorController(
animationController, this, true /* isLaunchForActivity */)
: animationController;
}
final ActivityLaunchAnimator.Controller animCallbackForLambda = animController;
// If we animate, we will dismiss the shade only once the animation is done. This is taken
// care of by the StatusBarLaunchAnimationController.
boolean dismissShadeDirectly = dismissShade && animController == null;
Runnable runnable = () -> {
mAssistManagerLazy.get().hideAssist();
intent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(flags);
int result = ActivityManager.START_CANCELED;
ActivityOptions options = new ActivityOptions(getActivityOptions(mDisplayId,
null /* remoteAnimation */));
options.setDisallowEnterPictureInPictureWhileLaunching(
disallowEnterPictureInPictureWhileLaunching);
if (CameraIntents.isInsecureCameraIntent(intent)) {
// Normally an activity will set it's requested rotation
// animation on its window. However when launching an activity
// causes the orientation to change this is too late. In these cases
// the default animation is used. This doesn't look good for
// the camera (as it rotates the camera contents out of sync
// with physical reality). So, we ask the WindowManager to
// force the crossfade animation if an orientation change
// happens to occur during the launch.
options.setRotationAnimationHint(
WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
}
if (intent.getAction() == Settings.Panel.ACTION_VOLUME) {
// Settings Panel is implemented as activity(not a dialog), so
// underlying app is paused and may enter picture-in-picture mode
// as a result.
// So we need to disable picture-in-picture mode here
// if it is volume panel.
options.setDisallowEnterPictureInPictureWhileLaunching(true);
}
try {
result = ActivityTaskManager.getService().startActivityAsUser(
null, mContext.getBasePackageName(), mContext.getAttributionTag(),
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
options.toBundle(), UserHandle.CURRENT.getIdentifier());
} catch (RemoteException e) {
Log.w(TAG, "Unable to start activity", e);
}
int[] result = new int[] { ActivityManager.START_CANCELED };
mActivityLaunchAnimator.startIntentWithAnimation(animCallbackForLambda, (adapter) -> {
ActivityOptions options = new ActivityOptions(
getActivityOptions(mDisplayId, adapter));
options.setDisallowEnterPictureInPictureWhileLaunching(
disallowEnterPictureInPictureWhileLaunching);
if (CameraIntents.isInsecureCameraIntent(intent)) {
// Normally an activity will set it's requested rotation
// animation on its window. However when launching an activity
// causes the orientation to change this is too late. In these cases
// the default animation is used. This doesn't look good for
// the camera (as it rotates the camera contents out of sync
// with physical reality). So, we ask the WindowManager to
// force the crossfade animation if an orientation change
// happens to occur during the launch.
options.setRotationAnimationHint(
WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
}
if (intent.getAction() == Settings.Panel.ACTION_VOLUME) {
// Settings Panel is implemented as activity(not a dialog), so
// underlying app is paused and may enter picture-in-picture mode
// as a result.
// So we need to disable picture-in-picture mode here
// if it is volume panel.
options.setDisallowEnterPictureInPictureWhileLaunching(true);
}
try {
result[0] = ActivityTaskManager.getService().startActivityAsUser(
null, mContext.getBasePackageName(), mContext.getAttributionTag(),
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
options.toBundle(), UserHandle.CURRENT.getIdentifier());
} catch (RemoteException e) {
Log.w(TAG, "Unable to start activity", e);
}
return result[0];
});
if (callback != null) {
callback.onActivityStarted(result);
callback.onActivityStarted(result[0]);
}
};
Runnable cancelRunnable = () -> {
@@ -2793,7 +2814,7 @@ public class StatusBar extends SystemUI implements DemoMode,
callback.onActivityStarted(ActivityManager.START_CANCELED);
}
};
executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade,
executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly,
afterKeyguardGone, true /* deferred */);
}
@@ -3151,12 +3172,21 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void postStartActivityDismissingKeyguard(final Intent intent, int delay) {
mHandler.postDelayed(() ->
handleStartActivityDismissingKeyguard(intent, true /*onlyProvisioned*/), delay);
postStartActivityDismissingKeyguard(intent, delay, null /* animationController */);
}
private void handleStartActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) {
startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
@Override
public void postStartActivityDismissingKeyguard(Intent intent, int delay,
@Nullable ActivityLaunchAnimator.Controller animationController) {
mHandler.postDelayed(
() ->
startActivityDismissingKeyguard(intent, true /* onlyProvisioned */,
true /* dismissShade */,
false /* disallowEnterPictureInPictureWhileLaunching */,
null /* callback */,
0 /* flags */,
animationController),
delay);
}
@Override
@@ -4071,7 +4101,8 @@ public class StatusBar extends SystemUI implements DemoMode,
final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
startActivityDismissingKeyguard(cameraIntent,
false /* onlyProvisioned */, true /* dismissShade */,
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0);
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
null /* animationController */);
} else {
if (!mDeviceInteractive) {
// Avoid flickering of the scrim when we instant launch the camera and the bouncer
@@ -4122,7 +4153,8 @@ public class StatusBar extends SystemUI implements DemoMode,
if (!mStatusBarKeyguardViewManager.isShowing()) {
startActivityDismissingKeyguard(emergencyIntent,
false /* onlyProvisioned */, true /* dismissShade */,
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0);
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
null /* animationController */);
return;
}
@@ -4504,8 +4536,7 @@ public class StatusBar extends SystemUI implements DemoMode,
&& mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
boolean animate =
animationController != null && areLaunchAnimationsEnabled() && !isOccluded();
boolean animate = animationController != null && areLaunchAnimationsEnabled();
boolean collapse = !animate;
executeActionDismissingKeyguard(() -> {
try {

View File

@@ -34,6 +34,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.Manifest;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -46,6 +47,7 @@ import android.testing.TestableLooper;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.FeatureFlagUtils;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -418,7 +420,7 @@ public class TileQueryHelperTest extends SysuiTestCase {
public void secondaryClick() {}
@Override
public void longClick() {}
public void longClick(@Nullable View view) {}
@Override
public void userSwitch(int currentUser) {}

View File

@@ -189,7 +189,7 @@ public class QSTileImplTest extends SysuiTestCase {
@Test
public void testLongClick_Metrics() {
mTile.longClick();
mTile.longClick(null /* view */);
verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS)));
assertEquals(1, mUiEventLoggerFake.numLogs());
UiEventLoggerFake.FakeUiEvent event = mUiEventLoggerFake.get(0);
@@ -201,7 +201,7 @@ public class QSTileImplTest extends SysuiTestCase {
public void testLongClick_log() {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
mTile.longClick();
mTile.longClick(null /* view */);
verify(mQsLogger).logTileLongClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
}