summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorSelim Cinek <cinek@google.com>2015-02-20 18:21:41 +0100
committerSelim Cinek <cinek@google.com>2015-03-13 12:51:20 -0700
commitb5605e58cb8080c8c887b1885336b707596c8094 (patch)
tree92a8a10be14fe0304ecbc3788744b7b261177a8d /packages
parentab29aebf00a0ebd286a92d129f35c182b6888f3b (diff)
downloadframeworks_base-b5605e58cb8080c8c887b1885336b707596c8094.zip
frameworks_base-b5605e58cb8080c8c887b1885336b707596c8094.tar.gz
frameworks_base-b5605e58cb8080c8c887b1885336b707596c8094.tar.bz2
Enable surfacing of notification children
Yo Dawg, I herd you like notifications, so I put a notification in your notification so you can be interrupted while you are being interrupted. Bug: 15869874 Bug: 15188947 Change-Id: I6c733d6f8e8a04f85036182f82d3e945c6feb5bc
Diffstat (limited to 'packages')
-rw-r--r--packages/SystemUI/res/drawable/ic_collapse_children.xml19
-rw-r--r--packages/SystemUI/res/drawable/ic_expand_children.xml18
-rw-r--r--packages/SystemUI/res/drawable/ic_expand_less.xml24
-rw-r--r--packages/SystemUI/res/drawable/ic_expand_more.xml24
-rw-r--r--packages/SystemUI/res/layout/notification_children_container.xml21
-rw-r--r--packages/SystemUI/res/layout/notification_children_divider.xml23
-rw-r--r--packages/SystemUI/res/layout/notification_collapse_button.xml33
-rw-r--r--packages/SystemUI/res/layout/notification_expand_button.xml40
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_row.xml16
-rw-r--r--packages/SystemUI/res/values/dimens.xml12
-rw-r--r--packages/SystemUI/res/values/strings.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/ExpandHelper.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedButton.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java382
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java116
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java406
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java152
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java17
31 files changed, 1500 insertions, 80 deletions
diff --git a/packages/SystemUI/res/drawable/ic_collapse_children.xml b/packages/SystemUI/res/drawable/ic_collapse_children.xml
new file mode 100644
index 0000000..b0ce1e6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_collapse_children.xml
@@ -0,0 +1,19 @@
+<!--
+ ~ 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
+ -->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetTop="2dp"
+ android:drawable="@drawable/ic_expand_less"/>
diff --git a/packages/SystemUI/res/drawable/ic_expand_children.xml b/packages/SystemUI/res/drawable/ic_expand_children.xml
new file mode 100644
index 0000000..1762be4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_expand_children.xml
@@ -0,0 +1,18 @@
+<!--
+ ~ 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
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetTop="2dp"
+ android:drawable="@drawable/ic_expand_more"/>
diff --git a/packages/SystemUI/res/drawable/ic_expand_less.xml b/packages/SystemUI/res/drawable/ic_expand_less.xml
new file mode 100644
index 0000000..e968013
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_expand_less.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20.0dp"
+ android:height="20.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M12.000000,8.000000l-6.000000,6.000000 1.400000,1.400000 4.600000,-4.599999 4.600000,4.599999 1.400000,-1.400000z"
+ android:fillColor="#FF000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_expand_more.xml b/packages/SystemUI/res/drawable/ic_expand_more.xml
new file mode 100644
index 0000000..72e98ec
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_expand_more.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20.0dp"
+ android:height="20.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M16.600000,8.600000l-4.600000,4.599999 -4.600000,-4.599999 -1.400000,1.400000 6.000000,6.000000 6.000000,-6.000000z"
+ android:fillColor="#FF000000"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/notification_children_container.xml b/packages/SystemUI/res/layout/notification_children_container.xml
new file mode 100644
index 0000000..ac6a000
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_children_container.xml
@@ -0,0 +1,21 @@
+<?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.stack.NotificationChildrenContainer
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
diff --git a/packages/SystemUI/res/layout/notification_children_divider.xml b/packages/SystemUI/res/layout/notification_children_divider.xml
new file mode 100644
index 0000000..f011afe
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_children_divider.xml
@@ -0,0 +1,23 @@
+<?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.AlphaOptimizedView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notification_more_divider"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_children_divider_height"
+ android:background="@*android:drawable/notification_template_divider" />
diff --git a/packages/SystemUI/res/layout/notification_collapse_button.xml b/packages/SystemUI/res/layout/notification_collapse_button.xml
new file mode 100644
index 0000000..3ec5f63
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_collapse_button.xml
@@ -0,0 +1,33 @@
+<?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
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_bottom_decor_height">
+ <TextView
+ android:id="@+id/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/notification_bottom_decor_height"
+ android:background="@null"
+ android:layout_gravity="top|center_horizontal"
+ android:gravity="center_vertical|center_horizontal"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+ android:text="@string/notification_collapse_button_text"
+ android:drawableEnd="@drawable/ic_collapse_children"
+ android:drawablePadding="1dp"
+ />
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/notification_expand_button.xml b/packages/SystemUI/res/layout/notification_expand_button.xml
new file mode 100644
index 0000000..3c478f7
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_expand_button.xml
@@ -0,0 +1,40 @@
+<?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
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_bottom_decor_height">
+ <TextView
+ android:id="@+id/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/notification_bottom_decor_height"
+ android:background="@null"
+ android:layout_gravity="top|center_horizontal"
+ android:gravity="center_vertical|center_horizontal"
+ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
+ android:text="@string/notification_expand_button_text"
+ android:drawableEnd="@drawable/ic_expand_children"
+ android:drawablePadding="1dp"
+ />
+ <com.android.systemui.statusbar.AlphaOptimizedView
+ android:id="@+id/notification_expand_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_gravity="top|center"
+ android:background="@*android:drawable/notification_template_divider" />
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index e9d86d6..ea7ce96 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -53,6 +53,22 @@
/>
<ViewStub
+ android:layout="@layout/notification_children_container"
+ android:id="@+id/child_container_stub"
+ android:inflatedId="@+id/notification_children_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <ViewStub
+ android:layout="@layout/notification_expand_button"
+ android:id="@+id/more_button_stub"
+ android:inflatedId="@+id/notification_more_button_container"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_bottom_decor_height"
+ />
+
+ <ViewStub
android:layout="@layout/notification_guts"
android:id="@+id/notification_guts_stub"
android:inflatedId="@+id/notification_guts"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 95a4009..c24cd64 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -376,6 +376,18 @@
<!-- radius of the corners of the material rounded rect background but negative-->
<dimen name="notification_material_rounded_rect_radius_negative">-2dp</dimen>
+ <!-- height of the bottom decor below the notification if present (eg. an expand action) -->
+ <dimen name="notification_bottom_decor_height">48dp</dimen>
+
+ <!-- The padding between notification children -->
+ <dimen name="notification_children_padding">2dp</dimen>
+
+ <!-- The height of the divider between the notfication children -->
+ <dimen name="notification_children_divider_height">1dp</dimen>
+
+ <!-- The vertical distance from which the notification appear when children are expanded -->
+ <dimen name="notification_appear_distance">140dp</dimen>
+
<!-- end margin for multi user switch in expanded quick settings -->
<dimen name="multi_user_switch_expanded_margin">8dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1b1b525..d9aff44 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -924,6 +924,12 @@
<!-- continue action for notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=20] -->
<string name="hidden_notifications_setup">Set up</string>
+ <!-- Text for the button to expand the notifications to show notification children [CHAR LIMIT=20] -->
+ <string name="notification_expand_button_text">See all</string>
+
+ <!-- Text for the button to expand the notifications to hide notification children [CHAR LIMIT=20] -->
+ <string name="notification_collapse_button_text">Hide all</string>
+
<!-- Accessibility string for current zen mode and selected exit condition. A template that simply concatenates existing mode string and the current condition description. [CHAR LIMIT=20] -->
<string name="zen_mode_and_condition"><xliff:g id="zen_mode" example="Priority interruptions only">%1$s</xliff:g>. <xliff:g id="exit_condition" example="For one hour">%2$s</xliff:g></string>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index aa7ce4e..bc7f745 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -146,14 +146,14 @@ public class ExpandHelper implements Gefingerpoken {
}
public void setHeight(float h) {
if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h);
- mView.setActualHeight((int) h);
+ mView.setContentHeight((int) h);
mCurrentHeight = h;
}
public float getHeight() {
- return mView.getActualHeight();
+ return mView.getContentHeight();
}
public int getNaturalHeight(int maximum) {
- return Math.min(maximum, mView.getMaxHeight());
+ return Math.min(maximum, mView.getMaxContentHeight());
}
}
@@ -386,7 +386,8 @@ public class ExpandHelper implements Gefingerpoken {
}
private boolean isFullyExpanded(ExpandableView underFocus) {
- return underFocus.getIntrinsicHeight() == underFocus.getMaxHeight();
+ return underFocus.areChildrenExpanded() || underFocus.getIntrinsicHeight()
+ - underFocus.getBottomDecorHeight() == underFocus.getMaxContentHeight();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index e021cd4..0e5fd94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -94,7 +94,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
= new PathInterpolator(0, 0, 0.5f, 1);
private final int mTintedRippleColor;
private final int mLowPriorityRippleColor;
- private final int mNormalRippleColor;
+ protected final int mNormalRippleColor;
private boolean mDimmed;
private boolean mDark;
@@ -115,7 +115,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private OnActivatedListener mOnActivatedListener;
private final Interpolator mLinearOutSlowInInterpolator;
- private final Interpolator mFastOutSlowInInterpolator;
+ protected final Interpolator mFastOutSlowInInterpolator;
private final Interpolator mSlowOutFastInInterpolator;
private final Interpolator mSlowOutLinearInInterpolator;
private final Interpolator mLinearInterpolator;
@@ -678,7 +678,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
}
- private int getRippleColor() {
+ protected int getRippleColor() {
if (mBgTint != 0) {
return mTintedRippleColor;
} else if (mShowingLegacyBackground) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedButton.java
new file mode 100644
index 0000000..87c12c2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedButton.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Button;
+
+/**
+ * A Button which doesn't have overlapping drawing commands
+ */
+public class AlphaOptimizedButton extends Button {
+ public AlphaOptimizedButton(Context context) {
+ super(context);
+ }
+
+ public AlphaOptimizedButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AlphaOptimizedButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public AlphaOptimizedButton(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index d92ae44..fab7409 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -122,6 +122,8 @@ public abstract class BaseStatusBar extends SystemUI implements
public static final boolean ENABLE_REMOTE_INPUT =
Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.enable_remote_input", false);
+ public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
+ && SystemProperties.getBoolean("debug.child_notifs", false);
protected static final int MSG_SHOW_RECENT_APPS = 1019;
protected static final int MSG_HIDE_RECENT_APPS = 1020;
@@ -437,10 +439,12 @@ public abstract class BaseStatusBar extends SystemUI implements
boolean isUpdate = mNotificationData.get(sbn.getKey()) != null
|| isHeadsUp(sbn.getKey());
- // Ignore children of notifications that have a summary, since we're not
- // going to show them anyway. This is true also when the summary is canceled,
+ // In case we don't allow child notifications, we ignore children of
+ // notifications that have a summary, since we're not going to show them
+ // anyway. This is true also when the summary is canceled,
// because children are automatically canceled by NoMan in that case.
- if (mGroupManager.isChildInGroupWithSummary(sbn)) {
+ if (!ENABLE_CHILD_NOTIFICATIONS
+ && mGroupManager.isChildInGroupWithSummary(sbn)) {
if (DEBUG) {
Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
}
@@ -1352,6 +1356,7 @@ public abstract class BaseStatusBar extends SystemUI implements
row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
parent, false);
row.setExpansionLogger(this, entry.notification.getKey());
+ row.setGroupManager(mGroupManager);
}
workAroundBadLayerDrawableOpacity(row);
@@ -1867,22 +1872,25 @@ public abstract class BaseStatusBar extends SystemUI implements
entry.row.setSystemExpanded(top);
}
}
+ boolean isInvisibleChild = !mGroupManager.isVisible(entry.notification);
boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
(onKeyguard && (visibleNotifications >= maxKeyguardNotifications
- || !showOnKeyguard))) {
+ || !showOnKeyguard || isInvisibleChild))) {
entry.row.setVisibility(View.GONE);
- if (onKeyguard && showOnKeyguard) {
+ if (onKeyguard && showOnKeyguard && !isInvisibleChild) {
mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
}
} else {
boolean wasGone = entry.row.getVisibility() == View.GONE;
entry.row.setVisibility(View.VISIBLE);
- if (wasGone) {
- // notify the scroller of a child addition
- mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */);
+ if (!isInvisibleChild) {
+ if (wasGone) {
+ // notify the scroller of a child addition
+ mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */);
+ }
+ visibleNotifications++;
}
- visibleNotifications++;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index c9f0260..15a092c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -162,20 +162,20 @@ public class DragDownHelper implements Gefingerpoken {
? RUBBERBAND_FACTOR_EXPANDABLE
: RUBBERBAND_FACTOR_STATIC;
float rubberband = heightDelta * rubberbandFactor;
- if (expandable && (rubberband + child.getMinHeight()) > child.getMaxHeight()) {
- float overshoot = (rubberband + child.getMinHeight()) - child.getMaxHeight();
+ if (expandable && (rubberband + child.getMinHeight()) > child.getMaxContentHeight()) {
+ float overshoot = (rubberband + child.getMinHeight()) - child.getMaxContentHeight();
overshoot *= (1 - RUBBERBAND_FACTOR_STATIC);
rubberband -= overshoot;
}
- child.setActualHeight((int) (child.getMinHeight() + rubberband));
+ child.setContentHeight((int) (child.getMinHeight() + rubberband));
}
private void cancelExpansion(final ExpandableView child) {
- if (child.getActualHeight() == child.getMinHeight()) {
+ if (child.getContentHeight() == child.getMinHeight()) {
return;
}
- ObjectAnimator anim = ObjectAnimator.ofInt(child, "actualHeight",
- child.getActualHeight(), child.getMinHeight());
+ ObjectAnimator anim = ObjectAnimator.ofInt(child, "contentHeight",
+ child.getContentHeight(), child.getMinHeight());
anim.setInterpolator(mInterpolator);
anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
anim.addListener(new AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 788db29..06a174e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -16,9 +16,13 @@
package com.android.systemui.statusbar;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
@@ -26,10 +30,24 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewStub;
import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
+
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.stack.StackScrollState;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.stack.StackViewState;
+
+import java.util.List;
public class ExpandableNotificationRow extends ActivatableNotificationView {
+
+ private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
+ private static final int COLORED_DIVIDER_ALPHA = 0x7B;
+ private final LinearInterpolator mLinearInterpolator = new LinearInterpolator();
private int mRowMinHeight;
/** Does this row contain layouts that can adapt to row expansion */
@@ -69,7 +87,27 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private StatusBarNotification mStatusBarNotification;
private boolean mIsHeadsUp;
+ private View mExpandButton;
+ private View mExpandButtonDivider;
+ private ViewStub mExpandButtonStub;
+ private ViewStub mChildrenContainerStub;
+ private NotificationGroupManager mGroupManager;
+ private View mExpandButtonContainer;
+ private boolean mChildrenExpanded;
+ private NotificationChildrenContainer mChildrenContainer;
+ private ValueAnimator mChildExpandAnimator;
+ private float mChildrenExpandProgress;
+ private float mExpandButtonStart;
private ViewStub mGutsStub;
+ private boolean mHasExpandAction;
+ private boolean mIsSystemChildExpanded;
+ private OnClickListener mExpandClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mGroupManager.setGroupExpanded(mStatusBarNotification,
+ !mChildrenExpanded);
+ }
+ };
public void setIconAnimationRunning(boolean running) {
setIconAnimationRunning(running, mPublicLayout);
@@ -119,6 +157,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
public void setStatusBarNotification(StatusBarNotification statusBarNotification) {
mStatusBarNotification = statusBarNotification;
updateVetoButton();
+ updateExpandButton();
}
public StatusBarNotification getStatusBarNotification() {
@@ -129,10 +168,101 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
mIsHeadsUp = isHeadsUp;
}
+ public void setGroupManager(NotificationGroupManager groupManager) {
+ mGroupManager = groupManager;
+ }
+
+ public void addChildNotification(ExpandableNotificationRow row) {
+ addChildNotification(row, -1);
+ }
+
+ /**
+ * Add a child notification to this view.
+ *
+ * @param row the row to add
+ * @param childIndex the index to add it at, if -1 it will be added at the end
+ */
+ public void addChildNotification(ExpandableNotificationRow row, int childIndex) {
+ if (mChildrenContainer == null) {
+ mChildrenContainerStub.inflate();
+ }
+ mChildrenContainer.addNotification(row, childIndex);
+ }
+
+ public void removeChildNotification(ExpandableNotificationRow row) {
+ if (mChildrenContainer != null) {
+ mChildrenContainer.removeNotification(row);
+ }
+ }
+
+ @Override
+ public boolean areChildrenExpanded() {
+ return mChildrenExpanded;
+ }
+
+ public List<ExpandableNotificationRow> getNotificationChildren() {
+ return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
+ }
+
+ /**
+ * Apply the order given in the list to the children.
+ *
+ * @param childOrder the new list order
+ * @return whether the list order has changed
+ */
+ public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder) {
+ return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder);
+ }
+
+ public void getChildrenStates(StackScrollState resultState) {
+ if (mChildrenExpanded) {
+ StackViewState parentState = resultState.getViewStateForView(this);
+ mChildrenContainer.getState(resultState, parentState);
+ }
+ }
+
+ public void applyChildrenState(StackScrollState state) {
+ if (mChildrenExpanded) {
+ mChildrenContainer.applyState(state);
+ }
+ }
+
+ public void prepareExpansionChanged(StackScrollState state) {
+ if (mChildrenExpanded) {
+ mChildrenContainer.prepareExpansionChanged(state);
+ }
+ }
+
+ public void startChildAnimation(StackScrollState finalState,
+ StackStateAnimator stateAnimator, boolean withDelays, long delay, long duration) {
+ if (mChildrenExpanded) {
+ mChildrenContainer.startAnimationToState(finalState, stateAnimator, withDelays, delay,
+ duration);
+ }
+ }
+
+ public ExpandableNotificationRow getViewAtPosition(float y) {
+ if (!mChildrenExpanded) {
+ return this;
+ } else {
+ ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
+ return view == null ? this : view;
+ }
+ }
+
public NotificationGuts getGuts() {
return mGuts;
}
+ protected int calculateContentHeightFromActualHeight(int actualHeight) {
+ int realActualHeight = actualHeight;
+ if (hasBottomDecor()) {
+ realActualHeight -= getBottomDecorHeight();
+ }
+ realActualHeight = Math.max(getMinHeight(), realActualHeight);
+ return realActualHeight;
+ }
+
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
@@ -194,6 +324,27 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
mGutsStub = null;
}
});
+ mExpandButtonStub = (ViewStub) findViewById(R.id.more_button_stub);
+ mExpandButtonStub.setOnInflateListener(new ViewStub.OnInflateListener() {
+
+ @Override
+ public void onInflate(ViewStub stub, View inflated) {
+ mExpandButtonContainer = inflated;
+ mExpandButton = inflated.findViewById(R.id.notification_expand_button);
+ mExpandButtonDivider = inflated.findViewById(R.id.notification_expand_divider);
+ mExpandButtonContainer.setOnClickListener(mExpandClickListener);
+ }
+ });
+ mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
+ mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() {
+
+ @Override
+ public void onInflate(ViewStub stub, View inflated) {
+ mChildrenContainer = (NotificationChildrenContainer) inflated;
+ mChildrenContainer.setCollapseClickListener(mExpandClickListener);
+ updateChildrenVisibility(false);
+ }
+ });
mVetoButton = findViewById(R.id.veto);
}
@@ -203,6 +354,54 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
}
+ private void updateChildrenVisibility(boolean animated) {
+ if (mChildrenContainer == null) {
+ return;
+ }
+ if (mChildExpandAnimator != null) {
+ mChildExpandAnimator.cancel();
+ }
+ float targetProgress = mChildrenExpanded ? 1.0f : 0.0f;
+ if (animated) {
+ if (mChildrenExpanded) {
+ mChildrenContainer.setVisibility(VISIBLE);
+ }
+ mExpandButtonStart = mExpandButtonContainer.getTranslationY();
+ mChildExpandAnimator = ValueAnimator.ofFloat(mChildrenExpandProgress, targetProgress);
+ mChildExpandAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ setChildrenExpandProgress((float) animation.getAnimatedValue());
+ }
+ });
+ mChildExpandAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mChildExpandAnimator = null;
+ if (!mChildrenExpanded) {
+ mChildrenContainer.setVisibility(INVISIBLE);
+ }
+ }
+ });
+ mChildExpandAnimator.setInterpolator(mLinearInterpolator);
+ mChildExpandAnimator.setDuration(
+ StackStateAnimator.ANIMATION_DURATION_EXPAND_CLICKED);
+ mChildExpandAnimator.start();
+ } else {
+ setChildrenExpandProgress(targetProgress);
+ mChildrenContainer.setVisibility(mChildrenExpanded ? VISIBLE : INVISIBLE);
+ }
+ }
+
+ private void setChildrenExpandProgress(float progress) {
+ mChildrenExpandProgress = progress;
+ updateExpandButtonAppearance();
+ NotificationContentView showingLayout = getShowingLayout();
+ float alpha = 1.0f - mChildrenExpandProgress;
+ alpha = PhoneStatusBar.ALPHA_OUT.getInterpolation(alpha);
+ showingLayout.setAlpha(alpha);
+ }
+
@Override
public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
if (super.onRequestSendAccessibilityEventInternal(child, event)) {
@@ -292,7 +491,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
if (expand != mIsSystemExpanded) {
final boolean wasExpanded = isExpanded();
mIsSystemExpanded = expand;
- notifyHeightChanged();
+ notifyHeightChanged(false /* needsAnimation */);
logExpansionEvent(false, wasExpanded);
}
}
@@ -306,7 +505,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
mExpansionDisabled = expansionDisabled;
logExpansionEvent(false, wasExpanded);
if (wasExpanded != isExpanded()) {
- notifyHeightChanged();
+ notifyHeightChanged(false /* needsAnimation */);
}
}
}
@@ -324,9 +523,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
public void applyExpansionToLayout() {
boolean expand = isExpanded();
if (expand && mExpandable) {
- setActualHeight(mMaxExpandHeight);
+ setContentHeight(mMaxExpandHeight);
} else {
- setActualHeight(mRowMinHeight);
+ setContentHeight(mRowMinHeight);
}
}
@@ -336,12 +535,26 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
return getActualHeight();
}
boolean inExpansionState = isExpanded();
- if (!inExpansionState) {
- // not expanded, so we return the collapsed size
- return mRowMinHeight;
+ int maxContentHeight;
+ if ((!inExpansionState && !mChildrenExpanded) || mShowingPublicForIntrinsicHeight) {
+ maxContentHeight = mRowMinHeight;
+ } else if (mChildrenExpanded) {
+ maxContentHeight = mChildrenContainer.getIntrinsicHeight();
+ } else {
+ maxContentHeight = getMaxExpandHeight();
}
+ return maxContentHeight + getBottomDecorHeight();
+ }
- return mShowingPublicForIntrinsicHeight ? mRowMinHeight : getMaxExpandHeight();
+ @Override
+ protected boolean hasBottomDecor() {
+ return BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
+ && !mIsHeadsUp && mGroupManager.hasGroupChildren(mStatusBarNotification);
+ }
+
+ @Override
+ protected boolean canHaveBottomDecor() {
+ return BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && !mIsHeadsUp;
}
/**
@@ -354,7 +567,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
*/
private boolean isExpanded() {
return !mExpansionDisabled
- && (!hasUserChangedExpansion() && isSystemExpanded() || isUserExpanded());
+ && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
+ || isUserExpanded());
+ }
+
+ private boolean isSystemChildExpanded() {
+ return mIsSystemChildExpanded;
+ }
+
+ public void setSystemChildExpanded(boolean expanded) {
+ mIsSystemChildExpanded = expanded;
}
@Override
@@ -368,11 +590,20 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
mWasReset = false;
}
+ @Override
+ protected boolean isChildInvisible(View child) {
+
+ // We don't want to layout the ChildrenContainer if this is a heads-up view, otherwise the
+ // view will get too high and the shadows will be off.
+ boolean isInvisibleChildContainer = child == mChildrenContainer && mIsHeadsUp;
+ return super.isChildInvisible(child) || isInvisibleChildContainer;
+ }
+
private void updateMaxExpandHeight() {
int intrinsicBefore = getIntrinsicHeight();
mMaxExpandHeight = mPrivateLayout.getMaxHeight();
if (intrinsicBefore != getIntrinsicHeight()) {
- notifyHeightChanged();
+ notifyHeightChanged(false /* needsAnimation */);
}
}
@@ -439,8 +670,127 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
mVetoButton.setVisibility(isClearable() && !mShowingPublic ? View.VISIBLE : View.GONE);
}
+ public void setChildrenExpanded(boolean expanded, boolean animate) {
+ mChildrenExpanded = expanded;
+ updateChildrenVisibility(animate);
+ }
+
+ public void updateExpandButton() {
+ boolean hasExpand = hasBottomDecor();
+ if (hasExpand != mHasExpandAction) {
+ if (hasExpand) {
+ if (mExpandButtonContainer == null) {
+ mExpandButtonStub.inflate();
+ }
+ mExpandButtonContainer.setVisibility(View.VISIBLE);
+ updateExpandButtonAppearance();
+ updateExpandButtonColor();
+ } else if (mExpandButtonContainer != null) {
+ mExpandButtonContainer.setVisibility(View.GONE);
+ }
+ notifyHeightChanged(true /* needsAnimation */);
+ }
+ mHasExpandAction = hasExpand;
+ }
+
+ private void updateExpandButtonAppearance() {
+ if (mExpandButtonContainer == null) {
+ return;
+ }
+ float expandButtonAlpha = 0.0f;
+ float expandButtonTranslation = 0.0f;
+ float containerTranslation = 0.0f;
+ int minHeight = getMinHeight();
+ if (!mChildrenExpanded || mChildExpandAnimator != null) {
+ int expandActionHeight = getBottomDecorHeight();
+ int translationY = getActualHeight() - expandActionHeight;
+ if (translationY > minHeight) {
+ containerTranslation = translationY;
+ expandButtonAlpha = 1.0f;
+ expandButtonTranslation = 0.0f;
+ } else {
+ containerTranslation = minHeight;
+ float progress = expandActionHeight != 0
+ ? (minHeight - translationY) / (float) expandActionHeight
+ : 1.0f;
+ expandButtonTranslation = -progress * expandActionHeight * 0.7f;
+ float alphaProgress = Math.min(progress / 0.7f, 1.0f);
+ alphaProgress = PhoneStatusBar.ALPHA_OUT.getInterpolation(alphaProgress);
+ expandButtonAlpha = 1.0f - alphaProgress;
+ }
+ }
+ if (mChildExpandAnimator != null || mChildrenExpanded) {
+ expandButtonAlpha = (1.0f - mChildrenExpandProgress)
+ * expandButtonAlpha;
+ expandButtonTranslation = (1.0f - mChildrenExpandProgress)
+ * expandButtonTranslation;
+ float newTranslation = -getBottomDecorHeight();
+
+ // We don't want to take the actual height of the view as this is already
+ // interpolated by a custom interpolator leading to a confusing animation. We want
+ // to have a stable end value to interpolate in between
+ float collapsedHeight = !mChildrenExpanded
+ ? Math.max(StackStateAnimator.getFinalActualHeight(this)
+ - getBottomDecorHeight(), minHeight)
+ : mExpandButtonStart;
+ float translationProgress = mFastOutSlowInInterpolator.getInterpolation(
+ mChildrenExpandProgress);
+ containerTranslation = (1.0f - translationProgress) * collapsedHeight
+ + translationProgress * newTranslation;
+ }
+ mExpandButton.setAlpha(expandButtonAlpha);
+ mExpandButtonDivider.setAlpha(expandButtonAlpha);
+ mExpandButton.setTranslationY(expandButtonTranslation);
+ mExpandButtonContainer.setTranslationY(containerTranslation);
+ NotificationContentView showingLayout = getShowingLayout();
+ float layoutTranslation =
+ mExpandButtonContainer.getTranslationY() - showingLayout.getContentHeight();
+ layoutTranslation = Math.min(layoutTranslation, 0);
+ if (!mChildrenExpanded && mChildExpandAnimator == null) {
+ // Needed for the DragDownHelper in order not to jump there, as the position
+ // can be negative for a short time.
+ layoutTranslation = 0;
+ }
+ showingLayout.setTranslationY(layoutTranslation);
+ if (mChildrenContainer != null) {
+ mChildrenContainer.setTranslationY(
+ mExpandButtonContainer.getTranslationY() + getBottomDecorHeight());
+ }
+ }
+
+ private void updateExpandButtonColor() {
+ // TODO: This needs some more baking, currently only the divider is colored according to
+ // the tint, but legacy black doesn't work yet perfectly for the button etc.
+ int color = getRippleColor();
+ if (color == mNormalRippleColor) {
+ color = 0;
+ }
+ if (mExpandButtonDivider != null) {
+ applyTint(mExpandButtonDivider, color);
+ }
+ if (mChildrenContainer != null) {
+ mChildrenContainer.setTintColor(color);
+ }
+ }
+
+ public static void applyTint(View v, int color) {
+ int alpha;
+ if (color != 0) {
+ alpha = COLORED_DIVIDER_ALPHA;
+ } else {
+ color = 0xff000000;
+ alpha = DEFAULT_DIVIDER_ALPHA;
+ }
+ if (v.getBackground() instanceof ColorDrawable) {
+ ColorDrawable background = (ColorDrawable) v.getBackground();
+ background.mutate();
+ background.setColor(color);
+ background.setAlpha(alpha);
+ }
+ }
+
public int getMaxExpandHeight() {
- return mShowingPublicForIntrinsicHeight ? mRowMinHeight : mMaxExpandHeight;
+ return mMaxExpandHeight;
}
@Override
@@ -451,17 +801,19 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
@Override
public void setActualHeight(int height, boolean notifyListeners) {
- mPrivateLayout.setActualHeight(height);
- mPublicLayout.setActualHeight(height);
+ super.setActualHeight(height, notifyListeners);
+ int contentHeight = calculateContentHeightFromActualHeight(height);
+ mPrivateLayout.setContentHeight(contentHeight);
+ mPublicLayout.setContentHeight(contentHeight);
if (mGuts != null) {
mGuts.setActualHeight(height);
}
invalidate();
- super.setActualHeight(height, notifyListeners);
+ updateExpandButtonAppearance();
}
@Override
- public int getMaxHeight() {
+ public int getMaxContentHeight() {
NotificationContentView showingLayout = getShowingLayout();
return showingLayout.getMaxHeight();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 1e5dcf7..7ae0d6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -33,8 +33,8 @@ import java.util.ArrayList;
*/
public abstract class ExpandableView extends FrameLayout {
-
- private OnHeightChangedListener mOnHeightChangedListener;
+ private final int mBottomDecorHeight;
+ protected OnHeightChangedListener mOnHeightChangedListener;
protected int mMaxViewHeight;
private int mActualHeight;
protected int mClipTopAmount;
@@ -48,6 +48,12 @@ public abstract class ExpandableView extends FrameLayout {
super(context, attrs);
mMaxViewHeight = getResources().getDimensionPixelSize(
R.dimen.notification_max_height);
+ mBottomDecorHeight = resolveBottomDecorHeight();
+ }
+
+ protected int resolveBottomDecorHeight() {
+ return getResources().getDimensionPixelSize(
+ R.dimen.notification_bottom_decor_height);
}
@Override
@@ -65,6 +71,9 @@ public abstract class ExpandableView extends FrameLayout {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
+ if (child.getVisibility() == GONE || isChildInvisible(child)) {
+ continue;
+ }
int childHeightSpec = newHeightSpec;
ViewGroup.LayoutParams layoutParams = child.getLayoutParams();
if (layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) {
@@ -93,6 +102,10 @@ public abstract class ExpandableView extends FrameLayout {
}
mMatchParentViews.clear();
int width = MeasureSpec.getSize(widthMeasureSpec);
+ if (canHaveBottomDecor()) {
+ // We always account for the expandAction as well.
+ ownHeight += mBottomDecorHeight;
+ }
setMeasuredDimension(width, ownHeight);
}
@@ -102,7 +115,7 @@ public abstract class ExpandableView extends FrameLayout {
if (!mActualHeightInitialized && mActualHeight == 0) {
int initialHeight = getInitialHeight();
if (initialHeight != 0) {
- setActualHeight(initialHeight);
+ setContentHeight(initialHeight);
}
}
}
@@ -145,12 +158,12 @@ public abstract class ExpandableView extends FrameLayout {
mActualHeight = actualHeight;
updateClipping();
if (notifyListeners) {
- notifyHeightChanged();
+ notifyHeightChanged(false /* needsAnimation */);
}
}
- public void setActualHeight(int actualHeight) {
- setActualHeight(actualHeight, true);
+ public void setContentHeight(int contentHeight) {
+ setActualHeight(contentHeight + getBottomDecorHeight(), true);
}
/**
@@ -163,14 +176,39 @@ public abstract class ExpandableView extends FrameLayout {
}
/**
+ * This view may have a bottom decor which will be placed below the content. If it has one, this
+ * view will be layouted higher than just the content by {@link #mBottomDecorHeight}.
+ * @return the height of the decor if it currently has one
+ */
+ public int getBottomDecorHeight() {
+ return hasBottomDecor() ? mBottomDecorHeight : 0;
+ }
+
+ /**
+ * @return whether this view may have a bottom decor at all. This will force the view to layout
+ * itself higher than just it's content
+ */
+ protected boolean canHaveBottomDecor() {
+ return false;
+ }
+
+ /**
+ * @return whether this view has a decor view below it's content. This will make the intrinsic
+ * height from {@link #getIntrinsicHeight()} higher as well
+ */
+ protected boolean hasBottomDecor() {
+ return false;
+ }
+
+ /**
* @return The maximum height of this notification.
*/
- public int getMaxHeight() {
+ public int getMaxContentHeight() {
return getHeight();
}
/**
- * @return The minimum height of this notification.
+ * @return The minimum content height of this notification.
*/
public int getMinHeight() {
return getHeight();
@@ -249,9 +287,9 @@ public abstract class ExpandableView extends FrameLayout {
return false;
}
- public void notifyHeightChanged() {
+ public void notifyHeightChanged(boolean needsAnimation) {
if (mOnHeightChangedListener != null) {
- mOnHeightChangedListener.onHeightChanged(this);
+ mOnHeightChangedListener.onHeightChanged(this, needsAnimation);
}
}
@@ -302,6 +340,21 @@ public abstract class ExpandableView extends FrameLayout {
outRect.top += getTranslationY() + getClipTopAmount();
}
+ public int getContentHeight() {
+ return mActualHeight - getBottomDecorHeight();
+ }
+
+ /**
+ * @return whether the given child can be ignored for layouting and measuring purposes
+ */
+ protected boolean isChildInvisible(View child) {
+ return false;
+ }
+
+ public boolean areChildrenExpanded() {
+ return false;
+ }
+
private void updateClipping() {
mClipRect.set(0, mClipTopOptimization, getWidth(), getActualHeight());
setClipBounds(mClipRect);
@@ -330,8 +383,9 @@ public abstract class ExpandableView extends FrameLayout {
/**
* @param view the view for which the height changed, or {@code null} if just the top
* padding or the padding between the elements changed
+ * @param needsAnimation whether the view height needs to be animated
*/
- void onHeightChanged(ExpandableView view);
+ void onHeightChanged(ExpandableView view, boolean needsAnimation);
/**
* Called when the view is reset and therefore the height will change abruptly
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 70c270c..745e75d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -47,7 +47,7 @@ public class NotificationContentView extends FrameLayout {
private int mSmallHeight;
private int mClipTopAmount;
- private int mActualHeight;
+ private int mContentHeight;
private final Interpolator mLinearInterpolator = new LinearInterpolator();
@@ -97,7 +97,7 @@ public class NotificationContentView extends FrameLayout {
mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
mContractedVisible = true;
if (resetActualHeight) {
- mActualHeight = mSmallHeight;
+ mContentHeight = mSmallHeight;
}
}
@@ -154,12 +154,17 @@ public class NotificationContentView extends FrameLayout {
}
}
- public void setActualHeight(int actualHeight) {
- mActualHeight = actualHeight;
+ public void setContentHeight(int contentHeight) {
+ contentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());
+ mContentHeight = contentHeight;
selectLayout(mAnimate /* animate */, false /* force */);
updateClipping();
}
+ public int getContentHeight() {
+ return mContentHeight;
+ }
+
public int getMaxHeight() {
// The maximum height is just the laid out height.
@@ -176,7 +181,7 @@ public class NotificationContentView extends FrameLayout {
}
private void updateClipping() {
- mClipBounds.set(0, mClipTopAmount, getWidth(), mActualHeight);
+ mClipBounds.set(0, mClipTopAmount, getWidth(), mContentHeight);
setClipBounds(mClipBounds);
}
@@ -235,7 +240,7 @@ public class NotificationContentView extends FrameLayout {
}
private boolean showContractedChild() {
- return mActualHeight <= mSmallHeight || mExpandedChild == null;
+ return mContentHeight <= mSmallHeight || mExpandedChild == null;
}
public void notifyContentUpdated() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 7e68c10..912f414 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -240,7 +240,8 @@ public class NotificationData {
return true;
}
- if (mGroupManager.isChildInGroupWithSummary(sbn)) {
+ if (!BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
+ && mGroupManager.isChildInGroupWithSummary(sbn)) {
return true;
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index bfa3aa5..5fa7070 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -62,4 +62,13 @@ public class NotificationOverflowContainer extends ActivatableNotificationView {
public NotificationOverflowIconsView getIconsView() {
return mIconsView;
}
+
+ protected int getContentHeightFromActualHeight(int actualHeight) {
+ int realActualHeight = actualHeight;
+ if (hasBottomDecor()) {
+ realActualHeight -= getBottomDecorHeight();
+ }
+ realActualHeight = Math.max(getMinHeight(), realActualHeight);
+ return realActualHeight;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 5214ab4..7072dcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -100,6 +100,8 @@ public class NotificationGroupManager {
}
}
});
+ } else {
+ group.summary.row.updateExpandButton();
}
}
}
@@ -116,11 +118,15 @@ public class NotificationGroupManager {
}
if (notif.isGroupSummary()) {
group.summary = added;
+ group.expanded = added.row.areChildrenExpanded();
if (!group.children.isEmpty()) {
mListener.onGroupCreatedFromChildren(group);
}
} else {
group.children.add(added);
+ if (group.summary != null && group.children.size() == 1 && !group.expanded) {
+ group.summary.row.updateExpandButton();
+ }
}
}
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 7513fc6..195da46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1680,7 +1680,7 @@ public class NotificationPanelView extends PanelView implements
}
@Override
- public void onHeightChanged(ExpandableView view) {
+ public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
// Block update if we are in quick settings and just the top padding changed
// (i.e. view == null).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e5eb747..f3ec34a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -170,6 +170,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -524,6 +525,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
goToLockedShade(null);
}
};
+ private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap
+ = new HashMap<>();
@Override
public void start() {
@@ -664,6 +667,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
R.id.notification_stack_scroller);
mStackScroller.setLongPressListener(getNotificationLongClicker());
mStackScroller.setPhoneStatusBar(this);
+ mStackScroller.setGroupManager(mGroupManager);
+ mGroupManager.setOnGroupChangeListener(mStackScroller);
mKeyguardIconOverflowContainer =
(NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
@@ -855,9 +860,20 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren);
for (int i = 0; i < numChildren; i++) {
final View child = mStackScroller.getChildAt(i);
- if (mStackScroller.canChildBeDismissed(child)) {
- if (child.getVisibility() == View.VISIBLE) {
- viewsToHide.add(child);
+ if (child instanceof ExpandableNotificationRow) {
+ if (mStackScroller.canChildBeDismissed(child)) {
+ if (child.getVisibility() == View.VISIBLE) {
+ viewsToHide.add(child);
+ }
+ }
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ List<ExpandableNotificationRow> children = row.getNotificationChildren();
+ if (row.areChildrenExpanded() && children != null) {
+ for (ExpandableNotificationRow childRow : children) {
+ if (childRow.getVisibility() == View.VISIBLE) {
+ viewsToHide.add(childRow);
+ }
+ }
}
}
}
@@ -1296,10 +1312,23 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
ent.row.setShowingLegacyBackground(true);
}
}
- toShow.add(ent.row);
+ if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
+ ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
+ ent.row.getStatusBarNotification());
+ List<ExpandableNotificationRow> orderedChildren =
+ mTmpChildOrderMap.get(summary);
+ if (orderedChildren == null) {
+ orderedChildren = new ArrayList<>();
+ mTmpChildOrderMap.put(summary, orderedChildren);
+ }
+ orderedChildren.add(ent.row);
+ } else {
+ toShow.add(ent.row);
+ }
+
}
- ArrayList<View> toRemove = new ArrayList<View>();
+ ArrayList<View> toRemove = new ArrayList<>();
for (int i=0; i< mStackScroller.getChildCount(); i++) {
View child = mStackScroller.getChildAt(i);
if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
@@ -1328,17 +1357,22 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
continue;
}
- if (child == toShow.get(j)) {
- // Everything is well, advance both lists.
- j++;
- continue;
+ ExpandableNotificationRow targetChild = toShow.get(j);
+ if (child != targetChild) {
+ // Oops, wrong notification at this position. Put the right one
+ // here and advance both lists.
+ mStackScroller.changeViewPosition(targetChild, i);
}
-
- // Oops, wrong notification at this position. Put the right one
- // here and advance both lists.
- mStackScroller.changeViewPosition(toShow.get(j), i);
j++;
+
}
+
+ // lets handle the child notifications now
+ updateNotificationShadeForChildren();
+
+ // clear the map again for the next usage
+ mTmpChildOrderMap.clear();
+
updateRowStates();
updateSpeedbump();
updateClearAll();
@@ -1353,6 +1387,52 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mShadeUpdates.check();
}
+ private void updateNotificationShadeForChildren() {
+ ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
+ boolean orderChanged = false;
+ for (int i = 0; i < mStackScroller.getChildCount(); i++) {
+ View view = mStackScroller.getChildAt(i);
+ if (!(view instanceof ExpandableNotificationRow)) {
+ // We don't care about non-notification views.
+ continue;
+ }
+
+ ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
+ List<ExpandableNotificationRow> children = parent.getNotificationChildren();
+ List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
+
+ // lets first remove all undesired children
+ if (children != null) {
+ toRemove.clear();
+ for (ExpandableNotificationRow childRow : children) {
+ if (orderedChildren == null || !orderedChildren.contains(childRow)) {
+ toRemove.add(childRow);
+ }
+ }
+ for (ExpandableNotificationRow remove : toRemove) {
+ parent.removeChildNotification(remove);
+ mStackScroller.notifyGroupChildRemoved(remove);
+ }
+ }
+
+ // We now add all the children which are not in there already
+ for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
+ childIndex++) {
+ ExpandableNotificationRow childView = orderedChildren.get(childIndex);
+ if (children == null || !children.contains(childView)) {
+ parent.addChildNotification(childView, childIndex);
+ mStackScroller.notifyGroupChildAdded(childView);
+ }
+ }
+
+ // Finally after removing and adding has been beformed we can apply the order.
+ orderChanged |= parent.applyChildOrder(orderedChildren);
+ }
+ if (orderChanged) {
+ mStackScroller.generateChildOrderChangedEvent();
+ }
+ }
+
private boolean packageHasVisibilityOverride(String key) {
return mNotificationData.getVisibilityOverride(key)
!= NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
@@ -1379,6 +1459,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
final int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
Entry entry = activeNotifications.get(i);
+ boolean isChild = !isTopLevelChild(entry);
+ if (isChild) {
+ continue;
+ }
if (entry.row.getVisibility() != View.GONE &&
mNotificationData.isAmbient(entry.key)) {
speedbumpIndex = currentIndex;
@@ -1389,6 +1473,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
}
+ public static boolean isTopLevelChild(Entry entry) {
+ return entry.row.getParent() instanceof NotificationStackScrollLayout;
+ }
+
@Override
protected void updateNotifications() {
mNotificationData.filterAndSort();
@@ -3074,7 +3162,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mLeaveOpenOnKeyguardHide = false;
if (mDraggedDownRow != null) {
mDraggedDownRow.setUserLocked(false);
- mDraggedDownRow.notifyHeightChanged();
+ mDraggedDownRow.notifyHeightChanged(false /* needsAnimation */);
mDraggedDownRow = null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 798467f..c49f620 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -158,13 +158,16 @@ public class StatusBarIconController {
final int N = activeNotifications.size();
ArrayList<StatusBarIconView> toShow = new ArrayList<>(N);
- // Filter out ambient notifications.
+ // Filter out ambient notifications and notification children.
for (int i = 0; i < N; i++) {
NotificationData.Entry ent = activeNotifications.get(i);
if (notificationData.isAmbient(ent.key)
&& !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
continue;
}
+ if (!PhoneStatusBar.isTopLevelChild(ent)) {
+ continue;
+ }
toShow.add(ent.icon);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index f8b8a9a..6c1cdcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -204,6 +204,9 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
}
private void resetViewForHeadsup() {
+ if (mHeadsUp.row.areChildrenExpanded()) {
+ mHeadsUp.row.setChildrenExpanded(false /* expanded */, false /* animated */);
+ }
mHeadsUp.row.setSystemExpanded(true);
mHeadsUp.row.setSensitive(false);
mHeadsUp.row.setHeadsUp(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
new file mode 100644
index 0000000..3c9e8cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -0,0 +1,406 @@
+/*
+ * 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.stack;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.ExpandableView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A container containing child notifications
+ */
+public class NotificationChildrenContainer extends ViewGroup {
+
+ private final int mChildPadding;
+ private final int mDividerHeight;
+ private final int mMaxNotificationHeight;
+ private final List<View> mDividers = new ArrayList<>();
+ private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
+ private final View mCollapseButton;
+ private final View mCollapseDivider;
+ private final int mCollapseButtonHeight;
+ private final int mNotificationAppearDistance;
+
+ public NotificationChildrenContainer(Context context) {
+ this(context, null);
+ }
+
+ public NotificationChildrenContainer(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mChildPadding = getResources().getDimensionPixelSize(
+ R.dimen.notification_children_padding);
+ mDividerHeight = getResources().getDimensionPixelSize(
+ R.dimen.notification_children_divider_height);
+ mMaxNotificationHeight = getResources().getDimensionPixelSize(
+ R.dimen.notification_max_height);
+ mNotificationAppearDistance = getResources().getDimensionPixelSize(
+ R.dimen.notification_appear_distance);
+ LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
+ mCollapseButton = inflater.inflate(R.layout.notification_collapse_button, this,
+ false);
+ mCollapseButtonHeight = getResources().getDimensionPixelSize(
+ R.dimen.notification_bottom_decor_height);
+ addView(mCollapseButton);
+ mCollapseDivider = inflateDivider();
+ addView(mCollapseDivider);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int childCount = mChildren.size();
+ boolean firstChild = true;
+ 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;
+ }
+ child.layout(0, 0, getWidth(), child.getMeasuredHeight());
+ if (!firstChild) {
+ mDividers.get(i - 1).layout(0, 0, getWidth(), mDividerHeight);
+ } else {
+ firstChild = false;
+ }
+ }
+ mCollapseButton.layout(0, 0, getWidth(), mCollapseButtonHeight);
+ mCollapseDivider.layout(0, mCollapseButtonHeight - mDividerHeight, getWidth(),
+ mCollapseButtonHeight);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int ownMaxHeight = mMaxNotificationHeight;
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
+ boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
+ if (hasFixedHeight || isHeightLimited) {
+ int size = MeasureSpec.getSize(heightMeasureSpec);
+ ownMaxHeight = Math.min(ownMaxHeight, size);
+ }
+ int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
+ int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
+ int collapseButtonHeightSpec = MeasureSpec.makeMeasureSpec(mCollapseButtonHeight,
+ MeasureSpec.EXACTLY);
+ mCollapseButton.measure(widthMeasureSpec, collapseButtonHeightSpec);
+ mCollapseDivider.measure(widthMeasureSpec, dividerHeightSpec);
+ int height = mCollapseButtonHeight;
+ int childCount = mChildren.size();
+ 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) {
+ // layout the divider
+ View divider = mDividers.get(i - 1);
+ divider.measure(widthMeasureSpec, dividerHeightSpec);
+ height += mChildPadding;
+ } else {
+ firstChild = false;
+ }
+ }
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ height = hasFixedHeight ? ownMaxHeight
+ : isHeightLimited ? Math.min(ownMaxHeight, height)
+ : height;
+ setMeasuredDimension(width, height);
+ }
+
+ /**
+ * Add a child notification to this view.
+ *
+ * @param row the row to add
+ * @param childIndex the index to add it at, if -1 it will be added at the end
+ */
+ public void addNotification(ExpandableNotificationRow row, int childIndex) {
+ int newIndex = childIndex < 0 ? mChildren.size() : childIndex;
+ mChildren.add(newIndex, row);
+ addView(row);
+ if (mChildren.size() != 1) {
+ View divider = inflateDivider();
+ addView(divider);
+ mDividers.add(Math.max(newIndex - 1, 0), divider);
+ }
+ // TODO: adapt background corners
+ // TODO: fix overdraw
+ }
+
+ public void removeNotification(ExpandableNotificationRow row) {
+ int childIndex = mChildren.indexOf(row);
+ mChildren.remove(row);
+ removeView(row);
+ if (!mDividers.isEmpty()) {
+ View divider = mDividers.remove(Math.max(childIndex - 1, 0));
+ removeView(divider);
+ }
+ row.setSystemChildExpanded(false);
+ // TODO: adapt background corners
+ }
+
+ private View inflateDivider() {
+ return LayoutInflater.from(mContext).inflate(
+ R.layout.notification_children_divider, this, false);
+ }
+
+ public List<ExpandableNotificationRow> getNotificationChildren() {
+ return mChildren;
+ }
+
+ /**
+ * Apply the order given in the list to the children.
+ *
+ * @param childOrder the new list order
+ * @return whether the list order has changed
+ */
+ public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder) {
+ if (childOrder == null) {
+ return false;
+ }
+ boolean result = false;
+ for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
+ ExpandableNotificationRow child = mChildren.get(i);
+ ExpandableNotificationRow desiredChild = childOrder.get(i);
+ if (child != desiredChild) {
+ mChildren.remove(desiredChild);
+ mChildren.add(i, desiredChild);
+ 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;
+ }
+ return result;
+ }
+
+ public int getIntrinsicHeight() {
+ int childCount = mChildren.size();
+ int intrinsicHeight = 0;
+ int visibleChildren = 0;
+ for (int i = 0; i < childCount; i++) {
+ ExpandableNotificationRow child = mChildren.get(i);
+ if (child.getVisibility() == View.GONE) {
+ continue;
+ }
+ intrinsicHeight += child.getIntrinsicHeight();
+ visibleChildren++;
+ }
+ if (visibleChildren > 0) {
+ intrinsicHeight += (visibleChildren - 1) * mDividerHeight;
+ }
+ return intrinsicHeight;
+ }
+
+ /**
+ * Update the state of all its children based on a linear layout algorithm.
+ *
+ * @param resultState the state to update
+ * @param parentState the state of the parent
+ */
+ public void getState(StackScrollState resultState, StackViewState parentState) {
+ int childCount = mChildren.size();
+ int yPosition = mCollapseButtonHeight;
+ boolean firstChild = true;
+ 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 {
+ firstChild = false;
+ }
+ StackViewState childState = resultState.getViewStateForView(child);
+ int intrinsicHeight = child.getIntrinsicHeight();
+ childState.yTranslation = yPosition;
+ childState.zTranslation = 0;
+ childState.height = intrinsicHeight;
+ childState.dimmed = parentState.dimmed;
+ childState.dark = parentState.dark;
+ childState.hideSensitive = parentState.hideSensitive;
+ childState.belowSpeedBump = parentState.belowSpeedBump;
+ childState.scale = parentState.scale;
+ childState.clipTopAmount = 0;
+ childState.topOverLap = 0;
+ childState.location = parentState.location;
+ yPosition += intrinsicHeight;
+ }
+ }
+
+ public void applyState(StackScrollState state) {
+ int childCount = mChildren.size();
+ boolean firstChild = true;
+ ViewState dividerState = 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
+ - (mChildPadding + mDividerHeight) / 2.0f);
+ dividerState.alpha = 1;
+ state.applyViewState(divider, dividerState);
+ } else {
+ firstChild = false;
+ }
+ state.applyState(child, viewState);
+ }
+ }
+
+ public void setCollapseClickListener(OnClickListener collapseClickListener) {
+ mCollapseButton.setOnClickListener(collapseClickListener);
+ }
+
+ /**
+ * This is called when the children expansion has changed and positions the children properly
+ * for an appear animation.
+ *
+ * @param state the new state we animate to
+ */
+ public void prepareExpansionChanged(StackScrollState state) {
+ int childCount = mChildren.size();
+ boolean firstChild = true;
+ StackViewState sourceState = new StackViewState();
+ ViewState dividerState = 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 = viewState.yTranslation
+ - (mChildPadding + mDividerHeight) / 2.0f + mNotificationAppearDistance;
+ dividerState.alpha = 0;
+ state.applyViewState(divider, dividerState);
+ } else {
+ firstChild = false;
+ }
+ sourceState.copyFrom(viewState);
+ sourceState.alpha = 0;
+ 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();
+ 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
+ ? difference * StackStateAnimator.ANIMATION_DELAY_PER_ELEMENT_EXPAND_CHILDREN
+ : 0;
+ delay += baseDelay;
+ if (!firstChild) {
+ // layout the divider
+ View divider = mDividers.get(i - 1);
+ dividerState.initFrom(divider);
+ dividerState.yTranslation = viewState.yTranslation
+ - (mChildPadding + mDividerHeight) / 2.0f;
+ dividerState.alpha = 1;
+ stateAnimator.startViewAnimations(divider, dividerState, delay, duration);
+ } else {
+ firstChild = false;
+ }
+ stateAnimator.startStackAnimations(child, viewState, state, -1, delay);
+ notGoneIndex++;
+ }
+ dividerState.initFrom(mCollapseButton);
+ dividerState.alpha = 1.0f;
+ stateAnimator.startViewAnimations(mCollapseButton, dividerState, baseDelay, duration);
+ dividerState.initFrom(mCollapseDivider);
+ dividerState.alpha = 1.0f;
+ dividerState.yTranslation = 0.0f;
+ stateAnimator.startViewAnimations(mCollapseDivider, dividerState, baseDelay, duration);
+ }
+
+ public ExpandableNotificationRow getViewAtPosition(float y) {
+ // find the view under the pointer, accounting for GONE views
+ final int count = mChildren.size();
+ for (int childIdx = 0; childIdx < count; childIdx++) {
+ ExpandableNotificationRow slidingChild = mChildren.get(childIdx);
+ float childTop = slidingChild.getTranslationY();
+ float top = childTop + slidingChild.getClipTopAmount();
+ float bottom = childTop + slidingChild.getActualHeight();
+ if (y >= top && y <= bottom) {
+ return slidingChild;
+ }
+ }
+ return null;
+ }
+
+ public void setTintColor(int color) {
+ ExpandableNotificationRow.applyTint(mCollapseDivider, color);
+ }
+}
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 4c7dbbb..2eafd57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -41,9 +41,11 @@ import com.android.systemui.statusbar.DismissView;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.SpeedBumpView;
import com.android.systemui.statusbar.StackScrollerDecorView;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.policy.ScrollAdapter;
@@ -55,7 +57,7 @@ import java.util.HashSet;
*/
public class NotificationStackScrollLayout extends ViewGroup
implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
- ExpandableView.OnHeightChangedListener {
+ ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener {
private static final String TAG = "NotificationStackScrollLayout";
private static final boolean DEBUG = false;
@@ -118,6 +120,7 @@ public class NotificationStackScrollLayout extends ViewGroup
*/
private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
private AmbientState mAmbientState = new AmbientState();
+ private NotificationGroupManager mGroupManager;
private ArrayList<View> mChildrenToAddAnimated = new ArrayList<View>();
private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<View>();
private ArrayList<View> mSnappedBackChildren = new ArrayList<View>();
@@ -180,6 +183,7 @@ public class NotificationStackScrollLayout extends ViewGroup
private boolean mDontReportNextOverScroll;
private boolean mRequestViewResizeAnimationOnLayout;
private boolean mNeedViewResizeAnimation;
+ private View mExpandedGroupView;
private boolean mEverythingNeedsAnimation;
/**
@@ -213,6 +217,7 @@ public class NotificationStackScrollLayout extends ViewGroup
};
private PhoneStatusBar mPhoneStatusBar;
private int[] mTempInt2 = new int[2];
+ private boolean mGenerateChildOrderChangedEvent;
private boolean mRemoveAnimationEnabled;
public NotificationStackScrollLayout(Context context) {
@@ -309,7 +314,7 @@ public class NotificationStackScrollLayout extends ViewGroup
private void notifyHeightChangeListener(ExpandableView view) {
if (mOnHeightChangedListener != null) {
- mOnHeightChangedListener.onHeightChanged(view);
+ mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
}
}
@@ -329,6 +334,9 @@ public class NotificationStackScrollLayout extends ViewGroup
float centerX = getWidth() / 2.0f;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
float width = child.getMeasuredWidth();
float height = child.getMeasuredHeight();
child.layout((int) (centerX - width / 2.0f),
@@ -339,16 +347,18 @@ public class NotificationStackScrollLayout extends ViewGroup
setMaxLayoutHeight(getHeight());
updateContentHeight();
clampScrollPosition();
- requestAnimationOnViewResize();
+ if (mRequestViewResizeAnimationOnLayout) {
+ requestAnimationOnViewResize();
+ mRequestViewResizeAnimationOnLayout = false;
+ }
requestChildrenUpdate();
}
private void requestAnimationOnViewResize() {
- if (mRequestViewResizeAnimationOnLayout && mIsExpanded && mAnimationsEnabled) {
+ if (mIsExpanded && mAnimationsEnabled) {
mNeedViewResizeAnimation = true;
mNeedsAnimation = true;
}
- mRequestViewResizeAnimationOnLayout = false;
}
public void updateSpeedBumpIndex(int newIndex) {
@@ -645,6 +655,10 @@ public class NotificationStackScrollLayout extends ViewGroup
int right = getWidth();
if (touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
+ if (slidingChild instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
+ return row.getViewAtPosition(touchY - childTop);
+ }
return slidingChild;
}
}
@@ -1543,6 +1557,14 @@ public class NotificationStackScrollLayout extends ViewGroup
@Override
protected void onViewRemoved(View child) {
super.onViewRemoved(child);
+ // we only call our internal methods if this is actually a removal and not just a
+ // notification which becomes a child notification
+ if (!isChildInGroup(child)) {
+ onViewRemovedInternal(child);
+ }
+ }
+
+ private void onViewRemovedInternal(View child) {
mStackScrollAlgorithm.notifyChildrenChanged(this);
if (mChangePositionInProgress) {
// This is only a position change, don't do anything special
@@ -1568,6 +1590,12 @@ public class NotificationStackScrollLayout extends ViewGroup
((ExpandableView) child).setClipTopOptimization(0);
}
+ private boolean isChildInGroup(View child) {
+ return child instanceof ExpandableNotificationRow
+ && mGroupManager.isChildInGroupWithSummary(
+ ((ExpandableNotificationRow) child).getStatusBarNotification());
+ }
+
/**
* Generate a remove animation for a child view.
*
@@ -1575,7 +1603,7 @@ public class NotificationStackScrollLayout extends ViewGroup
* @return Whether an animation was generated.
*/
private boolean generateRemoveAnimation(View child) {
- if (mIsExpanded && mAnimationsEnabled) {
+ if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
if (!mChildrenToAddAnimated.contains(child)) {
// Generate Animations
mChildrenToRemoveAnimated.add(child);
@@ -1591,6 +1619,23 @@ public class NotificationStackScrollLayout extends ViewGroup
}
/**
+ * @param child the child to query
+ * @return whether a view is not a top level child but a child notification and that group is
+ * not expanded
+ */
+ private boolean isChildInInvisibleGroup(View child) {
+ if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ ExpandableNotificationRow groupSummary =
+ mGroupManager.getGroupSummary(row.getStatusBarNotification());
+ if (groupSummary != null && groupSummary != row) {
+ return !groupSummary.areChildrenExpanded();
+ }
+ }
+ return false;
+ }
+
+ /**
* Updates the scroll position when a child was removed
*
* @param removedChild the removed child
@@ -1638,6 +1683,10 @@ public class NotificationStackScrollLayout extends ViewGroup
@Override
protected void onViewAdded(View child) {
super.onViewAdded(child);
+ onViewAddedInternal(child);
+ }
+
+ private void onViewAddedInternal(View child) {
mStackScrollAlgorithm.notifyChildrenChanged(this);
((ExpandableView) child).setOnHeightChangedListener(this);
generateAddAnimation(child, false /* fromMoreCard */);
@@ -1650,6 +1699,14 @@ public class NotificationStackScrollLayout extends ViewGroup
}
}
+ public void notifyGroupChildRemoved(View row) {
+ onViewRemovedInternal(row);
+ }
+
+ public void notifyGroupChildAdded(View row) {
+ onViewAddedInternal(row);
+ }
+
public void setAnimationsEnabled(boolean animationsEnabled) {
mAnimationsEnabled = animationsEnabled;
updateNotificationAnimationStates();
@@ -1745,10 +1802,20 @@ public class NotificationStackScrollLayout extends ViewGroup
generateDarkEvent();
generateGoToFullShadeEvent();
generateViewResizeEvent();
+ generateGroupExpansionEvent();
generateAnimateEverythingEvent();
mNeedsAnimation = false;
}
+ private void generateGroupExpansionEvent() {
+ // Generate a group expansion/collapsing event if there is such a group at all
+ if (mExpandedGroupView != null) {
+ mAnimationEvents.add(new AnimationEvent(mExpandedGroupView,
+ AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED));
+ mExpandedGroupView = null;
+ }
+ }
+
private void generateViewResizeEvent() {
if (mNeedViewResizeAnimation) {
mAnimationEvents.add(
@@ -1795,6 +1862,11 @@ public class NotificationStackScrollLayout extends ViewGroup
AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
}
mChildrenChangingPositions.clear();
+ if (mGenerateChildOrderChangedEvent) {
+ mAnimationEvents.add(new AnimationEvent(null,
+ AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
+ mGenerateChildOrderChangedEvent = false;
+ }
}
private void generateChildAdditionEvents() {
@@ -2063,11 +2135,14 @@ public class NotificationStackScrollLayout extends ViewGroup
}
@Override
- public void onHeightChanged(ExpandableView view) {
+ public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
updateContentHeight();
updateScrollPositionOnExpandInBottom(view);
clampScrollPosition();
notifyHeightChangeListener(view);
+ if (needsAnimation) {
+ requestAnimationOnViewResize();
+ }
requestChildrenUpdate();
}
@@ -2410,6 +2485,10 @@ public class NotificationStackScrollLayout extends ViewGroup
this.mPhoneStatusBar = phoneStatusBar;
}
+ public void setGroupManager(NotificationGroupManager groupManager) {
+ this.mGroupManager = groupManager;
+ }
+
public void onGoToKeyguard() {
requestAnimateEverything();
}
@@ -2455,6 +2534,51 @@ public class NotificationStackScrollLayout extends ViewGroup
mRemoveAnimationEnabled = enabled;
}
+ private void updateExpandButtons() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ row.updateExpandButton();
+ }
+ }
+ }
+
+ @Override
+ public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
+ boolean animated = mAnimationsEnabled && mIsExpanded;
+ if (animated) {
+ mExpandedGroupView = changedRow;
+ mNeedsAnimation = true;
+ }
+ changedRow.setChildrenExpanded(expanded, animated);
+ onHeightChanged(changedRow, false /* needsAnimation */);
+ }
+
+ @Override
+ public void onGroupsProhibitedChanged() {
+ updateExpandButtons();
+ }
+
+ @Override
+ public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
+ for (NotificationData.Entry entry : group.children) {
+ ExpandableNotificationRow row = entry.row;
+ if (indexOfChild(row) != -1) {
+ removeView(row);
+ group.summary.row.addChildNotification(row);
+ }
+ }
+ }
+
+ public void generateChildOrderChangedEvent() {
+ if (mIsExpanded && mAnimationsEnabled) {
+ mGenerateChildOrderChangedEvent = true;
+ mNeedsAnimation = true;
+ requestChildrenUpdate();
+ }
+ }
+
/**
* A listener that is notified when some child locations might have changed.
*/
@@ -2591,6 +2715,14 @@ public class NotificationStackScrollLayout extends ViewGroup
.animateY()
.animateZ(),
+ // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
+ new AnimationFilter()
+ .animateAlpha()
+ .animateHeight()
+ .animateTopInset()
+ .animateY()
+ .animateZ(),
+
// ANIMATION_TYPE_EVERYTHING
new AnimationFilter()
.animateAlpha()
@@ -2645,6 +2777,9 @@ public class NotificationStackScrollLayout extends ViewGroup
// ANIMATION_TYPE_VIEW_RESIZE
StackStateAnimator.ANIMATION_DURATION_STANDARD,
+ // ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
+ StackStateAnimator.ANIMATION_DURATION_EXPAND_CLICKED,
+
// ANIMATION_TYPE_EVERYTHING
StackStateAnimator.ANIMATION_DURATION_STANDARD,
};
@@ -2662,7 +2797,8 @@ public class NotificationStackScrollLayout extends ViewGroup
static final int ANIMATION_TYPE_GO_TO_FULL_SHADE = 10;
static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
- static final int ANIMATION_TYPE_EVERYTHING = 13;
+ static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
+ static final int ANIMATION_TYPE_EVERYTHING = 14;
static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
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 2c124d0..e7bf47b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -27,6 +27,7 @@ import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import java.util.ArrayList;
+import java.util.List;
/**
* The Algorithm of the {@link com.android.systemui.statusbar.stack
@@ -171,6 +172,19 @@ public class StackScrollAlgorithm {
updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
updateClipping(resultState, algorithmState);
updateSpeedBumpState(resultState, algorithmState, ambientState.getSpeedBumpIndex());
+ getNotificationChildrenStates(resultState, algorithmState);
+ }
+
+ private void getNotificationChildrenStates(StackScrollState resultState,
+ StackScrollAlgorithmState algorithmState) {
+ int childCount = algorithmState.visibleChildren.size();
+ for (int i = 0; i < childCount; i++) {
+ ExpandableView v = algorithmState.visibleChildren.get(i);
+ if (v instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ row.getChildrenStates(resultState);
+ }
+ }
}
private void updateSpeedBumpState(StackScrollState resultState,
@@ -328,6 +342,23 @@ public class StackScrollAlgorithm {
viewState.notGoneIndex = notGoneIndex;
state.visibleChildren.add(v);
notGoneIndex++;
+
+ // handle the notgoneIndex for the children as well
+ if (v instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ List<ExpandableNotificationRow> children =
+ row.getNotificationChildren();
+ if (row.areChildrenExpanded() && children != null) {
+ for (ExpandableNotificationRow childRow : children) {
+ if (childRow.getVisibility() != View.GONE) {
+ StackViewState childState
+ = resultState.getViewStateForView(childRow);
+ childState.notGoneIndex = notGoneIndex;
+ notGoneIndex++;
+ }
+ }
+ }
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index ffd700d..feae590 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -23,10 +23,12 @@ import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.statusbar.DismissView;
import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.SpeedBumpView;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -57,6 +59,18 @@ public class StackScrollState {
for (int i = 0; i < numChildren; i++) {
ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
resetViewState(child);
+
+ // handling reset for child notifications
+ if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ List<ExpandableNotificationRow> children =
+ row.getNotificationChildren();
+ if (row.areChildrenExpanded() && children != null) {
+ for (ExpandableNotificationRow childRow : children) {
+ resetViewState(childRow);
+ }
+ }
+ }
}
}
@@ -154,6 +168,10 @@ public class StackScrollState {
if (oldClipTopOptimization != state.topOverLap) {
view.setClipTopOptimization(state.topOverLap);
}
+ if (view instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ row.applyChildrenState(this);
+ }
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 47f0114..b249fbf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -26,6 +26,7 @@ import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.systemui.R;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.SpeedBumpView;
@@ -42,12 +43,15 @@ public class StackStateAnimator {
public static final int ANIMATION_DURATION_STANDARD = 360;
public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
+ public static final int ANIMATION_DURATION_EXPAND_CLICKED = 360;
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
+ public static final int ANIMATION_DELAY_PER_ELEMENT_EXPAND_CHILDREN = 54;
public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
public static final int ANIMATION_DELAY_PER_ELEMENT_DARK = 24;
- private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
+ public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
+ public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE_CHILDREN = 3;
private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
@@ -85,6 +89,7 @@ public class StackStateAnimator {
private ValueAnimator mTopOverScrollAnimator;
private ValueAnimator mBottomOverScrollAnimator;
+ private ExpandableNotificationRow mChildExpandingView;
public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
mHostLayout = hostLayout;
@@ -127,6 +132,7 @@ public class StackStateAnimator {
}
mNewEvents.clear();
mNewAddChildren.clear();
+ mChildExpandingView = null;
}
private int findLastNotAddedIndex(StackScrollState finalState) {
@@ -216,6 +222,10 @@ public class StackStateAnimator {
if (child instanceof SpeedBumpView) {
finalState.performSpeedBumpAnimation(i, (SpeedBumpView) child, viewState,
delay + duration);
+ } else if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ row.startChildAnimation(finalState, this, child == mChildExpandingView, delay,
+ duration);
}
}
@@ -813,6 +823,11 @@ public class StackStateAnimator {
// A race condition can trigger the view to be added to the overlay even though
// it is swiped out. So let's remove it
mHostLayout.getOverlay().remove(changingView);
+ } else if (event.animationType == NotificationStackScrollLayout
+ .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
+ row.prepareExpansionChanged(finalState);
+ mChildExpandingView = row;
}
mNewEvents.add(event);
}