summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/Notification.java45
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--packages/SystemUI/res/layout/heads_up_status_bar_layout.xml47
-rw-r--r--packages/SystemUI/res/layout/notification_icon_area.xml6
-rw-r--r--packages/SystemUI/res/layout/status_bar.xml72
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/styles.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java133
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java185
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java113
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java26
30 files changed, 917 insertions, 144 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d8e1c895a6eb..c6568e16086c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5577,6 +5577,24 @@ public class Notification implements Parcelable
public void setRebuildStyledRemoteViews(boolean rebuild) {
mRebuildStyledRemoteViews = rebuild;
}
+
+ /**
+ * Get the text that should be displayed in the statusBar when heads upped. This is
+ * usually just the app name, but may be different depending on the style.
+ *
+ * @param publicMode If true, return a text that is safe to display in public.
+ *
+ * @hide
+ */
+ public CharSequence getHeadsUpStatusBarText(boolean publicMode) {
+ if (mStyle != null && !publicMode) {
+ CharSequence text = mStyle.getHeadsUpStatusBarText();
+ if (!TextUtils.isEmpty(text)) {
+ return text;
+ }
+ }
+ return loadHeaderAppName();
+ }
}
/**
@@ -5954,6 +5972,16 @@ public class Notification implements Parcelable
* @hide
*/
public abstract boolean areNotificationsVisiblyDifferent(Style other);
+
+ /**
+ * @return the the text that should be displayed in the statusBar when heads-upped.
+ * If {@code null} is returned, the default implementation will be used.
+ *
+ * @hide
+ */
+ public CharSequence getHeadsUpStatusBarText() {
+ return null;
+ }
}
/**
@@ -6403,6 +6431,23 @@ public class Notification implements Parcelable
}
/**
+ * @return the the text that should be displayed in the statusBar when heads upped.
+ * If {@code null} is returned, the default implementation will be used.
+ *
+ * @hide
+ */
+ @Override
+ public CharSequence getHeadsUpStatusBarText() {
+ CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
+ ? super.mBigContentTitle
+ : mConversationTitle;
+ if (!TextUtils.isEmpty(conversationTitle) && !hasOnlyWhiteSpaceSenders()) {
+ return conversationTitle;
+ }
+ return null;
+ }
+
+ /**
* @return the user to be displayed for any replies sent by the user
*/
public Person getUser() {
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ad4d7ddfc269..ac01c4efc3ea 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3232,6 +3232,7 @@
<java-symbol type="id" name="remote_input_progress" />
<java-symbol type="id" name="remote_input_send" />
<java-symbol type="id" name="remote_input" />
+ <java-symbol type="dimen" name="notification_content_margin" />
<java-symbol type="dimen" name="slice_shortcut_size" />
<java-symbol type="dimen" name="slice_icon_size" />
<java-symbol type="dimen" name="slice_padding" />
diff --git a/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml b/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml
new file mode 100644
index 000000000000..999af23eae0c
--- /dev/null
+++ b/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<com.android.systemui.statusbar.HeadsUpStatusBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:visibility="invisible"
+ android:id="@+id/heads_up_status_bar_view"
+>
+ <!-- This is a space just used as a layout and it's not actually displaying anything. We're
+ repositioning the statusbar icon to the position where this is laid out when showing this
+ view. -->
+ <Space
+ android:id="@+id/icon_placeholder"
+ android:layout_width="@dimen/status_bar_icon_drawing_size"
+ android:layout_height="@dimen/status_bar_icon_drawing_size"
+ android:layout_gravity="center_vertical"
+ />
+ <TextView
+ android:id="@+id/text"
+ android:textAppearance="@style/TextAppearance.HeadsUpStatusBarText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:textAlignment="viewStart"
+ android:paddingStart="6dp"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ />
+
+</com.android.systemui.statusbar.HeadsUpStatusBarView>
diff --git a/packages/SystemUI/res/layout/notification_icon_area.xml b/packages/SystemUI/res/layout/notification_icon_area.xml
index 6732e6c62e8e..fa696cc1f54c 100644
--- a/packages/SystemUI/res/layout/notification_icon_area.xml
+++ b/packages/SystemUI/res/layout/notification_icon_area.xml
@@ -18,12 +18,14 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/notification_icon_area_inner"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="match_parent"
+ android:clipChildren="false">
<com.android.systemui.statusbar.phone.NotificationIconContainer
android:id="@+id/notificationIcons"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:gravity="center_vertical"
- android:orientation="horizontal"/>
+ android:orientation="horizontal"
+ android:clipChildren="false"/>
</com.android.keyguard.AlphaOptimizedLinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 8c0b9bab35a0..9d336e25985f 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -3,16 +3,16 @@
**
** Copyright 2006, 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
+** 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
+** 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
+** 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.
*/
-->
@@ -34,7 +34,7 @@
android:id="@+id/notification_lights_out"
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="match_parent"
- android:paddingStart="6dip"
+ android:paddingStart="@dimen/status_bar_padding_start"
android:paddingBottom="2dip"
android:src="@drawable/ic_sysbar_lights_out_dot_small"
android:scaleType="center"
@@ -44,7 +44,7 @@
<LinearLayout android:id="@+id/status_bar_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingStart="6dp"
+ android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="8dp"
android:orientation="horizontal"
>
@@ -53,33 +53,41 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout="@layout/operator_name" />
-
- <LinearLayout
+ <FrameLayout
android:layout_height="match_parent"
android:layout_width="0dp"
- android:layout_weight="1"
- >
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
- android:gravity="center_vertical|start"
- />
+ android:layout_weight="1">
- <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
- PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
- <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- android:id="@+id/notification_icon_area"
- android:layout_width="0dp"
+ <include layout="@layout/heads_up_status_bar_layout" />
+
+ <LinearLayout
android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="horizontal" />
+ android:layout_width="match_parent"
+ android:clipChildren="false"
+ >
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ android:singleLine="true"
+ android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+ android:gravity="center_vertical|start"
+ />
+
+ <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
+ PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ android:id="@+id/notification_icon_area"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:clipChildren="false"/>
- </LinearLayout>
+ </LinearLayout>
+ </FrameLayout>
<!-- Space should cover the notch (if it exists) and let other views lay out around it -->
<android.widget.Space
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index cb3c282f703b..9f19603a0129 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -110,6 +110,9 @@
<!-- Side padding on the lockscreen on the side of notifications -->
<dimen name="notification_side_paddings">4dp</dimen>
+ <!-- padding between the heads up and the statusbar -->
+ <dimen name="heads_up_status_bar_padding">8dp</dimen>
+
<!-- Height of a messaging notifications with actions at least. Not that this is an upper bound
and the notification won't use this much, but is measured with wrap_content -->
<dimen name="notification_messaging_actions_min_height">196dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 1470dfa4d05d..20d883435aef 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -490,6 +490,10 @@
<item name="android:paddingEnd">8dp</item>
</style>
+ <style name="TextAppearance.HeadsUpStatusBarText"
+ parent="@*android:style/TextAppearance.Material.Notification.Info">
+ </style>
+
<style name="edit_theme" parent="qs_base">
<item name="android:colorBackground">?android:attr/colorSecondary</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index be28d8cea776..4001f9e04a79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -139,6 +139,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mSensitiveHiddenInGeneral;
private boolean mShowingPublicInitialized;
private boolean mHideSensitiveForIntrinsicHeight;
+ private float mHeaderVisibleAmount = 1.0f;
/**
* Is this notification expanded by the system. The expansion state can be overridden by the
@@ -156,8 +157,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private NotificationContentView mPublicLayout;
private NotificationContentView mPrivateLayout;
private NotificationContentView[] mLayouts;
- private int mMaxExpandHeight;
- private int mHeadsUpHeight;
private int mNotificationColor;
private ExpansionLogger mLogger;
private String mLoggingKey;
@@ -534,6 +533,30 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
addChildNotification(row, -1);
}
+ /**
+ * Set the how much the header should be visible. A value of 0 will make the header fully gone
+ * and a value of 1 will make the notification look just like normal.
+ * This is being used for heads up notifications, when they are pinned to the top of the screen
+ * and the header content is extracted to the statusbar.
+ *
+ * @param headerVisibleAmount the amount the header should be visible.
+ */
+ public void setHeaderVisibleAmount(float headerVisibleAmount) {
+ if (mHeaderVisibleAmount != headerVisibleAmount) {
+ mHeaderVisibleAmount = headerVisibleAmount;
+ mPrivateLayout.setHeaderVisibleAmount(headerVisibleAmount);
+ if (mChildrenContainer != null) {
+ mChildrenContainer.setHeaderVisibleAmount(headerVisibleAmount);
+ }
+ notifyHeightChanged(false /* needsAnimation */);
+ }
+ }
+
+ @VisibleForTesting
+ public float getHeaderVisibleAmount() {
+ return mHeaderVisibleAmount;
+ }
+
@Override
public void setHeadsUpIsVisible() {
super.setHeadsUpIsVisible();
@@ -742,11 +765,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mChildrenContainer.getIntrinsicHeight();
}
if(mExpandedWhenPinned) {
- return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
+ return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
} else if (atLeastMinHeight) {
- return Math.max(getCollapsedHeight(), mHeadsUpHeight);
+ return Math.max(getCollapsedHeight(), getHeadsUpHeight());
} else {
- return mHeadsUpHeight;
+ return getHeadsUpHeight();
}
}
@@ -1923,9 +1946,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (isPinned() || mHeadsupDisappearRunning) {
return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
} else if (isExpanded()) {
- return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
+ return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
} else {
- return Math.max(getCollapsedHeight(), mHeadsUpHeight);
+ return Math.max(getCollapsedHeight(), getHeadsUpHeight());
}
} else if (isExpanded()) {
return getMaxExpandHeight();
@@ -1999,8 +2022,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ int intrinsicBefore = getIntrinsicHeight();
super.onLayout(changed, left, top, right, bottom);
- updateMaxHeights();
+ if (intrinsicBefore != getIntrinsicHeight()) {
+ notifyHeightChanged(true /* needsAnimation */);
+ }
if (mMenuRow.getMenuView() != null) {
mMenuRow.onHeightUpdate();
}
@@ -2024,15 +2050,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
- public void updateMaxHeights() {
- int intrinsicBefore = getIntrinsicHeight();
- mMaxExpandHeight = mPrivateLayout.getExpandHeight();
- mHeadsUpHeight = mPrivateLayout.getHeadsUpHeight();
- if (intrinsicBefore != getIntrinsicHeight()) {
- notifyHeightChanged(true /* needsAnimation */);
- }
- }
-
@Override
public void notifyHeightChanged(boolean needsAnimation) {
super.notifyHeightChanged(needsAnimation);
@@ -2175,7 +2192,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public int getMaxExpandHeight() {
- return mMaxExpandHeight;
+ return mPrivateLayout.getExpandHeight();
+ }
+
+
+ private int getHeadsUpHeight() {
+ return mPrivateLayout.getHeadsUpHeight();
}
public boolean areGutsExposed() {
@@ -2275,7 +2297,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} else if (mIsSummaryWithChildren && !isGroupExpanded() && !shouldShowPublic()) {
return mChildrenContainer.getMinHeight();
} else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) {
- return mHeadsUpHeight;
+ return getHeadsUpHeight();
}
NotificationContentView showingLayout = getShowingLayout();
return showingLayout.getMinHeight();
@@ -2321,10 +2343,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
- public boolean isMaxExpandHeightInitialized() {
- return mMaxExpandHeight != 0;
- }
-
public NotificationContentView getShowingLayout() {
return shouldShowPublic() ? mPublicLayout : mPrivateLayout;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
new file mode 100644
index 000000000000..9977fe95d5c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+
+/**
+ * The view in the statusBar that contains part of the heads-up information
+ */
+public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
+ private int mAbsoluteStartPadding;
+ private int mEndMargin;
+ private View mIconPlaceholder;
+ private TextView mTextView;
+ private NotificationData.Entry mShowingEntry;
+ private Rect mIconRect = new Rect();
+ private int[] mTmpPosition = new int[2];
+ private boolean mFirstLayout = true;
+ private boolean mPublicMode;
+
+ public HeadsUpStatusBarView(Context context) {
+ this(context, null);
+ }
+
+ public HeadsUpStatusBarView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ Resources res = getResources();
+ mAbsoluteStartPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings)
+ + res.getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin_start);
+ mEndMargin = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin_end);
+ setPaddingRelative(mAbsoluteStartPadding, 0, mEndMargin, 0);
+ }
+
+ @VisibleForTesting
+ public HeadsUpStatusBarView(Context context, View iconPlaceholder, TextView textView) {
+ this(context);
+ mIconPlaceholder = iconPlaceholder;
+ mTextView = textView;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mIconPlaceholder = findViewById(R.id.icon_placeholder);
+ mTextView = findViewById(R.id.text);
+ }
+
+ public void setEntry(NotificationData.Entry entry) {
+ if (entry != null) {
+ mShowingEntry = entry;
+ CharSequence text = entry.headsUpStatusBarText;
+ if (mPublicMode) {
+ text = entry.headsUpStatusBarTextPublic;
+ }
+ mTextView.setText(text);
+ } else {
+ mShowingEntry = null;
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ mIconPlaceholder.getLocationOnScreen(mTmpPosition);
+ int left = mTmpPosition[0];
+ int top = mTmpPosition[1];
+ int right = left + mIconPlaceholder.getWidth();
+ int bottom = top + mIconPlaceholder.getHeight();
+ mIconRect.set(left, top, right, bottom);
+ if (left != mAbsoluteStartPadding) {
+ int newPadding = mAbsoluteStartPadding - left + getPaddingStart();
+ setPaddingRelative(newPadding, 0, mEndMargin, 0);
+ }
+ if (mFirstLayout) {
+ // we need to do the padding calculation in the first frame, so the layout specified
+ // our visibility to be INVISIBLE in the beginning. let's correct that and set it
+ // to GONE.
+ setVisibility(GONE);
+ mFirstLayout = false;
+ }
+ }
+
+ public NotificationData.Entry getShowingEntry() {
+ return mShowingEntry;
+ }
+
+ public Rect getIconDrawingRect() {
+ return mIconRect;
+ }
+
+ public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+ mTextView.setTextColor(DarkIconDispatcher.getTint(area, this, tint));
+ }
+
+ public void setPublicMode(boolean publicMode) {
+ mPublicMode = publicMode;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 73c87953cf45..f64b1bc2abe8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -602,14 +602,15 @@ public class NotificationContentView extends FrameLayout {
&& (mIsHeadsUp || mHeadsUpAnimatingAway)
&& !mContainingNotification.isOnKeyguard();
if (transitioningBetweenHunAndExpanded || pinned) {
- return Math.min(mHeadsUpChild.getHeight(), mExpandedChild.getHeight());
+ return Math.min(getViewHeight(VISIBLE_TYPE_HEADSUP),
+ getViewHeight(VISIBLE_TYPE_EXPANDED));
}
}
// Size change of the expanded version
if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
&& mExpandedChild != null) {
- return Math.min(mContentHeightAtAnimationStart, mExpandedChild.getHeight());
+ return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
}
int hint;
@@ -619,16 +620,17 @@ public class NotificationContentView extends FrameLayout {
VISIBLE_TYPE_AMBIENT_SINGLELINE)) {
hint = mAmbientSingleLineChild.getHeight();
} else if (mHeadsUpChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_HEADSUP)) {
- hint = mHeadsUpChild.getHeight();
+ hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
} else if (mExpandedChild != null) {
- hint = mExpandedChild.getHeight();
+ hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
} else {
- hint = mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_action_list_height);
+ hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
+ + mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_action_list_height);
}
if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
- hint = Math.min(hint, mExpandedChild.getHeight());
+ hint = Math.min(hint, getViewHeight(VISIBLE_TYPE_EXPANDED));
}
return hint;
}
@@ -694,8 +696,8 @@ public class NotificationContentView extends FrameLayout {
}
private float calculateTransformationAmount() {
- int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
- int endHeight = getViewForVisibleType(mVisibleType).getHeight();
+ int startHeight = getViewHeight(mTransformationStartVisibleType);
+ int endHeight = getViewHeight(mVisibleType);
int progress = Math.abs(mContentHeight - startHeight);
int totalDistance = Math.abs(endHeight - startHeight);
if (totalDistance == 0) {
@@ -717,11 +719,23 @@ public class NotificationContentView extends FrameLayout {
if (mContainingNotification.isShowingAmbient()) {
return getShowingAmbientView().getHeight();
} else if (mExpandedChild != null) {
- return mExpandedChild.getHeight() + getExtraRemoteInputHeight(mExpandedRemoteInput);
+ return getViewHeight(VISIBLE_TYPE_EXPANDED)
+ + getExtraRemoteInputHeight(mExpandedRemoteInput);
} else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
- return mHeadsUpChild.getHeight() + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
+ return getViewHeight(VISIBLE_TYPE_HEADSUP)
+ + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
}
- return mContractedChild.getHeight();
+ return getViewHeight(VISIBLE_TYPE_CONTRACTED);
+ }
+
+ private int getViewHeight(int visibleType) {
+ View view = getViewForVisibleType(visibleType);
+ int height = view.getHeight();
+ NotificationViewWrapper viewWrapper = getWrapperForView(view);
+ if (viewWrapper != null) {
+ height += viewWrapper.getHeaderTranslation();
+ }
+ return height;
}
public int getMinHeight() {
@@ -732,7 +746,7 @@ public class NotificationContentView extends FrameLayout {
if (mContainingNotification.isShowingAmbient()) {
return getShowingAmbientView().getHeight();
} else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
- return mContractedChild.getHeight();
+ return getViewHeight(VISIBLE_TYPE_CONTRACTED);
} else {
return mSingleLineView.getHeight();
}
@@ -1046,7 +1060,7 @@ public class NotificationContentView extends FrameLayout {
private int getVisualTypeForHeight(float viewHeight) {
boolean noExpandedChild = mExpandedChild == null;
- if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
+ if (!noExpandedChild && viewHeight == getViewHeight(VISIBLE_TYPE_EXPANDED)) {
return VISIBLE_TYPE_EXPANDED;
}
if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
@@ -1055,13 +1069,13 @@ public class NotificationContentView extends FrameLayout {
if ((mIsHeadsUp || mHeadsUpAnimatingAway) && mHeadsUpChild != null
&& !mContainingNotification.isOnKeyguard()) {
- if (viewHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
+ if (viewHeight <= getViewHeight(VISIBLE_TYPE_HEADSUP) || noExpandedChild) {
return VISIBLE_TYPE_HEADSUP;
} else {
return VISIBLE_TYPE_EXPANDED;
}
} else {
- if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
+ if (noExpandedChild || (viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
&& (!mIsChildInGroup || isGroupExpanded()
|| !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
return VISIBLE_TYPE_CONTRACTED;
@@ -1616,19 +1630,19 @@ public class NotificationContentView extends FrameLayout {
}
public int getExpandHeight() {
- View expandedChild = mExpandedChild;
- if (expandedChild == null) {
- expandedChild = mContractedChild;
+ int viewType = VISIBLE_TYPE_EXPANDED;
+ if (mExpandedChild == null) {
+ viewType = VISIBLE_TYPE_CONTRACTED;
}
- return expandedChild.getHeight() + getExtraRemoteInputHeight(mExpandedRemoteInput);
+ return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
}
public int getHeadsUpHeight() {
- View headsUpChild = mHeadsUpChild;
- if (headsUpChild == null) {
- headsUpChild = mContractedChild;
+ int viewType = VISIBLE_TYPE_HEADSUP;
+ if (mHeadsUpChild == null) {
+ viewType = VISIBLE_TYPE_CONTRACTED;
}
- return headsUpChild.getHeight()+ getExtraRemoteInputHeight(mHeadsUpRemoteInput);
+ return getViewHeight(viewType) + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
}
public void setRemoteInputVisible(boolean remoteInputVisible) {
@@ -1641,4 +1655,16 @@ public class NotificationContentView extends FrameLayout {
clipChildren = clipChildren && !mRemoteInputVisible;
super.setClipChildren(clipChildren);
}
+
+ public void setHeaderVisibleAmount(float headerVisibleAmount) {
+ if (mContractedWrapper != null) {
+ mContractedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
+ }
+ if (mHeadsUpWrapper != null) {
+ mHeadsUpWrapper.setHeaderVisibleAmount(headerVisibleAmount);
+ }
+ if (mExpandedWrapper != null) {
+ mExpandedWrapper.setHeaderVisibleAmount(headerVisibleAmount);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 22a186ff1706..ebd22610b520 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -107,6 +107,8 @@ public class NotificationData {
public CharSequence remoteInputTextWhenReset;
public long lastRemoteInputSent = NOT_LAUNCHED_YET;
public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
+ public CharSequence headsUpStatusBarText;
+ public CharSequence headsUpStatusBarTextPublic;
public Entry(StatusBarNotification n) {
this.key = n.getKey();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 4f09133303de..abcdef3e7345 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -252,7 +252,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
float notificationClipEnd;
- boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight;
+ boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight
+ || row.isPinned();
boolean isLastChild = child == lastChild;
float rowTranslationY = row.getTranslationY();
if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index dfcd5e60719a..251e04b01afe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -52,6 +52,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
protected final ViewInvertHelper mInvertHelper;
protected final ViewTransformationHelper mTransformationHelper;
+ private final int mTranslationForHeader;
protected int mColor;
private ImageView mIcon;
@@ -63,6 +64,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
private boolean mIsLowPriority;
private boolean mTransformLowPriorityTitle;
private boolean mShowExpandButtonAtEnd;
+ protected float mHeaderTranslation;
protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
super(ctx, view, row);
@@ -99,6 +101,10 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
resolveHeaderViews();
updateInvertHelper();
addAppOpsOnClickListener(row);
+ mTranslationForHeader = ctx.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin)
+ - ctx.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin_top);
}
@Override
@@ -243,6 +249,19 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
}
@Override
+ public void setHeaderVisibleAmount(float headerVisibleAmount) {
+ super.setHeaderVisibleAmount(headerVisibleAmount);
+ mNotificationHeader.setAlpha(headerVisibleAmount);
+ mHeaderTranslation = (1.0f - headerVisibleAmount) * mTranslationForHeader;
+ mView.setTranslationY(mHeaderTranslation);
+ }
+
+ @Override
+ public int getHeaderTranslation() {
+ return (int) mHeaderTranslation;
+ }
+
+ @Override
public NotificationHeaderView getNotificationHeader() {
return mNotificationHeader;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
index f9671182ab8c..f5110a2dbc2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
@@ -179,6 +179,9 @@ public class NotificationInflater {
: builder.makeAmbientNotification();
}
result.packageContext = packageContext;
+ result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
+ result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
+ true /* showingPublic */);
return result;
}
@@ -456,6 +459,8 @@ public class NotificationInflater {
}
entry.cachedAmbientContentView = result.newAmbientView;
}
+ entry.headsUpStatusBarText = result.headsUpStatusBarText;
+ entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
if (endListener != null) {
endListener.onAsyncInflationFinished(row.getEntry());
}
@@ -665,6 +670,8 @@ public class NotificationInflater {
private View inflatedExpandedView;
private View inflatedAmbientView;
private View inflatedPublicView;
+ private CharSequence headsUpStatusBarText;
+ private CharSequence headsUpStatusBarTextPublic;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index d463eae6e43f..28beb21dba4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -278,7 +278,10 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
if (mActionsContainer != null) {
// We should never push the actions higher than they are in the headsup view.
int constrainedContentHeight = Math.max(mContentHeight, mMinHeightHint);
- mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight());
+
+ // We also need to compensate for any header translation, since we're always at the end.
+ mActionsContainer.setTranslationY(constrainedContentHeight - mView.getHeight()
+ - getHeaderTranslation());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 17eb4c110031..873f088f8431 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -133,6 +133,10 @@ public abstract class NotificationViewWrapper implements TransformableView {
return null;
}
+ public int getHeaderTranslation() {
+ return 0;
+ }
+
@Override
public TransformState getCurrentState(int fadingView) {
return null;
@@ -198,4 +202,7 @@ public abstract class NotificationViewWrapper implements TransformableView {
public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
return false;
}
+
+ public void setHeaderVisibleAmount(float headerVisibleAmount) {
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
new file mode 100644
index 000000000000..cd97c05ede25
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.graphics.Rect;
+import android.service.notification.StatusBarNotification;
+import android.util.EventLog;
+import android.util.Log;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.HeadsUpStatusBarView;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+import java.util.stream.Stream;
+
+/**
+ * Controls the appearance of heads up notifications in the icon area and the header itself.
+ */
+class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
+ DarkIconDispatcher.DarkReceiver {
+ private final NotificationIconAreaController mNotificationIconAreaController;
+ private final HeadsUpManagerPhone mHeadsUpManager;
+ private final NotificationStackScrollLayout mStackScroller;
+ private final HeadsUpStatusBarView mHeadsUpStatusBarView;
+ private final View mClockView;
+ private final DarkIconDispatcher mDarkIconDispatcher;
+ private float mExpandedHeight;
+ private boolean mIsExpanded;
+ private float mExpandFraction;
+ private ExpandableNotificationRow mTrackedChild;
+ private boolean mShown;
+
+ public HeadsUpAppearanceController(
+ NotificationIconAreaController notificationIconAreaController,
+ HeadsUpManagerPhone headsUpManager,
+ View statusbarView) {
+ this(notificationIconAreaController, headsUpManager,
+ statusbarView.findViewById(R.id.heads_up_status_bar_view),
+ statusbarView.findViewById(R.id.notification_stack_scroller),
+ statusbarView.findViewById(R.id.notification_panel),
+ statusbarView.findViewById(R.id.clock));
+ }
+
+ @VisibleForTesting
+ public HeadsUpAppearanceController(
+ NotificationIconAreaController notificationIconAreaController,
+ HeadsUpManagerPhone headsUpManager,
+ HeadsUpStatusBarView headsUpStatusBarView,
+ NotificationStackScrollLayout stackScroller,
+ NotificationPanelView panelView,
+ View clockView) {
+ mNotificationIconAreaController = notificationIconAreaController;
+ mHeadsUpManager = headsUpManager;
+ mHeadsUpManager.addListener(this);
+ mHeadsUpStatusBarView = headsUpStatusBarView;
+ mStackScroller = stackScroller;
+ panelView.addTrackingHeadsUpListener(this::setTrackingHeadsUp);
+ mStackScroller.addOnExpandedHeightListener(this::setExpandedHeight);
+ mClockView = clockView;
+ mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
+ mDarkIconDispatcher.addDarkReceiver(this);
+ }
+
+ @Override
+ public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+ updateTopEntry();
+ updateHeader(headsUp.getEntry());
+ }
+
+ private void updateTopEntry() {
+ NotificationData.Entry newEntry = null;
+ if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) {
+ newEntry = mHeadsUpManager.getTopEntry();
+ }
+ NotificationData.Entry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
+ mHeadsUpStatusBarView.setEntry(newEntry);
+ if (newEntry != previousEntry) {
+ if (newEntry == null) {
+ // no heads up anymore, lets start the disappear animation
+
+ setShown(false);
+ } else if (previousEntry == null) {
+ // We now have a headsUp and didn't have one before. Let's start the disappear
+ // animation
+ setShown(true);
+ }
+ mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
+ : newEntry.icon, mHeadsUpStatusBarView.getIconDrawingRect());
+ }
+ }
+
+ private void setShown(boolean isShown) {
+ mShown = isShown;
+ mHeadsUpStatusBarView.setVisibility(isShown ? View.VISIBLE : View.GONE);
+ mClockView.setVisibility(!isShown ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ @VisibleForTesting
+ public boolean isShown() {
+ return mShown;
+ }
+
+ @Override
+ public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ updateTopEntry();
+ updateHeader(headsUp.getEntry());
+ }
+
+ public void setExpandedHeight(float expandedHeight, float appearFraction) {
+ boolean changedHeight = expandedHeight != mExpandedHeight;
+ mExpandedHeight = expandedHeight;
+ mExpandFraction = appearFraction;
+ boolean isExpanded = expandedHeight > 0;
+ if (changedHeight) {
+ updateHeadsUpHeaders();
+ }
+ if (isExpanded != mIsExpanded) {
+ mIsExpanded = isExpanded;
+ updateTopEntry();
+ }
+ }
+
+ /**
+ * Set a headsUp to be tracked, meaning that it is currently being pulled down after being
+ * in a pinned state on the top. The expand animation is different in that case and we need
+ * to update the header constantly afterwards.
+ *
+ * @param trackedChild the tracked headsUp or null if it's not tracking anymore.
+ */
+ public void setTrackingHeadsUp(ExpandableNotificationRow trackedChild) {
+ ExpandableNotificationRow previousTracked = mTrackedChild;
+ mTrackedChild = trackedChild;
+ if (previousTracked != null) {
+ updateHeader(previousTracked.getEntry());
+ }
+ }
+
+ private void updateHeadsUpHeaders() {
+ mHeadsUpManager.getAllEntries().forEach(entry -> {
+ updateHeader(entry);
+ });
+ }
+
+ private void updateHeader(NotificationData.Entry entry) {
+ ExpandableNotificationRow row = entry.row;
+ float headerVisibleAmount = 1.0f;
+ if (row.isPinned() || row == mTrackedChild) {
+ headerVisibleAmount = mExpandFraction;
+ }
+ row.setHeaderVisibleAmount(headerVisibleAmount);
+ }
+
+ @Override
+ public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+ mHeadsUpStatusBarView.onDarkChanged(area, darkIntensity, tint);
+ }
+
+ public void setPublicMode(boolean publicMode) {
+ mHeadsUpStatusBarView.setPublicMode(publicMode);
+ updateTopEntry();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index d2cdc27d982c..fa0a774c249e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -29,6 +29,7 @@ import android.view.ViewTreeObserver;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
+import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
@@ -52,12 +53,13 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
private static final boolean DEBUG = false;
private final View mStatusBarWindowView;
- private int mStatusBarHeight;
private final NotificationGroupManager mGroupManager;
private final StatusBar mBar;
private final VisualStabilityManager mVisualStabilityManager;
-
private boolean mReleaseOnExpandFinish;
+
+ private int mStatusBarHeight;
+ private int mHeadsUpInset;
private boolean mTrackingHeadsUp;
private HashSet<String> mSwipedOutKeys = new HashSet<>();
private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
@@ -101,9 +103,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
mBar = bar;
mVisualStabilityManager = visualStabilityManager;
- Resources resources = mContext.getResources();
- mStatusBarHeight = resources.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
+ initResources();
addListener(new OnHeadsUpChangedListener() {
@Override
@@ -114,6 +114,20 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
});
}
+ private void initResources() {
+ Resources resources = mContext.getResources();
+ mStatusBarHeight = resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+ mHeadsUpInset = mStatusBarHeight + resources.getDimensionPixelSize(
+ R.dimen.heads_up_status_bar_padding);
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ super.onDensityOrFontScaleChanged();
+ initResources();
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////
// Public methods:
@@ -283,10 +297,10 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
topEntry.getLocationOnScreen(mTmpTwoArray);
int minX = mTmpTwoArray[0];
int maxX = mTmpTwoArray[0] + topEntry.getWidth();
- int maxY = topEntry.getIntrinsicHeight();
+ int height = topEntry.getIntrinsicHeight();
info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(minX, 0, maxX, maxY);
+ info.touchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
} else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 2bfdefe39017..903b81339869 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -23,7 +23,6 @@ import android.view.ViewConfiguration;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
/**
@@ -102,10 +101,11 @@ public class HeadsUpTouchHelper implements Gefingerpoken {
mCollapseSnoozes = h < 0;
mInitialTouchX = x;
mInitialTouchY = y;
- int expandedHeight = mPickedChild.getActualHeight();
- mPanel.setPanelScrimMinFraction((float) expandedHeight
+ int startHeight = (int) (mPickedChild.getActualHeight()
+ + mPickedChild.getTranslationY());
+ mPanel.setPanelScrimMinFraction((float) startHeight
/ mPanel.getMaxPanelHeight());
- mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight);
+ mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight);
mPanel.startExpandingFromPeek();
// This call needs to be after the expansion start otherwise we will get a
// flicker of one frame as it's not expanded yet.
@@ -135,7 +135,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken {
private void setTrackingHeadsUp(boolean tracking) {
mTrackingHeadsUp = tracking;
mHeadsUpManager.setTrackingHeadsUp(tracking);
- mPanel.setTrackingHeadsUp(tracking);
+ mPanel.setTrackedHeadsUp(tracking ? mPickedChild : null);
}
public void notifyFling(boolean collapse) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index e1aed7a04c35..51adebfd05c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -296,4 +296,8 @@ public class NotificationIconAreaController implements DarkReceiver {
mNotificationIcons.setDark(dark, false, 0);
mShelfIcons.setDark(dark, false, 0);
}
+
+ public void showIconIsolated(StatusBarIconView icon, Rect absoluteIconPosition) {
+ mNotificationIcons.showIconIsolated(icon, absoluteIconPosition);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index b29ac9067556..53ae0f82b0ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -21,12 +21,9 @@ import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.Rect;
import android.graphics.drawable.Icon;
-import android.os.AsyncTask;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.support.v4.util.ArrayMap;
-import android.support.v4.util.ArraySet;
import android.util.AttributeSet;
import android.view.View;
@@ -127,6 +124,9 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
private float mVisualOverflowStart;
// Keep track of overflow in range [0, 3]
private int mNumDots;
+ private StatusBarIconView mIsolatedIcon;
+ private Rect mIsolatedIconLocation;
+ private int[] mAbsolutePosition = new int[2];
public NotificationIconContainer(Context context, AttributeSet attrs) {
@@ -196,13 +196,18 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
mIconSize = child.getWidth();
}
}
+ getLocationOnScreen(mAbsolutePosition);
if (mIsStaticLayout) {
- resetViewStates();
- calculateIconTranslations();
- applyIconStates();
+ updateState();
}
}
+ private void updateState() {
+ resetViewStates();
+ calculateIconTranslations();
+ applyIconStates();
+ }
+
public void applyIconStates() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
@@ -306,7 +311,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
View view = getChildAt(i);
ViewState iconState = mIconStates.get(view);
iconState.initFrom(view);
- iconState.alpha = 1.0f;
+ iconState.alpha = mIsolatedIcon == null || view == mIsolatedIcon ? 1.0f : 0.0f;
iconState.hidden = false;
}
}
@@ -402,6 +407,16 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
iconState.xTranslation = getWidth() - iconState.xTranslation - view.getWidth();
}
}
+ if (mIsolatedIcon != null) {
+ IconState iconState = mIconStates.get(mIsolatedIcon);
+ if (iconState != null) {
+ // Most of the time the icon isn't yet added when this is called but only happening
+ // later
+ iconState.xTranslation = mIsolatedIconLocation.left - mAbsolutePosition[0]
+ - (1 - mIsolatedIcon.getIconScale()) * mIsolatedIcon.getWidth() / 2.0f;
+ iconState.visibleState = StatusBarIconView.STATE_ICON;
+ }
+ }
}
private float getLayoutEnd() {
@@ -573,6 +588,12 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
mReplacingIcons = replacingIcons;
}
+ public void showIconIsolated(StatusBarIconView icon, Rect absoluteIconPosition) {
+ mIsolatedIcon = icon;
+ mIsolatedIconLocation = absoluteIconPosition;
+ updateState();
+ }
+
public class IconState extends ViewState {
public static final int NO_VALUE = NotificationIconContainer.NO_VALUE;
public float iconAppearAmount = 1.0f;
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 2711d7ac7a89..63ee9ff684cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -74,7 +74,9 @@ import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
+import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
public class NotificationPanelView extends PanelView implements
ExpandableView.OnHeightChangedListener,
@@ -243,6 +245,8 @@ public class NotificationPanelView extends PanelView implements
private float mExpandOffset;
private boolean mHideIconsDuringNotificationLaunch = true;
private int mStackScrollerMeasuringPass;
+ private ArrayList<Consumer<ExpandableNotificationRow>> mTrackingHeadsUpListeners
+ = new ArrayList<>();
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -269,6 +273,7 @@ public class NotificationPanelView extends PanelView implements
mNotificationStackScroller.setOnHeightChangedListener(this);
mNotificationStackScroller.setOverscrollTopChangedListener(this);
mNotificationStackScroller.setOnEmptySpaceClickListener(this);
+ addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp);
mKeyguardBottomArea = findViewById(R.id.keyguard_bottom_area);
mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
mLastOrientation = getResources().getConfiguration().orientation;
@@ -1780,11 +1785,19 @@ public class NotificationPanelView extends PanelView implements
mQsExpandImmediate = false;
mTwoFingerQsExpandPossible = false;
mIsExpansionFromHeadsUp = false;
- mNotificationStackScroller.setTrackingHeadsUp(false);
+ notifyListenersTrackingHeadsUp(null);
mExpandingFromHeadsUp = false;
setPanelScrimMinFraction(0.0f);
}
+ private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
+ for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
+ Consumer<ExpandableNotificationRow> listener
+ = mTrackingHeadsUpListeners.get(i);
+ listener.accept(pickedChild);
+ }
+ }
+
private void setListening(boolean listening) {
mKeyguardStatusBar.setListening(listening);
if (mQs == null) return;
@@ -2351,9 +2364,9 @@ public class NotificationPanelView extends PanelView implements
this);
}
- public void setTrackingHeadsUp(boolean tracking) {
- if (tracking) {
- mNotificationStackScroller.setTrackingHeadsUp(true);
+ public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
+ if (pickedChild != null) {
+ notifyListenersTrackingHeadsUp(pickedChild);
mExpandingFromHeadsUp = true;
}
// otherwise we update the state when the expansion is finished
@@ -2696,4 +2709,8 @@ public class NotificationPanelView extends PanelView implements
}
}
}
+
+ public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
+ mTrackingHeadsUpListeners.add(listener);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 7422a4343cb4..4382e276d60c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -189,6 +189,7 @@ import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
@@ -602,6 +603,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private NavigationBarFragment mNavigationBar;
private View mNavigationBarView;
protected ActivityLaunchAnimator mActivityLaunchAnimator;
+ private HeadsUpAppearanceController mHeadsUpAppearanceController;
@Override
public void start() {
@@ -807,6 +809,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
+ mHeadsUpAppearanceController = new HeadsUpAppearanceController(
+ mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
setAreThereNotifications();
checkBarModes();
}).getFragmentManager()
@@ -1087,6 +1091,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mKeyguardUserSwitcher.onDensityOrFontScaleChanged();
}
mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
+ mHeadsUpManager.onDensityOrFontScaleChanged();
reevaluateStyles();
}
@@ -1991,7 +1996,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mVisualStabilityManager.setPanelExpanded(isExpanded);
if (isExpanded && getBarState() != StatusBarState.KEYGUARD) {
if (DEBUG) {
- Log.v(TAG, "clearing notification effects from setPanelExpanded");
+ Log.v(TAG, "clearing notification effects from setExpandedHeight");
}
clearNotificationEffects();
}
@@ -2813,7 +2818,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public void setRemoteInputActive(NotificationData.Entry entry,
boolean remoteInputActive) {
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
- entry.row.updateMaxHeights();
+ entry.row.notifyHeightChanged(true /* needsAnimation */);
}
public void lockScrollTo(NotificationData.Entry entry) {
mStackScroller.lockScrollTo(entry.row);
@@ -3833,6 +3838,9 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mStackScroller == null) return;
boolean onKeyguard = mState == StatusBarState.KEYGUARD;
boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
+ if (mHeadsUpAppearanceController != null) {
+ mHeadsUpAppearanceController.setPublicMode(publicMode);
+ }
mStackScroller.setHideSensitive(publicMode, goingToFullShade);
mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */);
mStackScroller.setExpandingEnabled(!onKeyguard);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 040d7ec32ecc..aeda55a8bf8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -443,6 +443,9 @@ public class HeadsUpManager {
entry.reset();
}
+ public void onDensityOrFontScaleChanged() {
+ }
+
/**
* This represents a notification and how long it is in a heads up mode. It also manages its
* lifecycle automatically when created.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index ac2a1e1e041f..e5ab712e9bdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -102,6 +102,9 @@ public class NotificationChildrenContainer extends ViewGroup {
private boolean mShowDividersWhenExpanded;
private boolean mHideDividersDuringExpand;
+ private int mTranslationForHeader;
+ private int mCurrentHeaderTranslation = 0;
+ private float mHeaderVisibleAmount = 1.0f;
public NotificationChildrenContainer(Context context) {
this(context, null);
@@ -142,6 +145,9 @@ public class NotificationChildrenContainer extends ViewGroup {
res.getBoolean(R.bool.config_showDividersWhenGroupNotificationExpanded);
mHideDividersDuringExpand =
res.getBoolean(R.bool.config_hideDividersDuringExpand);
+ mTranslationForHeader = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin)
+ - mNotificationHeaderMargin;
}
@Override
@@ -486,7 +492,7 @@ public class NotificationChildrenContainer extends ViewGroup {
if (showingAsLowPriority()) {
return mNotificationHeaderLowPriority.getHeight();
}
- int intrinsicHeight = mNotificationHeaderMargin;
+ int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
int visibleChildren = 0;
int childCount = mChildren.size();
boolean firstChild = true;
@@ -541,7 +547,7 @@ public class NotificationChildrenContainer extends ViewGroup {
public void getState(StackScrollState resultState, ExpandableViewState parentState,
AmbientState ambientState) {
int childCount = mChildren.size();
- int yPosition = mNotificationHeaderMargin;
+ int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
boolean firstChild = true;
int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
int lastVisibleIndex = maxAllowedVisibleChildren - 1;
@@ -645,6 +651,11 @@ public class NotificationChildrenContainer extends ViewGroup {
mHeaderViewState.zTranslation = childrenExpandedAndNotAnimating
? parentState.zTranslation
: 0;
+ mHeaderViewState.yTranslation = mCurrentHeaderTranslation;
+ mHeaderViewState.alpha = mHeaderVisibleAmount;
+ // The hiding is done automatically by the alpha, otherwise we'll pick it up again
+ // in the next frame with the initFrom call above and have an invisible header
+ mHeaderViewState.hidden = false;
}
}
@@ -1009,7 +1020,8 @@ public class NotificationChildrenContainer extends ViewGroup {
return getMinHeight(NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED, true
/* likeHighPriority */);
}
- int maxContentHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
+ int maxContentHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
+ + mNotificatonTopPadding;
int visibleChildren = 0;
int childCount = mChildren.size();
for (int i = 0; i < childCount; i++) {
@@ -1071,7 +1083,8 @@ public class NotificationChildrenContainer extends ViewGroup {
}
private int getVisibleChildrenExpandHeight() {
- int intrinsicHeight = mNotificationHeaderMargin + mNotificatonTopPadding + mDividerHeight;
+ int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
+ + mNotificatonTopPadding + mDividerHeight;
int visibleChildren = 0;
int childCount = mChildren.size();
int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
@@ -1110,7 +1123,7 @@ public class NotificationChildrenContainer extends ViewGroup {
if (!likeHighPriority && showingAsLowPriority()) {
return mNotificationHeaderLowPriority.getHeight();
}
- int minExpandHeight = mNotificationHeaderMargin;
+ int minExpandHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
int visibleChildren = 0;
boolean firstChild = true;
int childCount = mChildren.size();
@@ -1190,7 +1203,8 @@ public class NotificationChildrenContainer extends ViewGroup {
}
public int getPositionInLinearLayout(View childInGroup) {
- int position = mNotificationHeaderMargin + mNotificatonTopPadding;
+ int position = mNotificationHeaderMargin + mCurrentHeaderTranslation
+ + mNotificatonTopPadding;
for (int i = 0; i < mChildren.size(); i++) {
ExpandableNotificationRow child = mChildren.get(i);
@@ -1281,4 +1295,9 @@ public class NotificationChildrenContainer extends ViewGroup {
last = false;
}
}
+
+ public void setHeaderVisibleAmount(float headerVisibleAmount) {
+ mHeaderVisibleAmount = headerVisibleAmount;
+ mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
index 30997717f318..a36c966a3310 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
@@ -20,7 +20,6 @@ import android.view.View;
import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.HashSet;
@@ -35,6 +34,8 @@ class NotificationRoundnessManager implements OnHeadsUpChangedListener {
private ActivatableNotificationView mLast;
private HashSet<View> mAnimatedChildren;
private Runnable mRoundingChangedCallback;
+ private ExpandableNotificationRow mTrackedHeadsUp;
+ private float mAppearFraction;
@Override
public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
@@ -66,11 +67,20 @@ class NotificationRoundnessManager implements OnHeadsUpChangedListener {
if (view == mLast && !top) {
return 1.0f;
}
+ if (view == mTrackedHeadsUp && mAppearFraction <= 0.0f) {
+ // If we're pushing up on a headsup the appear fraction is < 0 and it needs to still be
+ // rounded.
+ return 1.0f;
+ }
return 0.0f;
}
- public void setExpanded(boolean expanded) {
- mExpanded = expanded;
+ public void setExpanded(float expandedHeight, float appearFraction) {
+ mExpanded = expandedHeight != 0.0f;
+ mAppearFraction = appearFraction;
+ if (mTrackedHeadsUp != null) {
+ updateRounding(mTrackedHeadsUp, true);
+ }
}
public void setFirstAndLastBackgroundChild(ActivatableNotificationView first,
@@ -106,4 +116,8 @@ class NotificationRoundnessManager implements OnHeadsUpChangedListener {
public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) {
mRoundingChangedCallback = roundingChangedCallback;
}
+
+ public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+ mTrackedHeadsUp = row;
+ }
}
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 e4551674116f..174e73476695 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -94,8 +94,8 @@ import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
@@ -108,6 +108,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
+import java.util.function.BiConsumer;
/**
* A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
@@ -404,6 +405,8 @@ public class NotificationStackScrollLayout extends ViewGroup
private final Rect mTmpRect = new Rect();
private int mClockBottom;
private int mAntiBurnInOffsetX;
+ private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
+ private int mHeadsUpInset;
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -443,6 +446,7 @@ public class NotificationStackScrollLayout extends ViewGroup
mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
+ addOnExpandedHeightListener(mRoundnessManager::setExpanded);
updateWillNotDraw();
mBackgroundPaint.setAntiAlias(true);
@@ -585,13 +589,15 @@ public class NotificationStackScrollLayout extends ViewGroup
res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
mMinTopOverScrollToEscape = res.getDimensionPixelSize(
R.dimen.min_top_overscroll_to_qs);
- mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
+ mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
mMinInteractionHeight = res.getDimensionPixelSize(
R.dimen.notification_min_interaction_height);
mCornerRadius = res.getDimensionPixelSize(
Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
+ mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
+ R.dimen.heads_up_status_bar_padding);
}
public void setDrawBackgroundAsSrc(boolean asSrc) {
@@ -704,7 +710,8 @@ public class NotificationStackScrollLayout extends ViewGroup
}
private void updateAlgorithmLayoutMinHeight() {
- mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() ? getLayoutMinHeight() : 0);
+ mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() || isHeadsUpTransition()
+ ? getLayoutMinHeight() : 0);
}
/**
@@ -857,11 +864,12 @@ public class NotificationStackScrollLayout extends ViewGroup
float translationY;
float appearEndPosition = getAppearEndPosition();
float appearStartPosition = getAppearStartPosition();
+ float appearFraction = 1.0f;
if (height >= appearEndPosition) {
translationY = 0;
stackHeight = (int) height;
} else {
- float appearFraction = getAppearFraction(height);
+ appearFraction = getAppearFraction(height);
if (appearFraction >= 0) {
translationY = NotificationUtils.interpolate(getExpandTranslationStart(), 0,
appearFraction);
@@ -870,7 +878,12 @@ public class NotificationStackScrollLayout extends ViewGroup
// start
translationY = height - appearStartPosition + getExpandTranslationStart();
}
- stackHeight = (int) (height - translationY);
+ if (isHeadsUpTransition()) {
+ stackHeight = mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
+ translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
+ } else {
+ stackHeight = (int) (height - translationY);
+ }
}
if (stackHeight != mCurrentStackHeight) {
mCurrentStackHeight = stackHeight;
@@ -878,6 +891,10 @@ public class NotificationStackScrollLayout extends ViewGroup
requestChildrenUpdate();
}
setStackTranslation(translationY);
+ for (int i = 0; i < mExpandedHeightListeners.size(); i++) {
+ BiConsumer<Float, Float> listener = mExpandedHeightListeners.get(i);
+ listener.accept(mExpandedHeight, appearFraction);
+ }
}
private void setRequestedClipBounds(Rect clipRect) {
@@ -912,12 +929,8 @@ public class NotificationStackScrollLayout extends ViewGroup
* Measured in absolute height.
*/
private float getAppearStartPosition() {
- if (mTrackingHeadsUp && mFirstVisibleBackgroundChild != null) {
- if (mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild)) {
- // If we ever expanded beyond the first notification, it's allowed to merge into
- // the shelf
- return mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
- }
+ if (isHeadsUpTransition()) {
+ return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
}
return getMinExpansionHeight();
}
@@ -951,17 +964,14 @@ public class NotificationStackScrollLayout extends ViewGroup
int appearPosition;
int notGoneChildCount = getNotGoneChildCount();
if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
- int minNotificationsForShelf = 1;
- if (mTrackingHeadsUp
+ if (isHeadsUpTransition()
|| (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
appearPosition = getTopHeadsUpPinnedHeight();
- minNotificationsForShelf = 2;
} else {
appearPosition = 0;
- }
- if (notGoneChildCount >= minNotificationsForShelf
- && mShelf.getVisibility() != GONE) {
- appearPosition += mShelf.getIntrinsicHeight();
+ if (notGoneChildCount >= 1 && mShelf.getVisibility() != GONE) {
+ appearPosition += mShelf.getIntrinsicHeight();
+ }
}
} else {
appearPosition = mEmptyShadeView.getHeight();
@@ -969,6 +979,11 @@ public class NotificationStackScrollLayout extends ViewGroup
return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
}
+ private boolean isHeadsUpTransition() {
+ return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
+ && mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
+ }
+
/**
* @param height the height of the panel
* @return the fraction of the appear animation that has been performed
@@ -2524,6 +2539,9 @@ public class NotificationStackScrollLayout extends ViewGroup
}
public int getLayoutMinHeight() {
+ if (isHeadsUpTransition()) {
+ return getTopHeadsUpPinnedHeight();
+ }
return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
}
@@ -3551,7 +3569,6 @@ public class NotificationStackScrollLayout extends ViewGroup
private void setIsExpanded(boolean isExpanded) {
boolean changed = isExpanded != mIsExpanded;
mIsExpanded = isExpanded;
- mRoundnessManager.setExpanded(isExpanded);
mStackScrollAlgorithm.setIsExpanded(isExpanded);
if (changed) {
if (!mIsExpanded) {
@@ -4300,8 +4317,9 @@ public class NotificationStackScrollLayout extends ViewGroup
requestChildrenUpdate();
}
- public void setTrackingHeadsUp(boolean trackingHeadsUp) {
- mTrackingHeadsUp = trackingHeadsUp;
+ public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+ mTrackingHeadsUp = row != null;
+ mRoundnessManager.setTrackingHeadsUp(row);
}
public void setScrimController(ScrimController scrimController) {
@@ -4484,6 +4502,16 @@ public class NotificationStackScrollLayout extends ViewGroup
}
/**
+ * Add a listener whenever the expanded height changes. The first value passed as an argument
+ * is the expanded height and the second one is the appearFraction.
+ *
+ * @param listener the listener to notify.
+ */
+ public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
+ mExpandedHeightListeners.add(listener);
+ }
+
+ /**
* A listener that is notified when the empty space below the notifications is clicked on
*/
public interface OnEmptySpaceClickListener {
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 51737a863748..bf6af22a0cca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -50,6 +50,7 @@ public class StackScrollAlgorithm {
private boolean mIsExpanded;
private boolean mClipNotificationScrollToTop;
private int mStatusBarHeight;
+ private float mHeadsUpInset;
public StackScrollAlgorithm(Context context) {
initView(context);
@@ -68,6 +69,8 @@ public class StackScrollAlgorithm {
mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
mClipNotificationScrollToTop = res.getBoolean(R.bool.config_clipNotificationScrollToTop);
+ mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
+ R.dimen.heads_up_status_bar_padding);
}
public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
@@ -457,7 +460,7 @@ public class StackScrollAlgorithm {
}
}
if (row.isPinned()) {
- childState.yTranslation = Math.max(childState.yTranslation, 0);
+ childState.yTranslation = Math.max(childState.yTranslation, mHeadsUpInset);
childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
childState.hidden = false;
ExpandableViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
new file mode 100644
index 000000000000..c904ef30dd16
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.TestableDependency;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.HeadsUpStatusBarView;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
+
+ private HeadsUpAppearanceController mHeadsUpAppearanceController;
+ private ExpandableNotificationRow mFirst;
+ private HeadsUpStatusBarView mHeadsUpStatusBarView;
+ private HeadsUpManagerPhone mHeadsUpManager;
+
+ @Before
+ public void setUp() throws Exception {
+ NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
+ mFirst = testHelper.createRow();
+ mDependency.injectMockDependency(DarkIconDispatcher.class);
+ mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class),
+ mock(TextView.class));
+ mHeadsUpManager = mock(HeadsUpManagerPhone.class);
+ mHeadsUpAppearanceController = new HeadsUpAppearanceController(
+ mock(NotificationIconAreaController.class),
+ mHeadsUpManager,
+ mHeadsUpStatusBarView,
+ mock(NotificationStackScrollLayout.class),
+ mock(NotificationPanelView.class),
+ new View(mContext));
+ mHeadsUpAppearanceController.setExpandedHeight(0.0f, 0.0f);
+ }
+
+ @Test
+ public void testShowinEntryUpdated() {
+ mFirst.setPinned(true);
+ when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
+ when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ Assert.assertEquals(mFirst.getEntry(), mHeadsUpStatusBarView.getShowingEntry());
+
+ mFirst.setPinned(false);
+ when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ Assert.assertEquals(null, mHeadsUpStatusBarView.getShowingEntry());
+ }
+
+ @Test
+ public void testShownUpdated() {
+ mFirst.setPinned(true);
+ when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
+ when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ Assert.assertTrue(mHeadsUpAppearanceController.isShown());
+
+ mFirst.setPinned(false);
+ when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ Assert.assertFalse(mHeadsUpAppearanceController.isShown());
+ }
+
+ @Test
+ public void testHeaderUpdated() {
+ mFirst.setPinned(true);
+ when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
+ when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
+ mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+ Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 0.0f, 0.0f);
+
+ mFirst.setPinned(false);
+ when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
+ mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+ Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 1.0f, 0.0f);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
index ea754c71cad6..1d2c01dbd564 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
@@ -65,7 +65,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
mRoundnessManager.setOnRoundingChangedCallback(mRoundnessCallback);
mRoundnessManager.setAnimatedChildren(mAnimatedChildren);
mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mFirst);
- mRoundnessManager.setExpanded(true);
+ mRoundnessManager.setExpanded(1.0f, 1.0f);
}
@Test
@@ -112,7 +112,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
@Test
public void testRoundedWhenPinnedAndCollapsed() {
mFirst.setPinned(true);
- mRoundnessManager.setExpanded(false);
+ mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */);
mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
@@ -121,7 +121,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
@Test
public void testRoundedWhenGoingAwayAndCollapsed() {
mFirst.setHeadsUpAnimatingAway(true);
- mRoundnessManager.setExpanded(false);
+ mRoundnessManager.setExpanded(0.0f /* expandedHeight */, 0.0f /* appearFraction */);
mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
@@ -130,7 +130,25 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
@Test
public void testRoundedNormalRoundingWhenExpanded() {
mFirst.setHeadsUpAnimatingAway(true);
- mRoundnessManager.setExpanded(true);
+ mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.0f /* appearFraction */);
+ mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+ Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+ }
+
+ @Test
+ public void testTrackingHeadsUpRoundedIfPushingUp() {
+ mRoundnessManager.setExpanded(1.0f /* expandedHeight */, -0.5f /* appearFraction */);
+ mRoundnessManager.setTrackingHeadsUp(mFirst);
+ mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+ Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+ }
+
+ @Test
+ public void testTrackingHeadsUpNotRoundedIfPushingDown() {
+ mRoundnessManager.setExpanded(1.0f /* expandedHeight */, 0.5f /* appearFraction */);
+ mRoundnessManager.setTrackingHeadsUp(mFirst);
mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);