diff options
| author | 2018-01-25 16:05:15 +0000 | |
|---|---|---|
| committer | 2018-01-25 16:05:15 +0000 | |
| commit | d41477f494ab03e5f8837effd681a77ad9a2c5db (patch) | |
| tree | d34777c73cc5d5d67e1a7d607f545af1e76a571b | |
| parent | 8e725bbad2780e74c7c974f9513efb71c6ecbab4 (diff) | |
| parent | de4de0e204a028f8f8608d7fd649fe1a60784c4e (diff) | |
Merge changes from topic "notification_launch"
* changes:
Added the reply draft as an extra to the content intent
Launching notification settings correctly inline
Launching Notification animations inline
29 files changed, 710 insertions, 74 deletions
diff --git a/api/current.txt b/api/current.txt index d10f0784c0e3..a9bc98054903 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5227,6 +5227,7 @@ package android.app { field public static final java.lang.String EXTRA_PROGRESS = "android.progress"; field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate"; field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax"; + field public static final java.lang.String EXTRA_REMOTE_INPUT_DRAFT = "android.remoteInputDraft"; field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory"; field public static final deprecated java.lang.String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName"; field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer"; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 4914ffaf8912..ea940426f5a5 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -913,14 +913,14 @@ class ContextImpl extends Context { /** @hide */ @Override - public void startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) { + public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) { if ((intents[0].getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { throw new AndroidRuntimeException( "Calling startActivities() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag on first Intent." + " Is this really what you want?"); } - mMainThread.getInstrumentation().execStartActivitiesAsUser( + return mMainThread.getInstrumentation().execStartActivitiesAsUser( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intents, options, userHandle.getIdentifier()); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 3c38a4ec5fe4..f90b276ac17f 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1688,9 +1688,13 @@ public class Instrumentation { * {@link ActivityMonitor} objects only match against the first activity in * the array. * + * @return The corresponding flag {@link ActivityManager#START_CANCELED}, + * {@link ActivityManager#START_SUCCESS} etc. indicating whether the launch was + * successful. + * * {@hide} */ - public void execStartActivitiesAsUser(Context who, IBinder contextThread, + public int execStartActivitiesAsUser(Context who, IBinder contextThread, IBinder token, Activity target, Intent[] intents, Bundle options, int userId) { IApplicationThread whoThread = (IApplicationThread) contextThread; @@ -1705,11 +1709,11 @@ public class Instrumentation { } if (result != null) { am.mHits++; - return; + return ActivityManager.START_CANCELED; } else if (am.match(who, null, intents[0])) { am.mHits++; if (am.isBlocking()) { - return; + return ActivityManager.START_CANCELED; } break; } @@ -1727,6 +1731,7 @@ public class Instrumentation { .startActivities(whoThread, who.getBasePackageName(), intents, resolvedTypes, token, options, userId); checkStartActivityResult(result, intents[0]); + return result; } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index d6fddfca986e..0b5b363ddecd 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -200,6 +200,16 @@ public class Notification implements Parcelable */ private static final int MAX_REPLY_HISTORY = 5; + + /** + * If the notification contained an unsent draft for a RemoteInput when the user clicked on it, + * we're adding the draft as a String extra to the {@link #contentIntent} using this key. + * + * <p>Apps may use this extra to prepopulate text fields in the app, where the user usually + * sends messages.</p> + */ + public static final String EXTRA_REMOTE_INPUT_DRAFT = "android.remoteInputDraft"; + /** * A timestamp related to this notification, in milliseconds since the epoch. * diff --git a/core/java/android/app/TaskStackBuilder.java b/core/java/android/app/TaskStackBuilder.java index bab993f855e9..ab5974777c9e 100644 --- a/core/java/android/app/TaskStackBuilder.java +++ b/core/java/android/app/TaskStackBuilder.java @@ -213,13 +213,13 @@ public class TaskStackBuilder { * Start the task stack constructed by this builder. * @hide */ - public void startActivities(Bundle options, UserHandle userHandle) { + public int startActivities(Bundle options, UserHandle userHandle) { if (mIntents.isEmpty()) { throw new IllegalStateException( "No intents added to TaskStackBuilder; cannot startActivities"); } - mSourceContext.startActivitiesAsUser(getIntents(), options, userHandle); + return mSourceContext.startActivitiesAsUser(getIntents(), options, userHandle); } /** diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 1b050330ceb6..5177e102425a 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -32,6 +32,7 @@ import android.annotation.StyleableRes; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UserIdInt; +import android.app.ActivityManager; import android.app.IApplicationThread; import android.app.IServiceConnection; import android.app.VrManager; @@ -1834,13 +1835,17 @@ public abstract class Context { * See {@link android.content.Context#startActivity(Intent, Bundle)} * Context.startActivity(Intent, Bundle)} for more details. * + * @return The corresponding flag {@link ActivityManager#START_CANCELED}, + * {@link ActivityManager#START_SUCCESS} etc. indicating whether the launch was + * successful. + * * @throws ActivityNotFoundException * * @see #startActivities(Intent[]) * @see PackageManager#resolveActivity */ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) - public void startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) { + public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) { throw new RuntimeException("Not implemented. Must override in a subclass."); } diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 67de4fe6bc4b..8c1293e5b298 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -418,8 +418,8 @@ public class ContextWrapper extends Context { /** @hide */ @Override - public void startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) { - mBase.startActivitiesAsUser(intents, options, userHandle); + public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) { + return mBase.startActivitiesAsUser(intents, options, userHandle); } @Override diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 0949a90877e1..6c8aaf0b1e8c 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -350,6 +350,7 @@ applications that come with the platform <permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/> <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/> <permission name="android.permission.CONNECTIVITY_INTERNAL"/> + <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/> <permission name="android.permission.CONTROL_VPN"/> <permission name="android.permission.DUMP"/> <permission name="android.permission.GET_APP_OPS_STATS"/> diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 6c3aea2202a2..f5a6f4910b1b 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -42,6 +42,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.SweepGradient; +import android.graphics.Xfermode; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; @@ -814,6 +815,16 @@ public class GradientDrawable extends Drawable { } } + /** + * @param mode to draw this drawable with + * @hide + */ + @Override + public void setXfermode(@Nullable Xfermode mode) { + super.setXfermode(mode); + mFillPaint.setXfermode(mode); + } + private void buildPathIfDirty() { final GradientState st = mGradientState; if (mPathIsDirty) { diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 42b7213e84f0..1fc36bef215d 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -200,6 +200,9 @@ <!-- to access instant apps --> <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS" /> + <!-- to control remote app transitions --> + <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" /> + <!-- to change themes - light or dark --> <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" /> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index e59c703afdd8..eb5619b11487 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -122,7 +122,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private Interpolator mCurrentAppearInterpolator; private Interpolator mCurrentAlphaInterpolator; - private NotificationBackgroundView mBackgroundNormal; + protected NotificationBackgroundView mBackgroundNormal; private NotificationBackgroundView mBackgroundDimmed; private ObjectAnimator mBackgroundAnimator; private RectF mAppearAnimationRect = new RectF(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 5112d37c830c..1056ecc9bf6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar; +import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback; import android.animation.Animator; @@ -37,6 +38,7 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.AttributeSet; import android.util.FloatProperty; +import android.util.MathUtils; import android.util.Property; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -67,6 +69,7 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.statusbar.NotificationGuts.GutsContent; import com.android.systemui.statusbar.notification.AboveShelfChangedListener; +import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.HybridNotificationView; import com.android.systemui.statusbar.notification.NotificationInflater; import com.android.systemui.statusbar.notification.NotificationUtils; @@ -75,6 +78,7 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.stack.AmbientState; import com.android.systemui.statusbar.stack.AnimationProperties; import com.android.systemui.statusbar.stack.ExpandableViewState; import com.android.systemui.statusbar.stack.NotificationChildrenContainer; @@ -113,6 +117,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private int mNotificationMaxHeight; private int mNotificationAmbientHeight; private int mIncreasedPaddingBetweenElements; + private int mNotificationLaunchHeight; private boolean mMustStayOnScreen; /** Does this row contain layouts that can adapt to row expansion */ @@ -172,6 +177,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private boolean mIsSystemChildExpanded; private boolean mIsPinned; private FalsingManager mFalsingManager; + private boolean mExpandAnimationRunning; private AboveShelfChangedListener mAboveShelfChangedListener; private HeadsUpManager mHeadsUpManager; private View mHelperButton; @@ -270,6 +276,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private float mTranslationWhenRemoved; private boolean mWasChildInGroupWhenRemoved; private int mNotificationColorAmbient; + private NotificationViewState mNotificationViewState; @Override public boolean isGroupExpansionChanging() { @@ -363,6 +370,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mNotificationInflater.inflateNotificationViews(); } + @Override + public void setPressed(boolean pressed) { + if (isOnKeyguard() || mEntry.notification.getNotification().contentIntent == null) { + // We're dropping the ripple if we have a collapse / launch animation + super.setPressed(pressed); + } + } + public void onNotificationUpdated() { for (NotificationContentView l : mLayouts) { l.onNotificationUpdated(mEntry); @@ -1169,6 +1184,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } private void updateContentTransformation() { + if (mExpandAnimationRunning) { + return; + } float contentAlpha; float translationY = -mContentTransformationAmount * mIconTransformContentShift; if (mIsLastChild) { @@ -1450,6 +1468,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mMenuRow.resetMenu(); } + public CharSequence getActiveRemoteInputText() { + return mPrivateLayout.getActiveRemoteInputText(); + } + + public void animateTranslateNotification(final float leftTarget) { if (mTranslateAnim != null) { mTranslateAnim.cancel(); @@ -1537,10 +1560,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } private void updateChildrenVisibility() { - mPrivateLayout.setVisibility(!shouldShowPublic() && !mIsSummaryWithChildren ? VISIBLE - : INVISIBLE); + boolean hideContentWhileLaunching = mExpandAnimationRunning && mGuts != null + && mGuts.isExposed(); + mPrivateLayout.setVisibility(!shouldShowPublic() && !mIsSummaryWithChildren + && !hideContentWhileLaunching ? VISIBLE : INVISIBLE); if (mChildrenContainer != null) { - mChildrenContainer.setVisibility(!shouldShowPublic() && mIsSummaryWithChildren ? VISIBLE + mChildrenContainer.setVisibility(!shouldShowPublic() && mIsSummaryWithChildren + && !hideContentWhileLaunching ? VISIBLE : INVISIBLE); } // The limits might have changed if the view suddenly became a group or vice versa @@ -1579,6 +1605,62 @@ public class ExpandableNotificationRow extends ActivatableNotificationView updateShelfIconColor(); } + public void applyExpandAnimationParams(ExpandAnimationParameters params) { + if (params == null) { + return; + } + setTranslationY(params.getTop()); + float zProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation( + params.getProgress(0, 50)); + float translationZ = MathUtils.lerp(params.getStartTranslationZ(), + mNotificationLaunchHeight, + zProgress); + setTranslationZ(translationZ); + setActualHeight(params.getHeight()); + mBackgroundNormal.setExpandAnimationParams(params); + } + + public void setExpandAnimationRunning(boolean expandAnimationRunning) { + if (expandAnimationRunning) { + View contentView; + if (mIsSummaryWithChildren) { + contentView = mChildrenContainer; + } else { + contentView = getShowingLayout(); + } + if (mGuts != null && mGuts.isExposed()) { + contentView = mGuts; + } + contentView.animate() + .alpha(0f) + .setDuration(ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT) + .setInterpolator(Interpolators.ALPHA_OUT); + setAboveShelf(true); + mExpandAnimationRunning = true; + mNotificationViewState.cancelAnimations(this); + mNotificationLaunchHeight = AmbientState.getNotificationLaunchHeight(getContext()); + } else { + mExpandAnimationRunning = false; + setAboveShelf(isAboveShelf()); + if (mGuts != null) { + mGuts.setAlpha(1.0f); + } + } + updateChildrenVisibility(); + updateClipping(); + mBackgroundNormal.setExpandAnimationRunning(expandAnimationRunning); + } + + @Override + protected boolean shouldClipToActualHeight() { + return super.shouldClipToActualHeight() && !mExpandAnimationRunning; + } + + @Override + public boolean isExpandAnimationRunning() { + return mExpandAnimationRunning; + } + /** * Tap sounds should not be played when we're unlocking. * Doing so would cause audio collision and the system would feel unpolished. @@ -2152,6 +2234,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public void setClipBottomAmount(int clipBottomAmount) { + if (mExpandAnimationRunning) { + return; + } if (clipBottomAmount != mClipBottomAmount) { super.setClipBottomAmount(clipBottomAmount); for (NotificationContentView l : mLayouts) { @@ -2361,13 +2446,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public ExpandableViewState createNewViewState(StackScrollState stackScrollState) { - return new NotificationViewState(stackScrollState); + mNotificationViewState = new NotificationViewState(stackScrollState); + return mNotificationViewState; } @Override public boolean isAboveShelf() { return !isOnKeyguard() - && (mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf)); + && (mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf) + || mExpandAnimationRunning); } public void setShowAmbient(boolean showAmbient) { @@ -2453,9 +2540,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public void applyToView(View view) { - super.applyToView(view); if (view instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) view; + if (row.isExpandAnimationRunning()) { + return; + } + super.applyToView(view); row.applyChildrenState(mOverallState); } } @@ -2473,9 +2563,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public void animateTo(View child, AnimationProperties properties) { - super.animateTo(child, properties); if (child instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; + if (row.isExpandAnimationRunning()) { + return; + } + super.animateTo(child, properties); row.startChildAnimation(mOverallState, properties); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index eafa825dd89d..1496a41a9b47 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -151,6 +151,10 @@ public abstract class ExpandableView extends FrameLayout { return mActualHeight; } + public boolean isExpandAnimationRunning() { + return false; + } + /** * @return The maximum height of this notification. */ @@ -375,8 +379,8 @@ public abstract class ExpandableView extends FrameLayout { return false; } - private void updateClipping() { - if (mClipToActualHeight) { + protected void updateClipping() { + if (mClipToActualHeight && shouldClipToActualHeight()) { int top = getClipTopAmount(); mClipRect.set(0, top, getWidth(), Math.max(getActualHeight() + getExtraBottomPadding() - mClipBottomAmount, top)); @@ -386,6 +390,10 @@ public abstract class ExpandableView extends FrameLayout { } } + protected boolean shouldClipToActualHeight() { + return true; + } + public void setClipToActualHeight(boolean clipToActualHeight) { mClipToActualHeight = clipToActualHeight; updateClipping(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java index 45b35d014e70..d6beb7fb2699 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Canvas; import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.LayerDrawable; @@ -27,7 +28,9 @@ import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; import android.view.View; +import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; /** * A view that can be used for both the dimmed and normal background of an notification. @@ -44,6 +47,9 @@ public class NotificationBackgroundView extends View { private boolean mBottomIsRounded; private int mBackgroundTop; private boolean mBottomAmountClips = true; + private boolean mExpandAnimationRunning; + private float mActualWidth; + private int mDrawableAlpha = 255; public NotificationBackgroundView(Context context, AttributeSet attrs) { super(context, attrs); @@ -53,9 +59,12 @@ public class NotificationBackgroundView extends View { @Override protected void onDraw(Canvas canvas) { - if (mClipTopAmount + mClipBottomAmount < mActualHeight - mBackgroundTop) { + if (mClipTopAmount + mClipBottomAmount < mActualHeight - mBackgroundTop + || mExpandAnimationRunning) { canvas.save(); - canvas.clipRect(0, mClipTopAmount, getWidth(), mActualHeight - mClipBottomAmount); + if (!mExpandAnimationRunning) { + canvas.clipRect(0, mClipTopAmount, getWidth(), mActualHeight - mClipBottomAmount); + } draw(canvas, mBackground); canvas.restore(); } @@ -64,10 +73,16 @@ public class NotificationBackgroundView extends View { private void draw(Canvas canvas, Drawable drawable) { if (drawable != null) { int bottom = mActualHeight; - if (mBottomIsRounded && mBottomAmountClips) { + if (mBottomIsRounded && mBottomAmountClips && !mExpandAnimationRunning) { bottom -= mClipBottomAmount; } - drawable.setBounds(0, mBackgroundTop, getWidth(), bottom); + int left = 0; + int right = getWidth(); + if (mExpandAnimationRunning) { + left = (int) ((getWidth() - mActualWidth) / 2.0f); + right = (int) (left + mActualWidth); + } + drawable.setBounds(left, mBackgroundTop, right, bottom); drawable.draw(canvas); } } @@ -133,6 +148,9 @@ public class NotificationBackgroundView extends View { } public void setActualHeight(int actualHeight) { + if (mExpandAnimationRunning) { + return; + } mActualHeight = actualHeight; invalidate(); } @@ -170,6 +188,10 @@ public class NotificationBackgroundView extends View { } public void setDrawableAlpha(int drawableAlpha) { + mDrawableAlpha = drawableAlpha; + if (mExpandAnimationRunning) { + return; + } mBackground.setAlpha(drawableAlpha); } @@ -208,4 +230,29 @@ public class NotificationBackgroundView extends View { mBackgroundTop = backgroundTop; invalidate(); } + + public void setExpandAnimationParams(ActivityLaunchAnimator.ExpandAnimationParameters params) { + mActualHeight = params.getHeight(); + mActualWidth = params.getWidth(); + float alphaProgress = Interpolators.ALPHA_IN.getInterpolation( + params.getProgress( + ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT /* delay */, + ActivityLaunchAnimator.ANIMATION_DURATION_FADE_APP /* duration */)); + mBackground.setAlpha((int) (mDrawableAlpha * (1.0f - alphaProgress))); + invalidate(); + } + + public void setExpandAnimationRunning(boolean running) { + mExpandAnimationRunning = running; + if (mBackground instanceof LayerDrawable) { + GradientDrawable gradientDrawable = + (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0); + gradientDrawable.setXfermode( + running ? new PorterDuffXfermode(PorterDuff.Mode.SRC) : null); + } + if (!mExpandAnimationRunning) { + setDrawableAlpha(mDrawableAlpha); + } + invalidate(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index a4c17e3681b0..e811ed3d77ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -1563,4 +1563,14 @@ public class NotificationContentView extends FrameLayout { } return visibleWrapper.shouldClipToRounding(topRounded, bottomRounded); } + + public CharSequence getActiveRemoteInputText() { + if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) { + return mExpandedRemoteInput.getText(); + } + if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) { + return mHeadsUpRemoteInput.getText(); + } + return null; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java index 9d8892da3c74..b9e672515499 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java @@ -41,7 +41,6 @@ import com.android.systemui.statusbar.phone.StatusBar; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -133,14 +132,14 @@ public class NotificationGutsManager implements Dumpable { * channel. */ private void startAppNotificationSettingsActivity(String packageName, final int appUid, - final NotificationChannel channel) { + final NotificationChannel channel, ExpandableNotificationRow row) { final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName); intent.putExtra(Settings.EXTRA_APP_UID, appUid); if (channel != null) { intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId()); } - mPresenter.startNotificationGutsIntent(intent, appUid); + mPresenter.startNotificationGutsIntent(intent, appUid, row); } public void bindGuts(final ExpandableNotificationRow row) { @@ -198,14 +197,14 @@ public class NotificationGutsManager implements Dumpable { mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO); guts.resetFalsingCheck(); mOnSettingsClickListener.onClick(sbn.getKey()); - startAppNotificationSettingsActivity(pkg, appUid, channel); + startAppNotificationSettingsActivity(pkg, appUid, channel, row); }; } final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v, Intent intent) -> { mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS); guts.resetFalsingCheck(); - mPresenter.startNotificationGutsIntent(intent, sbn.getUid()); + mPresenter.startNotificationGutsIntent(intent, sbn.getUid(), row); }; final View.OnClickListener onDoneClick = (View v) -> { saveAndCloseNotificationMenu(row, guts, v); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java index 43be44deedc8..0c19ec091ca6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; + import android.view.View; import android.view.ViewGroup; @@ -179,4 +181,11 @@ public interface NotificationListContainer { * @return true if has pulsing notifications */ boolean hasPulsingNotifications(); + + /** + * Apply parameters of the expand animation to the layout + */ + default void applyExpandAnimationParams(ExpandAnimationParameters params) {} + + default void setExpandingNotification(ExpandableNotificationRow row) {} } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java index 12641a0eca8c..5263bb4173a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java @@ -16,9 +16,7 @@ package com.android.systemui.statusbar; import android.content.Intent; -import android.content.pm.PackageManager; import android.os.Handler; -import android.service.notification.StatusBarNotification; import android.view.View; /** @@ -48,7 +46,7 @@ public interface NotificationPresenter extends NotificationData.Environment, * Runs the given intent. The presenter may want to run some animations or close itself when * this happens. */ - void startNotificationGutsIntent(Intent intent, int appUid); + void startNotificationGutsIntent(Intent intent, int appUid, ExpandableNotificationRow row); /** * Returns the Handler for NotificationPresenter. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java new file mode 100644 index 000000000000..8336d29f2870 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -0,0 +1,242 @@ +/* + * 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 android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.app.ActivityOptions; +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.Surface; +import android.view.SurfaceControl; +import android.view.ViewRootImpl; + +import com.android.systemui.Interpolators; +import com.android.systemui.statusbar.ExpandableNotificationRow; +import com.android.systemui.statusbar.NotificationListContainer; +import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; +import com.android.systemui.statusbar.phone.NotificationPanelView; +import com.android.systemui.statusbar.phone.StatusBarWindowView; +import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; + +import java.util.function.Consumer; + +/** + * 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 final NotificationPanelView mNotificationPanel; + private final NotificationListContainer mNotificationContainer; + private final StatusBarWindowView mStatusBarWindow; + private final Consumer<Boolean> mPanelCollapser; + + public ActivityLaunchAnimator(StatusBarWindowView statusBarWindow, + Consumer<Boolean> panelCollapser, + NotificationPanelView notificationPanel, + NotificationListContainer container) { + mNotificationPanel = notificationPanel; + mNotificationContainer = container; + mStatusBarWindow = statusBarWindow; + mPanelCollapser = panelCollapser; + } + + public ActivityOptions getLaunchAnimation( + ExpandableNotificationRow sourceNofitication) { + AnimationRunner animationRunner = new AnimationRunner(sourceNofitication); + return ActivityOptions.makeRemoteAnimation( + new RemoteAnimationAdapter(animationRunner, 1000 /* Duration */, 0 /* delay */)); + } + + class AnimationRunner extends IRemoteAnimationRunner.Stub { + + private final ExpandableNotificationRow mSourceNotification; + private final ExpandAnimationParameters mParams; + private final Rect mWindowCrop = new Rect(); + private boolean mLeashShown; + private boolean mInstantCollapsePanel = true; + + public AnimationRunner(ExpandableNotificationRow sourceNofitication) { + mSourceNotification = sourceNofitication; + mParams = new ExpandAnimationParameters(); + } + + @Override + public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets, + IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback) + throws RemoteException { + mSourceNotification.post(() -> { + boolean first = true; + for (RemoteAnimationTarget app : remoteAnimationTargets) { + if (app.mode == RemoteAnimationTarget.MODE_OPENING) { + setExpandAnimationRunning(true); + mInstantCollapsePanel = app.position.y == 0 + && app.sourceContainerBounds.height() + >= mNotificationPanel.getHeight(); + if (!mInstantCollapsePanel) { + mNotificationPanel.collapseWithDuration(ANIMATION_DURATION); + } + ValueAnimator anim = ValueAnimator.ofFloat(0, 1); + mParams.startPosition = mSourceNotification.getLocationOnScreen(); + mParams.startTranslationZ = mSourceNotification.getTranslationZ(); + int targetWidth = app.sourceContainerBounds.width(); + int notificationHeight = mSourceNotification.getActualHeight(); + int notificationWidth = mSourceNotification.getWidth(); + 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], + app.position.y, progress); + mParams.bottom = (int) MathUtils.lerp(mParams.startPosition[1] + + notificationHeight, + app.position.y + app.sourceContainerBounds.bottom, + progress); + applyParamsToWindow(app); + applyParamsToNotification(mParams); + applyParamsToNotificationList(mParams); + } + }); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + setExpandAnimationRunning(false); + if (mInstantCollapsePanel) { + mPanelCollapser.accept(false /* animate */); + } + try { + iRemoteAnimationFinishedCallback.onAnimationFinished(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + }); + anim.start(); + break; + } + } + }); + } + + private void setExpandAnimationRunning(boolean running) { + mNotificationPanel.setLaunchingNotification(running); + mSourceNotification.setExpandAnimationRunning(running); + mStatusBarWindow.setExpandAnimationRunning(running); + mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null); + if (!running) { + applyParamsToNotification(null); + applyParamsToNotificationList(null); + } + + } + + private void applyParamsToNotificationList(ExpandAnimationParameters params) { + mNotificationContainer.applyExpandAnimationParams(params); + mNotificationPanel.applyExpandAnimationParams(params); + } + + private void applyParamsToNotification(ExpandAnimationParameters params) { + mSourceNotification.applyExpandAnimationParams(params); + } + + private void applyParamsToWindow(RemoteAnimationTarget app) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + if (!mLeashShown) { + t.show(app.leash); + mLeashShown = true; + } + Matrix m = new Matrix(); + m.postTranslate(0, (float) (mParams.top - app.position.y)); + t.setMatrix(app.leash, m, new float[9]); + mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight()); + t.setWindowCrop(app.leash, mWindowCrop); + ViewRootImpl viewRootImpl = mSourceNotification.getViewRootImpl(); + if (viewRootImpl != null) { + Surface systemUiSurface = viewRootImpl.mSurface; + t.deferTransactionUntilSurface(app.leash, systemUiSurface, + systemUiSurface.getNextFrameNumber()); + } + t.apply(); + } + + @Override + public void onAnimationCancelled() throws RemoteException { + } + }; + + public static class ExpandAnimationParameters { + float linearProgress; + int[] startPosition; + float startTranslationZ; + int left; + int top; + int right; + int bottom; + + public ExpandAnimationParameters() { + } + + public int getTop() { + return top; + } + + public int getWidth() { + return right - left; + } + + public int getHeight() { + return bottom - top; + } + + public int getTopChange() { + return Math.min(top - startPosition[1], 0); + } + + + public float getProgress(long delay, long duration) { + return MathUtils.constrain((linearProgress * ANIMATION_DURATION - delay) + / duration, 0.0f, 1.0f); + } + + public float getStartTranslationZ() { + return startTranslationZ; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index 61cb61ce1df4..f7f791ebaf63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -51,6 +51,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue public static final String TAG = "CollapsedStatusBarFragment"; private static final String EXTRA_PANEL_STATE = "panel_state"; + public static final int FADE_IN_DURATION = 320; + public static final int FADE_IN_DELAY = 50; private PhoneStatusBarView mStatusBar; private KeyguardMonitor mKeyguardMonitor; private NetworkController mNetworkController; @@ -257,9 +259,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } v.animate() .alpha(1f) - .setDuration(320) + .setDuration(FADE_IN_DURATION) .setInterpolator(Interpolators.ALPHA_IN) - .setStartDelay(50) + .setStartDelay(FADE_IN_DELAY) // We need to clean up any pending end action from animateHide if we call // both hide and show in the same frame before the animation actually gets started. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 66cb59e3d317..5d3cc34ca8b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -65,6 +67,7 @@ import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; @@ -241,6 +244,8 @@ public class NotificationPanelView extends PanelView implements private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private boolean mUserSetupComplete; private int mQsNotificationTopPadding; + private float mExpandOffset; + private boolean mHideIconsDuringNotificationLaunch = true; public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); @@ -1675,8 +1680,9 @@ public class NotificationPanelView extends PanelView implements if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) { return 0; } - float translation = NotificationUtils.interpolate(-mQsMinExpansionHeight, 0, - mNotificationStackScroller.getAppearFraction(mExpandedHeight)); + float translation = MathUtils.lerp(-mQsMinExpansionHeight, 0, + Math.min(1.0f, mNotificationStackScroller.getAppearFraction(mExpandedHeight))) + + mExpandOffset; return Math.min(0, translation); } @@ -2540,6 +2546,9 @@ public class NotificationPanelView extends PanelView implements } public boolean hideStatusBarIconsWhenExpanded() { + if (mLaunchingNotification) { + return mHideIconsDuringNotificationLaunch; + } return !isFullWidth() || !mShowIconsWhenExpanded; } @@ -2665,4 +2674,19 @@ public class NotificationPanelView extends PanelView implements public LockIcon getLockIcon() { return mKeyguardBottomArea.getLockIcon(); } + + public void applyExpandAnimationParams(ExpandAnimationParameters params) { + mExpandOffset = params != null ? params.getTopChange() : 0; + updateQsExpansion(); + if (params != null) { + boolean hideIcons = params.getProgress( + ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f; + if (hideIcons != mHideIconsDuringNotificationLaunch) { + mHideIconsDuringNotificationLaunch = hideIcons; + if (!hideIcons) { + mStatusBar.recomputeDisableFlags(true /* animate */); + } + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 2fc22caa6c05..a62a424caaf5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -60,12 +60,15 @@ public abstract class PanelView extends FrameLayout { public static final String TAG = PanelView.class.getSimpleName(); private static final int INITIAL_OPENING_PEEK_DURATION = 200; private static final int PEEK_ANIMATION_DURATION = 360; + private static final int NO_FIXED_DURATION = -1; private long mDownTime; private float mMinExpandHeight; private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); private boolean mPanelUpdateWhenAnimatorEnds; private boolean mVibrateOnOpening; private boolean mVibrationEnabled; + protected boolean mLaunchingNotification; + private int mFixedDuration = NO_FIXED_DURATION; private final void logf(String fmt, Object... args) { Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args)); @@ -785,6 +788,9 @@ public abstract class PanelView extends FrameLayout { if (vel == 0) { animator.setDuration((long) (animator.getDuration() / collapseSpeedUpFactor)); } + if (mFixedDuration != NO_FIXED_DURATION) { + animator.setDuration(mFixedDuration); + } } animator.addListener(new AnimatorListenerAdapter() { private boolean mCancelled; @@ -1249,4 +1255,14 @@ public abstract class PanelView extends FrameLayout { public void setHeadsUpManager(HeadsUpManager headsUpManager) { mHeadsUpManager = headsUpManager; } + + public void setLaunchingNotification(boolean launchingNotification) { + mLaunchingNotification = launchingNotification; + } + + public void collapseWithDuration(int animationDuration) { + mFixedDuration = animationDuration; + collapse(false /* delayed */, 1.0f /* speedUpFactor */); + mFixedDuration = NO_FIXED_DURATION; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index a54b26589628..7d8455002300 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -100,6 +100,8 @@ import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; +import android.text.SpannedString; +import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; @@ -205,6 +207,7 @@ import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.SignalClusterView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.AboveShelfObserver; +import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener; import com.android.systemui.statusbar.policy.BatteryController; @@ -586,6 +589,7 @@ public class StatusBar extends SystemUI implements DemoMode, private NavigationBarFragment mNavigationBar; private View mNavigationBarView; + private ActivityLaunchAnimator mActivityLaunchAnimator; @Override public void start() { @@ -755,6 +759,10 @@ public class StatusBar extends SystemUI implements DemoMode, // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot. mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel); mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller); + mActivityLaunchAnimator = new ActivityLaunchAnimator(mStatusBarWindow, + this::collapsePanel, + mNotificationPanel, + mStackScroller); mGutsManager.setUpWithPresenter(this, mEntryManager, mStackScroller, mCheckSaveListener, key -> { try { @@ -2796,7 +2804,8 @@ public class StatusBar extends SystemUI implements DemoMode, intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); int result = ActivityManager.START_CANCELED; - ActivityOptions options = new ActivityOptions(getActivityOptions()); + ActivityOptions options = new ActivityOptions(getActivityOptions( + null /* sourceNotification */)); options.setDisallowEnterPictureInPictureWhileLaunching( disallowEnterPictureInPictureWhileLaunching); if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) { @@ -4911,6 +4920,7 @@ public class StatusBar extends SystemUI implements DemoMode, ActivityManager.getService().resumeAppSwitches(); } catch (RemoteException e) { } + int launchResult = ActivityManager.START_CANCELED; if (intent != null) { // If we are launching a work activity and require to launch // separate work challenge, we defer the activity action and cancel @@ -4925,12 +4935,30 @@ public class StatusBar extends SystemUI implements DemoMode, notificationKey)) { // Show work challenge, do not run PendingIntent and // remove notification + collapsePanel(); return; } } } + Intent fillInIntent = null; + Entry entry = row.getEntry(); + CharSequence remoteInputText = null; + RemoteInputController controller = mRemoteInputManager.getController(); + if (controller.isRemoteInputActive(entry)) { + remoteInputText = row.getActiveRemoteInputText(); + } + if (TextUtils.isEmpty(remoteInputText) + && !TextUtils.isEmpty(entry.remoteInputText)) { + remoteInputText = entry.remoteInputText; + } + if (!TextUtils.isEmpty(remoteInputText) + && !controller.isSpinning(entry.key)) { + fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT, + remoteInputText.toString()); + } try { - intent.send(null, 0, null, null, null, null, getActivityOptions()); + launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null, + null, null, getActivityOptions(row)); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. // Just log the exception message. @@ -4942,6 +4970,13 @@ public class StatusBar extends SystemUI implements DemoMode, mAssistManager.hideAssist(); } } + if (shouldCollapse(launchResult)) { + if (Looper.getMainLooper().isCurrentThread()) { + collapsePanel(); + } else { + mStackScroller.post(this::collapsePanel); + } + } try { mBarService.onNotificationClick(notificationKey); @@ -4964,19 +4999,45 @@ public class StatusBar extends SystemUI implements DemoMode, new Thread(runnable).start(); } - if (!mNotificationPanel.isFullyCollapsed()) { - // close the shade if it was open - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, - true /* delayed */); - visibilityChanged(false); - - return true; - } else { - return false; - } + return !mNotificationPanel.isFullyCollapsed(); }, afterKeyguardGone); } + private boolean shouldCollapse(int launchResult) { + return mState != StatusBarState.SHADE + || (launchResult != ActivityManager.START_TASK_TO_FRONT + && launchResult != ActivityManager.START_SUCCESS); + } + + public void onExpandAnimationFinished() { + if (!isPresenterFullyCollapsed()) { + instantCollapseNotificationPanel(); + visibilityChanged(false); + } + } + + public void collapsePanel(boolean animate) { + if (animate) { + collapsePanel(); + } else if (!isPresenterFullyCollapsed()) { + instantCollapseNotificationPanel(); + visibilityChanged(false); + } + } + + private boolean collapsePanel() { + if (!mNotificationPanel.isFullyCollapsed()) { + // close the shade if it was open + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, + true /* delayed */); + visibilityChanged(false); + + return true; + } else { + return false; + } + } + private void removeNotification(StatusBarNotification notification) { // We have to post it to the UI thread for synchronization mHandler.post(() -> { @@ -5059,15 +5120,20 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public void startNotificationGutsIntent(final Intent intent, final int appUid) { + public void startNotificationGutsIntent(final Intent intent, final int appUid, + ExpandableNotificationRow row) { dismissKeyguardThenExecute(() -> { AsyncTask.execute(() -> { - TaskStackBuilder.create(mContext) + int launchResult = TaskStackBuilder.create(mContext) .addNextIntentWithParentStack(intent) - .startActivities(getActivityOptions(), + .startActivities(getActivityOptions(row), new UserHandle(UserHandle.getUserId(appUid))); + if (shouldCollapse(launchResult)) { + // Putting it back on the main thread, since we're touching views + mStatusBarWindow.post(() -> animateCollapsePanels( + CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */)); + } }); - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); return true; }, false /* afterKeyguardGone */); } @@ -5193,7 +5259,8 @@ public class StatusBar extends SystemUI implements DemoMode, } catch (RemoteException e) { } try { - intent.send(null, 0, null, null, null, null, getActivityOptions()); + intent.send(null, 0, null, null, null, null, getActivityOptions( + null /* sourceNotification */)); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. // Just log the exception message. @@ -5206,16 +5273,7 @@ public class StatusBar extends SystemUI implements DemoMode, } }).start(); - if (!mNotificationPanel.isFullyCollapsed()) { - // close the shade if it was open - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, - true /* delayed */); - visibilityChanged(false); - - return true; - } else { - return false; - } + return collapsePanel(); }, afterKeyguardGone); } @@ -5230,10 +5288,15 @@ public class StatusBar extends SystemUI implements DemoMode, return true; } - protected Bundle getActivityOptions() { + protected Bundle getActivityOptions(ExpandableNotificationRow sourceNotification) { + ActivityOptions options; + if (sourceNotification != null) { + options = mActivityLaunchAnimator.getLaunchAnimation(sourceNotification); + } else { + options = ActivityOptions.makeBasic(); + } // Anything launched from the notification shade should always go into the secondary // split-screen windowing mode. - final ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); return options.toBundle(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index 4accd86cce98..f7d0967c2378 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -88,6 +88,7 @@ public class StatusBarWindowView extends FrameLayout { private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener; private boolean mTouchCancelled; private boolean mTouchActive; + private boolean mExpandAnimationRunning; public StatusBarWindowView(Context context, AttributeSet attrs) { super(context, attrs); @@ -267,7 +268,7 @@ public class StatusBarWindowView extends FrameLayout { || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) { setTouchActive(false); } - if (mTouchCancelled) { + if (mTouchCancelled || mExpandAnimationRunning) { return false; } mFalsingManager.onTouchEvent(ev, getWidth(), getHeight()); @@ -388,6 +389,10 @@ public class StatusBarWindowView extends FrameLayout { } } + public void setExpandAnimationRunning(boolean expandAnimationRunning) { + mExpandAnimationRunning = expandAnimationRunning; + } + public class LayoutParams extends FrameLayout.LayoutParams { public boolean ignoreRightInset; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index b63c1da59cba..179c0d57aa50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -169,6 +169,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } + public CharSequence getText() { + return mEditText.getText(); + } + public static RemoteInputView inflate(Context context, ViewGroup root, NotificationData.Entry entry, RemoteInputController controller) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index ebf4cda457e7..424858a86e58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -68,6 +68,8 @@ public class AmbientState { private boolean mUnlockHintRunning; private boolean mQsCustomizerShowing; private int mIntrinsicPadding; + private int mExpandAnimationTopChange; + private ExpandableNotificationRow mExpandingNotification; public AmbientState(Context context) { reload(context); @@ -77,9 +79,25 @@ public class AmbientState { * Reload the dimens e.g. if the density changed. */ public void reload(Context context) { - mZDistanceBetweenElements = Math.max(1, context.getResources() + mZDistanceBetweenElements = getZDistanceBetweenElements(context); + mBaseZHeight = getBaseHeight(mZDistanceBetweenElements); + } + + private static int getZDistanceBetweenElements(Context context) { + return Math.max(1, context.getResources() .getDimensionPixelSize(R.dimen.z_distance_between_notifications)); - mBaseZHeight = 4 * mZDistanceBetweenElements; + } + + private static int getBaseHeight(int zdistanceBetweenElements) { + return 4 * zdistanceBetweenElements; + } + + /** + * @return the launch height for notifications that are launched + */ + public static int getNotificationLaunchHeight(Context context) { + int zDistance = getZDistanceBetweenElements(context); + return getBaseHeight(zDistance) * 2; } /** @@ -202,7 +220,8 @@ public class AmbientState { } public int getInnerHeight() { - return Math.max(Math.min(mLayoutHeight, mMaxLayoutHeight) - mTopPadding, mLayoutMinHeight); + return Math.max(Math.min(mLayoutHeight, mMaxLayoutHeight) - mTopPadding + - mExpandAnimationTopChange, mLayoutMinHeight); } public boolean isShadeExpanded() { @@ -380,4 +399,20 @@ public class AmbientState { public boolean isDozingAndNotPulsing(ExpandableNotificationRow row) { return isDark() && !isPulsing(row.getEntry()); } + + public void setExpandAnimationTopChange(int expandAnimationTopChange) { + mExpandAnimationTopChange = expandAnimationTopChange; + } + + public void setExpandingNotification(ExpandableNotificationRow row) { + mExpandingNotification = row; + } + + public ExpandableNotificationRow getExpandingNotification() { + return mExpandingNotification; + } + + public int getExpandAnimationTopChange() { + return mExpandAnimationTopChange; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java index 0650e23db883..3bf7d892ea0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java @@ -467,4 +467,21 @@ public class ExpandableViewState extends ViewState { return getChildTag(view, TAG_END_HEIGHT); } } + + @Override + public void cancelAnimations(View view) { + super.cancelAnimations(view); + Animator animator = getChildTag(view, TAG_ANIMATOR_HEIGHT); + if (animator != null) { + animator.cancel(); + } + animator = getChildTag(view, TAG_ANIMATOR_SHADOW_ALPHA); + if (animator != null) { + animator.cancel(); + } + animator = getChildTag(view, TAG_ANIMATOR_TOP_INSET); + if (animator != null) { + animator.cancel(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 53e470938f3f..ad8a0eb98ead 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.stack; +import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -728,7 +730,7 @@ public class NotificationStackScrollLayout extends ViewGroup } private void updateClippingToTopRoundedCorner() { - Float clipStart = (float) mTopPadding; + Float clipStart = (float) mTopPadding + mAmbientState.getExpandAnimationTopChange(); Float clipEnd = clipStart + mCornerRadius; boolean first = true; for (int i = 0; i < getChildCount(); i++) { @@ -2988,6 +2990,17 @@ public class NotificationStackScrollLayout extends ViewGroup && (mIsExpanded || isPinnedHeadsUp(child)), child); } + @Override + public void setExpandingNotification(ExpandableNotificationRow row) { + mAmbientState.setExpandingNotification(row); + requestChildrenUpdate(); + } + + @Override + public void applyExpandAnimationParams(ExpandAnimationParameters params) { + mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange()); + requestChildrenUpdate(); + } private void updateAnimationState(boolean running, View child) { if (child instanceof ExpandableNotificationRow) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 2ce6df275588..d68a7b1dc205 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -130,7 +130,8 @@ public class StackScrollAlgorithm { private void updateClipping(StackScrollState resultState, StackScrollAlgorithmState algorithmState, AmbientState ambientState) { float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding() - + ambientState.getStackTranslation() : 0; + + ambientState.getStackTranslation() + ambientState.getExpandAnimationTopChange() + : 0; float previousNotificationEnd = 0; float previousNotificationStart = 0; int childCount = algorithmState.visibleChildren.size(); @@ -320,6 +321,10 @@ public class StackScrollAlgorithm { lastView = v; } } + ExpandableNotificationRow expandingNotification = ambientState.getExpandingNotification(); + state.indexOfExpandingNotification = expandingNotification != null + ? state.visibleChildren.indexOf(expandingNotification) + : -1; } private float getPaddingForValue(Float increasedPadding) { @@ -381,6 +386,9 @@ public class StackScrollAlgorithm { childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA; float inset = ambientState.getTopPadding() + ambientState.getStackTranslation(); + if (i < algorithmState.getIndexOfExpandingNotification()) { + inset += ambientState.getExpandAnimationTopChange(); + } if (child.mustStayOnScreen() && childViewState.yTranslation >= 0) { // Even if we're not scrolled away we're in view and we're also not in the // shelf. We can relax the constraints and let us scroll off the top! @@ -394,7 +402,7 @@ public class StackScrollAlgorithm { childViewState.yTranslation = ambientState.getInnerHeight() - childHeight + ambientState.getStackTranslation() * 0.25f; } else { - clampPositionToShelf(childViewState, ambientState); + clampPositionToShelf(child, childViewState, ambientState); } currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild; @@ -492,10 +500,12 @@ public class StackScrollAlgorithm { * Clamp the height of the child down such that its end is at most on the beginning of * the shelf. * + * @param child * @param childViewState the view state of the child * @param ambientState the ambient state */ - private void clampPositionToShelf(ExpandableViewState childViewState, + private void clampPositionToShelf(ExpandableView child, + ExpandableViewState childViewState, AmbientState ambientState) { if (ambientState.getShelf() == null) { return; @@ -505,7 +515,7 @@ public class StackScrollAlgorithm { - ambientState.getShelf().getIntrinsicHeight(); childViewState.yTranslation = Math.min(childViewState.yTranslation, shelfStart); if (childViewState.yTranslation >= shelfStart) { - childViewState.hidden = true; + childViewState.hidden = !child.isExpandAnimationRunning(); childViewState.inShelf = true; childViewState.headsUpIsVisible = false; } @@ -602,6 +612,7 @@ public class StackScrollAlgorithm { * The padding after each child measured in pixels. */ public final HashMap<ExpandableView, Float> paddingMap = new HashMap<>(); + private int indexOfExpandingNotification; public int getPaddingAfterChild(ExpandableView child) { Float padding = paddingMap.get(child); @@ -611,6 +622,10 @@ public class StackScrollAlgorithm { } return (int) padding.floatValue(); } + + public int getIndexOfExpandingNotification() { + return indexOfExpandingNotification; + } } } |