Changed the appearance of notification bundles

Children now show up as one line notifications even
if the summary is not expanded. The childrenContainer
shows a summary if there are some which don't fit
in there currently.

Bug: 24866646
Change-Id: I0cfae9342722c9f8941f51704618190cfe4e76b4
This commit is contained in:
Selim Cinek
2015-10-22 13:26:54 -07:00
parent 8d6440dbab
commit 83bc783384
15 changed files with 570 additions and 150 deletions

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 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
-->
<com.android.systemui.statusbar.notification.HybridNotificationView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_max_height">
<TextView
android:id="@+id/notification_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:singleLine="true"
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
android:paddingEnd="4dp"
/>
<TextView
android:id="@+id/notification_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textAppearance="@*android:style/TextAppearance.Material.Notification"
android:singleLine="true"
/>
</com.android.systemui.statusbar.notification.HybridNotificationView>

View File

@@ -36,6 +36,18 @@
<!-- The font size for the clock -->
<dimen name="status_bar_clock_size">14sp</dimen>
<!-- The margin on the start of the content view -->
<dimen name="notification_content_margin_start">16dp</dimen>
<!-- The maximum size of the title when in single line mode -->
<dimen name="notification_maximum_title_length">150sp</dimen>
<!-- The margin on the end of the content view -->
<dimen name="notification_content_margin_end">8dp</dimen>
<!-- Height of a single line notification in the status bar -->
<dimen name="notification_single_line_height">32sp</dimen>
<!-- Height of a small notification in the status bar -->
<dimen name="notification_min_height">64dp</dimen>

View File

@@ -1026,6 +1026,9 @@
<!-- VolumeUI restoration notification: text -->
<string name="volumeui_notification_text">Touch to restore the original.</string>
<!-- Describes the way 2 names are concatenated. An example would be ", " to produce "Peter Muller, Paul Curry". Please also include a space here if it's appropriate in the language and if it's a RTL language include it on the left. [CHAR LIMIT=3] -->
<string name="group_summary_concadenation">, </string>
<!-- Toast shown when user unlocks screen and managed profile activity is in the foreground -->
<string name="managed_profile_foreground_toast">You\'re using your work profile</string>

View File

@@ -1929,27 +1929,29 @@ public abstract class BaseStatusBar extends SystemUI implements
for (int i = 0; i < N; i++) {
NotificationData.Entry entry = activeNotifications.get(i);
if (onKeyguard) {
entry.row.setExpansionDisabled(true);
entry.row.setOnKeyguard(true);
} else {
entry.row.setExpansionDisabled(false);
if (!entry.row.isUserLocked()) {
boolean top = (i == 0);
entry.row.setSystemExpanded(top);
}
entry.row.setOnKeyguard(false);
boolean top = (i == 0);
entry.row.setSystemExpanded(top);
}
boolean isInvisibleChild = !mGroupManager.isVisible(entry.notification);
boolean childNotification = mGroupManager.isChildInGroupWithSummary(entry.notification);
boolean childWithVisibleSummary = childNotification
&& mGroupManager.getGroupSummary(entry.notification).getVisibility()
== View.VISIBLE;
boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
(onKeyguard && (visibleNotifications >= maxKeyguardNotifications
|| !showOnKeyguard || isInvisibleChild))) {
&& !childWithVisibleSummary
|| !showOnKeyguard))) {
entry.row.setVisibility(View.GONE);
if (onKeyguard && showOnKeyguard && !isInvisibleChild) {
if (onKeyguard && showOnKeyguard && !childNotification) {
mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
}
} else {
boolean wasGone = entry.row.getVisibility() == View.GONE;
entry.row.setVisibility(View.VISIBLE);
if (!isInvisibleChild) {
if (!childNotification) {
if (wasGone) {
// notify the scroller of a child addition
mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */);

View File

@@ -70,9 +70,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private boolean mIsSystemExpanded;
/**
* Whether the notification expansion is disabled. This is the case on Keyguard.
* Whether the notification is on the keyguard and the expansion is disabled.
*/
private boolean mExpansionDisabled;
private boolean mOnKeyguard;
private NotificationContentView mPublicLayout;
private NotificationContentView mPrivateLayout;
@@ -162,6 +162,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private void setStatusBarNotification(StatusBarNotification statusBarNotification) {
mStatusBarNotification = statusBarNotification;
mPrivateLayout.setStatusBarNotification(statusBarNotification);
updateVetoButton();
onChildrenCountChanged();
}
@@ -185,6 +186,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
public void setGroupManager(NotificationGroupManager groupManager) {
mGroupManager = groupManager;
mPrivateLayout.setGroupManager(groupManager);
}
public void addChildNotification(ExpandableNotificationRow row) {
@@ -225,6 +227,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {
mChildInGroup = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
mShowNoBackground = mChildInGroup && hasSameBgColor(parent);
mPrivateLayout.setIsChildInGroup(mShowNoBackground);
updateBackground();
}
@@ -265,34 +268,34 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
public void getChildrenStates(StackScrollState resultState) {
if (mChildrenExpanded) {
if (mIsSummaryWithChildren) {
StackViewState parentState = resultState.getViewStateForView(this);
mChildrenContainer.getState(resultState, parentState);
}
}
public void applyChildrenState(StackScrollState state) {
if (mChildrenExpanded) {
if (mIsSummaryWithChildren) {
mChildrenContainer.applyState(state);
}
}
public void prepareExpansionChanged(StackScrollState state) {
if (mChildrenExpanded) {
if (mIsSummaryWithChildren) {
mChildrenContainer.prepareExpansionChanged(state);
}
}
public void startChildAnimation(StackScrollState finalState,
StackStateAnimator stateAnimator, boolean withDelays, long delay, long duration) {
if (mChildrenExpanded) {
if (mIsSummaryWithChildren) {
mChildrenContainer.startAnimationToState(finalState, stateAnimator, withDelays, delay,
duration);
}
}
public ExpandableNotificationRow getViewAtPosition(float y) {
if (!mChildrenExpanded) {
if (!mIsSummaryWithChildren || !mChildrenExpanded) {
return this;
} else {
ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
@@ -416,7 +419,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
mSensitive = false;
mShowingPublicInitialized = false;
mIsSystemExpanded = false;
mExpansionDisabled = false;
mOnKeyguard = false;
mPublicLayout.reset(mIsHeadsUp);
mPrivateLayout.reset(mIsHeadsUp);
resetHeight();
@@ -580,16 +583,19 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
mIsSystemExpanded = expand;
notifyHeightChanged(false /* needsAnimation */);
logExpansionEvent(false, wasExpanded);
if (mChildrenContainer != null) {
mChildrenContainer.updateGroupOverflow();
}
}
}
/**
* @param expansionDisabled whether to prevent notification expansion
* @param onKeyguard whether to prevent notification expansion
*/
public void setExpansionDisabled(boolean expansionDisabled) {
if (expansionDisabled != mExpansionDisabled) {
public void setOnKeyguard(boolean onKeyguard) {
if (onKeyguard != mOnKeyguard) {
final boolean wasExpanded = isExpanded();
mExpansionDisabled = expansionDisabled;
mOnKeyguard = onKeyguard;
logExpansionEvent(false, wasExpanded);
if (wasExpanded != isExpanded()) {
notifyHeightChanged(false /* needsAnimation */);
@@ -622,23 +628,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
return getActualHeight();
}
boolean inExpansionState = isExpanded();
int maxContentHeight;
if (mSensitive && mHideSensitiveForIntrinsicHeight) {
return mRowMinHeight;
} else if (mIsSummaryWithChildren && !mOnKeyguard) {
return mChildrenContainer.getIntrinsicHeight()
+ mNotificationHeader.getHeight();
} else if (mIsHeadsUp) {
if (inExpansionState) {
maxContentHeight = Math.max(mMaxExpandHeight, mHeadsUpHeight);
return Math.max(mMaxExpandHeight, mHeadsUpHeight);
} else {
maxContentHeight = Math.max(mRowMinHeight, mHeadsUpHeight);
return Math.max(mRowMinHeight, mHeadsUpHeight);
}
} else if ((!inExpansionState && !mChildrenExpanded)) {
maxContentHeight = mRowMinHeight;
} else if (mChildrenExpanded) {
maxContentHeight = mChildrenContainer.getIntrinsicHeight();
} else if (!inExpansionState || (mChildInGroup && !isGroupExpanded())) {
return getMinHeight();
} else {
maxContentHeight = getMaxExpandHeight();
return getMaxExpandHeight();
}
return maxContentHeight;
}
private boolean isGroupExpanded() {
@@ -670,8 +675,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
*
* @return whether the view state is currently expanded.
*/
private boolean isExpanded() {
return !mExpansionDisabled
public boolean isExpanded() {
return !mOnKeyguard
&& (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
|| isUserExpanded());
}
@@ -778,6 +783,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
public void setChildrenExpanded(boolean expanded, boolean animate) {
mChildrenExpanded = expanded;
if (mChildrenContainer != null) {
mChildrenContainer.setChildrenExpanded(expanded);
}
}
public void updateNotificationHeader() {
@@ -844,12 +852,20 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
@Override
public int getMaxContentHeight() {
if (mIsSummaryWithChildren && !mShowingPublic) {
return mChildrenContainer.getMaxContentHeight()
+ mNotificationHeader.getHeight();
}
NotificationContentView showingLayout = getShowingLayout();
return showingLayout.getMaxHeight();
}
@Override
public int getMinHeight() {
if (mIsSummaryWithChildren && !mOnKeyguard) {
return mChildrenContainer.getMinHeight()
+ mNotificationHeader.getHeight();
}
NotificationContentView showingLayout = getShowingLayout();
return showingLayout.getMinHeight();
}

View File

@@ -22,6 +22,7 @@ import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -32,6 +33,9 @@ import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.HybridNotificationView;
import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
/**
* A frame layout containing the actual payload of the notification, including the contracted,
@@ -44,8 +48,10 @@ public class NotificationContentView extends FrameLayout {
private static final int VISIBLE_TYPE_CONTRACTED = 0;
private static final int VISIBLE_TYPE_EXPANDED = 1;
private static final int VISIBLE_TYPE_HEADSUP = 2;
private static final int VISIBLE_TYPE_SINGLELINE = 3;
private final Rect mClipBounds = new Rect();
private final int mSingleLineHeight;
private final int mSmallHeight;
private final int mHeadsUpHeight;
private final int mRoundRectRadius;
@@ -55,10 +61,12 @@ public class NotificationContentView extends FrameLayout {
private View mContractedChild;
private View mExpandedChild;
private View mHeadsUpChild;
private HybridNotificationView mSingleLineView;
private NotificationViewWrapper mContractedWrapper;
private NotificationViewWrapper mExpandedWrapper;
private NotificationViewWrapper mHeadsUpWrapper;
private HybridNotificationViewManager mHybridViewManager;
private int mClipTopAmount;
private int mContentHeight;
private int mUnrestrictedContentHeight;
@@ -68,6 +76,8 @@ public class NotificationContentView extends FrameLayout {
private boolean mAnimate;
private boolean mIsHeadsUp;
private boolean mShowingLegacyBackground;
private boolean mIsChildInGroup;
private StatusBarNotification mStatusBarNotification;
private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
= new ViewTreeObserver.OnPreDrawListener() {
@@ -86,10 +96,14 @@ public class NotificationContentView extends FrameLayout {
mRoundRectRadius);
}
};
private NotificationGroupManager mGroupManager;
public NotificationContentView(Context context, AttributeSet attrs) {
super(context, attrs);
mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
mSingleLineHeight = getResources().getDimensionPixelSize(
R.dimen.notification_single_line_height);
mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
mHeadsUpHeight = getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
mRoundRectRadius = getResources().getDimensionPixelSize(
@@ -140,6 +154,12 @@ public class NotificationContentView extends FrameLayout {
MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
maxChildHeight = Math.max(maxChildHeight, mHeadsUpChild.getMeasuredHeight());
}
if (mSingleLineView != null) {
int size = Math.min(maxSize, mSingleLineHeight);
mSingleLineView.measure(widthMeasureSpec,
MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY));
maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
}
int ownHeight = Math.min(maxChildHeight, maxSize);
int width = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(width, ownHeight);
@@ -271,7 +291,15 @@ public class NotificationContentView extends FrameLayout {
}
public int getMinHeight() {
return mSmallHeight;
if (mIsChildInGroup && !isGroupExpanded()) {
return mSingleLineHeight;
} else {
return mSmallHeight;
}
}
private boolean isGroupExpanded() {
return mGroupManager.isGroupExpanded(mStatusBarNotification);
}
public void setClipTopAmount(int clipTopAmount) {
@@ -313,6 +341,7 @@ public class NotificationContentView extends FrameLayout {
if (visibleType != mVisibleType || force) {
if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
|| (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
|| (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
|| visibleType == VISIBLE_TYPE_CONTRACTED)) {
runSwitchAnimation(visibleType);
} else {
@@ -339,6 +368,12 @@ public class NotificationContentView extends FrameLayout {
mHeadsUpChild.setAlpha(headsUpVisible ? 1f : 0f);
mHeadsUpChild.setLayerType(LAYER_TYPE_NONE, null);
}
if (mSingleLineView != null) {
boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
mSingleLineView.setVisibility(singleLineVisible ? View.VISIBLE : View.INVISIBLE);
mSingleLineView.setAlpha(singleLineVisible ? 1f : 0f);
mSingleLineView.setLayerType(LAYER_TYPE_NONE, null);
}
setLayerType(LAYER_TYPE_NONE, null);
updateRoundRectClipping();
}
@@ -379,6 +414,8 @@ public class NotificationContentView extends FrameLayout {
return mExpandedChild;
case VISIBLE_TYPE_HEADSUP:
return mHeadsUpChild;
case VISIBLE_TYPE_SINGLELINE:
return mSingleLineView;
default:
return mContractedChild;
}
@@ -396,7 +433,9 @@ public class NotificationContentView extends FrameLayout {
return VISIBLE_TYPE_EXPANDED;
}
} else {
if (mContentHeight <= mSmallHeight || noExpandedChild) {
if (mIsChildInGroup && !isGroupExpanded()) {
return VISIBLE_TYPE_SINGLELINE;
} else if (mContentHeight <= mSmallHeight || noExpandedChild) {
return VISIBLE_TYPE_CONTRACTED;
} else {
return VISIBLE_TYPE_EXPANDED;
@@ -405,6 +444,7 @@ public class NotificationContentView extends FrameLayout {
}
public void notifyContentUpdated() {
updateSingleLineView();
selectLayout(false /* animate */, true /* force */);
if (mContractedChild != null) {
mContractedWrapper.notifyContentUpdated();
@@ -443,6 +483,23 @@ public class NotificationContentView extends FrameLayout {
mShowingLegacyBackground = showing;
}
public void setIsChildInGroup(boolean isChildInGroup) {
mIsChildInGroup = isChildInGroup;
updateSingleLineView();
}
public void setStatusBarNotification(StatusBarNotification statusBarNotification) {
mStatusBarNotification = statusBarNotification;
updateSingleLineView();
}
private void updateSingleLineView() {
if (mIsChildInGroup) {
mSingleLineView = mHybridViewManager.bindFromNotification(
mSingleLineView, mStatusBarNotification.getNotification());
}
}
public void setSubTextVisible(boolean visible) {
if (mExpandedChild != null) {
mExpandedWrapper.setSubTextVisible(visible);
@@ -454,4 +511,8 @@ public class NotificationContentView extends FrameLayout {
mHeadsUpWrapper.setSubTextVisible(visible);
}
}
public void setGroupManager(NotificationGroupManager groupManager) {
mGroupManager = groupManager;
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2015 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.annotation.Nullable;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
/**
* A hybrid view which may contain information about one ore more notifications.
*/
public class HybridNotificationView extends AlphaOptimizedFrameLayout {
protected final int mSingleLineHeight;
protected final int mStartMargin;
protected final int mEndMargin;
protected TextView mTitleView;
protected TextView mTextView;
public HybridNotificationView(Context context) {
this(context, null);
}
public HybridNotificationView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public HybridNotificationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public HybridNotificationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mSingleLineHeight = context.getResources().getDimensionPixelSize(
R.dimen.notification_single_line_height);
mStartMargin = context.getResources().getDimensionPixelSize(
R.dimen.notification_content_margin_start);
mEndMargin = context.getResources().getDimensionPixelSize(
R.dimen.notification_content_margin_end);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int totalWidth = MeasureSpec.getSize(widthMeasureSpec);
int remainingWidth = totalWidth - mStartMargin - mEndMargin;
int newHeightSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.AT_MOST);
int newWidthSpec = MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST);
mTitleView.measure(newWidthSpec, newHeightSpec);
int maxTitleLength = getResources().getDimensionPixelSize(
R.dimen.notification_maximum_title_length);
int titleWidth = mTitleView.getMeasuredWidth();
int heightSpec = MeasureSpec.makeMeasureSpec(mSingleLineHeight, MeasureSpec.AT_MOST);
boolean hasText = !TextUtils.isEmpty(mTextView.getText());
if (titleWidth > maxTitleLength && hasText) {
titleWidth = maxTitleLength;
int widthSpec = MeasureSpec.makeMeasureSpec(titleWidth, MeasureSpec.EXACTLY);
mTitleView.measure(widthSpec, heightSpec);
}
if (hasText) {
remainingWidth -= titleWidth;
int widthSpec = MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST);
mTextView.measure(widthSpec, newHeightSpec);
}
setMeasuredDimension(totalWidth, mSingleLineHeight);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childLeft = mStartMargin;
int childRight = childLeft + mTitleView.getMeasuredWidth();
int childBottom = (mSingleLineHeight + mTitleView.getMeasuredHeight()) / 2;
int childTop = childBottom - mTitleView.getMeasuredHeight();
int rtlLeft = transformForRtl(childLeft);
int rtlRight = transformForRtl(childRight);
mTitleView.layout(Math.min(rtlLeft, rtlRight), childTop, Math.max(rtlLeft, rtlRight),
childBottom);
childLeft = childRight;
childRight = childLeft + mTextView.getMeasuredWidth();
childTop = mTitleView.getTop() + mTitleView.getBaseline() - mTextView.getBaseline();
childBottom = childTop + mTextView.getMeasuredHeight();
rtlLeft = transformForRtl(childLeft);
rtlRight = transformForRtl(childRight);
mTextView.layout(Math.min(rtlLeft, rtlRight), childTop, Math.max(rtlLeft, rtlRight),
childBottom);
}
private int transformForRtl(int left) {
return isLayoutRtl() ? getWidth() - left : left;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTitleView = (TextView) findViewById(R.id.notification_title);
mTextView = (TextView) findViewById(R.id.notification_text);
}
public void bind(CharSequence title) {
bind(title, null);
}
public void bind(CharSequence title, CharSequence text) {
mTitleView.setText(title);
mTextView.setText(text);
requestLayout();
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2015 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.app.Notification;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import java.util.List;
/**
* A class managing {@link HybridNotificationView} views
*/
public class HybridNotificationViewManager {
private final Context mContext;
private ViewGroup mParent;
private String mConcadenationString;
public HybridNotificationViewManager(Context ctx, ViewGroup parent) {
mContext = ctx;
mParent = parent;
mConcadenationString = mContext.getString(R.string.group_summary_concadenation);
}
private HybridNotificationView inflateHybridView() {
LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
HybridNotificationView hybrid = (HybridNotificationView) inflater.inflate(
R.layout.hybrid_notification, mParent, false);
mParent.addView(hybrid);
return hybrid;
}
public HybridNotificationView bindFromNotification(HybridNotificationView reusableView,
Notification notification) {
CharSequence titleText = resolveTitle(notification);
if (titleText == null) {
if (reusableView != null) {
mParent.removeView(reusableView);
}
return null;
}
if (reusableView == null) {
reusableView = inflateHybridView();
}
CharSequence contentText = resolveText(notification);
reusableView.bind(titleText, contentText);
return reusableView;
}
private CharSequence resolveText(Notification notification) {
CharSequence contentText = notification.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
if (contentText == null) {
contentText = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
}
return contentText;
}
private CharSequence resolveTitle(Notification notification) {
CharSequence titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
if (titleText == null) {
titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
}
return titleText;
}
public HybridNotificationView bindFromNotificationGroup(
HybridNotificationView reusableView,
List<ExpandableNotificationRow> group, int startIndex) {
if (reusableView == null) {
reusableView = inflateHybridView();
}
CharSequence summary = null;
int childCount = group.size();
for (int i = startIndex; i < childCount; i++) {
ExpandableNotificationRow child = group.get(i);
CharSequence titleText = resolveTitle(
child.getStatusBarNotification().getNotification());
if (titleText == null) {
continue;
}
if (TextUtils.isEmpty(summary)) {
summary = titleText;
} else if (reusableView.isLayoutRtl()) {
summary = titleText + mConcadenationString + summary;
} else {
summary = summary + mConcadenationString + titleText;
}
}
reusableView.bind(summary);
return reusableView;
}
}

View File

@@ -21,7 +21,6 @@ import android.service.notification.StatusBarNotification;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
import java.util.HashMap;
import java.util.HashSet;
@@ -93,21 +92,8 @@ public class NotificationGroupManager {
if (group.children.isEmpty()) {
if (group.summary == null) {
mGroupMap.remove(groupKey);
} else {
if (group.expanded) {
// only the summary is left. Change it to unexpanded in a few ms. We do this to
// avoid raceconditions
removed.row.post(new Runnable() {
@Override
public void run() {
if (group.children.isEmpty()) {
setGroupExpanded(sbn, false);
}
}
});
} else {
group.summary.row.updateNotificationHeader();
}
} else if (!group.expanded) {
group.summary.row.updateNotificationHeader();
}
}
}
@@ -155,9 +141,6 @@ public class NotificationGroupManager {
}
public boolean hasGroupChildren(StatusBarNotification sbn) {
if (areGroupsProhibited()) {
return false;
}
if (!sbn.getNotification().isGroupSummary()) {
return false;
}
@@ -168,29 +151,6 @@ public class NotificationGroupManager {
return !group.children.isEmpty();
}
public void setStatusBarState(int newState) {
if (mBarState == newState) {
return;
}
boolean prohibitedBefore = areGroupsProhibited();
mBarState = newState;
boolean nowProhibited = areGroupsProhibited();
if (nowProhibited != prohibitedBefore) {
if (nowProhibited) {
for (NotificationGroup group : mGroupMap.values()) {
if (group.expanded) {
setGroupExpanded(group, false);
}
}
}
mListener.onGroupsProhibitedChanged();
}
}
private boolean areGroupsProhibited() {
return mBarState == StatusBarState.KEYGUARD;
}
/**
* @return whether a given notification is a child in a group which has a summary
*/
@@ -226,6 +186,18 @@ public class NotificationGroupManager {
: group.summary.row;
}
public void onEntryHeadsUped(NotificationData.Entry headsUp) {
// TODO: handle this nicely
}
public void toggleGroupExpansion(StatusBarNotification sbn) {
NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
if (group == null) {
return;
}
setGroupExpanded(group, !group.expanded);
}
public static class NotificationGroup {
public final HashSet<NotificationData.Entry> children = new HashSet<>();
public NotificationData.Entry summary;
@@ -241,11 +213,6 @@ public class NotificationGroupManager {
*/
void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded);
/**
* Children group policy has changed and children may no be prohibited or allowed.
*/
void onGroupsProhibitedChanged();
/**
* A group of children just received a summary notification and should therefore become
* children of it.

View File

@@ -714,7 +714,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
R.color.notification_panel_solid_background)));
}
mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow);
mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow, mGroupManager);
mHeadsUpManager.setBar(this);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
@@ -3859,7 +3859,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
clearNotificationEffects();
}
mState = state;
mGroupManager.setStatusBarState(state);
mFalsingManager.setStatusBarState(state);
mStatusBarWindowManager.setStatusBarState(state);
updateDozing();

View File

@@ -33,6 +33,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import java.io.FileDescriptor;
@@ -85,6 +86,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
private final int mStatusBarHeight;
private final int mNotificationsTopPadding;
private final Context mContext;
private final NotificationGroupManager mGroupManager;
private PhoneStatusBar mBar;
private int mSnoozeLengthMs;
private ContentObserver mSettingsObserver;
@@ -104,7 +106,8 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
private boolean mIsObserving;
private boolean mRemoteInputActive;
public HeadsUpManager(final Context context, View statusBarWindowView) {
public HeadsUpManager(final Context context, View statusBarWindowView,
NotificationGroupManager groupManager) {
mContext = context;
Resources resources = mContext.getResources();
mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
@@ -132,6 +135,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
mSettingsObserver);
mStatusBarWindowView = statusBarWindowView;
mGroupManager = groupManager;
mStatusBarHeight = resources.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
mNotificationsTopPadding = context.getResources()
@@ -181,12 +185,12 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
if (DEBUG) Log.v(TAG, "updateNotification");
headsUp.row.setChildrenExpanded(false /* expanded */, false /* animated */);
headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
if (alert) {
HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(headsUp.key);
headsUpEntry.updateEntry();
mGroupManager.onEntryHeadsUped(headsUp);
setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp));
}
}

View File

@@ -24,7 +24,8 @@ import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.notification.HybridNotificationView;
import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
import java.util.ArrayList;
import java.util.List;
@@ -34,6 +35,10 @@ import java.util.List;
*/
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 final int mChildPadding;
private final int mDividerHeight;
private final int mMaxNotificationHeight;
@@ -42,6 +47,12 @@ public class NotificationChildrenContainer extends ViewGroup {
private final int mNotificationHeaderHeight;
private final int mNotificationAppearDistance;
private final float mHeaderTopPaddingSubstraction;
private final HybridNotificationViewManager mHybridViewManager;
private final float mCollapsedBottompadding;
private boolean mChildrenExpanded;
private ExpandableNotificationRow mNotificationParent;
private HybridNotificationView mGroupOverflowContainer;
private ViewState mGroupOverFlowState;
public NotificationChildrenContainer(Context context) {
this(context, null);
@@ -69,6 +80,8 @@ public class NotificationChildrenContainer extends ViewGroup {
mNotificationHeaderHeight = getResources().getDimensionPixelSize(
R.dimen.notification_header_height);
mHeaderTopPaddingSubstraction = 2 * getResources().getDisplayMetrics().density;
mCollapsedBottompadding = 10 * getResources().getDisplayMetrics().density;
mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
}
@Override
@@ -78,14 +91,6 @@ public class NotificationChildrenContainer extends ViewGroup {
for (int i = 0; i < childCount; i++) {
View child = mChildren.get(i);
boolean viewGone = child.getVisibility() == View.GONE;
if (i != 0) {
View divider = mDividers.get(i - 1);
int dividerVisibility = divider.getVisibility();
int newVisibility = viewGone ? INVISIBLE : VISIBLE;
if (dividerVisibility != newVisibility) {
divider.setVisibility(newVisibility);
}
}
if (viewGone) {
continue;
}
@@ -96,6 +101,10 @@ public class NotificationChildrenContainer extends ViewGroup {
firstChild = false;
}
}
if (mGroupOverflowContainer != null) {
mGroupOverflowContainer.layout(0, 0, getWidth(),
mGroupOverflowContainer.getMeasuredHeight());
}
}
@Override
@@ -115,9 +124,6 @@ public class NotificationChildrenContainer extends ViewGroup {
boolean firstChild = true;
for (int i = 0; i < childCount; i++) {
View child = mChildren.get(i);
if (child.getVisibility() == View.GONE) {
continue;
}
child.measure(widthMeasureSpec, newHeightSpec);
height += child.getMeasuredHeight();
if (!firstChild) {
@@ -130,9 +136,9 @@ public class NotificationChildrenContainer extends ViewGroup {
}
}
int width = MeasureSpec.getSize(widthMeasureSpec);
height = hasFixedHeight ? ownMaxHeight
: isHeightLimited ? Math.min(ownMaxHeight, height)
: height;
if (mGroupOverflowContainer != null) {
mGroupOverflowContainer.measure(widthMeasureSpec, newHeightSpec);
}
setMeasuredDimension(width, height);
}
@@ -151,8 +157,7 @@ public class NotificationChildrenContainer extends ViewGroup {
addView(divider);
mDividers.add(Math.max(newIndex - 1, 0), divider);
}
// TODO: adapt background corners
// TODO: fix overdraw
updateGroupOverflow();
}
public void removeNotification(ExpandableNotificationRow row) {
@@ -164,7 +169,26 @@ public class NotificationChildrenContainer extends ViewGroup {
removeView(divider);
}
row.setSystemChildExpanded(false);
// TODO: adapt background corners
updateGroupOverflow();
}
public void updateGroupOverflow() {
int childCount = mChildren.size();
int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
boolean hasOverflow = childCount > maxAllowedVisibleChildren;
int lastVisibleIndex = hasOverflow ? maxAllowedVisibleChildren - 2
: maxAllowedVisibleChildren - 1;
if (hasOverflow) {
mGroupOverflowContainer = mHybridViewManager.bindFromNotificationGroup(
mGroupOverflowContainer, mChildren, lastVisibleIndex + 1);
if (mGroupOverFlowState == null) {
mGroupOverFlowState = new ViewState();
}
} else if (mGroupOverflowContainer != null) {
removeView(mGroupOverflowContainer);
mGroupOverflowContainer = null;
mGroupOverFlowState = null;
}
}
private View inflateDivider() {
@@ -196,25 +220,42 @@ public class NotificationChildrenContainer extends ViewGroup {
result = true;
}
}
// Let's make the first child expanded!
boolean first = true;
for (int i = 0; i < childOrder.size(); i++) {
ExpandableNotificationRow child = childOrder.get(i);
child.setSystemChildExpanded(first);
first = false;
}
updateExpansionStates();
return result;
}
private void updateExpansionStates() {
// Let's make the first child expanded if the parent is
for (int i = 0; i < mChildren.size(); i++) {
ExpandableNotificationRow child = mChildren.get(i);
child.setSystemChildExpanded(false);
}
}
/**
*
* @return the intrinsic size of this children container, i.e the natural fully expanded state
*/
public int getIntrinsicHeight() {
int childCount = mChildren.size();
int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
return getIntrinsicHeight(maxAllowedVisibleChildren);
}
/**
* @return the intrinsic height with a number of children given
* in @param maxAllowedVisibleChildren
*/
private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
int intrinsicHeight = 0;
int visibleChildren = 0;
int childCount = mChildren.size();
for (int i = 0; i < childCount; i++) {
if (visibleChildren >= maxAllowedVisibleChildren) {
break;
}
ExpandableNotificationRow child = mChildren.get(i);
if (child.getVisibility() == View.GONE) {
continue;
if (i == 0 && child.hasSameBgColor(mNotificationParent)) {
intrinsicHeight -= mHeaderTopPaddingSubstraction;
}
intrinsicHeight += child.getIntrinsicHeight();
visibleChildren++;
@@ -222,6 +263,9 @@ public class NotificationChildrenContainer extends ViewGroup {
if (visibleChildren > 0) {
intrinsicHeight += (visibleChildren - 1) * mDividerHeight;
}
if (!mChildrenExpanded) {
intrinsicHeight += mCollapsedBottompadding;
}
return intrinsicHeight;
}
@@ -235,15 +279,21 @@ public class NotificationChildrenContainer extends ViewGroup {
int childCount = mChildren.size();
int yPosition = mNotificationHeaderHeight;
boolean firstChild = true;
int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
boolean hasOverflow = !mChildrenExpanded && childCount > maxAllowedVisibleChildren
&& maxAllowedVisibleChildren != NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
int lastVisibleIndex = hasOverflow
? maxAllowedVisibleChildren - 2
: maxAllowedVisibleChildren - 1;
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
if (child.getVisibility() == View.GONE) {
continue;
}
if (!firstChild) {
// There's a divider
yPosition += mChildPadding;
} else {
if (child.hasSameBgColor(mNotificationParent)) {
yPosition -= mHeaderTopPaddingSubstraction;
}
firstChild = false;
}
StackViewState childState = resultState.getViewStateForView(child);
@@ -255,37 +305,62 @@ public class NotificationChildrenContainer extends ViewGroup {
childState.dark = parentState.dark;
childState.hideSensitive = parentState.hideSensitive;
childState.belowSpeedBump = parentState.belowSpeedBump;
childState.scale = parentState.scale;
childState.scale = 1.0f;
childState.clipTopAmount = 0;
childState.topOverLap = 0;
boolean visible = i <= lastVisibleIndex;
childState.alpha = visible ? 1 : 0;
childState.location = parentState.location;
yPosition += intrinsicHeight;
}
if (mGroupOverflowContainer != null) {
mGroupOverFlowState.initFrom(mGroupOverflowContainer);
if (hasOverflow) {
StackViewState firstOverflowState =
resultState.getViewStateForView(mChildren.get(lastVisibleIndex + 1));
mGroupOverFlowState.yTranslation = firstOverflowState.yTranslation;
}
mGroupOverFlowState.alpha = mChildrenExpanded || !hasOverflow ? 0.0f : 1.0f;
}
}
private int getMaxAllowedVisibleChildren() {
return getMaxAllowedVisibleChildren(false /* likeCollapsed */);
}
private int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
if (!likeCollapsed && (mChildrenExpanded || mNotificationParent.isUserLocked())) {
return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
}
if (mNotificationParent.isExpanded()) {
return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
}
return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
}
public void applyState(StackScrollState state) {
int childCount = mChildren.size();
boolean firstChild = true;
ViewState dividerState = new ViewState();
ViewState tmpState = new ViewState();
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
StackViewState viewState = state.getViewStateForView(child);
if (child.getVisibility() == View.GONE) {
continue;
}
if (!firstChild) {
// layout the divider
View divider = mDividers.get(i - 1);
dividerState.initFrom(divider);
dividerState.yTranslation = (int) (viewState.yTranslation
tmpState.initFrom(divider);
tmpState.yTranslation = (int) (viewState.yTranslation
- (mChildPadding + mDividerHeight) / 2.0f);
dividerState.alpha = 1;
state.applyViewState(divider, dividerState);
tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
state.applyViewState(divider, tmpState);
} else {
firstChild = false;
}
state.applyState(child, viewState);
}
if (mGroupOverflowContainer != null) {
state.applyViewState(mGroupOverflowContainer, mGroupOverFlowState);
}
}
/**
@@ -295,6 +370,10 @@ public class NotificationChildrenContainer extends ViewGroup {
* @param state the new state we animate to
*/
public void prepareExpansionChanged(StackScrollState state) {
if (true) {
// TODO: do something that makes sense
return;
}
int childCount = mChildren.size();
boolean firstChild = true;
StackViewState sourceState = new StackViewState();
@@ -302,9 +381,6 @@ public class NotificationChildrenContainer extends ViewGroup {
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
StackViewState viewState = state.getViewStateForView(child);
if (child.getVisibility() == View.GONE) {
continue;
}
if (!firstChild) {
// layout the divider
View divider = mDividers.get(i - 1);
@@ -321,23 +397,17 @@ public class NotificationChildrenContainer extends ViewGroup {
sourceState.yTranslation += mNotificationAppearDistance;
state.applyState(child, sourceState);
}
mCollapseButton.setAlpha(0);
mCollapseDivider.setAlpha(0);
mCollapseDivider.setTranslationY(mNotificationAppearDistance / 4);
}
public void startAnimationToState(StackScrollState state, StackStateAnimator stateAnimator,
boolean withDelays, long baseDelay, long duration) {
int childCount = mChildren.size();
boolean firstChild = true;
ViewState dividerState = new ViewState();
ViewState tmpState = new ViewState();
int notGoneIndex = 0;
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
StackViewState viewState = state.getViewStateForView(child);
if (child.getVisibility() == View.GONE) {
continue;
}
int difference = Math.min(StackStateAnimator.DELAY_EFFECT_MAX_INDEX_DIFFERENCE_CHILDREN,
notGoneIndex + 1);
long delay = withDelays
@@ -347,17 +417,20 @@ public class NotificationChildrenContainer extends ViewGroup {
if (!firstChild) {
// layout the divider
View divider = mDividers.get(i - 1);
dividerState.initFrom(divider);
dividerState.yTranslation = viewState.yTranslation
tmpState.initFrom(divider);
tmpState.yTranslation = viewState.yTranslation
- (mChildPadding + mDividerHeight) / 2.0f;
dividerState.alpha = 1;
stateAnimator.startViewAnimations(divider, dividerState, delay, duration);
tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;;
stateAnimator.startViewAnimations(divider, tmpState, delay, duration);
} else {
firstChild = false;
}
stateAnimator.startStackAnimations(child, viewState, state, -1, delay);
notGoneIndex++;
}
if (mGroupOverflowContainer != null) {
stateAnimator.startViewAnimations(mGroupOverflowContainer, mGroupOverFlowState, -1, 0);
}
}
public ExpandableNotificationRow getViewAtPosition(float y) {
@@ -375,7 +448,15 @@ public class NotificationChildrenContainer extends ViewGroup {
return null;
}
public void setTintColor(int color) {
ExpandableNotificationRow.applyTint(mCollapseDivider, color);
public void setChildrenExpanded(boolean childrenExpanded) {
mChildrenExpanded = childrenExpanded;
}
public int getMaxContentHeight() {
return getIntrinsicHeight(NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
}
public int getMinHeight() {
return getIntrinsicHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */));
}
}

View File

@@ -1726,7 +1726,7 @@ public class NotificationStackScrollLayout extends ViewGroup
ExpandableNotificationRow groupSummary =
mGroupManager.getGroupSummary(row.getStatusBarNotification());
if (groupSummary != null && groupSummary != row) {
return !groupSummary.areChildrenExpanded();
return row.getVisibility() == View.INVISIBLE;
}
}
return false;
@@ -2799,10 +2799,6 @@ public class NotificationStackScrollLayout extends ViewGroup
onHeightChanged(changedRow, false /* needsAnimation */);
}
@Override
public void onGroupsProhibitedChanged() {
}
@Override
public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
for (NotificationData.Entry entry : group.children) {

View File

@@ -361,7 +361,7 @@ public class StackScrollAlgorithm {
// handle the notgoneIndex for the children as well
List<ExpandableNotificationRow> children =
row.getNotificationChildren();
if (row.areChildrenExpanded() && children != null) {
if (row.isSummaryWithChildren() && children != null) {
for (ExpandableNotificationRow childRow : children) {
if (childRow.getVisibility() != View.GONE) {
StackViewState childState

View File

@@ -65,7 +65,7 @@ public class StackScrollState {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
List<ExpandableNotificationRow> children =
row.getNotificationChildren();
if (row.areChildrenExpanded() && children != null) {
if (row.isSummaryWithChildren() && children != null) {
for (ExpandableNotificationRow childRow : children) {
resetViewState(childRow);
}