Merge changes from topic "heads_up_redesign" into pi-dev
* changes:
Added new appear and disappear animations for heads up
Polished the heads up experience
Removed the heads up scrim and replaced it with more elevation
Insetting heads up notifications
Ensured that the heads-up notifications are always rounded
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d8e1c89..c6568e1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5577,6 +5577,24 @@
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 @@
* @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 @@
}
/**
+ * @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 ad4d7dd..ac01c4e 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/drawable/heads_up_scrim.xml b/packages/SystemUI/res/drawable/heads_up_scrim.xml
deleted file mode 100644
index 59000fc..0000000
--- a/packages/SystemUI/res/drawable/heads_up_scrim.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <gradient
- android:type="linear"
- android:angle="-90"
- android:startColor="#55000000"
- android:endColor="#00000000" />
-</shape>
\ No newline at end of file
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 0000000..bacb5c1
--- /dev/null
+++ b/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml
@@ -0,0 +1,48 @@
+<?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"
+ android:alpha="0"
+>
+ <!-- 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 6732e6c..fa696cc 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 8c0b9ba..9d336e2 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"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="horizontal" />
+ <include layout="@layout/heads_up_status_bar_layout" />
- </LinearLayout>
+ <LinearLayout
+ android:layout_height="match_parent"
+ 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>
+ </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/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index ef442e5..75403b9 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -51,14 +51,6 @@
sysui:ignoreRightInset="true"
/>
- <com.android.systemui.statusbar.AlphaOptimizedView
- android:id="@+id/heads_up_scrim"
- android:layout_width="match_parent"
- android:layout_height="@dimen/heads_up_scrim_height"
- android:background="@drawable/heads_up_scrim"
- sysui:ignoreRightInset="true"
- android:importantForAccessibility="no"/>
-
<FrameLayout
android:id="@+id/status_bar_container"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index cb3c282..d65e42b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -110,6 +110,12 @@
<!-- 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>
+
+ <!-- heads up elevation that is added if the view is pinned -->
+ <dimen name="heads_up_pinned_elevation">16dp</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>
@@ -451,7 +457,6 @@
<dimen name="keyguard_clock_notifications_margin">30dp</dimen>
<!-- Minimum margin between clock and status bar -->
<dimen name="keyguard_clock_top_margin">36dp</dimen>
- <dimen name="heads_up_scrim_height">250dp</dimen>
<item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 1470dfa..20d8834 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/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 91edfda..391843c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -100,10 +100,10 @@
}
public ScrimController createScrimController(LightBarController lightBarController,
- ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
- LockscreenWallpaper lockscreenWallpaper, Consumer<Integer> scrimVisibleListener,
- DozeParameters dozeParameters, AlarmManager alarmManager) {
- return new ScrimController(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
+ ScrimView scrimBehind, ScrimView scrimInFront, LockscreenWallpaper lockscreenWallpaper,
+ Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
+ AlarmManager alarmManager) {
+ return new ScrimController(lightBarController, scrimBehind, scrimInFront,
scrimVisibleListener, dozeParameters, alarmManager);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 0876507..8c28af5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -26,6 +26,7 @@
import android.graphics.Color;
import android.graphics.RectF;
import android.util.AttributeSet;
+import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewAnimationUtils;
@@ -178,6 +179,10 @@
private boolean mNeedsDimming;
private int mDimmedAlpha;
private boolean mBlockNextTouch;
+ private boolean mIsHeadsUpAnimation;
+ private int mHeadsUpAddStartLocation;
+ private float mHeadsUpLocation;
+ private boolean mIsAppearing;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -204,6 +209,18 @@
makeInactive(true /* animate */);
}
}, super::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
+ initDimens();
+ }
+
+ private void initDimens() {
+ mHeadsUpAddStartLocation = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin_start);
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ super.onDensityOrFontScaleChanged();
+ initDimens();
}
@Override
@@ -745,27 +762,34 @@
}
@Override
- public void performRemoveAnimation(long duration, float translationDirection,
- Runnable onFinishedRunnable) {
+ public void performRemoveAnimation(long duration, long delay,
+ float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+ Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
enableAppearDrawing(true);
+ mIsHeadsUpAnimation = isHeadsUpAnimation;
+ mHeadsUpLocation = endLocation;
if (mDrawingAppearAnimation) {
startAppearAnimation(false /* isAppearing */, translationDirection,
- 0, duration, onFinishedRunnable);
+ delay, duration, onFinishedRunnable, animationListener);
} else if (onFinishedRunnable != null) {
onFinishedRunnable.run();
}
}
@Override
- public void performAddAnimation(long delay, long duration) {
+ public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
enableAppearDrawing(true);
+ mIsHeadsUpAnimation = isHeadsUpAppear;
+ mHeadsUpLocation = mHeadsUpAddStartLocation;
if (mDrawingAppearAnimation) {
- startAppearAnimation(true /* isAppearing */, -1.0f, delay, duration, null);
+ startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
+ duration, null, null);
}
}
private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
- long duration, final Runnable onFinishedRunnable) {
+ long duration, final Runnable onFinishedRunnable,
+ AnimatorListenerAdapter animationListener) {
cancelAppearAnimation();
mAnimationTranslationY = translationDirection * getActualHeight();
if (mAppearAnimationFraction == -1.0f) {
@@ -778,6 +802,7 @@
mAppearAnimationTranslation = 0;
}
}
+ mIsAppearing = isAppearing;
float targetValue;
if (isAppearing) {
@@ -803,6 +828,9 @@
invalidate();
}
});
+ if (animationListener != null) {
+ mAppearAnimator.addListener(animationListener);
+ }
if (delay > 0) {
// we need to apply the initial state already to avoid drawn frames in the wrong state
updateAppearAnimationAlpha();
@@ -862,9 +890,21 @@
/ (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
- float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) *
- widthFraction);
- float right = getWidth() - left;
+ float startWidthFraction = HORIZONTAL_COLLAPSED_REST_PARTIAL;
+ if (mIsHeadsUpAnimation && !mIsAppearing) {
+ startWidthFraction = 0;
+ }
+ float width = MathUtils.lerp(startWidthFraction, 1.0f, 1.0f - widthFraction)
+ * getWidth();
+ float left;
+ float right;
+ if (mIsHeadsUpAnimation) {
+ left = MathUtils.lerp(mHeadsUpLocation, 0, 1.0f - widthFraction);
+ right = left + width;
+ } else {
+ left = getWidth() * 0.5f - width / 2.0f;
+ right = getWidth() - left;
+ }
// handle top animation
float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
@@ -1046,6 +1086,14 @@
return calculateBgColor(false /* withTint */, false /* withOverride */);
}
+ public boolean isPinned() {
+ return false;
+ }
+
+ public boolean isHeadsUpAnimatingAway() {
+ return false;
+ }
+
public interface OnActivatedListener {
void onActivated(ActivatableNotificationView view);
void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index e8f0925..4728a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -28,11 +28,17 @@
public static final long ANIMATION_DURATION_LENGTH = 210;
public static void fadeOut(final View view, final Runnable endRunnable) {
+ fadeOut(view, ANIMATION_DURATION_LENGTH, 0, endRunnable);
+ }
+
+ public static void fadeOut(final View view, long duration, int delay,
+ final Runnable endRunnable) {
view.animate().cancel();
view.animate()
.alpha(0f)
- .setDuration(ANIMATION_DURATION_LENGTH)
+ .setDuration(duration)
.setInterpolator(Interpolators.ALPHA_OUT)
+ .setStartDelay(delay)
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -93,6 +99,10 @@
}
public static void fadeIn(final View view) {
+ fadeIn(view, ANIMATION_DURATION_LENGTH, 0);
+ }
+
+ public static void fadeIn(final View view, long duration, int delay) {
view.animate().cancel();
if (view.getVisibility() == View.INVISIBLE) {
view.setAlpha(0.0f);
@@ -100,7 +110,8 @@
}
view.animate()
.alpha(1f)
- .setDuration(ANIMATION_DURATION_LENGTH)
+ .setDuration(duration)
+ .setStartDelay(delay)
.setInterpolator(Interpolators.ALPHA_IN)
.withEndAction(null);
if (view.hasOverlappingRendering()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index e5c5dcd..9bd8080 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 @@
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 @@
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 @@
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 */);
+ }
+ }
+
+ @Override
+ public float getHeaderVisibleAmount() {
+ return mHeaderVisibleAmount;
+ }
+
@Override
public void setHeadsUpIsVisible() {
super.setHeadsUpIsVisible();
@@ -722,6 +745,7 @@
}
}
+ @Override
public boolean isPinned() {
return mIsPinned;
}
@@ -741,11 +765,11 @@
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();
}
}
@@ -1034,11 +1058,12 @@
}
}
- public void setDismissed(boolean dismissed, boolean fromAccessibility) {
- mDismissed = dismissed;
+ public void setDismissed(boolean fromAccessibility) {
+ mDismissed = true;
mGroupParentWhenDismissed = mNotificationParent;
mRefocusOnDismiss = fromAccessibility;
mChildAfterViewWhenDismissed = null;
+ mEntry.icon.setDismissed();
if (isChildInGroup()) {
List<ExpandableNotificationRow> notificationChildren =
mNotificationParent.getNotificationChildren();
@@ -1101,6 +1126,7 @@
* @return if the view was just heads upped and is now animating away. During such a time the
* layout needs to be kept consistent
*/
+ @Override
public boolean isHeadsUpAnimatingAway() {
return mHeadsupDisappearRunning;
}
@@ -1121,7 +1147,7 @@
groupSummary.performDismiss(fromAccessibility);
}
}
- setDismissed(true, fromAccessibility);
+ setDismissed(fromAccessibility);
if (isClearable()) {
if (mOnDismissRunnable != null) {
mOnDismissRunnable.run();
@@ -1921,9 +1947,9 @@
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();
@@ -1997,8 +2023,11 @@
@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();
}
@@ -2022,15 +2051,6 @@
}
}
- 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);
@@ -2173,7 +2193,12 @@
}
public int getMaxExpandHeight() {
- return mMaxExpandHeight;
+ return mPrivateLayout.getExpandHeight();
+ }
+
+
+ private int getHeadsUpHeight() {
+ return mPrivateLayout.getHeadsUpHeight();
}
public boolean areGutsExposed() {
@@ -2273,7 +2298,7 @@
} else if (mIsSummaryWithChildren && !isGroupExpanded() && !shouldShowPublic()) {
return mChildrenContainer.getMinHeight();
} else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) {
- return mHeadsUpHeight;
+ return getHeadsUpHeight();
}
NotificationContentView showingLayout = getShowingLayout();
return showingLayout.getMinHeight();
@@ -2319,10 +2344,6 @@
}
}
- public boolean isMaxExpandHeightInitialized() {
- return mMaxExpandHeight != 0;
- }
-
public NotificationContentView getShowingLayout() {
return shouldShowPublic() ? mPublicLayout : mPrivateLayout;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index 8bc2201..67268c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -276,12 +276,18 @@
setClipToOutline(mAlwaysRoundBothCorners);
}
- public void setTopRoundness(float topRoundness, boolean animate) {
+ /**
+ * Set the topRoundness of this view.
+ * @return Whether the roundness was changed.
+ */
+ public boolean setTopRoundness(float topRoundness, boolean animate) {
if (mTopRoundness != topRoundness) {
mTopRoundness = topRoundness;
PropertyAnimator.setProperty(this, TOP_ROUNDNESS, topRoundness,
ROUNDNESS_PROPERTIES, animate);
+ return true;
}
+ return false;
}
protected void applyRoundness() {
@@ -305,12 +311,18 @@
return mCurrentBottomRoundness * mOutlineRadius;
}
- public void setBottomRoundness(float bottomRoundness, boolean animate) {
+ /**
+ * Set the bottom roundness of this view.
+ * @return Whether the roundness was changed.
+ */
+ public boolean setBottomRoundness(float bottomRoundness, boolean animate) {
if (mBottomRoundness != bottomRoundness) {
mBottomRoundness = bottomRoundness;
PropertyAnimator.setProperty(this, BOTTOM_ROUNDNESS, bottomRoundness,
ROUNDNESS_PROPERTIES, animate);
+ return true;
}
+ return false;
}
protected void setBackgroundTop(int backgroundTop) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 204adc8..df6a977 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar;
+import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -296,19 +297,24 @@
/**
* Perform a remove animation on this view.
- *
* @param duration The duration of the remove animation.
+ * @param delay The delay of the animation
* @param translationDirection The direction value from [-1 ... 1] indicating in which the
- * animation should be performed. A value of -1 means that The
- * remove animation should be performed upwards,
- * such that the child appears to be going away to the top. 1
- * Should mean the opposite.
+ * animation should be performed. A value of -1 means that The
+ * remove animation should be performed upwards,
+ * such that the child appears to be going away to the top. 1
+ * Should mean the opposite.
+ * @param isHeadsUpAnimation Is this a headsUp animation.
+ * @param endLocation The location where the horizonal heads up disappear animation should end.
* @param onFinishedRunnable A runnable which should be run when the animation is finished.
+ * @param animationListener An animation listener to add to the animation.
*/
- public abstract void performRemoveAnimation(long duration, float translationDirection,
- Runnable onFinishedRunnable);
+ public abstract void performRemoveAnimation(long duration,
+ long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+ Runnable onFinishedRunnable,
+ AnimatorListenerAdapter animationListener);
- public abstract void performAddAnimation(long delay, long duration);
+ public abstract void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear);
/**
* Set the notification appearance to be below the speed bump.
@@ -390,6 +396,10 @@
}
}
+ public float getHeaderVisibleAmount() {
+ return 1.0f;
+ }
+
protected boolean shouldClipToActualHeight() {
return true;
}
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 0000000..5e03fbf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -0,0 +1,193 @@
+/*
+ * 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.Configuration;
+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 mLayoutedIconRect = new Rect();
+ private int[] mTmpPosition = new int[2];
+ private boolean mFirstLayout = true;
+ private boolean mPublicMode;
+ private int mMaxWidth;
+ private View mRootView;
+ private int mLeftInset;
+ private Rect mIconDrawingRect = new Rect();
+ private Runnable mOnDrawingRectChangedListener;
+
+ 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);
+ updateMaxWidth();
+ }
+
+ private void updateMaxWidth() {
+ int maxWidth = getResources().getDimensionPixelSize(R.dimen.qs_panel_width);
+ if (maxWidth != mMaxWidth) {
+ // maxWidth doesn't work with fill_parent, let's manually make it at most as big as the
+ // notification panel
+ mMaxWidth = maxWidth;
+ requestLayout();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mMaxWidth > 0) {
+ int newSize = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth);
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(newSize,
+ MeasureSpec.getMode(widthMeasureSpec));
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateMaxWidth();
+ }
+
+ @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 = (int) (mTmpPosition[0] - getTranslationX());
+ int top = mTmpPosition[1];
+ int right = left + mIconPlaceholder.getWidth();
+ int bottom = top + mIconPlaceholder.getHeight();
+ mLayoutedIconRect.set(left, top, right, bottom);
+ updateDrawingRect();
+ int targetPadding = mAbsoluteStartPadding + mLeftInset;
+ if (left != targetPadding) {
+ int newPadding = targetPadding - 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;
+ }
+ }
+
+ @Override
+ public void setTranslationX(float translationX) {
+ super.setTranslationX(translationX);
+ updateDrawingRect();
+ }
+
+ private void updateDrawingRect() {
+ float oldLeft = mIconDrawingRect.left;
+ mIconDrawingRect.set(mLayoutedIconRect);
+ mIconDrawingRect.offset((int) getTranslationX(), 0);
+ if (oldLeft != mIconDrawingRect.left && mOnDrawingRectChangedListener != null) {
+ mOnDrawingRectChangedListener.run();
+ }
+ }
+
+ @Override
+ protected boolean fitSystemWindows(Rect insets) {
+ mLeftInset = insets.left;
+ return super.fitSystemWindows(insets);
+ }
+
+ public NotificationData.Entry getShowingEntry() {
+ return mShowingEntry;
+ }
+
+ public Rect getIconDrawingRect() {
+ return mIconDrawingRect;
+ }
+
+ public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+ mTextView.setTextColor(DarkIconDispatcher.getTint(area, this, tint));
+ }
+
+ public void setPublicMode(boolean publicMode) {
+ mPublicMode = publicMode;
+ }
+
+ public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) {
+ mOnDrawingRectChangedListener = onDrawingRectChangedListener;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 73c8795..f64b1bc 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 @@
&& (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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 58e492f..775faee 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 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 4f09133..abcdef3 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 @@
}
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/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
index 0a7ee51..badc40d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar;
+import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
@@ -112,14 +113,16 @@
}
@Override
- public void performRemoveAnimation(long duration, float translationDirection,
- Runnable onFinishedRunnable) {
+ public void performRemoveAnimation(long duration, long delay,
+ float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+ Runnable onFinishedRunnable,
+ AnimatorListenerAdapter animationListener) {
// TODO: Use duration
performVisibilityAnimation(false);
}
@Override
- public void performAddAnimation(long delay, long duration) {
+ public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
// TODO: use delay and duration
performVisibilityAnimation(true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 6cfd42f..5bb85e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -27,7 +27,6 @@
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -43,7 +42,6 @@
import android.util.Log;
import android.util.Property;
import android.util.TypedValue;
-import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Interpolator;
@@ -144,6 +142,8 @@
private ColorMatrixColorFilter mMatrixColorFilter;
private boolean mIsInShelf;
private Runnable mLayoutRunnable;
+ private boolean mDismissed;
+ private Runnable mOnDismissListener;
public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
this(context, slot, sbn, false);
@@ -668,6 +668,19 @@
}
public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable) {
+ setVisibleState(visibleState, animate, endRunnable, 0);
+ }
+
+ /**
+ * Set the visibleState of this view.
+ *
+ * @param visibleState The new state.
+ * @param animate Should we animate?
+ * @param endRunnable The runnable to run at the end.
+ * @param duration The duration of an animation or 0 if the default should be taken.
+ */
+ public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable,
+ long duration) {
boolean runnableAdded = false;
if (visibleState != mVisibleState) {
mVisibleState = visibleState;
@@ -689,7 +702,8 @@
mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT,
currentAmount, targetAmount);
mIconAppearAnimator.setInterpolator(interpolator);
- mIconAppearAnimator.setDuration(ANIMATION_DURATION_FAST);
+ mIconAppearAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
+ : duration);
mIconAppearAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -711,8 +725,9 @@
if (targetAmount != currentAmount) {
mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT,
currentAmount, targetAmount);
- mDotAnimator.setInterpolator(interpolator);
- mDotAnimator.setDuration(ANIMATION_DURATION_FAST);
+ mDotAnimator.setInterpolator(interpolator);;
+ mDotAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
+ : duration);
final boolean runRunnable = !runnableAdded;
mDotAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -837,6 +852,21 @@
mLayoutRunnable = runnable;
}
+ public void setDismissed() {
+ mDismissed = true;
+ if (mOnDismissListener != null) {
+ mOnDismissListener.run();
+ }
+ }
+
+ public boolean isDismissed() {
+ return mDismissed;
+ }
+
+ public void setOnDismissListener(Runnable onDismissListener) {
+ mOnDismissListener = onDismissListener;
+ }
+
public interface OnVisibilityChangedListener {
void onVisibilityChanged(int newVisibility);
}
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 dfcd5e6..251e04b 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 @@
protected final ViewInvertHelper mInvertHelper;
protected final ViewTransformationHelper mTransformationHelper;
+ private final int mTranslationForHeader;
protected int mColor;
private ImageView mIcon;
@@ -63,6 +64,7 @@
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 @@
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 @@
}
@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 f967118..f5110a2d 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 @@
: builder.makeAmbientNotification();
}
result.packageContext = packageContext;
+ result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
+ result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
+ true /* showingPublic */);
return result;
}
@@ -456,6 +459,8 @@
}
entry.cachedAmbientContentView = result.newAmbientView;
}
+ entry.headsUpStatusBarText = result.headsUpStatusBarText;
+ entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
if (endListener != null) {
endListener.onAsyncInflationFinished(row.getEntry());
}
@@ -665,6 +670,8 @@
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 d463eae..28beb21 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 @@
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 17eb4c1..873f088 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 @@
return null;
}
+ public int getHeaderTranslation() {
+ return 0;
+ }
+
@Override
public TransformState getCurrentState(int fadingView) {
return null;
@@ -198,4 +202,7 @@
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 0000000..c576801
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -0,0 +1,225 @@
+/*
+ * 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.graphics.Rect;
+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.CrossFadeHelper;
+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;
+
+/**
+ * Controls the appearance of heads up notifications in the icon area and the header itself.
+ */
+class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
+ DarkIconDispatcher.DarkReceiver {
+ public static final int CONTENT_FADE_DURATION = 110;
+ public static final int CONTENT_FADE_DELAY = 100;
+ 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;
+ headsUpStatusBarView.setOnDrawingRectChangedListener(
+ () -> updateIsolatedIconLocation(true /* requireUpdate */));
+ mStackScroller = stackScroller;
+ panelView.addTrackingHeadsUpListener(this::setTrackingHeadsUp);
+ panelView.setVerticalTranslationListener(this::updatePanelTranslation);
+ panelView.setHeadsUpAppearanceController(this);
+ mStackScroller.addOnExpandedHeightListener(this::setExpandedHeight);
+ mStackScroller.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
+ -> updatePanelTranslation());
+ mClockView = clockView;
+ mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
+ mDarkIconDispatcher.addDarkReceiver(this);
+ }
+
+ private void updateIsolatedIconLocation(boolean requireStateUpdate) {
+ mNotificationIconAreaController.setIsolatedIconLocation(
+ mHeadsUpStatusBarView.getIconDrawingRect(), requireStateUpdate);
+ }
+
+ @Override
+ public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+ updateTopEntry();
+ updateHeader(headsUp.getEntry());
+ }
+
+ public void updatePanelTranslation() {
+ float newTranslation = mStackScroller.getLeft() + mStackScroller.getTranslationX();
+ mHeadsUpStatusBarView.setTranslationX(newTranslation);
+ }
+
+ 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) {
+ boolean animateIsolation = false;
+ if (newEntry == null) {
+ // no heads up anymore, lets start the disappear animation
+
+ setShown(false);
+ animateIsolation = !mIsExpanded;
+ } else if (previousEntry == null) {
+ // We now have a headsUp and didn't have one before. Let's start the disappear
+ // animation
+ setShown(true);
+ animateIsolation = !mIsExpanded;
+ }
+ updateIsolatedIconLocation(false /* requireUpdate */);
+ mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
+ : newEntry.icon, animateIsolation);
+ }
+ }
+
+ private void setShown(boolean isShown) {
+ if (mShown != isShown) {
+ mShown = isShown;
+ if (isShown) {
+ mHeadsUpStatusBarView.setVisibility(View.VISIBLE);
+ CrossFadeHelper.fadeIn(mHeadsUpStatusBarView, CONTENT_FADE_DURATION /* duration */,
+ CONTENT_FADE_DELAY /* delay */);
+ CrossFadeHelper.fadeOut(mClockView, CONTENT_FADE_DURATION/* duration */,
+ 0 /* delay */, () -> mClockView.setVisibility(View.INVISIBLE));
+ } else {
+ CrossFadeHelper.fadeIn(mClockView, CONTENT_FADE_DURATION /* duration */,
+ CONTENT_FADE_DELAY /* delay */);
+ CrossFadeHelper.fadeOut(mHeadsUpStatusBarView, CONTENT_FADE_DURATION/* duration */,
+ 0 /* delay */, () -> mHeadsUpStatusBarView.setVisibility(View.GONE));
+
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public boolean isShown() {
+ return mShown;
+ }
+
+ /**
+ * Should the headsup status bar view be visible right now? This may be different from isShown,
+ * since the headsUp manager might not have notified us yet of the state change.
+ *
+ * @return if the heads up status bar view should be shown
+ */
+ public boolean shouldBeVisible() {
+ return !mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp();
+ }
+
+ @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 d2cdc27..fa0a774 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 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 @@
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 @@
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 @@
});
}
+ 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 @@
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 2bfdefe..903b813 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 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 @@
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 @@
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 e1aed7a..9063dea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -12,9 +12,11 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.NotificationColorUtil;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.NotificationEntryManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -31,6 +33,8 @@
*/
public class NotificationIconAreaController implements DarkReceiver {
private final NotificationColorUtil mNotificationColorUtil;
+ private final NotificationEntryManager mEntryManager;
+ private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;
private int mIconSize;
private int mIconHPadding;
@@ -48,6 +52,7 @@
mStatusBar = statusBar;
mNotificationColorUtil = NotificationColorUtil.getInstance(context);
mContext = context;
+ mEntryManager = Dependency.get(NotificationEntryManager.class);
initializeNotificationAreaViews(context);
}
@@ -129,8 +134,8 @@
}
protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
- NotificationData notificationData, boolean showAmbient) {
- if (notificationData.isAmbient(entry.key) && !showAmbient) {
+ boolean showAmbient, boolean hideDismissed) {
+ if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
return false;
}
if (!StatusBar.isTopLevelChild(entry)) {
@@ -139,9 +144,13 @@
if (entry.row.getVisibility() == View.GONE) {
return false;
}
+ if (entry.row.isDismissed() && hideDismissed) {
+ return false;
+ }
// showAmbient == show in shade but not shelf
- if (!showAmbient && notificationData.shouldSuppressStatusBar(entry.key)) {
+ if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(
+ entry.key)) {
return false;
}
@@ -151,28 +160,30 @@
/**
* Updates the notifications with the given list of notifications to display.
*/
- public void updateNotificationIcons(NotificationData notificationData) {
+ public void updateNotificationIcons() {
- updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons,
- false /* showAmbient */);
- updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons,
- NotificationShelf.SHOW_AMBIENT_ICONS);
+ updateStatusBarIcons();
+ updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
+ NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */);
applyNotificationIconsTint();
}
+ private void updateStatusBarIcons() {
+ updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
+ false /* showAmbient */, true /* hideDismissed */);
+ }
+
/**
* Updates the notification icons for a host layout. This will ensure that the notification
* host layout will have the same icons like the ones in here.
- *
- * @param notificationData the notification data to look up which notifications are relevant
* @param function A function to look up an icon view based on an entry
* @param hostLayout which layout should be updated
* @param showAmbient should ambient notification icons be shown
+ * @param hideDismissed should dismissed icons be hidden
*/
- private void updateIconsForLayout(NotificationData notificationData,
- Function<NotificationData.Entry, StatusBarIconView> function,
- NotificationIconContainer hostLayout, boolean showAmbient) {
+ private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
+ NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed) {
ArrayList<StatusBarIconView> toShow = new ArrayList<>(
mNotificationScrollLayout.getChildCount());
@@ -181,7 +192,7 @@
View view = mNotificationScrollLayout.getChildAt(i);
if (view instanceof ExpandableNotificationRow) {
NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
- if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) {
+ if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed)) {
toShow.add(function.apply(ent));
}
}
@@ -243,10 +254,13 @@
final FrameLayout.LayoutParams params = generateIconLayoutParams();
for (int i = 0; i < toShow.size(); i++) {
- View v = toShow.get(i);
+ StatusBarIconView v = toShow.get(i);
// The view might still be transiently added if it was just removed and added again
hostLayout.removeTransientView(v);
if (v.getParent() == null) {
+ if (hideDismissed) {
+ v.setOnDismissListener(mUpdateStatusBarIcons);
+ }
hostLayout.addView(v, i, params);
}
}
@@ -296,4 +310,12 @@
mNotificationIcons.setDark(dark, false, 0);
mShelfIcons.setDark(dark, false, 0);
}
+
+ public void showIconIsolated(StatusBarIconView icon, boolean animated) {
+ mNotificationIcons.showIconIsolated(icon, animated);
+ }
+
+ public void setIsolatedIconLocation(Rect iconDrawingRect, boolean requireStateUpdate) {
+ mNotificationIcons.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate);
+ }
}
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 b29ac90..5517434 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -16,17 +16,17 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DURATION;
+import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DELAY;
+
import android.content.Context;
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;
@@ -100,6 +100,33 @@
}
}.setDuration(200).setDelay(50);
+ /**
+ * The animation property used for all icons that were not isolated, when the isolation ends.
+ * This just fades the alpha and doesn't affect the movement and has a delay.
+ */
+ private static final AnimationProperties UNISOLATION_PROPERTY_OTHERS
+ = new AnimationProperties() {
+ private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
+
+ @Override
+ public AnimationFilter getAnimationFilter() {
+ return mAnimationFilter;
+ }
+ }.setDuration(CONTENT_FADE_DURATION);
+
+ /**
+ * The animation property used for the icon when its isolation ends.
+ * This animates the translation back to the right position.
+ */
+ private static final AnimationProperties UNISOLATION_PROPERTY = new AnimationProperties() {
+ private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
+
+ @Override
+ public AnimationFilter getAnimationFilter() {
+ return mAnimationFilter;
+ }
+ }.setDuration(CONTENT_FADE_DURATION);
+
public static final int MAX_VISIBLE_ICONS_WHEN_DARK = 5;
public static final int MAX_STATIC_ICONS = 4;
private static final int MAX_DOTS = 3;
@@ -127,6 +154,10 @@
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];
+ private View mIsolatedIconForAnimation;
public NotificationIconContainer(Context context, AttributeSet attrs) {
@@ -196,13 +227,18 @@
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);
@@ -214,6 +250,7 @@
mAddAnimationStartIndex = -1;
mCannedAnimationStartIndex = -1;
mDisallowNextAnimation = false;
+ mIsolatedIconForAnimation = null;
}
@Override
@@ -281,8 +318,10 @@
mIconStates.remove(child);
if (!isReplacingIcon) {
addTransientView(icon, 0);
+ boolean isIsolatedIcon = child == mIsolatedIcon;
icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */,
- () -> removeTransientView(icon));
+ () -> removeTransientView(icon),
+ isIsolatedIcon ? CONTENT_FADE_DURATION : 0);
}
}
}
@@ -306,7 +345,7 @@
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 +441,16 @@
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 +622,21 @@
mReplacingIcons = replacingIcons;
}
+ public void showIconIsolated(StatusBarIconView icon, boolean animated) {
+ if (animated) {
+ mIsolatedIconForAnimation = icon != null ? icon : mIsolatedIcon;
+ }
+ mIsolatedIcon = icon;
+ updateState();
+ }
+
+ public void setIsolatedIconLocation(Rect isolatedIconLocation, boolean requireUpdate) {
+ mIsolatedIconLocation = isolatedIconLocation;
+ if (requireUpdate) {
+ updateState();
+ }
+ }
+
public class IconState extends ViewState {
public static final int NO_VALUE = NotificationIconContainer.NO_VALUE;
public float iconAppearAmount = 1.0f;
@@ -646,6 +710,18 @@
animationProperties.setDuration(CANNED_ANIMATION_DURATION);
animate = true;
}
+ if (mIsolatedIconForAnimation != null) {
+ if (view == mIsolatedIconForAnimation) {
+ animationProperties = UNISOLATION_PROPERTY;
+ animationProperties.setDelay(
+ mIsolatedIcon != null ? CONTENT_FADE_DELAY : 0);
+ } else {
+ animationProperties = UNISOLATION_PROPERTY_OTHERS;
+ animationProperties.setDelay(
+ mIsolatedIcon == null ? CONTENT_FADE_DELAY : 0);
+ }
+ animate = true;
+ }
}
icon.setVisibleState(visibleState, animationsAllowed);
icon.setIconColor(iconColor, needsCannedAnimation && animationsAllowed);
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 2711d7a..53f2690 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.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,10 @@
private float mExpandOffset;
private boolean mHideIconsDuringNotificationLaunch = true;
private int mStackScrollerMeasuringPass;
+ private ArrayList<Consumer<ExpandableNotificationRow>> mTrackingHeadsUpListeners
+ = new ArrayList<>();
+ private Runnable mVerticalTranslationListener;
+ private HeadsUpAppearanceController mHeadsUpAppearanceController;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -269,6 +275,7 @@
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 +1787,19 @@
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 +2366,9 @@
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
@@ -2400,6 +2415,9 @@
protected void setVerticalPanelTranslation(float translation) {
mNotificationStackScroller.setTranslationX(translation);
mQsFrame.setTranslationX(translation);
+ if (mVerticalTranslationListener != null) {
+ mVerticalTranslationListener.run();
+ }
}
protected void updateExpandedHeight(float expandedHeight) {
@@ -2555,6 +2573,10 @@
if (mLaunchingNotification) {
return mHideIconsDuringNotificationLaunch;
}
+ if (mHeadsUpAppearanceController != null
+ && mHeadsUpAppearanceController.shouldBeVisible()) {
+ return false;
+ }
return !isFullWidth() || !mShowIconsWhenExpanded;
}
@@ -2696,4 +2718,17 @@
}
}
}
+
+ public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
+ mTrackingHeadsUpListeners.add(listener);
+ }
+
+ public void setVerticalTranslationListener(Runnable verticalTranslationListener) {
+ mVerticalTranslationListener = verticalTranslationListener;
+ }
+
+ public void setHeadsUpAppearanceController(
+ HeadsUpAppearanceController headsUpAppearanceController) {
+ mHeadsUpAppearanceController = headsUpAppearanceController;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 739d8d5..f8ae7f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -49,7 +49,6 @@
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.stack.ViewState;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.DelayedWakeLock;
@@ -63,8 +62,8 @@
* Controls both the scrim behind the notifications and in front of the notifications (when a
* security method gets shown).
*/
-public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
- OnHeadsUpChangedListener, OnColorsChangedListener, Dumpable {
+public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener,
+ Dumpable {
private static final String TAG = "ScrimController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -106,7 +105,6 @@
private final Context mContext;
protected final ScrimView mScrimBehind;
protected final ScrimView mScrimInFront;
- private final View mHeadsUpScrim;
private final LightBarController mLightBarController;
private final UnlockMethodCache mUnlockMethodCache;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -140,9 +138,6 @@
private int mCurrentInFrontTint;
private int mCurrentBehindTint;
private boolean mWallpaperVisibilityTimedOut;
- private int mPinnedHeadsUpCount;
- private float mTopHeadsUpDragAmount;
- private View mDraggedHeadsUpView;
private int mScrimsVisibility;
private final Consumer<Integer> mScrimVisibleListener;
private boolean mBlankScreen;
@@ -161,11 +156,10 @@
private boolean mKeyguardOccluded;
public ScrimController(LightBarController lightBarController, ScrimView scrimBehind,
- ScrimView scrimInFront, View headsUpScrim, Consumer<Integer> scrimVisibleListener,
+ ScrimView scrimInFront, Consumer<Integer> scrimVisibleListener,
DozeParameters dozeParameters, AlarmManager alarmManager) {
mScrimBehind = scrimBehind;
mScrimInFront = scrimInFront;
- mHeadsUpScrim = headsUpScrim;
mScrimVisibleListener = scrimVisibleListener;
mContext = scrimBehind.getContext();
mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
@@ -196,7 +190,6 @@
}
mState = ScrimState.UNINITIALIZED;
- updateHeadsUpScrim(false);
updateScrims();
}
@@ -363,10 +356,6 @@
return;
}
- if (mPinnedHeadsUpCount != 0) {
- updateHeadsUpScrim(false);
- }
-
setOrAdaptCurrentAnimation(mScrimBehind);
setOrAdaptCurrentAnimation(mScrimInFront);
}
@@ -610,8 +599,6 @@
return mCurrentInFrontAlpha;
} else if (scrim == mScrimBehind) {
return mCurrentBehindAlpha;
- } else if (scrim == mHeadsUpScrim) {
- return calculateHeadsUpAlpha();
} else {
throw new IllegalArgumentException("Unknown scrim view");
}
@@ -622,8 +609,6 @@
return mCurrentInFrontTint;
} else if (scrim == mScrimBehind) {
return mCurrentBehindTint;
- } else if (scrim == mHeadsUpScrim) {
- return Color.TRANSPARENT;
} else {
throw new IllegalArgumentException("Unknown scrim view");
}
@@ -670,40 +655,6 @@
mScrimBehind.setDrawAsSrc(asSrc);
}
- @Override
- public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
- }
-
- @Override
- public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
- mPinnedHeadsUpCount++;
- updateHeadsUpScrim(true);
- }
-
- @Override
- public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
- mPinnedHeadsUpCount--;
- if (headsUp == mDraggedHeadsUpView) {
- mDraggedHeadsUpView = null;
- mTopHeadsUpDragAmount = 0.0f;
- }
- updateHeadsUpScrim(true);
- }
-
- @Override
- public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
- }
-
- private void updateHeadsUpScrim(boolean animate) {
- if (animate) {
- mAnimationDuration = ANIMATION_DURATION;
- cancelAnimator((ValueAnimator) mHeadsUpScrim.getTag(TAG_KEY_ANIM));
- startScrimAnimation(mHeadsUpScrim, mHeadsUpScrim.getAlpha());
- } else {
- setOrAdaptCurrentAnimation(mHeadsUpScrim);
- }
- }
-
@VisibleForTesting
void setOnAnimationFinished(Runnable onAnimationFinished) {
mOnAnimationFinished = onAnimationFinished;
@@ -810,33 +761,6 @@
return Handler.getMain();
}
- /**
- * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means
- * the heads up is in its resting space and 1 means it's fully dragged out.
- *
- * @param draggedHeadsUpView the dragged view
- * @param topHeadsUpDragAmount how far is it dragged
- */
- public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) {
- mTopHeadsUpDragAmount = topHeadsUpDragAmount;
- mDraggedHeadsUpView = draggedHeadsUpView;
- updateHeadsUpScrim(false);
- }
-
- private float calculateHeadsUpAlpha() {
- float alpha;
- if (mPinnedHeadsUpCount >= 2) {
- alpha = 1.0f;
- } else if (mPinnedHeadsUpCount == 0) {
- alpha = 0.0f;
- } else {
- alpha = 1.0f - mTopHeadsUpDragAmount;
- }
- float expandFactor = (1.0f - mExpansionFraction);
- expandFactor = Math.max(expandFactor, 0.0f);
- return alpha * expandFactor;
- }
-
public void setExcludedBackgroundArea(Rect area) {
mScrimBehind.setExcludedArea(area);
}
@@ -851,13 +775,6 @@
mScrimBehind.setChangeRunnable(changeRunnable);
}
- public void onDensityOrFontScaleChanged() {
- ViewGroup.LayoutParams layoutParams = mHeadsUpScrim.getLayoutParams();
- layoutParams.height = mHeadsUpScrim.getResources().getDimensionPixelSize(
- R.dimen.heads_up_scrim_height);
- mHeadsUpScrim.setLayoutParams(layoutParams);
- }
-
public void setCurrentUser(int currentUser) {
// Don't care in the base class.
}
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 7422a43..099de19 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.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 @@
private NavigationBarFragment mNavigationBar;
private View mNavigationBarView;
protected ActivityLaunchAnimator mActivityLaunchAnimator;
+ private HeadsUpAppearanceController mHeadsUpAppearanceController;
@Override
public void start() {
@@ -807,6 +809,8 @@
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
+ mHeadsUpAppearanceController = new HeadsUpAppearanceController(
+ mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
setAreThereNotifications();
checkBarModes();
}).getFragmentManager()
@@ -899,9 +903,8 @@
ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front);
- View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
mScrimController = SystemUIFactory.getInstance().createScrimController(mLightBarController,
- scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper,
+ scrimBehind, scrimInFront, mLockscreenWallpaper,
scrimsVisible -> {
if (mStatusBarWindowManager != null) {
mStatusBarWindowManager.setScrimsVisibility(scrimsVisible);
@@ -916,7 +919,6 @@
mBackdrop.setOnVisibilityChangedRunnable(runnable);
runnable.run();
}
- mHeadsUpManager.addListener(mScrimController);
mStackScroller.setScrimController(mScrimController);
mDozeScrimController = new DozeScrimController(mScrimController, context);
@@ -1073,7 +1075,6 @@
mReinflateNotificationsOnUserSwitched = true;
}
// end old BaseStatusBar.onDensityOrFontScaleChanged().
- mScrimController.onDensityOrFontScaleChanged();
// TODO: Remove this.
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onDensityOrFontScaleChanged();
@@ -1087,6 +1088,7 @@
mKeyguardUserSwitcher.onDensityOrFontScaleChanged();
}
mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
+ mHeadsUpManager.onDensityOrFontScaleChanged();
reevaluateStyles();
}
@@ -1384,8 +1386,7 @@
updateQsExpansionEnabled();
// Let's also update the icons
- mNotificationIconAreaController.updateNotificationIcons(
- mEntryManager.getNotificationData());
+ mNotificationIconAreaController.updateNotificationIcons();
}
@Override
@@ -1991,7 +1992,7 @@
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 +2814,7 @@
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 +3834,9 @@
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 040d7ec..aeda55a 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 @@
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/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 53377d9..c26568e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -21,12 +21,12 @@
import android.view.View;
import java.util.ArrayList;
-import java.util.Set;
/**
* Filters the animations for only a certain type of properties.
*/
public class AnimationFilter {
+ public static final int NO_DELAY = -1;
boolean animateAlpha;
boolean animateX;
boolean animateY;
@@ -40,7 +40,7 @@
public boolean animateShadowAlpha;
boolean hasDelays;
boolean hasGoToFullShadeEvent;
- boolean hasHeadsUpDisappearClickEvent;
+ long customDelay;
private ArraySet<Property> mAnimatedProperties = new ArraySet<>();
public AnimationFilter animateAlpha() {
@@ -129,8 +129,14 @@
hasGoToFullShadeEvent = true;
}
if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
+ .ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
+ customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
+ } else if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
- hasHeadsUpDisappearClickEvent = true;
+ // We need both timeouts when clicking, one to delay it and one for the animation
+ // to look nice
+ customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP_CLICKED
+ + StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
}
}
}
@@ -165,7 +171,7 @@
animateHideSensitive = false;
hasDelays = false;
hasGoToFullShadeEvent = false;
- hasHeadsUpDisappearClickEvent = false;
+ customDelay = NO_DELAY;
mAnimatedProperties.clear();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
index 3bf7d89..a7925aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
@@ -233,7 +233,8 @@
expandableView.setDark(this.dark, animationFilter.animateDark, properties.delay);
if (properties.wasAdded(child) && !hidden) {
- expandableView.performAddAnimation(properties.delay, properties.duration);
+ expandableView.performAddAnimation(properties.delay, properties.duration,
+ false /* isHeadsUpAppear */);
}
if (!expandableView.isInShelf() && this.inShelf) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
index 05c0099..59ce0ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
@@ -23,6 +23,11 @@
* An interpolator specifically designed for the appear animation of heads up notifications.
*/
public class HeadsUpAppearInterpolator extends PathInterpolator {
+
+ private static float X1 = 250f;
+ private static float X2 = 200f;
+ private static float XTOT = (X1 + X2);;
+
public HeadsUpAppearInterpolator() {
super(getAppearPath());
}
@@ -30,22 +35,18 @@
private static Path getAppearPath() {
Path path = new Path();
path.moveTo(0, 0);
- float x1 = 250f;
- float x2 = 150f;
- float x3 = 100f;
float y1 = 90f;
- float y2 = 78f;
- float y3 = 80f;
- float xTot = (x1 + x2 + x3);
- path.cubicTo(x1 * 0.9f / xTot, 0f,
- x1 * 0.8f / xTot, y1 / y3,
- x1 / xTot , y1 / y3);
- path.cubicTo((x1 + x2 * 0.4f) / xTot, y1 / y3,
- (x1 + x2 * 0.2f) / xTot, y2 / y3,
- (x1 + x2) / xTot, y2 / y3);
- path.cubicTo((x1 + x2 + x3 * 0.4f) / xTot, y2 / y3,
- (x1 + x2 + x3 * 0.2f) / xTot, 1f,
- 1f, 1f);
+ float y2 = 80f;
+ path.cubicTo(X1 * 0.8f / XTOT, y1 / y2,
+ X1 * 0.8f / XTOT, y1 / y2,
+ X1 / XTOT, y1 / y2);
+ path.cubicTo((X1 + X2 * 0.4f) / XTOT, y1 / y2,
+ (X1 + X2 * 0.2f) / XTOT, 1.0f,
+ 1.0f , 1.0f);
return path;
}
+
+ public static float getFractionUntilOvershoot() {
+ return X1 / XTOT;
+ }
}
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 ac2a1e1..e5ab712 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 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
}
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 @@
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 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 @@
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
new file mode 100644
index 0000000..a36c966
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
@@ -0,0 +1,123 @@
+/*
+ * 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.stack;
+
+import android.view.View;
+
+import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import java.util.HashSet;
+
+/**
+ * A class that manages the roundness for notification views
+ */
+class NotificationRoundnessManager implements OnHeadsUpChangedListener {
+
+ private boolean mExpanded;
+ private ActivatableNotificationView mFirst;
+ private ActivatableNotificationView mLast;
+ private HashSet<View> mAnimatedChildren;
+ private Runnable mRoundingChangedCallback;
+ private ExpandableNotificationRow mTrackedHeadsUp;
+ private float mAppearFraction;
+
+ @Override
+ public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+ updateRounding(headsUp, false /* animate */);
+ }
+
+ @Override
+ public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+ updateRounding(headsUp, true /* animate */);
+ }
+
+ private void updateRounding(ActivatableNotificationView view, boolean animate) {
+ float topRoundness = getRoundness(view, true /* top */);
+ float bottomRoundness = getRoundness(view, false /* top */);
+ boolean firstChanged = view.setTopRoundness(topRoundness, animate);
+ boolean secondChanged = view.setBottomRoundness(bottomRoundness, animate);
+ if ((view == mFirst || view == mLast) && (firstChanged || secondChanged)) {
+ mRoundingChangedCallback.run();
+ }
+ }
+
+ private float getRoundness(ActivatableNotificationView view, boolean top) {
+ if ((view.isPinned() || view.isHeadsUpAnimatingAway()) && !mExpanded) {
+ return 1.0f;
+ }
+ if (view == mFirst && top) {
+ return 1.0f;
+ }
+ 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(float expandedHeight, float appearFraction) {
+ mExpanded = expandedHeight != 0.0f;
+ mAppearFraction = appearFraction;
+ if (mTrackedHeadsUp != null) {
+ updateRounding(mTrackedHeadsUp, true);
+ }
+ }
+
+ public void setFirstAndLastBackgroundChild(ActivatableNotificationView first,
+ ActivatableNotificationView last) {
+ boolean firstChanged = mFirst != first;
+ boolean lastChanged = mLast != last;
+ if (!firstChanged && !lastChanged) {
+ return;
+ }
+ ActivatableNotificationView oldFirst = mFirst;
+ ActivatableNotificationView oldLast = mLast;
+ mFirst = first;
+ mLast = last;
+ if (firstChanged && oldFirst != null && !oldFirst.isRemoved()) {
+ updateRounding(oldFirst, oldFirst.isShown());
+ }
+ if (lastChanged && oldLast != null && !oldLast.isRemoved()) {
+ updateRounding(oldLast, oldLast.isShown());
+ }
+ if (mFirst != null) {
+ updateRounding(mFirst, mFirst.isShown() && !mAnimatedChildren.contains(mFirst));
+ }
+ if (mLast != null) {
+ updateRounding(mLast, mLast.isShown() && !mAnimatedChildren.contains(mLast));
+ }
+ mRoundingChangedCallback.run();
+ }
+
+ public void setAnimatedChildren(HashSet<View> animatedChildren) {
+ mAnimatedChildren = animatedChildren;
+ }
+
+ 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 a85f4e2..14e801f 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.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.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.
@@ -289,6 +290,7 @@
private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
= new HashSet<>();
private HeadsUpManagerPhone mHeadsUpManager;
+ private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
private boolean mTrackingHeadsUp;
private ScrimController mScrimController;
private boolean mForceNoOverlappingRendering;
@@ -403,6 +405,8 @@
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);
@@ -440,6 +444,9 @@
mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
+ mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
+ mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
+ addOnExpandedHeightListener(mRoundnessManager::setExpanded);
updateWillNotDraw();
mBackgroundPaint.setAntiAlias(true);
@@ -582,13 +589,15 @@
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) {
@@ -701,7 +710,8 @@
}
private void updateAlgorithmLayoutMinHeight() {
- mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() ? getLayoutMinHeight() : 0);
+ mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() || isHeadsUpTransition()
+ ? getLayoutMinHeight() : 0);
}
/**
@@ -854,11 +864,12 @@
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);
@@ -867,7 +878,12 @@
// 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;
@@ -875,6 +891,10 @@
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) {
@@ -909,12 +929,8 @@
* 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();
}
@@ -948,17 +964,14 @@
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();
@@ -966,6 +979,11 @@
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
@@ -1076,10 +1094,6 @@
@Override
public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
- if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) {
- mScrimController.setTopHeadsUpDragAmount(animView,
- Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f));
- }
// Returning true prevents alpha fading.
return !mFadeNotificationsOnDismiss;
}
@@ -2521,6 +2535,9 @@
}
public int getLayoutMinHeight() {
+ if (isHeadsUpTransition()) {
+ return getTopHeadsUpPinnedHeight();
+ }
return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
}
@@ -2929,42 +2946,18 @@
private void updateFirstAndLastBackgroundViews() {
ActivatableNotificationView firstChild = getFirstChildWithBackground();
ActivatableNotificationView lastChild = getLastChildWithBackground();
- boolean firstChanged = firstChild != mFirstVisibleBackgroundChild;
- boolean lastChanged = lastChild != mLastVisibleBackgroundChild;
if (mAnimationsEnabled && mIsExpanded) {
- mAnimateNextBackgroundTop = firstChanged;
- mAnimateNextBackgroundBottom = lastChanged;
+ mAnimateNextBackgroundTop = firstChild != mFirstVisibleBackgroundChild;
+ mAnimateNextBackgroundBottom = lastChild != mLastVisibleBackgroundChild;
} else {
mAnimateNextBackgroundTop = false;
mAnimateNextBackgroundBottom = false;
}
- if (firstChanged && mFirstVisibleBackgroundChild != null
- && !mFirstVisibleBackgroundChild.isRemoved()) {
- mFirstVisibleBackgroundChild.setTopRoundness(0.0f,
- mFirstVisibleBackgroundChild.isShown());
- }
- if (lastChanged && mLastVisibleBackgroundChild != null
- && !mLastVisibleBackgroundChild.isRemoved()) {
- mLastVisibleBackgroundChild.setBottomRoundness(0.0f,
- mLastVisibleBackgroundChild.isShown());
- }
mFirstVisibleBackgroundChild = firstChild;
mLastVisibleBackgroundChild = lastChild;
mAmbientState.setLastVisibleBackgroundChild(lastChild);
- applyRoundedNess();
- }
-
- private void applyRoundedNess() {
- if (mFirstVisibleBackgroundChild != null) {
- mFirstVisibleBackgroundChild.setTopRoundness(1.0f,
- mFirstVisibleBackgroundChild.isShown()
- && !mChildrenToAddAnimated.contains(mFirstVisibleBackgroundChild));
- }
- if (mLastVisibleBackgroundChild != null) {
- mLastVisibleBackgroundChild.setBottomRoundness(1.0f,
- mLastVisibleBackgroundChild.isShown()
- && !mChildrenToAddAnimated.contains(mLastVisibleBackgroundChild));
- }
+ mRoundnessManager.setFirstAndLastBackgroundChild(mFirstVisibleBackgroundChild,
+ mLastVisibleBackgroundChild);
invalidate();
}
@@ -4288,6 +4281,7 @@
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
mAmbientState.setHeadsUpManager(headsUpManager);
+ mHeadsUpManager.addListener(mRoundnessManager);
}
public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
@@ -4319,8 +4313,9 @@
requestChildrenUpdate();
}
- public void setTrackingHeadsUp(boolean trackingHeadsUp) {
- mTrackingHeadsUp = trackingHeadsUp;
+ public void setTrackingHeadsUp(ExpandableNotificationRow row) {
+ mTrackingHeadsUp = row != null;
+ mRoundnessManager.setTrackingHeadsUp(row);
}
public void setScrimController(ScrimController scrimController) {
@@ -4503,6 +4498,16 @@
}
/**
+ * 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 {
@@ -4880,7 +4885,8 @@
.animateHeight()
.animateTopInset()
.animateY()
- .animateZ(),
+ .animateZ()
+ .hasDelays(),
// ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
new AnimationFilter()
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 51737a8..805ce37 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,8 @@
private boolean mIsExpanded;
private boolean mClipNotificationScrollToTop;
private int mStatusBarHeight;
+ private float mHeadsUpInset;
+ private int mPinnedZTranslationExtra;
public StackScrollAlgorithm(Context context) {
initView(context);
@@ -68,6 +70,10 @@
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);
+ mPinnedZTranslationExtra = res.getDimensionPixelSize(
+ R.dimen.heads_up_pinned_elevation);
}
public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
@@ -457,7 +463,7 @@
}
}
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);
@@ -522,9 +528,6 @@
childViewState.inShelf = true;
childViewState.headsUpIsVisible = false;
}
- if (!ambientState.isShadeExpanded()) {
- childViewState.height = (int) (mStatusBarHeight - childViewState.yTranslation);
- }
}
protected int getMaxAllowedChildHeight(View child) {
@@ -592,6 +595,13 @@
} else {
childViewState.zTranslation = baseZ;
}
+
+ // We need to scrim the notification more from its surrounding content when we are pinned,
+ // and we therefore elevate it higher.
+ // We can use the headerVisibleAmount for this, since the value nicely goes from 0 to 1 when
+ // expanding after which we have a normal elevation again.
+ childViewState.zTranslation += (1.0f - child.getHeaderVisibleAmount())
+ * mPinnedZTranslationExtra;
return childrenOnTop;
}
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 236c348..d48ae76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.StatusBarIconView;
import java.util.ArrayList;
import java.util.HashSet;
@@ -45,13 +46,17 @@
public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
- public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 650;
- public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 230;
+ public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 550;
+ public static final int ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED
+ = (int) (ANIMATION_DURATION_HEADS_UP_APPEAR
+ * HeadsUpAppearInterpolator.getFractionUntilOvershoot());
+ public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300;
public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
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 DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
public static final int ANIMATION_DELAY_HEADS_UP = 120;
+ public static final int ANIMATION_DELAY_HEADS_UP_CLICKED= 120;
private final int mGoToFullShadeAppearingTranslation;
private final ExpandableViewState mTmpState = new ExpandableViewState();
@@ -74,8 +79,9 @@
private ValueAnimator mBottomOverScrollAnimator;
private int mHeadsUpAppearHeightBottom;
private boolean mShadeExpanded;
- private ArrayList<View> mChildrenToClearFromOverlay = new ArrayList<>();
private NotificationShelf mShelf;
+ private float mStatusBarIconLocation;
+ private int[] mTmpLocation = new int[2];
public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
mHostLayout = hostLayout;
@@ -222,8 +228,8 @@
if (mAnimationFilter.hasGoToFullShadeEvent) {
return calculateDelayGoToFullShade(viewState);
}
- if (mAnimationFilter.hasHeadsUpDisappearClickEvent) {
- return ANIMATION_DELAY_HEADS_UP;
+ if (mAnimationFilter.customDelay != AnimationFilter.NO_DELAY) {
+ return mAnimationFilter.customDelay;
}
long minDelay = 0;
for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
@@ -327,10 +333,6 @@
private void onAnimationFinished() {
mHostLayout.onChildAnimationFinished();
- for (View v : mChildrenToClearFromOverlay) {
- removeFromOverlay(v);
- }
- mChildrenToClearFromOverlay.clear();
}
/**
@@ -396,13 +398,14 @@
}
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
- translationDirection, new Runnable() {
+ 0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
+ 0, new Runnable() {
@Override
public void run() {
// remove the temporary overlay
removeFromOverlay(changingView);
}
- });
+ }, null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
// A race condition can trigger the view to be added to the overlay even though
@@ -424,7 +427,9 @@
if (event.headsUpFromBottom) {
mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
} else {
- mTmpState.yTranslation = -mTmpState.height;
+ mTmpState.yTranslation = 0;
+ changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED,
+ true /* isHeadsUpAppear */);
}
mHeadsUpAppearChildren.add(changingView);
mTmpState.applyToView(changingView);
@@ -433,22 +438,56 @@
event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
mHeadsUpDisappearChildren.add(changingView);
+ Runnable endRunnable = null;
+ // We need some additional delay in case we were removed to make sure we're not
+ // lagging
+ int extraDelay = event.animationType == NotificationStackScrollLayout
+ .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+ ? ANIMATION_DELAY_HEADS_UP_CLICKED
+ : 0;
if (changingView.getParent() == null) {
// This notification was actually removed, so we need to add it to the overlay
mHostLayout.getOverlay().add(changingView);
mTmpState.initFrom(changingView);
- mTmpState.yTranslation = -changingView.getActualHeight();
+ mTmpState.yTranslation = 0;
// We temporarily enable Y animations, the real filter will be combined
// afterwards anyway
mAnimationFilter.animateY = true;
- mAnimationProperties.delay =
- event.animationType == NotificationStackScrollLayout
- .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
- ? ANIMATION_DELAY_HEADS_UP
- : 0;
+ mAnimationProperties.delay = extraDelay + ANIMATION_DELAY_HEADS_UP;
mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
mTmpState.animateTo(changingView, mAnimationProperties);
- mChildrenToClearFromOverlay.add(changingView);
+ endRunnable = () -> {
+ // remove the temporary overlay
+ removeFromOverlay(changingView);
+ };
+ }
+ float targetLocation = 0;
+ boolean needsAnimation = true;
+ if (changingView instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) changingView;
+ if (row.isDismissed()) {
+ needsAnimation = false;
+ }
+ StatusBarIconView icon = row.getEntry().icon;
+ if (icon.getParent() != null) {
+ icon.getLocationOnScreen(mTmpLocation);
+ float iconPosition = mTmpLocation[0] - icon.getTranslationX()
+ + ViewState.getFinalTranslationX(icon) + icon.getWidth() * 0.25f;
+ mHostLayout.getLocationOnScreen(mTmpLocation);
+ targetLocation = iconPosition - mTmpLocation[0];
+ }
+ }
+
+ if (needsAnimation) {
+ // We need to add the global animation listener, since once no animations are
+ // running anymore, the panel will instantly hide itself. We need to wait until
+ // the animation is fully finished for this though.
+ changingView.performRemoveAnimation(ANIMATION_DURATION_HEADS_UP_DISAPPEAR
+ + ANIMATION_DELAY_HEADS_UP, extraDelay, 0.0f,
+ true /* isHeadsUpAppear */, targetLocation, endRunnable,
+ getGlobalAnimationFinishedListener());
+ } else if (endRunnable != null) {
+ endRunnable.run();
}
}
mNewEvents.add(event);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index 04a7bd7..4b3643f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -641,6 +641,22 @@
}
/**
+ * Get the end value of the xTranslation animation running on a view or the xTranslation
+ * if no animation is running.
+ */
+ public static float getFinalTranslationX(View view) {
+ if (view == null) {
+ return 0;
+ }
+ ValueAnimator xAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
+ if (xAnimator == null) {
+ return view.getTranslationX();
+ } else {
+ return getChildTag(view, TAG_END_TRANSLATION_X);
+ }
+ }
+
+ /**
* Get the end value of the yTranslation animation running on a view or the yTranslation
* if no animation is running.
*/
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 0000000..c904ef3
--- /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/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index d32c9a8..72d9cc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -64,7 +64,6 @@
private SynchronousScrimController mScrimController;
private ScrimView mScrimBehind;
private ScrimView mScrimInFront;
- private View mHeadsUpScrim;
private Consumer<Integer> mScrimVisibilityCallback;
private int mScrimVisibility;
private LightBarController mLightBarController;
@@ -78,7 +77,6 @@
mLightBarController = mock(LightBarController.class);
mScrimBehind = new ScrimView(getContext());
mScrimInFront = new ScrimView(getContext());
- mHeadsUpScrim = new View(getContext());
mWakeLock = mock(WakeLock.class);
mAlarmManager = mock(AlarmManager.class);
mAlwaysOnEnabled = true;
@@ -87,7 +85,7 @@
when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
mScrimController = new SynchronousScrimController(mLightBarController, mScrimBehind,
- mScrimInFront, mHeadsUpScrim, mScrimVisibilityCallback, mDozeParamenters,
+ mScrimInFront, mScrimVisibilityCallback, mDozeParamenters,
mAlarmManager);
}
@@ -415,56 +413,6 @@
}
@Test
- public void testHeadsUpScrimOpacity() {
- mScrimController.setPanelExpansion(0f);
- mScrimController.onHeadsUpPinned(null /* row */);
- mScrimController.finishAnimationsImmediately();
-
- Assert.assertNotEquals("Heads-up scrim should be visible", 0f,
- mHeadsUpScrim.getAlpha(), 0.01f);
-
- mScrimController.onHeadsUpUnPinned(null /* row */);
- mScrimController.finishAnimationsImmediately();
-
- Assert.assertEquals("Heads-up scrim should have disappeared", 0f,
- mHeadsUpScrim.getAlpha(), 0.01f);
- }
-
- @Test
- public void testHeadsUpScrimCounting() {
- mScrimController.setPanelExpansion(0f);
- mScrimController.onHeadsUpPinned(null /* row */);
- mScrimController.onHeadsUpPinned(null /* row */);
- mScrimController.onHeadsUpPinned(null /* row */);
- mScrimController.finishAnimationsImmediately();
-
- Assert.assertNotEquals("Heads-up scrim should be visible", 0f,
- mHeadsUpScrim.getAlpha(), 0.01f);
-
- mScrimController.onHeadsUpUnPinned(null /* row */);
- mScrimController.finishAnimationsImmediately();
-
- Assert.assertEquals("Heads-up scrim should only disappear when counter reaches 0", 1f,
- mHeadsUpScrim.getAlpha(), 0.01f);
-
- mScrimController.onHeadsUpUnPinned(null /* row */);
- mScrimController.onHeadsUpUnPinned(null /* row */);
- mScrimController.finishAnimationsImmediately();
- Assert.assertEquals("Heads-up scrim should have disappeared", 0f,
- mHeadsUpScrim.getAlpha(), 0.01f);
- }
-
- @Test
- public void testNoHeadsUpScrimExpanded() {
- mScrimController.setPanelExpansion(1f);
- mScrimController.onHeadsUpPinned(null /* row */);
- mScrimController.finishAnimationsImmediately();
-
- Assert.assertEquals("Heads-up scrim should not be visible when shade is expanded", 0f,
- mHeadsUpScrim.getAlpha(), 0.01f);
- }
-
- @Test
public void testScrimFocus() {
mScrimController.transitionTo(ScrimState.AOD);
Assert.assertFalse("Should not be focusable on AOD", mScrimBehind.isFocusable());
@@ -542,10 +490,10 @@
private boolean mAnimationCancelled;
SynchronousScrimController(LightBarController lightBarController,
- ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
+ ScrimView scrimBehind, ScrimView scrimInFront,
Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
AlarmManager alarmManager) {
- super(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
+ super(lightBarController, scrimBehind, scrimInFront,
scrimVisibleListener, dozeParameters, alarmManager);
mHandler = new FakeHandler(Looper.myLooper());
}
@@ -562,7 +510,6 @@
// Force finish all animations.
endAnimation(mScrimBehind, TAG_KEY_ANIM);
endAnimation(mScrimInFront, TAG_KEY_ANIM);
- endAnimation(mHeadsUpScrim, TAG_KEY_ANIM);
if (!animationFinished[0]) {
throw new IllegalStateException("Animation never finished");
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
new file mode 100644
index 0000000..1d2c01d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.stack;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+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 NotificationRoundnessManagerTest extends SysuiTestCase {
+
+ private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
+ private HashSet<View> mAnimatedChildren = new HashSet<>();
+ private Runnable mRoundnessCallback = mock(Runnable.class);
+ private ExpandableNotificationRow mFirst;
+ private ExpandableNotificationRow mSecond;
+
+ @Before
+ public void setUp() throws Exception {
+ NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
+ mFirst = testHelper.createRow();
+ mSecond = testHelper.createRow();
+ mRoundnessManager.setOnRoundingChangedCallback(mRoundnessCallback);
+ mRoundnessManager.setAnimatedChildren(mAnimatedChildren);
+ mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mFirst);
+ mRoundnessManager.setExpanded(1.0f, 1.0f);
+ }
+
+ @Test
+ public void testCallbackCalledWhenSecondChanged() {
+ mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mSecond);
+ verify(mRoundnessCallback, atLeast(1)).run();
+ }
+
+ @Test
+ public void testCallbackCalledWhenFirstChanged() {
+ mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mFirst);
+ verify(mRoundnessCallback, atLeast(1)).run();
+ }
+
+ @Test
+ public void testRoundnessSetOnLast() {
+ mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, mSecond);
+ Assert.assertEquals(1.0f, mSecond.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(0.0f, mSecond.getCurrentTopRoundness(), 0.0f);
+ }
+
+ @Test
+ public void testRoundnessSetOnNew() {
+ mRoundnessManager.setFirstAndLastBackgroundChild(mFirst, null);
+ Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+ }
+
+ @Test
+ public void testCompleteReplacement() {
+ mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+ Assert.assertEquals(0.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(0.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+ }
+
+ @Test
+ public void testNotCalledWhenRemoved() {
+ mFirst.setRemoved();
+ mRoundnessManager.setFirstAndLastBackgroundChild(mSecond, mSecond);
+ Assert.assertEquals(1.0f, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
+ }
+
+ @Test
+ public void testRoundedWhenPinnedAndCollapsed() {
+ mFirst.setPinned(true);
+ 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);
+ }
+
+ @Test
+ public void testRoundedWhenGoingAwayAndCollapsed() {
+ mFirst.setHeadsUpAnimatingAway(true);
+ 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);
+ }
+
+ @Test
+ public void testRoundedNormalRoundingWhenExpanded() {
+ mFirst.setHeadsUpAnimatingAway(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);
+ }
+}