Launching Notification animations inline
Using the new control mechanism introduced in order
to coordinate notification launches and smoothly
transform the notification into the launching window.
Bug: 69168591
Test: add notification, launch it
Change-Id: Ib2d671c65f276ec596a2f07edf64d65bf27a2882
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 0949a90..6c8aaf0 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -350,6 +350,7 @@
<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 6c3aea2..f5a6f49 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -42,6 +42,7 @@
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 @@
}
}
+ /**
+ * @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 42b7213..1fc36be 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 e59c703..eb5619b 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 @@
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 5f4854a..7dc8571 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.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.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.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 @@
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 @@
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 @@
private float mTranslationWhenRemoved;
private boolean mWasChildInGroupWhenRemoved;
private int mNotificationColorAmbient;
+ private NotificationViewState mNotificationViewState;
@Override
public boolean isGroupExpansionChanging() {
@@ -363,6 +370,14 @@
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);
@@ -1159,6 +1174,9 @@
}
private void updateContentTransformation() {
+ if (mExpandAnimationRunning) {
+ return;
+ }
float contentAlpha;
float translationY = -mContentTransformationAmount * mIconTransformContentShift;
if (mIsLastChild) {
@@ -1569,6 +1587,56 @@
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();
+ }
+ 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());
+ }
+ mExpandAnimationRunning = expandAnimationRunning;
+ 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.
@@ -2142,6 +2210,9 @@
@Override
public void setClipBottomAmount(int clipBottomAmount) {
+ if (mExpandAnimationRunning) {
+ return;
+ }
if (clipBottomAmount != mClipBottomAmount) {
super.setClipBottomAmount(clipBottomAmount);
for (NotificationContentView l : mLayouts) {
@@ -2352,13 +2423,15 @@
@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) {
@@ -2444,9 +2517,12 @@
@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);
}
}
@@ -2464,9 +2540,12 @@
@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 eafa825..1496a41 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 @@
return mActualHeight;
}
+ public boolean isExpandAnimationRunning() {
+ return false;
+ }
+
/**
* @return The maximum height of this notification.
*/
@@ -375,8 +379,8 @@
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 @@
}
}
+ 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 45b35d0..d6beb7f 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.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.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 @@
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 @@
@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 @@
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 void setActualHeight(int actualHeight) {
+ if (mExpandAnimationRunning) {
+ return;
+ }
mActualHeight = actualHeight;
invalidate();
}
@@ -170,6 +188,10 @@
}
public void setDrawableAlpha(int drawableAlpha) {
+ mDrawableAlpha = drawableAlpha;
+ if (mExpandAnimationRunning) {
+ return;
+ }
mBackground.setAlpha(drawableAlpha);
}
@@ -208,4 +230,29 @@
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/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index 9d8892d..b9e6725 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 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 @@
* 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 @@
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 43be44d..0c19ec0 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 @@
* @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 12641a0..5263bb4 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 @@
* 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 0000000..8336d29
--- /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 61cb61c..f7f791e 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 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 @@
}
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 66cb59e..5d3cc34 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.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 @@
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 @@
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 boolean hideStatusBarIconsWhenExpanded() {
+ if (mLaunchingNotification) {
+ return mHideIconsDuringNotificationLaunch;
+ }
return !isFullWidth() || !mShowIconsWhenExpanded;
}
@@ -2665,4 +2674,19 @@
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 2fc22ca..a62a424 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 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 @@
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 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 d13ecae..602a7ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -205,6 +205,7 @@
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 +587,7 @@
private NavigationBarFragment mNavigationBar;
private View mNavigationBarView;
+ private ActivityLaunchAnimator mActivityLaunchAnimator;
@Override
public void start() {
@@ -755,6 +757,10 @@
// 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 {
@@ -2795,7 +2801,8 @@
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) {
@@ -4910,6 +4917,7 @@
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
@@ -4924,12 +4932,14 @@
notificationKey)) {
// Show work challenge, do not run PendingIntent and
// remove notification
+ collapsePanel();
return;
}
}
}
try {
- intent.send(null, 0, null, null, null, null, getActivityOptions());
+ launchResult = intent.sendAndReturnResult(null, 0, null, null, null, null,
+ getActivityOptions(row));
} catch (PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
// Just log the exception message.
@@ -4941,6 +4951,15 @@
mAssistManager.hideAssist();
}
}
+ if (mState != StatusBarState.SHADE
+ || (launchResult != ActivityManager.START_TASK_TO_FRONT
+ && launchResult != ActivityManager.START_SUCCESS)) {
+ if (Looper.getMainLooper().isCurrentThread()) {
+ collapsePanel();
+ } else {
+ mStackScroller.post(this::collapsePanel);
+ }
+ }
try {
mBarService.onNotificationClick(notificationKey);
@@ -4963,19 +4982,39 @@
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);
}
+ 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(() -> {
@@ -5058,12 +5097,13 @@
}
@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)
.addNextIntentWithParentStack(intent)
- .startActivities(getActivityOptions(),
+ .startActivities(getActivityOptions(row),
new UserHandle(UserHandle.getUserId(appUid)));
});
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
@@ -5192,7 +5232,8 @@
} 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.
@@ -5205,16 +5246,7 @@
}
}).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);
}
@@ -5229,10 +5261,15 @@
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 4accd86..f7d0967 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 @@
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 @@
|| ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
setTouchActive(false);
}
- if (mTouchCancelled) {
+ if (mTouchCancelled || mExpandAnimationRunning) {
return false;
}
mFalsingManager.onTouchEvent(ev, getWidth(), getHeight());
@@ -388,6 +389,10 @@
}
}
+ 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/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index ebf4cda..424858a 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 @@
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 @@
* 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 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 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 0650e23..3bf7d89 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 @@
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 af3d64b..351fea2 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 @@
}
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++) {
@@ -3006,6 +3008,17 @@
&& (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 2ce6df2..d68a7b1 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 @@
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 @@
lastView = v;
}
}
+ ExpandableNotificationRow expandingNotification = ambientState.getExpandingNotification();
+ state.indexOfExpandingNotification = expandingNotification != null
+ ? state.visibleChildren.indexOf(expandingNotification)
+ : -1;
}
private float getPaddingForValue(Float increasedPadding) {
@@ -381,6 +386,9 @@
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 @@
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 @@
* 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 @@
- 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 @@
* 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 @@
}
return (int) padding.floatValue();
}
+
+ public int getIndexOfExpandingNotification() {
+ return indexOfExpandingNotification;
+ }
}
}