diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 0041879453a88..06509ae9ae595 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -4257,15 +4257,17 @@ public class Notification implements Parcelable * Construct a RemoteViews for the final notification header only. This will not be * colorized. * + * @param ambient if true, generate the header for the ambient display layout. * @hide */ - public RemoteViews makeNotificationHeader() { + public RemoteViews makeNotificationHeader(boolean ambient) { Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED); mN.extras.putBoolean(EXTRA_COLORIZED, false); RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(), - R.layout.notification_template_header); + ambient ? R.layout.notification_template_ambient_header + : R.layout.notification_template_header); resetNotificationHeader(header); - bindNotificationHeader(header, false /* ambient */); + bindNotificationHeader(header, ambient); if (colorized != null) { mN.extras.putBoolean(EXTRA_COLORIZED, colorized); } else { @@ -4407,7 +4409,7 @@ public class Notification implements Parcelable } } - RemoteViews header = makeNotificationHeader(); + RemoteViews header = makeNotificationHeader(false /* ambient */); header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true); if (summary != null) { mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary); diff --git a/core/res/res/layout/notification_template_ambient_header.xml b/core/res/res/layout/notification_template_ambient_header.xml new file mode 100644 index 0000000000000..c00acd5bfc60c --- /dev/null +++ b/core/res/res/layout/notification_template_ambient_header.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index cff6eb198473e..2adf87a0e4f2d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2933,6 +2933,8 @@ + + diff --git a/packages/SystemUI/res/layout/hybrid_notification.xml b/packages/SystemUI/res/layout/hybrid_notification.xml index 476f52b11c0ff..f4501da944e20 100644 --- a/packages/SystemUI/res/layout/hybrid_notification.xml +++ b/packages/SystemUI/res/layout/hybrid_notification.xml @@ -19,23 +19,22 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="@*android:dimen/notification_content_margin_start" - android:paddingEnd="12dp" - android:gravity="bottom|start"> + android:gravity="bottom|start" + style="?attr/hybridNotificationStyle"> - + android:textAppearance="@*android:style/TextAppearance.Material.Notification" + style="?attr/hybridNotificationTextStyle" + /> + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/hybrid_overflow_number.xml b/packages/SystemUI/res/layout/hybrid_overflow_number.xml index f3dde8dcd4557..792f4242d28d5 100644 --- a/packages/SystemUI/res/layout/hybrid_overflow_number.xml +++ b/packages/SystemUI/res/layout/hybrid_overflow_number.xml @@ -20,7 +20,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="@*android:style/TextAppearance.Material.Notification" - android:paddingEnd="@*android:dimen/notification_content_margin_end" + android:paddingEnd="@dimen/group_overflow_number_padding" android:gravity="end" android:singleLine="true" /> \ No newline at end of file diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 008fcf0c62b49..a57b17e20f0b9 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -108,6 +108,12 @@ + + + + + + diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index f0728491bd728..537467322f293 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -57,6 +57,11 @@ 1.0 + @*android:dimen/notification_text_size + 16sp + @*android:dimen/notification_content_margin_end + @*android:dimen/notification_extra_margin_ambient + 100dp diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index dbdbd1e287aaf..9650cea20460b 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -82,6 +82,56 @@ @anim/forced_resizable_exit + + + + + + + + + + + + + + + + + + + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 93687478fc868..208be8c206b9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -227,6 +227,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private boolean mUseIncreasedHeadsUpHeight; private float mTranslationWhenRemoved; private boolean mWasChildInGroupWhenRemoved; + private int mNotificationColorAmbient; @Override public boolean isGroupExpansionChanging() { @@ -862,12 +863,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mNotificationColor = NotificationColorUtil.resolveContrastColor(mContext, getStatusBarNotification().getNotification().color, getBackgroundColorWithoutTint()); + mNotificationColorAmbient = NotificationColorUtil.resolveAmbientColor(mContext, + getStatusBarNotification().getNotification().color); } public HybridNotificationView getSingleLineView() { return mPrivateLayout.getSingleLineView(); } + public HybridNotificationView getAmbientSingleLineView() { + return getShowingLayout().getAmbientSingleLineChild(); + } + public boolean isOnKeyguard() { return mOnKeyguard; } @@ -1131,6 +1138,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mNotificationInflater; } + public int getNotificationColorAmbient() { + return mNotificationColorAmbient; + } + public interface ExpansionLogger { public void logNotificationExpansion(String key, boolean userAction, boolean expanded); } @@ -1509,13 +1520,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mGuts.getIntrinsicHeight(); } else if ((isChildInGroup() && !isGroupExpanded())) { return mPrivateLayout.getMinHeight(); - } else if (mShowAmbient) { - return getAmbientHeight(); } else if (mSensitive && mHideSensitiveForIntrinsicHeight) { return getMinHeight(); - } else if (mIsSummaryWithChildren && !mOnKeyguard) { + } else if (mIsSummaryWithChildren && (!mOnKeyguard || mShowAmbient)) { return mChildrenContainer.getIntrinsicHeight(); - } else if (!mOnKeyguard && (mIsHeadsUp || mHeadsupDisappearRunning)) { + } else if (isHeadsUpAllowed() && (mIsHeadsUp || mHeadsupDisappearRunning)) { if (isPinned() || mHeadsupDisappearRunning) { return getPinnedHeadsUpHeight(true /* atLeastMinHeight */); } else if (isExpanded()) { @@ -1530,6 +1539,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } + private boolean isHeadsUpAllowed() { + return !mOnKeyguard && !mShowAmbient; + } + @Override public boolean isGroupExpanded() { return mGroupManager.isGroupExpanded(mStatusBarNotification); @@ -1849,24 +1862,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public int getMinHeight() { if (mGuts != null && mGuts.isExposed()) { return mGuts.getIntrinsicHeight(); - } else if (!mOnKeyguard && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) { + } else if (isHeadsUpAllowed() && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) { return getPinnedHeadsUpHeight(false /* atLeastMinHeight */); } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) { return mChildrenContainer.getMinHeight(); - } else if (!mOnKeyguard && mIsHeadsUp) { + } else if (isHeadsUpAllowed() && mIsHeadsUp) { return mHeadsUpHeight; } NotificationContentView showingLayout = getShowingLayout(); return showingLayout.getMinHeight(); } - private int getAmbientHeight() { - NotificationContentView showingLayout = getShowingLayout(); - return showingLayout.getAmbientChild() != null - ? showingLayout.getAmbientChild().getHeight() - : getCollapsedHeight(); - } - @Override public int getCollapsedHeight() { if (mIsSummaryWithChildren && !mShowingPublic) { @@ -2101,10 +2107,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public void setShowAmbient(boolean showAmbient) { if (showAmbient != mShowAmbient) { mShowAmbient = showAmbient; + if (mChildrenContainer != null) { + mChildrenContainer.notifyShowAmbientChanged(); + } notifyHeightChanged(false /* needsAnimation */); } } + public boolean isShowingAmbient() { + return mShowAmbient; + } + public void setAboveShelf(boolean aboveShelf) { mAboveShelf = aboveShelf; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index e7bf98336e8c0..baf0c2cce2e0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -54,6 +54,7 @@ public class NotificationContentView extends FrameLayout { private static final int VISIBLE_TYPE_HEADSUP = 2; private static final int VISIBLE_TYPE_SINGLELINE = 3; private static final int VISIBLE_TYPE_AMBIENT = 4; + private static final int VISIBLE_TYPE_AMBIENT_SINGLELINE = 5; public static final int UNDEFINED = -1; private final Rect mClipBounds = new Rect(); @@ -65,6 +66,7 @@ public class NotificationContentView extends FrameLayout { private View mHeadsUpChild; private HybridNotificationView mSingleLineView; private View mAmbientChild; + private HybridNotificationView mAmbientSingleLineChild; private RemoteInputView mExpandedRemoteInput; private RemoteInputView mHeadsUpRemoteInput; @@ -252,6 +254,27 @@ public class NotificationContentView extends FrameLayout { : MeasureSpec.AT_MOST)); maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight()); } + if (mAmbientSingleLineChild != null) { + int size = Math.min(maxSize, mNotificationAmbientHeight); + ViewGroup.LayoutParams layoutParams = mAmbientSingleLineChild.getLayoutParams(); + boolean useExactly = false; + if (layoutParams.height >= 0) { + // An actual height is set + size = Math.min(size, layoutParams.height); + useExactly = true; + } + int ambientSingleLineWidthSpec = widthMeasureSpec; + if (mSingleLineWidthIndention != 0 + && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) { + ambientSingleLineWidthSpec = MeasureSpec.makeMeasureSpec( + width - mSingleLineWidthIndention + mAmbientSingleLineChild.getPaddingEnd(), + MeasureSpec.EXACTLY); + } + mAmbientSingleLineChild.measure(ambientSingleLineWidthSpec, + MeasureSpec.makeMeasureSpec(size, useExactly ? MeasureSpec.EXACTLY + : MeasureSpec.AT_MOST)); + maxChildHeight = Math.max(maxChildHeight, mAmbientSingleLineChild.getMeasuredHeight()); + } int ownHeight = Math.min(maxChildHeight, maxSize); setMeasuredDimension(width, ownHeight); } @@ -345,6 +368,10 @@ public class NotificationContentView extends FrameLayout { return mAmbientChild; } + public HybridNotificationView getAmbientSingleLineChild() { + return mAmbientSingleLineChild; + } + public void setContractedChild(View child) { if (mContractedChild != null) { mContractedChild.animate().cancel(); @@ -533,6 +560,9 @@ public class NotificationContentView extends FrameLayout { int hint; if (mAmbientChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) { hint = mAmbientChild.getHeight(); + } else if (mAmbientSingleLineChild != null && isVisibleOrTransitioning( + VISIBLE_TYPE_AMBIENT_SINGLELINE)) { + hint = mAmbientSingleLineChild.getHeight(); } else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) { hint = mHeadsUpChild.getHeight(); } else if (mExpandedChild != null) { @@ -622,7 +652,9 @@ public class NotificationContentView extends FrameLayout { } public int getMaxHeight() { - if (mExpandedChild != null) { + if (mContainingNotification.isShowingAmbient()) { + return getShowingAmbientView().getHeight(); + } else if (mExpandedChild != null) { return mExpandedChild.getHeight(); } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) { return mHeadsUpChild.getHeight(); @@ -635,13 +667,24 @@ public class NotificationContentView extends FrameLayout { } public int getMinHeight(boolean likeGroupExpanded) { - if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) { + if (mContainingNotification.isShowingAmbient()) { + return getShowingAmbientView().getHeight(); + } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) { return mContractedChild.getHeight(); } else { return mSingleLineView.getHeight(); } } + public View getShowingAmbientView() { + View v = mIsChildInGroup ? mAmbientSingleLineChild : mAmbientChild; + if (v != null) { + return v; + } else { + return mContractedChild; + } + } + private boolean isGroupExpanded() { return mGroupManager.isGroupExpanded(mStatusBarNotification); } @@ -723,6 +766,8 @@ public class NotificationContentView extends FrameLayout { forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper); forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView); forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper); + forceUpdateVisibility(VISIBLE_TYPE_AMBIENT_SINGLELINE, mAmbientSingleLineChild, + mAmbientSingleLineChild); fireExpandedVisibleListenerIfVisible(); // forceUpdateVisibilities cancels outstanding animations without updating the // mAnimationStartVisibleType. Do so here instead. @@ -791,6 +836,8 @@ public class NotificationContentView extends FrameLayout { mSingleLineView, mSingleLineView); updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper); + updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE, + mAmbientSingleLineChild, mAmbientSingleLineChild); fireExpandedVisibleListenerIfVisible(); // updateViewVisibilities cancels outstanding animations without updating the // mAnimationStartVisibleType. Do so here instead. @@ -853,6 +900,8 @@ public class NotificationContentView extends FrameLayout { return mSingleLineView; case VISIBLE_TYPE_AMBIENT: return mAmbientWrapper; + case VISIBLE_TYPE_AMBIENT_SINGLELINE: + return mAmbientSingleLineChild; default: return mContractedWrapper; } @@ -872,6 +921,8 @@ public class NotificationContentView extends FrameLayout { return mSingleLineView; case VISIBLE_TYPE_AMBIENT: return mAmbientChild; + case VISIBLE_TYPE_AMBIENT_SINGLELINE: + return mAmbientSingleLineChild; default: return mContractedChild; } @@ -896,9 +947,14 @@ public class NotificationContentView extends FrameLayout { * @return one of the static enum types in this view, calculated form the current state */ public int calculateVisibleType() { - if (mDark && !mIsChildInGroup) { - // TODO: Handle notification groups - return VISIBLE_TYPE_AMBIENT; + if (mContainingNotification.isShowingAmbient()) { + if (mIsChildInGroup && mAmbientSingleLineChild != null) { + return VISIBLE_TYPE_AMBIENT_SINGLELINE; + } else if (mAmbientChild != null) { + return VISIBLE_TYPE_AMBIENT; + } else { + return VISIBLE_TYPE_CONTRACTED; + } } if (mUserExpanding) { int height = !mIsChildInGroup || isGroupExpanded() @@ -1021,13 +1077,13 @@ public class NotificationContentView extends FrameLayout { if (mAmbientChild != null) { mAmbientWrapper.setIsChildInGroup(mIsChildInGroup); } - updateSingleLineView(); + updateAllSingleLineViews(); } public void onNotificationUpdated(NotificationData.Entry entry) { mStatusBarNotification = entry.notification; mBeforeN = entry.targetSdk < Build.VERSION_CODES.N; - updateSingleLineView(); + updateAllSingleLineViews(); if (mContractedChild != null) { mContractedWrapper.notifyContentUpdated(entry.row); } @@ -1048,6 +1104,10 @@ public class NotificationContentView extends FrameLayout { mPreviousHeadsUpRemoteInputIntent = null; } + private void updateAllSingleLineViews() { + updateSingleLineView(); + updateAmbientSingleLineView(); + } private void updateSingleLineView() { if (mIsChildInGroup) { mSingleLineView = mHybridGroupManager.bindFromNotification( @@ -1058,6 +1118,16 @@ public class NotificationContentView extends FrameLayout { } } + private void updateAmbientSingleLineView() { + if (mIsChildInGroup) { + mAmbientSingleLineChild = mHybridGroupManager.bindAmbientFromNotification( + mAmbientSingleLineChild, mStatusBarNotification.getNotification()); + } else if (mAmbientSingleLineChild != null) { + removeView(mAmbientSingleLineChild); + mAmbientSingleLineChild = null; + } + } + private void applyRemoteInput(final NotificationData.Entry entry) { if (mRemoteInputController == null) { return; @@ -1255,7 +1325,7 @@ public class NotificationContentView extends FrameLayout { if (mIsChildInGroup && mSingleLineView != null) { removeView(mSingleLineView); mSingleLineView = null; - updateSingleLineView(); + updateAllSingleLineViews(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java index 7373607501823..3ed8cce21ac54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java @@ -18,20 +18,14 @@ package com.android.systemui.statusbar.notification; import android.app.Notification; import android.content.Context; -import android.text.BidiFormatter; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import android.text.style.TextAppearanceSpan; +import android.content.res.Resources; +import android.util.TypedValue; +import android.view.ContextThemeWrapper; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.android.systemui.R; -import com.android.systemui.statusbar.ExpandableNotificationRow; - -import java.util.List; /** * A class managing hybrid groups that include {@link HybridNotificationView} and the notification @@ -40,16 +34,37 @@ import java.util.List; public class HybridGroupManager { private final Context mContext; - private ViewGroup mParent; + private final NotificationDozeHelper mDozer; + private final ViewGroup mParent; + + private final float mOverflowNumberSizeDark; + private final int mOverflowNumberPaddingDark; + private final float mOverflowNumberSize; + private final int mOverflowNumberPadding; + private int mOverflowNumberColor; + private int mOverflowNumberColorDark; + private float mDarkAmount = 0f; public HybridGroupManager(Context ctx, ViewGroup parent) { mContext = ctx; mParent = parent; + mDozer = new NotificationDozeHelper(); + + Resources res = mContext.getResources(); + mOverflowNumberSize = res.getDimensionPixelSize( + R.dimen.group_overflow_number_size); + mOverflowNumberSizeDark = res.getDimensionPixelSize( + R.dimen.group_overflow_number_size_dark); + mOverflowNumberPadding = res.getDimensionPixelSize( + R.dimen.group_overflow_number_padding); + mOverflowNumberPaddingDark = mOverflowNumberPadding + res.getDimensionPixelSize( + R.dimen.group_overflow_number_extra_padding_dark); } - private HybridNotificationView inflateHybridView() { - LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class); + private HybridNotificationView inflateHybridViewWithStyle(int style) { + LayoutInflater inflater = new ContextThemeWrapper(mContext, style) + .getSystemService(LayoutInflater.class); HybridNotificationView hybrid = (HybridNotificationView) inflater.inflate( R.layout.hybrid_notification, mParent, false); mParent.addView(hybrid); @@ -66,11 +81,13 @@ public class HybridGroupManager { } private void updateOverFlowNumberColor(TextView numberView) { - numberView.setTextColor(mOverflowNumberColor); + numberView.setTextColor(NotificationUtils.interpolateColors( + mOverflowNumberColor, mOverflowNumberColorDark, mDarkAmount)); } - public void setOverflowNumberColor(TextView numberView, int overflowNumberColor) { - mOverflowNumberColor = overflowNumberColor; + public void setOverflowNumberColor(TextView numberView, int colorRegular, int colorDark) { + mOverflowNumberColor = colorRegular; + mOverflowNumberColorDark = colorDark; if (numberView != null) { updateOverFlowNumberColor(numberView); } @@ -78,8 +95,20 @@ public class HybridGroupManager { public HybridNotificationView bindFromNotification(HybridNotificationView reusableView, Notification notification) { + return bindFromNotificationWithStyle(reusableView, notification, + R.style.HybridNotification); + } + + public HybridNotificationView bindAmbientFromNotification(HybridNotificationView reusableView, + Notification notification) { + return bindFromNotificationWithStyle(reusableView, notification, + R.style.HybridNotification_Ambient); + } + + private HybridNotificationView bindFromNotificationWithStyle( + HybridNotificationView reusableView, Notification notification, int style) { if (reusableView == null) { - reusableView = inflateHybridView(); + reusableView = inflateHybridViewWithStyle(style); } CharSequence titleText = resolveTitle(notification); CharSequence contentText = resolveText(notification); @@ -118,4 +147,16 @@ public class HybridGroupManager { reusableView.setContentDescription(contentDescription); return reusableView; } + + public void setOverflowNumberDark(TextView view, boolean dark, boolean fade, long delay) { + mDozer.setIntensityDark((f)->{ + mDarkAmount = f; + updateOverFlowNumberColor(view); + }, dark, fade, delay); + view.setTextSize(TypedValue.COMPLEX_UNIT_PX, + dark ? mOverflowNumberSizeDark : mOverflowNumberSize); + int paddingEnd = dark ? mOverflowNumberPaddingDark : mOverflowNumberPadding; + view.setPaddingRelative(view.getPaddingStart(), view.getPaddingTop(), paddingEnd, + view.getPaddingBottom()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java index fe249a6dc8bfb..cb1f44ededa7b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java @@ -30,7 +30,6 @@ import android.widget.RemoteViews; import android.widget.TextView; import com.android.systemui.R; -import com.android.systemui.ViewInvertHelper; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationHeaderUtil; @@ -39,7 +38,6 @@ import com.android.systemui.statusbar.notification.HybridNotificationView; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.NotificationViewWrapper; import com.android.systemui.statusbar.notification.VisualStabilityManager; -import com.android.systemui.statusbar.phone.NotificationPanelView; import java.util.ArrayList; import java.util.List; @@ -52,6 +50,7 @@ public class NotificationChildrenContainer extends ViewGroup { private static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2; private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5; private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8; + private static final int NUMBER_OF_CHILDREN_WHEN_AMBIENT = 3; private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() { private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); @@ -70,7 +69,6 @@ public class NotificationChildrenContainer extends ViewGroup { private int mNotificationHeaderMargin; private int mNotificatonTopPadding; private float mCollapsedBottompadding; - private ViewInvertHelper mOverflowInvertHelper; private boolean mChildrenExpanded; private ExpandableNotificationRow mContainingNotification; private TextView mOverflowNumber; @@ -85,12 +83,14 @@ public class NotificationChildrenContainer extends ViewGroup { private NotificationViewWrapper mNotificationHeaderWrapper; private NotificationHeaderView mNotificationHeaderLowPriority; private NotificationViewWrapper mNotificationHeaderWrapperLowPriority; + private ViewGroup mNotificationHeaderAmbient; + private NotificationViewWrapper mNotificationHeaderWrapperAmbient; private NotificationHeaderUtil mHeaderUtil; private ViewState mHeaderViewState; private int mClipBottomAmount; private boolean mIsLowPriority; private OnClickListener mHeaderClickListener; - private boolean mShowingNormalHeader; + private ViewGroup mCurrentHeader; public NotificationChildrenContainer(Context context) { this(context, null); @@ -152,6 +152,11 @@ public class NotificationChildrenContainer extends ViewGroup { mNotificationHeaderLowPriority.getMeasuredWidth(), mNotificationHeaderLowPriority.getMeasuredHeight()); } + if (mNotificationHeaderAmbient != null) { + mNotificationHeaderAmbient.layout(0, 0, + mNotificationHeaderAmbient.getMeasuredWidth(), + mNotificationHeaderAmbient.getMeasuredHeight()); + } } @Override @@ -203,6 +208,10 @@ public class NotificationChildrenContainer extends ViewGroup { headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY); mNotificationHeaderLowPriority.measure(widthMeasureSpec, headerHeightSpec); } + if (mNotificationHeaderAmbient != null) { + headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY); + mNotificationHeaderAmbient.measure(widthMeasureSpec, headerHeightSpec); + } setMeasuredDimension(width, height); } @@ -273,7 +282,7 @@ public class NotificationChildrenContainer extends ViewGroup { StatusBarNotification notification = mContainingNotification.getStatusBarNotification(); final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(), notification.getNotification()); - RemoteViews header = builder.makeNotificationHeader(); + RemoteViews header = builder.makeNotificationHeader(false /* ambient */); if (mNotificationHeader == null) { mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this); final View expandButton = mNotificationHeader.findViewById( @@ -289,10 +298,33 @@ public class NotificationChildrenContainer extends ViewGroup { } mNotificationHeaderWrapper.notifyContentUpdated(mContainingNotification); recreateLowPriorityHeader(builder); - updateHeaderVisibility(false /* animate */); + recreateAmbientHeader(builder); + resetHeaderVisibilityIfNeeded(mNotificationHeader, calculateDesiredHeader()); updateChildrenHeaderAppearance(); } + private void recreateAmbientHeader(Notification.Builder builder) { + RemoteViews header; + StatusBarNotification notification = mContainingNotification.getStatusBarNotification(); + if (builder == null) { + builder = Notification.Builder.recoverBuilder(getContext(), + notification.getNotification()); + } + header = builder.makeNotificationHeader(true /* ambient */); + if (mNotificationHeaderAmbient == null) { + mNotificationHeaderAmbient = (ViewGroup) header.apply(getContext(), this); + mNotificationHeaderWrapperAmbient = NotificationViewWrapper.wrap(getContext(), + mNotificationHeaderAmbient, mContainingNotification); + mNotificationHeaderWrapperAmbient.notifyContentUpdated(mContainingNotification); + addView(mNotificationHeaderAmbient, 0); + invalidate(); + } else { + header.reapply(getContext(), mNotificationHeaderAmbient); + } + resetHeaderVisibilityIfNeeded(mNotificationHeaderAmbient, calculateDesiredHeader()); + mNotificationHeaderWrapperAmbient.notifyContentUpdated(mContainingNotification); + } + /** * Recreate the low-priority header. * @@ -322,6 +354,7 @@ public class NotificationChildrenContainer extends ViewGroup { header.reapply(getContext(), mNotificationHeaderLowPriority); } mNotificationHeaderWrapperLowPriority.notifyContentUpdated(mContainingNotification); + resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, calculateDesiredHeader()); } else { removeView(mNotificationHeaderLowPriority); mNotificationHeaderLowPriority = null; @@ -339,10 +372,6 @@ public class NotificationChildrenContainer extends ViewGroup { if (childCount > maxAllowedVisibleChildren) { mOverflowNumber = mHybridGroupManager.bindOverflowNumber( mOverflowNumber, childCount - maxAllowedVisibleChildren); - if (mOverflowInvertHelper == null) { - mOverflowInvertHelper = new ViewInvertHelper(mOverflowNumber, - NotificationPanelView.DOZE_ANIMATION_DURATION); - } if (mGroupOverFlowState == null) { mGroupOverFlowState = new ViewState(); mNeverAppliedGroupState = true; @@ -360,7 +389,6 @@ public class NotificationChildrenContainer extends ViewGroup { }); } mOverflowNumber = null; - mOverflowInvertHelper = null; mGroupOverFlowState = null; } } @@ -449,6 +477,7 @@ public class NotificationChildrenContainer extends ViewGroup { if (mUserLocked) { expandFactor = getGroupExpandFraction(); } + boolean childrenExpanded = mChildrenExpanded || mContainingNotification.isShowingAmbient(); for (int i = 0; i < childCount; i++) { if (visibleChildren >= maxAllowedVisibleChildren) { break; @@ -458,7 +487,7 @@ public class NotificationChildrenContainer extends ViewGroup { intrinsicHeight += NotificationUtils.interpolate(mChildPadding, mDividerHeight, expandFactor); } else { - intrinsicHeight += mChildrenExpanded ? mDividerHeight : mChildPadding; + intrinsicHeight += childrenExpanded ? mDividerHeight : mChildPadding; } } else { if (mUserLocked) { @@ -467,7 +496,7 @@ public class NotificationChildrenContainer extends ViewGroup { mNotificatonTopPadding + mDividerHeight, expandFactor); } else { - intrinsicHeight += mChildrenExpanded + intrinsicHeight += childrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0; } @@ -480,7 +509,7 @@ public class NotificationChildrenContainer extends ViewGroup { if (mUserLocked) { intrinsicHeight += NotificationUtils.interpolate(mCollapsedBottompadding, 0.0f, expandFactor); - } else if (!mChildrenExpanded) { + } else if (!childrenExpanded) { intrinsicHeight += mCollapsedBottompadding; } return intrinsicHeight; @@ -515,7 +544,7 @@ public class NotificationChildrenContainer extends ViewGroup { yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight, expandFactor); } else { - yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding; + yPosition += childrenExpanded ? mDividerHeight : mChildPadding; } } else { if (expandingToExpandedGroup) { @@ -524,7 +553,7 @@ public class NotificationChildrenContainer extends ViewGroup { mNotificatonTopPadding + mDividerHeight, expandFactor); } else { - yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0; + yPosition += childrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0; } firstChild = false; } @@ -560,15 +589,21 @@ public class NotificationChildrenContainer extends ViewGroup { ExpandableNotificationRow overflowView = mChildren.get(Math.min( getMaxAllowedVisibleChildren(true /* likeCollpased */), childCount) - 1); mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView)); - if (!mChildrenExpanded) { - if (mUserLocked) { - HybridNotificationView singleLineView = overflowView.getSingleLineView(); - View mirrorView = singleLineView.getTextView(); + + if (mContainingNotification.isShowingAmbient() || !mChildrenExpanded) { + HybridNotificationView alignView = null; + if (mContainingNotification.isShowingAmbient()) { + alignView = overflowView.getAmbientSingleLineView(); + } else if (mUserLocked) { + alignView = overflowView.getSingleLineView(); + } + if (alignView != null) { + View mirrorView = alignView.getTextView(); if (mirrorView.getVisibility() == GONE) { - mirrorView = singleLineView.getTitleView(); + mirrorView = alignView.getTitleView(); } if (mirrorView.getVisibility() == GONE) { - mirrorView = singleLineView; + mirrorView = alignView; } mGroupOverFlowState.yTranslation += NotificationUtils.getRelativeYOffset( mirrorView, overflowView); @@ -620,6 +655,9 @@ public class NotificationChildrenContainer extends ViewGroup { } private int getMaxAllowedVisibleChildren(boolean likeCollapsed) { + if (mContainingNotification.isShowingAmbient()) { + return NUMBER_OF_CHILDREN_WHEN_AMBIENT; + } if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())) { return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED; } @@ -794,40 +832,78 @@ public class NotificationChildrenContainer extends ViewGroup { return mNotificationHeaderLowPriority; } + public void notifyShowAmbientChanged() { + updateHeaderVisibility(false); + } + private void updateHeaderVisibility(boolean animate) { - NotificationHeaderView visibleHeader = mNotificationHeader; - NotificationHeaderView hiddenHeader = mNotificationHeaderLowPriority; - boolean normalHeaderVisible = true; - if (showingAsLowPriority()) { - visibleHeader = mNotificationHeaderLowPriority; - hiddenHeader = mNotificationHeader; - normalHeaderVisible = false; + ViewGroup desiredHeader; + ViewGroup currentHeader = mCurrentHeader; + desiredHeader = calculateDesiredHeader(); + + if (currentHeader == desiredHeader) { + return; } + if (desiredHeader == mNotificationHeaderAmbient + || currentHeader == mNotificationHeaderAmbient) { + animate = false; + } + if (animate) { - if (visibleHeader != null && hiddenHeader != null - && mShowingNormalHeader != normalHeaderVisible) { - hiddenHeader.setVisibility(VISIBLE); - visibleHeader.setVisibility(VISIBLE); - NotificationViewWrapper visibleWrapper = getWrapperForView(visibleHeader); - NotificationViewWrapper hiddenWrapper = getWrapperForView(hiddenHeader); + if (desiredHeader != null && currentHeader != null) { + currentHeader.setVisibility(VISIBLE); + desiredHeader.setVisibility(VISIBLE); + NotificationViewWrapper visibleWrapper = getWrapperForView(desiredHeader); + NotificationViewWrapper hiddenWrapper = getWrapperForView(currentHeader); visibleWrapper.transformFrom(hiddenWrapper); hiddenWrapper.transformTo(visibleWrapper, () -> updateHeaderVisibility(false)); - startChildAlphaAnimations(normalHeaderVisible); + startChildAlphaAnimations(desiredHeader == mNotificationHeader); } else { animate = false; } } if (!animate) { - if (visibleHeader != null) { - getWrapperForView(visibleHeader).setVisible(true); - visibleHeader.setVisibility(VISIBLE); + if (desiredHeader != null) { + getWrapperForView(desiredHeader).setVisible(true); + desiredHeader.setVisibility(VISIBLE); } - if (hiddenHeader != null) { - getWrapperForView(hiddenHeader).setVisible(false); - hiddenHeader.setVisibility(INVISIBLE); + if (currentHeader != null) { + getWrapperForView(currentHeader).setVisible(false); + currentHeader.setVisibility(INVISIBLE); } } - mShowingNormalHeader = normalHeaderVisible; + + resetHeaderVisibilityIfNeeded(mNotificationHeader, desiredHeader); + resetHeaderVisibilityIfNeeded(mNotificationHeaderAmbient, desiredHeader); + resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, desiredHeader); + + mCurrentHeader = currentHeader; + } + + private void resetHeaderVisibilityIfNeeded(View header, View desiredHeader) { + if (header == null) { + return; + } + if (header != mCurrentHeader && header != desiredHeader) { + getWrapperForView(header).setVisible(false); + header.setVisibility(INVISIBLE); + } + if (header == desiredHeader && header.getVisibility() != VISIBLE) { + getWrapperForView(header).setVisible(true); + header.setVisibility(VISIBLE); + } + } + + private ViewGroup calculateDesiredHeader() { + ViewGroup desiredHeader; + if (mContainingNotification.isShowingAmbient()) { + desiredHeader = mNotificationHeaderAmbient; + } else if (showingAsLowPriority()) { + desiredHeader = mNotificationHeaderLowPriority; + } else { + desiredHeader = mNotificationHeader; + } + return desiredHeader; } private void startChildAlphaAnimations(boolean toVisible) { @@ -861,10 +937,13 @@ public class NotificationChildrenContainer extends ViewGroup { } - private NotificationViewWrapper getWrapperForView(NotificationHeaderView visibleHeader) { + private NotificationViewWrapper getWrapperForView(View visibleHeader) { if (visibleHeader == mNotificationHeader) { return mNotificationHeaderWrapper; } + if (visibleHeader == mNotificationHeaderAmbient) { + return mNotificationHeaderWrapperAmbient; + } return mNotificationHeaderWrapperLowPriority; } @@ -971,7 +1050,9 @@ public class NotificationChildrenContainer extends ViewGroup { } public int getMinHeight() { - return getMinHeight(NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */); + return getMinHeight(mContainingNotification.isShowingAmbient() + ? NUMBER_OF_CHILDREN_WHEN_AMBIENT + : NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */); } public int getCollapsedHeight() { @@ -1016,7 +1097,7 @@ public class NotificationChildrenContainer extends ViewGroup { public void setDark(boolean dark, boolean fade, long delay) { if (mOverflowNumber != null) { - mOverflowInvertHelper.setInverted(dark, fade, delay); + mHybridGroupManager.setOverflowNumberDark(mOverflowNumber, dark, fade, delay); } mNotificationHeaderWrapper.setDark(dark, fade, delay); } @@ -1030,6 +1111,10 @@ public class NotificationChildrenContainer extends ViewGroup { removeView(mNotificationHeaderLowPriority); mNotificationHeaderLowPriority = null; } + if (mNotificationHeaderAmbient != null) { + removeView(mNotificationHeaderAmbient); + mNotificationHeaderAmbient = null; + } recreateNotificationHeader(listener); initDimens(); for (int i = 0; i < mDividers.size(); i++) { @@ -1042,7 +1127,6 @@ public class NotificationChildrenContainer extends ViewGroup { } removeView(mOverflowNumber); mOverflowNumber = null; - mOverflowInvertHelper = null; mGroupOverFlowState = null; updateGroupOverflow(); } @@ -1061,7 +1145,8 @@ public class NotificationChildrenContainer extends ViewGroup { public void onNotificationUpdated() { mHybridGroupManager.setOverflowNumberColor(mOverflowNumber, - mContainingNotification.getNotificationColor()); + mContainingNotification.getNotificationColor(), + mContainingNotification.getNotificationColorAmbient()); } public int getPositionInLinearLayout(View childInGroup) {