summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI/src/com/android/systemui/statusbar
diff options
context:
space:
mode:
authorSelim Cinek <cinek@google.com>2015-03-16 17:09:28 -0700
committerSelim Cinek <cinek@google.com>2015-04-15 12:32:00 -0700
commitb8f09cf5533d458868a335ce334e4880b2b0788d (patch)
tree2eaf9145bf9bc4d92e6de1f3169c10fc47d56c02 /packages/SystemUI/src/com/android/systemui/statusbar
parentba67acff25950a32476273b5d60192a3874c2130 (diff)
downloadframeworks_base-b8f09cf5533d458868a335ce334e4880b2b0788d.zip
frameworks_base-b8f09cf5533d458868a335ce334e4880b2b0788d.tar.gz
frameworks_base-b8f09cf5533d458868a335ce334e4880b2b0788d.tar.bz2
Integrate Heads-up notifications into the shade
Change-Id: I4ca0fb4e76e7c974490538c168da0564fe97e0ae
Diffstat (limited to 'packages/SystemUI/src/com/android/systemui/statusbar')
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java319
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java161
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java145
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java243
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java474
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java622
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java145
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java131
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java14
16 files changed, 1333 insertions, 1100 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index ee607a7..b9b3388 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -96,7 +96,7 @@ import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.phone.NavigationBarView;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.PreviewInflater;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -130,9 +130,6 @@ public abstract class BaseStatusBar extends SystemUI implements
protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
- protected static final int MSG_SHOW_HEADS_UP = 1028;
- protected static final int MSG_HIDE_HEADS_UP = 1029;
- protected static final int MSG_ESCALATE_HEADS_UP = 1030;
protected static final boolean ENABLE_HEADS_UP = true;
// scores above this threshold should be displayed in heads up mode.
@@ -158,8 +155,7 @@ public abstract class BaseStatusBar extends SystemUI implements
protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
// for heads up notifications
- protected HeadsUpNotificationView mHeadsUpNotificationView;
- protected int mHeadsUpNotificationDecay;
+ protected HeadsUpManager mHeadsUpManager;
protected int mCurrentUserId = 0;
final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
@@ -446,9 +442,8 @@ public abstract class BaseStatusBar extends SystemUI implements
@Override
public void run() {
processForRemoteInput(sbn.getNotification());
- Notification n = sbn.getNotification();
- boolean isUpdate = mNotificationData.get(sbn.getKey()) != null
- || isHeadsUp(sbn.getKey());
+ String key = sbn.getKey();
+ boolean isUpdate = mNotificationData.get(key) != null;
// In case we don't allow child notifications, we ignore children of
// notifications that have a summary, since we're not going to show them
@@ -462,7 +457,7 @@ public abstract class BaseStatusBar extends SystemUI implements
// Remove existing notification to avoid stale data.
if (isUpdate) {
- removeNotification(sbn.getKey(), rankingMap);
+ removeNotification(key, rankingMap);
} else {
mNotificationData.updateRanking(rankingMap);
}
@@ -693,13 +688,11 @@ public abstract class BaseStatusBar extends SystemUI implements
}
private void setHeadsUpUser(int newUserId) {
- if (mHeadsUpNotificationView != null) {
- mHeadsUpNotificationView.setUser(newUserId);
- }
+ mHeadsUpManager.setUser(newUserId);
}
public boolean isHeadsUp(String key) {
- return mHeadsUpNotificationView != null && mHeadsUpNotificationView.isShowing(key);
+ return mHeadsUpManager.isHeadsUp(key);
}
@Override // NotificationData.Environment
@@ -758,8 +751,8 @@ public abstract class BaseStatusBar extends SystemUI implements
protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
View vetoButton = row.findViewById(R.id.veto);
- if (n.isClearable() || (mHeadsUpNotificationView.getEntry() != null
- && mHeadsUpNotificationView.getEntry().row == row)) {
+ // TODO: make headsup non-clearable if it is in the shade
+ if (n.isClearable() || (mHeadsUpManager.isHeadsUp(n.getKey()))) {
final String _pkg = n.getPackageName();
final String _tag = n.getTag();
final int _id = n.getId();
@@ -1002,9 +995,6 @@ public abstract class BaseStatusBar extends SystemUI implements
}
}
- public void onHeadsUpDismissed() {
- }
-
@Override
public void showRecentApps(boolean triggeredFromAltTab) {
int msg = MSG_SHOW_RECENT_APPS;
@@ -1141,13 +1131,10 @@ public abstract class BaseStatusBar extends SystemUI implements
// Do nothing
}
- public abstract void scheduleHeadsUpDecay(long delay);
-
- public abstract void scheduleHeadsUpOpen();
-
- public abstract void scheduleHeadsUpClose();
-
- public abstract void scheduleHeadsUpEscalation();
+ /**
+ * if the interrupting notification had a fullscreen intent, fire it now.
+ */
+ public abstract void escalateHeadsUp();
/**
* Save the current "public" (locked and secure) state of the lockscreen.
@@ -1238,15 +1225,8 @@ public abstract class BaseStatusBar extends SystemUI implements
protected void workAroundBadLayerDrawableOpacity(View v) {
}
- protected boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
- return inflateViews(entry, parent, false);
- }
-
- protected boolean inflateViewsForHeadsUp(NotificationData.Entry entry, ViewGroup parent) {
- return inflateViews(entry, parent, true);
- }
-
- private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent, boolean isHeadsUp) {
+ protected boolean inflateViews(NotificationData.Entry entry, ViewGroup parent,
+ boolean isHeadsUp) {
PackageManager pmUser = getPackageManagerForUser(
entry.notification.getUser().getIdentifier());
@@ -1284,7 +1264,6 @@ public abstract class BaseStatusBar extends SystemUI implements
hasUserChangedExpansion = row.hasUserChangedExpansion();
userExpanded = row.isUserExpanded();
userLocked = row.isUserLocked();
- entry.row.setHeadsUp(isHeadsUp);
entry.reset();
if (hasUserChangedExpansion) {
row.setUserExpanded(userExpanded);
@@ -1597,12 +1576,12 @@ public abstract class BaseStatusBar extends SystemUI implements
mCurrentUserId);
dismissKeyguardThenExecute(new OnDismissAction() {
public boolean onDismiss() {
- if (mNotificationKey.equals(mHeadsUpNotificationView.getKey())) {
+ if (mHeadsUpManager.isHeadsUp(mNotificationKey)) {
// Release the HUN notification to the shade.
//
// In most cases, when FLAG_AUTO_CANCEL is set, the notification will
// become canceled shortly by NoMan, but we can't assume that.
- mHeadsUpNotificationView.releaseImmediately();
+ mHeadsUpManager.releaseImmediately(mNotificationKey);
}
new Thread() {
@Override
@@ -1740,7 +1719,8 @@ public abstract class BaseStatusBar extends SystemUI implements
return entry.notification;
}
- protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
+ protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn,
+ boolean isHeadsUp) {
if (DEBUG) {
Log.d(TAG, "createNotificationViews(notification=" + sbn);
}
@@ -1751,7 +1731,7 @@ public abstract class BaseStatusBar extends SystemUI implements
// Construct the expanded view.
NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
- if (!inflateViews(entry, mStackScroller)) {
+ if (!inflateViews(entry, mStackScroller, isHeadsUp)) {
handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
return null;
}
@@ -1889,90 +1869,29 @@ public abstract class BaseStatusBar extends SystemUI implements
if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
final String key = notification.getKey();
- boolean wasHeadsUp = isHeadsUp(key);
- Entry oldEntry;
- if (wasHeadsUp) {
- oldEntry = mHeadsUpNotificationView.getEntry();
- } else {
- oldEntry = mNotificationData.get(key);
- }
- if (oldEntry == null) {
+ Entry entry = mNotificationData.get(key);
+ if (entry == null) {
return;
}
- final StatusBarNotification oldNotification = oldEntry.notification;
-
- // XXX: modify when we do something more intelligent with the two content views
- final RemoteViews oldContentView = oldNotification.getNotification().contentView;
Notification n = notification.getNotification();
- final RemoteViews contentView = n.contentView;
- final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
- final RemoteViews bigContentView = n.bigContentView;
- final RemoteViews oldHeadsUpContentView = oldNotification.getNotification().headsUpContentView;
- final RemoteViews headsUpContentView = n.headsUpContentView;
- final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
- final RemoteViews oldPublicContentView = oldPublicNotification != null
- ? oldPublicNotification.contentView : null;
- final Notification publicNotification = n.publicVersion;
- final RemoteViews publicContentView = publicNotification != null
- ? publicNotification.contentView : null;
-
if (DEBUG) {
- Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
- + " ongoing=" + oldNotification.isOngoing()
- + " expanded=" + oldEntry.expanded
- + " contentView=" + oldContentView
- + " bigContentView=" + oldBigContentView
- + " publicView=" + oldPublicContentView
- + " rowParent=" + oldEntry.row.getParent());
- Log.d(TAG, "new notification: when=" + n.when
- + " ongoing=" + oldNotification.isOngoing()
- + " contentView=" + contentView
- + " bigContentView=" + bigContentView
- + " publicView=" + publicContentView);
- }
-
- // Can we just reapply the RemoteViews in place?
-
- // 1U is never null
- boolean contentsUnchanged = oldEntry.expanded != null
- && contentView.getPackage() != null
- && oldContentView.getPackage() != null
- && oldContentView.getPackage().equals(contentView.getPackage())
- && oldContentView.getLayoutId() == contentView.getLayoutId();
- // large view may be null
- boolean bigContentsUnchanged =
- (oldEntry.getBigContentView() == null && bigContentView == null)
- || ((oldEntry.getBigContentView() != null && bigContentView != null)
- && bigContentView.getPackage() != null
- && oldBigContentView.getPackage() != null
- && oldBigContentView.getPackage().equals(bigContentView.getPackage())
- && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
- boolean headsUpContentsUnchanged =
- (oldHeadsUpContentView == null && headsUpContentView == null)
- || ((oldHeadsUpContentView != null && headsUpContentView != null)
- && headsUpContentView.getPackage() != null
- && oldHeadsUpContentView.getPackage() != null
- && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
- && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
- boolean publicUnchanged =
- (oldPublicContentView == null && publicContentView == null)
- || ((oldPublicContentView != null && publicContentView != null)
- && publicContentView.getPackage() != null
- && oldPublicContentView.getPackage() != null
- && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
- && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
-
+ logUpdate(entry, n);
+ }
+ boolean applyInPlace = shouldApplyInPlace(entry, n);
final boolean shouldInterrupt = shouldInterrupt(notification);
- final boolean alertAgain = shouldInterrupt && alertAgain(oldEntry, n);
+ final boolean alertAgain = shouldInterrupt && alertAgain(entry, n);
+ boolean isHeadsUp = shouldInterrupt && alertAgain;
+
+ entry.notification = notification;
+ mGroupManager.onEntryUpdated(entry, entry.notification);
+
boolean updateSuccessful = false;
- if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
- && publicUnchanged) {
+ if (applyInPlace) {
+ // We can just reapply the notifications in place
if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
- oldEntry.notification = notification;
- mGroupManager.onEntryUpdated(oldEntry, oldNotification);
try {
- if (oldEntry.icon != null) {
+ if (entry.icon != null) {
// Update the icon
final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
notification.getUser(),
@@ -1980,88 +1899,39 @@ public abstract class BaseStatusBar extends SystemUI implements
n.iconLevel,
n.number,
n.tickerText);
- oldEntry.icon.setNotification(n);
- if (!oldEntry.icon.set(ic)) {
+ entry.icon.setNotification(n);
+ if (!entry.icon.set(ic)) {
handleNotificationError(notification, "Couldn't update icon: " + ic);
return;
}
}
-
- if (wasHeadsUp) {
- // Release may hang on to the views for a bit, so we should always update them.
- updateHeadsUpViews(oldEntry, notification);
- mHeadsUpNotificationView.updateNotification(oldEntry, alertAgain);
- if (!shouldInterrupt) {
- // we updated the notification above, so release to build a new shade entry
- mHeadsUpNotificationView.release();
- return;
- }
- } else {
- if (shouldInterrupt && alertAgain) {
- mStackScroller.setRemoveAnimationEnabled(false);
- removeNotificationViews(key, ranking);
- mStackScroller.setRemoveAnimationEnabled(true);
- addNotification(notification, ranking, oldEntry); //this will pop the headsup
- } else {
- updateNotificationViews(oldEntry, notification);
- }
- }
- mNotificationData.updateRanking(ranking);
- updateNotifications();
+ updateNotificationViews(entry, notification, isHeadsUp);
updateSuccessful = true;
}
catch (RuntimeException e) {
// It failed to add cleanly. Log, and remove the view from the panel.
- Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
+ Log.w(TAG, "Couldn't reapply views for package " + n.contentView.getPackage(), e);
}
}
if (!updateSuccessful) {
if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
- if (wasHeadsUp) {
- if (DEBUG) Log.d(TAG, "rebuilding heads up for key: " + key);
- ViewGroup holder = mHeadsUpNotificationView.getHolder();
- if (inflateViewsForHeadsUp(oldEntry, holder)) {
- mHeadsUpNotificationView.updateNotification(oldEntry, alertAgain);
- } else {
- Log.w(TAG, "Couldn't create new updated headsup for package "
- + contentView.getPackage());
- }
- if (!shouldInterrupt) {
- if (DEBUG) Log.d(TAG, "releasing heads up for key: " + key);
- oldEntry.notification = notification;
- mGroupManager.onEntryUpdated(oldEntry, oldNotification);
- mHeadsUpNotificationView.release();
- return;
- }
- } else {
- if (shouldInterrupt && alertAgain) {
- if (DEBUG) Log.d(TAG, "reposting to invoke heads up for key: " + key);
- mStackScroller.setRemoveAnimationEnabled(false);
- removeNotificationViews(key, ranking);
- mStackScroller.setRemoveAnimationEnabled(true);
- addNotification(notification, ranking, oldEntry); //this will pop the headsup
- } else {
- if (DEBUG) Log.d(TAG, "rebuilding update in place for key: " + key);
- oldEntry.notification = notification;
- mGroupManager.onEntryUpdated(oldEntry, oldNotification);
- final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
- notification.getUser(),
- n.icon,
- n.iconLevel,
- n.number,
- n.tickerText);
- oldEntry.icon.setNotification(n);
- oldEntry.icon.set(ic);
- inflateViews(oldEntry, mStackScroller, wasHeadsUp);
- mNotificationData.updateRanking(ranking);
- updateNotifications();
- }
- }
+ final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
+ notification.getUser(),
+ n.icon,
+ n.iconLevel,
+ n.number,
+ n.tickerText);
+ entry.icon.setNotification(n);
+ entry.icon.set(ic);
+ inflateViews(entry, mStackScroller, isHeadsUp);
}
+ mNotificationData.updateRanking(ranking);
+ updateNotifications();
+ updateHeadsUp(key, entry, shouldInterrupt, alertAgain);
// Update the veto button accordingly (and as a result, whether this row is
// swipe-dismissable)
- updateNotificationVetoButton(oldEntry.row, notification);
+ updateNotificationVetoButton(entry.row, notification);
// Is this for you?
boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
@@ -2071,14 +1941,81 @@ public abstract class BaseStatusBar extends SystemUI implements
setAreThereNotifications();
}
- private void updateNotificationViews(NotificationData.Entry entry,
- StatusBarNotification notification) {
- updateNotificationViews(entry, notification, false);
+ private void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
+ boolean alertAgain) {
+ final boolean wasHeadsUp = isHeadsUp(key);
+ if (wasHeadsUp) {
+ mHeadsUpManager.updateNotification(entry, alertAgain);
+ if (!shouldInterrupt) {
+ mHeadsUpManager.removeNotification(key);
+ }
+ }
+ }
+
+ private void logUpdate(Entry oldEntry, Notification n) {
+ StatusBarNotification oldNotification = oldEntry.notification;
+ Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
+ + " ongoing=" + oldNotification.isOngoing()
+ + " expanded=" + oldEntry.expanded
+ + " contentView=" + oldNotification.getNotification().contentView
+ + " bigContentView=" + oldNotification.getNotification().bigContentView
+ + " publicView=" + oldNotification.getNotification().publicVersion
+ + " rowParent=" + oldEntry.row.getParent());
+ Log.d(TAG, "new notification: when=" + n.when
+ + " ongoing=" + oldNotification.isOngoing()
+ + " contentView=" + n.contentView
+ + " bigContentView=" + n.bigContentView
+ + " publicView=" + n.publicVersion);
}
- private void updateHeadsUpViews(NotificationData.Entry entry,
- StatusBarNotification notification) {
- updateNotificationViews(entry, notification, true);
+ /**
+ * @return whether we can just reapply the RemoteViews in place when it is updated
+ */
+ private boolean shouldApplyInPlace(Entry entry, Notification n) {
+ StatusBarNotification oldNotification = entry.notification;
+ // XXX: modify when we do something more intelligent with the two content views
+ final RemoteViews oldContentView = oldNotification.getNotification().contentView;
+ final RemoteViews contentView = n.contentView;
+ final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
+ final RemoteViews bigContentView = n.bigContentView;
+ final RemoteViews oldHeadsUpContentView
+ = oldNotification.getNotification().headsUpContentView;
+ final RemoteViews headsUpContentView = n.headsUpContentView;
+ final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
+ final RemoteViews oldPublicContentView = oldPublicNotification != null
+ ? oldPublicNotification.contentView : null;
+ final Notification publicNotification = n.publicVersion;
+ final RemoteViews publicContentView = publicNotification != null
+ ? publicNotification.contentView : null;
+ boolean contentsUnchanged = entry.expanded != null
+ && contentView.getPackage() != null
+ && oldContentView.getPackage() != null
+ && oldContentView.getPackage().equals(contentView.getPackage())
+ && oldContentView.getLayoutId() == contentView.getLayoutId();
+ // large view may be null
+ boolean bigContentsUnchanged =
+ (entry.getBigContentView() == null && bigContentView == null)
+ || ((entry.getBigContentView() != null && bigContentView != null)
+ && bigContentView.getPackage() != null
+ && oldBigContentView.getPackage() != null
+ && oldBigContentView.getPackage().equals(bigContentView.getPackage())
+ && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
+ boolean headsUpContentsUnchanged =
+ (oldHeadsUpContentView == null && headsUpContentView == null)
+ || ((oldHeadsUpContentView != null && headsUpContentView != null)
+ && headsUpContentView.getPackage() != null
+ && oldHeadsUpContentView.getPackage() != null
+ && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
+ && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
+ boolean publicUnchanged =
+ (oldPublicContentView == null && publicContentView == null)
+ || ((oldPublicContentView != null && publicContentView != null)
+ && publicContentView.getPackage() != null
+ && oldPublicContentView.getPackage() != null
+ && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
+ && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
+ return contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
+ && publicUnchanged;
}
private void updateNotificationViews(NotificationData.Entry entry,
@@ -2115,10 +2052,8 @@ public abstract class BaseStatusBar extends SystemUI implements
applyRemoteInput(entry);
}
- protected void notifyHeadsUpScreenOn(boolean screenOn) {
- if (!screenOn) {
- scheduleHeadsUpEscalation();
- }
+ protected void notifyHeadsUpScreenOff() {
+ escalateHeadsUp();
}
private boolean alertAgain(Entry oldEntry, Notification newNotification) {
@@ -2134,7 +2069,7 @@ public abstract class BaseStatusBar extends SystemUI implements
return false;
}
- if (mHeadsUpNotificationView.isSnoozed(sbn.getPackageName())) {
+ if (mHeadsUpManager.isSnoozed(sbn.getPackageName())) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 06a174e..60093df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -108,6 +108,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
!mChildrenExpanded);
}
};
+ private boolean mInShade;
public void setIconAnimationRunning(boolean running) {
setIconAnimationRunning(running, mPublicLayout);
@@ -164,6 +165,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
return mStatusBarNotification;
}
+ public boolean isHeadsUp() {
+ return mIsHeadsUp;
+ }
+
public void setHeadsUp(boolean isHeadsUp) {
mIsHeadsUp = isHeadsUp;
}
@@ -263,6 +268,19 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
return realActualHeight;
}
+ public void setInShade(boolean inShade) {
+ mInShade = inShade;
+ }
+
+ public boolean isInShade() {
+ return mInShade;
+ }
+
+ public int getHeadsUpHeight() {
+ // TODO: improve this logic
+ return getIntrinsicHeight();
+ }
+
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
new file mode 100644
index 0000000..386be7e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import com.android.systemui.Gefingerpoken;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+/**
+ * A Helper class to handle touches on the heads-up views
+ */
+public class HeadsUpTouchHelper implements Gefingerpoken {
+
+ private HeadsUpManager mHeadsUpManager;
+ private NotificationStackScrollLayout mStackScroller;
+ private int mTrackingPointer;
+ private float mTouchSlop;
+ private float mInitialTouchX;
+ private float mInitialTouchY;
+ private boolean mMotionOnHeadsUpView;
+ private boolean mTrackingHeadsUp;
+ private boolean mCollapseSnoozes;
+ private NotificationPanelView mPanel;
+ private ExpandableNotificationRow mPickedChild;
+
+ public boolean isTrackingHeadsUp() {
+ return mTrackingHeadsUp;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (!mMotionOnHeadsUpView && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
+ return false;
+ }
+ int pointerIndex = event.findPointerIndex(mTrackingPointer);
+ if (pointerIndex < 0) {
+ pointerIndex = 0;
+ mTrackingPointer = event.getPointerId(pointerIndex);
+ }
+ final float x = event.getX(pointerIndex);
+ final float y = event.getY(pointerIndex);
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mInitialTouchY = y;
+ mInitialTouchX = x;
+ setTrackingHeadsUp(false);
+ ExpandableView child = mStackScroller.getChildAtPosition(x, y);
+ mMotionOnHeadsUpView = false;
+ if (child instanceof ExpandableNotificationRow) {
+ mPickedChild = (ExpandableNotificationRow) child;
+ mMotionOnHeadsUpView = mPickedChild.isHeadsUp() && !mPickedChild.isInShade();
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ final int upPointer = event.getPointerId(event.getActionIndex());
+ if (mTrackingPointer == upPointer) {
+ // gesture is ongoing, find a new pointer to track
+ final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+ mTrackingPointer = event.getPointerId(newIndex);
+ mInitialTouchX = event.getX(newIndex);
+ mInitialTouchY = event.getY(newIndex);
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ final float h = y - mInitialTouchY;
+ if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
+ setTrackingHeadsUp(true);
+ mCollapseSnoozes = h < 0;
+ mInitialTouchX = x;
+ mInitialTouchY = y;
+ mHeadsUpManager.releaseAllToShade();
+ int expandedHeight = mPickedChild.getActualHeight();
+ mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight);
+ return true;
+ }
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ if (mPickedChild != null && mMotionOnHeadsUpView) {
+ if (mHeadsUpManager.shouldSwallowClick(
+ mPickedChild.getStatusBarNotification().getKey())) {
+ endMotion();
+ return true;
+ }
+ }
+ endMotion();
+ break;
+ }
+ return false;
+ }
+
+ private void setTrackingHeadsUp(boolean tracking) {
+ mTrackingHeadsUp = tracking;
+ mHeadsUpManager.setTrackingHeadsUp(tracking);
+ mPanel.setTrackingHeadsUp(tracking);
+ }
+
+ public void notifyFling(boolean collapse) {
+ if (collapse && mCollapseSnoozes) {
+ mHeadsUpManager.snooze();
+ }
+ mCollapseSnoozes = false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (!mTrackingHeadsUp) {
+ return false;
+ }
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ endMotion();
+ setTrackingHeadsUp(false);
+ break;
+ }
+ return true;
+ }
+
+ private void endMotion() {
+ mTrackingPointer = -1;
+ mPickedChild = null;
+ mMotionOnHeadsUpView = false;
+ }
+
+ public ExpandableView getPickedChild() {
+ return mPickedChild;
+ }
+
+ public void bind(HeadsUpManager headsUpManager, NotificationStackScrollLayout stackScroller,
+ NotificationPanelView notificationPanelView) {
+ mHeadsUpManager = headsUpManager;
+ mStackScroller = stackScroller;
+ mPanel = notificationPanelView;
+ Context context = stackScroller.getContext();
+ final ViewConfiguration configuration = ViewConfiguration.get(context);
+ mTouchSlop = configuration.getScaledTouchSlop();
+ }
+}
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 02b6c19..6fe609e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -32,6 +32,7 @@ import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -39,8 +40,8 @@ import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.keyguard.KeyguardStatusView;
-import com.android.systemui.EventLogTags;
import com.android.systemui.EventLogConstants;
+import com.android.systemui.EventLogTags;
import com.android.systemui.R;
import com.android.systemui.qs.QSContainer;
import com.android.systemui.qs.QSPanel;
@@ -48,7 +49,9 @@ import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyguardAffordanceView;
+import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -56,7 +59,8 @@ import com.android.systemui.statusbar.stack.StackStateAnimator;
public class NotificationPanelView extends PanelView implements
ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
- KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener {
+ KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener,
+ HeadsUpManager.OnHeadsUpChangedListener {
private static final boolean DEBUG = false;
@@ -81,7 +85,7 @@ public class NotificationPanelView extends PanelView implements
private TextView mClockView;
private View mReserveNotificationSpace;
private View mQsNavbarScrim;
- private View mNotificationContainerParent;
+ private NotificationsQuickSettingsContainer mNotificationContainerParent;
private NotificationStackScrollLayout mNotificationStackScroller;
private int mNotificationTopPadding;
private boolean mAnimateNextTopPaddingChange;
@@ -177,6 +181,16 @@ public class NotificationPanelView extends PanelView implements
private float mKeyguardStatusBarAnimateAlpha = 1f;
private int mOldLayoutDirection;
+ private HeadsUpTouchHelper mHeadsUpTouchHelper = new HeadsUpTouchHelper();
+ private boolean mPinnedHeadsUpExist;
+ private boolean mExpansionIsFromHeadsUp;
+ private int mBottomBarHeight;
+ private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
+ @Override
+ public void run() {
+ notifyBarPanelExpansionChanged();
+ }
+ };
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -201,7 +215,8 @@ public class NotificationPanelView extends PanelView implements
mScrollView.setListener(this);
mScrollView.setFocusable(false);
mReserveNotificationSpace = findViewById(R.id.reserve_notification_space);
- mNotificationContainerParent = findViewById(R.id.notification_container_parent);
+ mNotificationContainerParent = (NotificationsQuickSettingsContainer)
+ findViewById(R.id.notification_container_parent);
mNotificationStackScroller = (NotificationStackScrollLayout)
findViewById(R.id.notification_stack_scroller);
mNotificationStackScroller.setOnHeightChangedListener(this);
@@ -317,6 +332,7 @@ public class NotificationPanelView extends PanelView implements
if (mQsSizeChangeAnimator == null) {
mQsContainer.setHeightOverride(mQsContainer.getDesiredHeight());
}
+ updateMaxHeadsUpTranslation();
}
@Override
@@ -493,22 +509,42 @@ public class NotificationPanelView extends PanelView implements
}
@Override
+ protected void flingToHeight(float vel, boolean expand, float target) {
+ if (!expand && mHeadsUpManager.hasPinnedHeadsUp()) {
+ target = mHeadsUpManager.getHighestPinnedHeadsUp();
+ }
+ mHeadsUpTouchHelper.notifyFling(!expand);
+ super.flingToHeight(vel, expand, target);
+ }
+
+ @Override
public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
event.getText().add(getKeyguardOrLockScreenString());
mLastAnnouncementWasQuickSettings = false;
return true;
}
-
return super.dispatchPopulateAccessibilityEventInternal(event);
}
@Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
+ public boolean
+ onInterceptTouchEvent(MotionEvent event) {
if (mBlockTouches) {
return false;
}
initDownStates(event);
+ if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
+ mExpansionIsFromHeadsUp = true;
+ return true;
+ }
+ if (onQsIntercept(event)) {
+ return true;
+ }
+ return super.onInterceptTouchEvent(event);
+ }
+
+ private boolean onQsIntercept(MotionEvent event) {
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
@@ -583,7 +619,7 @@ public class NotificationPanelView extends PanelView implements
mIntercepting = false;
break;
}
- return super.onInterceptTouchEvent(event);
+ return false;
}
@Override
@@ -652,6 +688,15 @@ public class NotificationPanelView extends PanelView implements
if (mOnlyAffordanceInThisMotion) {
return true;
}
+ mHeadsUpTouchHelper.onTouchEvent(event);
+ if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQSTouch(event)) {
+ return true;
+ }
+ super.onTouchEvent(event);
+ return true;
+ }
+
+ private boolean handleQSTouch(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
&& mStatusBar.getBarState() != StatusBarState.KEYGUARD && !mQsExpanded
&& mQsExpansionEnabled) {
@@ -691,8 +736,7 @@ public class NotificationPanelView extends PanelView implements
// earlier so the state is already up to date when dragging down.
setListening(true);
}
- super.onTouchEvent(event);
- return true;
+ return false;
}
private boolean isInQsArea(float x, float y) {
@@ -834,13 +878,13 @@ public class NotificationPanelView extends PanelView implements
setQsExpansion(mQsExpansionHeight);
flingSettings(!mQsExpansionEnabled && open ? 0f : velocity, open && mQsExpansionEnabled,
new Runnable() {
- @Override
- public void run() {
- mStackScrollerOverscrolling = false;
- mQsExpansionFromOverscroll = false;
- updateQsState();
- }
- });
+ @Override
+ public void run() {
+ mStackScrollerOverscrolling = false;
+ mQsExpansionFromOverscroll = false;
+ updateQsState();
+ }
+ });
}
private void onQsExpansionStarted() {
@@ -869,6 +913,7 @@ public class NotificationPanelView extends PanelView implements
mNotificationStackScroller.setInterceptDelegateEnabled(expanded);
mStatusBar.setQsExpanded(expanded);
mQsPanel.setExpanded(expanded);
+ mNotificationContainerParent.setQsExpanded(expanded);
}
}
@@ -1056,7 +1101,7 @@ public class NotificationPanelView extends PanelView implements
mKeyguardBottomArea.animate()
.alpha(0f)
.setStartDelay(mStatusBar.getKeyguardFadingAwayDelay())
- .setDuration(mStatusBar.getKeyguardFadingAwayDuration()/2)
+ .setDuration(mStatusBar.getKeyguardFadingAwayDuration() / 2)
.setInterpolator(PhoneStatusBar.ALPHA_OUT)
.withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable)
.start();
@@ -1402,6 +1447,8 @@ public class NotificationPanelView extends PanelView implements
updateHeader();
updateUnlockIcon();
updateNotificationTranslucency();
+ mHeadsUpManager.setIsExpanded(!isShadeCollapsed());
+ mNotificationStackScroller.setShadeExpanded(!isShadeCollapsed());
if (DEBUG) {
invalidate();
}
@@ -1544,7 +1591,14 @@ public class NotificationPanelView extends PanelView implements
return mExpandedHeight / HEADER_RUBBERBAND_FACTOR - mQsMinExpansionHeight;
}
}
- return Math.min(0, mNotificationStackScroller.getTranslationY()) / HEADER_RUBBERBAND_FACTOR;
+ float paddingOffset = mNotificationStackScroller.getPaddingOffset();
+ float translation = paddingOffset / HEADER_RUBBERBAND_FACTOR;
+ if (mHeadsUpManager.hasPinnedHeadsUp() || mExpansionIsFromHeadsUp) {
+ translation = mNotificationStackScroller.getTopPadding()
+ + mNotificationStackScroller.getPaddingOffset()
+ - mNotificationTopPadding - mQsMinExpansionHeight;
+ }
+ return Math.min(0, translation);
}
/**
@@ -1605,8 +1659,10 @@ public class NotificationPanelView extends PanelView implements
protected void onExpandingFinished() {
super.onExpandingFinished();
mNotificationStackScroller.onExpansionStopped();
+ mHeadsUpManager.onExpandingFinished();
mIsExpanding = false;
mScrollYOverride = -1;
+ // TODO: look into whether this is still correct
if (mExpandedHeight == 0f) {
setListening(false);
} else {
@@ -1614,6 +1670,8 @@ public class NotificationPanelView extends PanelView implements
}
mQsExpandImmediate = false;
mTwoFingerQsExpandPossible = false;
+ mExpansionIsFromHeadsUp = false;
+ mNotificationStackScroller.setTrackingHeadsUp(mHeadsUpTouchHelper.isTrackingHeadsUp());
}
private void setListening(boolean listening) {
@@ -1709,6 +1767,17 @@ public class NotificationPanelView extends PanelView implements
}
@Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ mBottomBarHeight = insets.getSystemWindowInsetBottom();
+ updateMaxHeadsUpTranslation();
+ return insets;
+ }
+
+ private void updateMaxHeadsUpTranslation() {
+ mNotificationStackScroller.setMaxHeadsUpTranslation(getHeight() - mBottomBarHeight);
+ }
+
+ @Override
public void onRtlPropertiesChanged(int layoutDirection) {
if (layoutDirection != mOldLayoutDirection) {
mAfforanceHelper.onRtlPropertiesChanged();
@@ -2066,4 +2135,44 @@ public class NotificationPanelView extends PanelView implements
mNotificationStackScroller.getTopPadding(), p);
}
}
+
+ @Override
+ public void OnPinnedHeadsUpExistChanged(final boolean exist, boolean changeImmediatly) {
+ if (exist != mPinnedHeadsUpExist) {
+ mPinnedHeadsUpExist = exist;
+ if (!exist) {
+ mNotificationStackScroller.performOnAnimationFinished(
+ mHeadsUpExistenceChangedRunnable);
+ } else {
+ mHeadsUpExistenceChangedRunnable.run();
+ }
+ }
+ }
+
+ @Override
+ public void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+ // TODO: figure out the conditions when not to generate an animation
+ mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp);
+ if (isShadeCollapsed()) {
+ setExpandedHeight(mHeadsUpManager.getHighestPinnedHeadsUp());
+ }
+ }
+
+ private boolean isShadeCollapsed() {
+ // TODO: handle this cleaner
+ return mHeader.getTranslationY() + mHeader.getCollapsedHeight() <= 0;
+ }
+
+ @Override
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ super.setHeadsUpManager(headsUpManager);
+ mHeadsUpTouchHelper.bind(headsUpManager, mNotificationStackScroller, this);
+ }
+
+ public void setTrackingHeadsUp(boolean tracking) {
+ if (tracking) {
+ // otherwise we update the state when the expansion is finished
+ mNotificationStackScroller.setTrackingHeadsUp(true);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index a03c297..cbb71c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -37,6 +37,7 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
private View mStackScroller;
private View mKeyguardStatusBar;
private boolean mInflated;
+ private boolean mQsExpanded;
public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -64,26 +65,29 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
boolean userSwitcherVisible = mInflated && mUserSwitcher.getVisibility() == View.VISIBLE;
boolean statusBarVisible = mKeyguardStatusBar.getVisibility() == View.VISIBLE;
+ View stackQsTop = mQsExpanded ? mStackScroller : mScrollView;
+ View stackQsBottom = !mQsExpanded ? mStackScroller : mScrollView;
// Invert the order of the scroll view and user switcher such that the notifications receive
// touches first but the panel gets drawn above.
if (child == mScrollView) {
- return super.drawChild(canvas, mStackScroller, drawingTime);
- } else if (child == mStackScroller) {
- return super.drawChild(canvas,
- userSwitcherVisible && statusBarVisible ? mUserSwitcher
+ return super.drawChild(canvas, userSwitcherVisible && statusBarVisible ? mUserSwitcher
: statusBarVisible ? mKeyguardStatusBar
: userSwitcherVisible ? mUserSwitcher
- : mScrollView,
+ : stackQsBottom, drawingTime);
+ } else if (child == mStackScroller) {
+ return super.drawChild(canvas,
+ userSwitcherVisible && statusBarVisible ? mKeyguardStatusBar
+ : statusBarVisible || userSwitcherVisible ? stackQsBottom
+ : stackQsTop,
drawingTime);
} else if (child == mUserSwitcher) {
return super.drawChild(canvas,
- userSwitcherVisible && statusBarVisible ? mKeyguardStatusBar
- : mScrollView,
+ userSwitcherVisible && statusBarVisible ? stackQsBottom
+ : stackQsTop,
drawingTime);
} else if (child == mKeyguardStatusBar) {
return super.drawChild(canvas,
- userSwitcherVisible && statusBarVisible ? mScrollView
- : mScrollView,
+ stackQsTop,
drawingTime);
}else {
return super.drawChild(canvas, child, drawingTime);
@@ -97,4 +101,11 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
mInflated = true;
}
}
+
+ public void setQsExpanded(boolean expanded) {
+ if (mQsExpanded != expanded) {
+ mQsExpanded = expanded;
+ invalidate();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index c6e1be9..240438a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -157,8 +157,7 @@ public class PanelBar extends FrameLayout {
if (DEBUG) LOG("panelExpansionChanged: start state=%d panel=%s", mState, panel.getName());
mPanelExpandedFractionSum = 0f;
for (PanelView pv : mPanels) {
- boolean visible = pv.getExpandedHeight() > 0;
- pv.setVisibility(visible ? View.VISIBLE : View.GONE);
+ pv.setVisibility(expanded ? View.VISIBLE : View.INVISIBLE);
// adjust any other panels that may be partially visible
if (expanded) {
if (mState == STATE_CLOSED) {
@@ -167,7 +166,7 @@ public class PanelBar extends FrameLayout {
}
fullyClosed = false;
final float thisFrac = pv.getExpandedFraction();
- mPanelExpandedFractionSum += (visible ? thisFrac : 0);
+ mPanelExpandedFractionSum += thisFrac;
if (DEBUG) LOG("panelExpansionChanged: -> %s: f=%.1f", pv.getName(), thisFrac);
if (panel == pv) {
if (thisFrac == 1f) fullyOpenedPanel = panel;
@@ -196,7 +195,6 @@ public class PanelBar extends FrameLayout {
} else {
pv.resetViews();
pv.setExpandedFraction(0); // just in case
- pv.setVisibility(View.GONE);
pv.cancelPeek();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 4bbf690..1b89b3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -38,6 +38,7 @@ import com.android.systemui.R;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -45,6 +46,7 @@ import java.io.PrintWriter;
public abstract class PanelView extends FrameLayout {
public static final boolean DEBUG = PanelBar.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
+ protected HeadsUpManager mHeadsUpManager;
private final void logf(String fmt, Object... args) {
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
@@ -238,10 +240,7 @@ public abstract class PanelView extends FrameLayout {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- mInitialTouchY = y;
- mInitialTouchX = x;
- mInitialOffsetOnTouch = mExpandedHeight;
- mTouchSlopExceeded = false;
+ startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
mJustPeeked = false;
mPanelClosedOnDown = mExpandedHeight == 0.0f;
mHasLayoutedSinceDown = false;
@@ -274,9 +273,7 @@ public abstract class PanelView extends FrameLayout {
final float newY = event.getY(newIndex);
final float newX = event.getX(newIndex);
mTrackingPointer = event.getPointerId(newIndex);
- mInitialOffsetOnTouch = mExpandedHeight;
- mInitialTouchY = newY;
- mInitialTouchX = newX;
+ startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
@@ -297,9 +294,7 @@ public abstract class PanelView extends FrameLayout {
mTouchSlopExceeded = true;
if (waitForTouchSlop && !mTracking) {
if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
- mInitialOffsetOnTouch = mExpandedHeight;
- mInitialTouchX = x;
- mInitialTouchY = y;
+ startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
h = 0;
}
cancelHeightAnimator();
@@ -334,6 +329,17 @@ public abstract class PanelView extends FrameLayout {
return !waitForTouchSlop || mTracking;
}
+ protected void startExpandMotion(float newX, float newY, boolean startTracking,
+ float expandedHeight) {
+ mInitialOffsetOnTouch = expandedHeight;
+ mInitialTouchY = newY;
+ mInitialTouchX = newX;
+ if (startTracking) {
+ mTouchSlopExceeded = true;
+ onTrackingStarted();
+ }
+ }
+
private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
mTrackingPointer = -1;
if ((mTracking && mTouchSlopExceeded)
@@ -474,12 +480,7 @@ public abstract class PanelView extends FrameLayout {
if (scrolledToBottom || mTouchStartedInEmptyArea) {
if (h < -mTouchSlop && h < -Math.abs(x - mInitialTouchX)) {
cancelHeightAnimator();
- mInitialOffsetOnTouch = mExpandedHeight;
- mInitialTouchY = y;
- mInitialTouchX = x;
- mTracking = true;
- mTouchSlopExceeded = true;
- onTrackingStarted();
+ startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
return true;
}
}
@@ -564,7 +565,10 @@ public abstract class PanelView extends FrameLayout {
protected void fling(float vel, boolean expand) {
cancelPeek();
float target = expand ? getMaxPanelHeight() : 0.0f;
+ flingToHeight(vel, expand, target);
+ }
+ protected void flingToHeight(float vel, boolean expand, float target) {
// Hack to make the expand transition look nice when clear all button is visible - we make
// the animation only to the last notification, and then jump to the maximum panel height so
// clear all just fades in and the decelerating motion is towards the last notification.
@@ -644,7 +648,7 @@ public abstract class PanelView extends FrameLayout {
mHasLayoutedSinceDown = true;
if (mUpdateFlingOnLayout) {
abortAnimations();
- fling(mUpdateFlingVelocity, true);
+ fling(mUpdateFlingVelocity, true /* expands */);
mUpdateFlingOnLayout = false;
}
}
@@ -805,7 +809,7 @@ public abstract class PanelView extends FrameLayout {
if (mExpanding) {
notifyExpandingFinished();
}
- setVisibility(VISIBLE);
+ notifyBarPanelExpansionChanged();
// Wait for window manager to pickup the change, so we know the maximum height of the panel
// then.
@@ -941,9 +945,9 @@ public abstract class PanelView extends FrameLayout {
return animator;
}
- private void notifyBarPanelExpansionChanged() {
+ protected void notifyBarPanelExpansionChanged() {
mBar.panelExpansionChanged(this, mExpandedFraction, mExpandedFraction > 0f || mPeekPending
- || mPeekAnimator != null);
+ || mPeekAnimator != null || mInstantExpanding || mHeadsUpManager.hasPinnedHeadsUp());
}
/**
@@ -1014,4 +1018,8 @@ public abstract class PanelView extends FrameLayout {
* @return the height of the clear all button, in pixels
*/
protected abstract int getClearAllHeight();
+
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ mHeadsUpManager = headsUpManager;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index b0d6178..74a26a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -17,19 +17,6 @@
package com.android.systemui.statusbar.phone;
-import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
-import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.app.StatusBarManager.windowStateToString;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.NonNull;
@@ -92,7 +79,6 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewStub;
import android.view.WindowManager;
@@ -137,7 +123,6 @@ import com.android.systemui.statusbar.NotificationOverflowContainer;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.SpeedBumpView;
-import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.AccessibilityController;
@@ -147,7 +132,7 @@ import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.CastControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HotspotControllerImpl;
import com.android.systemui.statusbar.policy.KeyButtonView;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -172,11 +157,26 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.TreeMap;
+
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
+import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.app.StatusBarManager.windowStateToString;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
- DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener {
+ DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener, HeadsUpManager.OnHeadsUpChangedListener {
static final String TAG = "PhoneStatusBar";
public static final boolean DEBUG = BaseStatusBar.DEBUG;
public static final boolean SPEW = false;
@@ -360,11 +360,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
if (wasUsing != mUseHeadsUp) {
if (!mUseHeadsUp) {
Log.d(TAG, "dismissing any existing heads up notification on disable event");
- setHeadsUpVisibility(false);
- mHeadsUpNotificationView.releaseImmediately();
- removeHeadsUpView();
- } else {
- addHeadsUpView();
+ mHeadsUpManager.releaseAllImmediately();
}
}
}
@@ -528,6 +524,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
};
private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap
= new HashMap<>();
+ private HashSet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new HashSet<>();
+ private RankingMap mLatestRankingMap;
@Override
public void start() {
@@ -598,7 +596,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
}
return mStatusBarWindow.onTouchEvent(event);
- }});
+ }
+ });
mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
mStatusBarView.setBar(this);
@@ -615,12 +614,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mNotificationPanel.setBackground(new FastColorDrawable(context.getColor(
R.color.notification_panel_solid_background)));
}
- if (ENABLE_HEADS_UP) {
- mHeadsUpNotificationView =
- (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null);
- mHeadsUpNotificationView.setVisibility(View.GONE);
- mHeadsUpNotificationView.setBar(this);
- }
+
+ mHeadsUpManager = new HeadsUpManager(context);
+ mHeadsUpManager.setBar(this);
+ mHeadsUpManager.addListener(this);
+ mHeadsUpManager.addListener(mNotificationPanel);
+ mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
+
if (MULTIUSER_DEBUG) {
mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
R.id.header_debug_info);
@@ -667,6 +667,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mStackScroller.setLongPressListener(getNotificationLongClicker());
mStackScroller.setPhoneStatusBar(this);
mStackScroller.setGroupManager(mGroupManager);
+ mStackScroller.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setOnGroupChangeListener(mStackScroller);
mKeyguardIconOverflowContainer =
@@ -1084,32 +1085,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
return lp;
}
- private void addHeadsUpView() {
- int headsUpHeight = mContext.getResources()
- .getDimensionPixelSize(R.dimen.heads_up_window_height);
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, headsUpHeight,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSLUCENT);
- lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- lp.gravity = Gravity.TOP;
- lp.setTitle("Heads Up");
- lp.packageName = mContext.getPackageName();
- lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
-
- mWindowManager.addView(mHeadsUpNotificationView, lp);
- }
-
- private void removeHeadsUpView() {
- mWindowManager.removeView(mHeadsUpNotificationView);
- }
-
public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
mIconController.addSystemIcon(slot, index, viewIndex, icon);
}
@@ -1131,30 +1106,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
public void addNotification(StatusBarNotification notification, RankingMap ranking,
Entry oldEntry) {
if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
- if (mUseHeadsUp && shouldInterrupt(notification)) {
- if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
- Entry interruptionCandidate = oldEntry;
- if (interruptionCandidate == null) {
- final StatusBarIconView iconView = createIcon(notification);
- if (iconView == null) {
- return;
- }
- interruptionCandidate = new Entry(notification, iconView);
- }
- ViewGroup holder = mHeadsUpNotificationView.getHolder();
- if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
- // 1. Populate mHeadsUpNotificationView
- mHeadsUpNotificationView.showNotification(interruptionCandidate);
-
- // do not show the notification in the shade, yet.
- return;
- }
- }
+ boolean isHeadsUp = mUseHeadsUp && shouldInterrupt(notification);
- Entry shadeEntry = createNotificationViews(notification);
+ Entry shadeEntry = createNotificationViews(notification, isHeadsUp);
if (shadeEntry == null) {
return;
}
+ if (isHeadsUp) {
+ mHeadsUpManager.showNotification(shadeEntry);
+ }
if (notification.getNotification().fullScreenIntent != null) {
// Stop screensaver if the notification has a full-screen intent.
@@ -1175,18 +1135,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
setAreThereNotifications();
}
- public void displayNotificationFromHeadsUp(Entry shadeEntry) {
-
- // The notification comes from the headsup, let's inflate the normal layout again
- inflateViews(shadeEntry, mStackScroller);
- shadeEntry.setInterruption();
- shadeEntry.row.setHeadsUp(false);
-
- addNotificationViews(shadeEntry, null);
- // Recalculate the position of the sliding windows and the titles.
- setAreThereNotifications();
- }
-
@Override
protected void updateNotificationRanking(RankingMap ranking) {
mNotificationData.updateRanking(ranking);
@@ -1195,10 +1143,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
@Override
public void removeNotification(String key, RankingMap ranking) {
- if (ENABLE_HEADS_UP) {
- mHeadsUpNotificationView.removeNotification(key);
+ boolean defferRemoval = false;
+ if (mHeadsUpManager.isHeadsUp(key)) {
+ defferRemoval = !mHeadsUpManager.removeNotification(key);
+ }
+ if (defferRemoval) {
+ mLatestRankingMap = ranking;
+ mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
+ return;
}
-
StatusBarNotification old = removeNotificationViews(key, ranking);
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
@@ -1870,6 +1823,38 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
logStateToEventlog();
}
+ @Override
+ public void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly) {
+ if (exist) {
+ mStatusBarWindowManager.setHeadsUpShowing(true);
+ } else {
+ Runnable endRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (!mHeadsUpManager.hasPinnedHeadsUp()) {
+ mStatusBarWindowManager.setHeadsUpShowing(false);
+ }
+ }
+ };
+ if (changeImmediatly) {
+ endRunnable.run();
+ } else {
+ mStackScroller.performOnAnimationFinished(endRunnable);
+ }
+ }
+ }
+
+ @Override
+ public void OnHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
+ if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
+ removeNotification(entry.key, mLatestRankingMap);
+ mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
+ if (mHeadsUpEntriesToRemoveOnSwitch.isEmpty()) {
+ mLatestRankingMap = null;
+ }
+ }
+ }
+
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -1886,15 +1871,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
case MSG_CLOSE_PANELS:
animateCollapsePanels();
break;
- case MSG_SHOW_HEADS_UP:
- setHeadsUpVisibility(true);
- break;
- case MSG_ESCALATE_HEADS_UP:
- escalateHeadsUp();
- case MSG_HIDE_HEADS_UP:
- mHeadsUpNotificationView.releaseImmediately();
- setHeadsUpVisibility(false);
- break;
case MSG_LAUNCH_TRANSITION_TIMEOUT:
onLaunchTransitionTimeout();
break;
@@ -1903,44 +1879,16 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
@Override
- public void scheduleHeadsUpDecay(long delay) {
- mHandler.removeMessages(MSG_HIDE_HEADS_UP);
- if (mHeadsUpNotificationView.isClearable()) {
- mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, delay);
- }
- }
-
- @Override
- public void scheduleHeadsUpOpen() {
- mHandler.removeMessages(MSG_HIDE_HEADS_UP);
- mHandler.removeMessages(MSG_SHOW_HEADS_UP);
- mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
- }
-
- @Override
- public void scheduleHeadsUpClose() {
- mHandler.removeMessages(MSG_HIDE_HEADS_UP);
- if (mHeadsUpNotificationView.getVisibility() != View.GONE) {
- mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
- }
- }
-
- @Override
- public void scheduleHeadsUpEscalation() {
- mHandler.removeMessages(MSG_HIDE_HEADS_UP);
- mHandler.removeMessages(MSG_ESCALATE_HEADS_UP);
- mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
- }
-
- /** if the interrupting notification had a fullscreen intent, fire it now. */
- private void escalateHeadsUp() {
- if (mHeadsUpNotificationView.getEntry() != null) {
- final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification;
- mHeadsUpNotificationView.releaseImmediately();
+ public void escalateHeadsUp() {
+ TreeMap<String, HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getEntries();
+ for (String key : entries.keySet()) {
+ Entry entry = entries.get(key).entry;
+ final StatusBarNotification sbn = entry.notification;
final Notification notification = sbn.getNotification();
if (notification.fullScreenIntent != null) {
- if (DEBUG)
+ if (DEBUG) {
Log.d(TAG, "converting a heads up to fullScreen");
+ }
try {
EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
sbn.getKey());
@@ -1949,6 +1897,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
}
}
+ mHeadsUpManager.releaseAllImmediately();
}
boolean panelsEnabled() {
@@ -2081,10 +2030,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
// Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
mStatusBarView.collapseAllPanels(/*animate=*/ false, false /* delayed*/);
- // reset things to their proper state
- mStackScroller.setVisibility(View.VISIBLE);
- mNotificationPanel.setVisibility(View.GONE);
-
mNotificationPanel.closeQs();
mExpandedVisible = false;
@@ -2478,8 +2423,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
pw.println(Settings.Global.zenModeToString(mZenMode));
pw.print(" mUseHeadsUp=");
pw.println(mUseHeadsUp);
- pw.print(" interrupting package: ");
- pw.println(hunStateToString(mHeadsUpNotificationView.getEntry()));
dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
if (mNavigationBarView != null) {
pw.print(" mNavigationBarWindowState=");
@@ -2571,10 +2514,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
if (mSecurityController != null) {
mSecurityController.dump(fd, pw, args);
}
- if (mHeadsUpNotificationView != null) {
- mHeadsUpNotificationView.dump(fd, pw, args);
+ if (mHeadsUpManager != null) {
+ mHeadsUpManager.dump(fd, pw, args);
} else {
- pw.println(" mHeadsUpNotificationView: null");
+ pw.println(" mHeadsUpManager: null");
}
pw.println("SharedPreferences:");
@@ -2672,7 +2615,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
mScreenOn = false;
notifyNavigationBarScreenOn(false);
- notifyHeadsUpScreenOn(false);
+ notifyHeadsUpScreenOff();
finishBarAnimations();
resetUserExpandedStates();
}
@@ -2764,15 +2707,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mUserSetupObserver, mCurrentUserId);
}
- private void setHeadsUpVisibility(boolean vis) {
- if (!ENABLE_HEADS_UP) return;
- if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
- EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_STATUS,
- vis ? mHeadsUpNotificationView.getKey() : "",
- vis ? 1 : 0);
- mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
- }
-
/**
* Reload some of our resources when the configuration changes.
*
@@ -2791,9 +2725,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
if (mNotificationPanel != null) {
mNotificationPanel.updateResources();
}
- if (mHeadsUpNotificationView != null) {
- mHeadsUpNotificationView.updateResources();
- }
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.updateResources();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 63bbf97..6e0bf8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -130,7 +130,7 @@ public class StatusBarWindowManager {
private void applyHeight(State state) {
boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded
- || state.keyguardFadingAway || state.bouncerShowing;
+ || state.keyguardFadingAway || state.bouncerShowing || state.headsUpShowing;
if (expanded) {
mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT;
} else {
@@ -218,6 +218,11 @@ public class StatusBarWindowManager {
apply(mCurrentState);
}
+ public void setHeadsUpShowing(boolean showing) {
+ mCurrentState.headsUpShowing = showing;
+ apply(mCurrentState);
+ }
+
/**
* @param state The {@link StatusBarState} of the status bar.
*/
@@ -235,6 +240,7 @@ public class StatusBarWindowManager {
boolean bouncerShowing;
boolean keyguardFadingAway;
boolean qsExpanded;
+ boolean headsUpShowing;
/**
* The {@link BaseStatusBar} state from the status bar.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
new file mode 100644
index 0000000..d43af59
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2011 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.policy;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pools;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Stack;
+import java.util.TreeMap;
+
+public class HeadsUpManager {
+ private static final String TAG = "HeadsUpManager";
+ private static final boolean DEBUG = false;
+ private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
+
+ private final int mHeadsUpNotificationDecay;
+ private final int mMinimumDisplayTime;
+
+ private final int mTouchSensitivityDelay;
+ private final ArrayMap<String, Long> mSnoozedPackages;
+ private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
+ private final int mDefaultSnoozeLengthMs;
+ private final Handler mHandler = new Handler();
+ private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() {
+
+ private Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
+
+ @Override
+ public HeadsUpEntry acquire() {
+ if (!mPoolObjects.isEmpty()) {
+ return mPoolObjects.pop();
+ }
+ return new HeadsUpEntry();
+ }
+
+ @Override
+ public boolean release(HeadsUpEntry instance) {
+ instance.removeAutoCancelCallbacks();
+ mPoolObjects.push(instance);
+ return true;
+ }
+ };
+
+
+ private PhoneStatusBar mBar;
+ private int mSnoozeLengthMs;
+ private ContentObserver mSettingsObserver;
+
+ private TreeMap<String ,HeadsUpEntry> mHeadsUpEntries = new TreeMap<>();
+ private HashSet<String> mSwipedOutKeys = new HashSet<>();
+ private int mUser;
+ private Clock mClock;
+ private boolean mReleaseOnExpandFinish;
+ private boolean mTrackingHeadsUp;
+ private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
+ private boolean mIsExpanded;
+ private boolean mHasPinnedHeadsUp;
+
+ public HeadsUpManager(final Context context) {
+ Resources resources = context.getResources();
+ mTouchSensitivityDelay = resources.getInteger(R.integer.heads_up_sensitivity_delay);
+ if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
+ mSnoozedPackages = new ArrayMap<>();
+ mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
+ mSnoozeLengthMs = mDefaultSnoozeLengthMs;
+ mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
+ mHeadsUpNotificationDecay = 2000000;
+ mClock = new Clock();
+ // TODO: shadow mSwipeHelper.setMaxSwipeProgress(mMaxAlpha);
+
+ mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
+ SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
+ mSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ final int packageSnoozeLengthMs = Settings.Global.getInt(
+ context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
+ if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) {
+ mSnoozeLengthMs = packageSnoozeLengthMs;
+ if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
+ }
+ }
+ };
+ context.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
+ mSettingsObserver);
+ if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
+
+ // TODO: investigate whether this is still needed
+// if (!mHeadsUpEntries.isEmpty()) {
+// whoops, we're on already!
+// showNotification(mHeadsUpEntries);
+// }
+ }
+
+ public void setBar(PhoneStatusBar bar) {
+ mBar = bar;
+ }
+
+ public void addListener(OnHeadsUpChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ public PhoneStatusBar getBar() {
+ return mBar;
+ }
+
+ /**
+ * Called when posting a new notification to the heads up.
+ */
+ public void showNotification(NotificationData.Entry headsUp) {
+ if (DEBUG) Log.v(TAG, "showNotification");
+ addHeadsUpEntry(headsUp);
+ updateNotification(headsUp, true);
+ headsUp.setInterruption();
+ updatePinnedHeadsUpState(false);
+ }
+
+ /**
+ * Called when updating or posting a notification to the heads up.
+ */
+ public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
+ if (DEBUG) Log.v(TAG, "updateNotification");
+
+ headsUp.row.setChildrenExpanded(false /* expanded */, false /* animated */);
+ headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+
+ if (alert) {
+ HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(headsUp.key);
+ headsUpEntry.updateEntry();
+ headsUpEntry.entry.row.setInShade(mIsExpanded);
+ }
+ }
+
+ private void addHeadsUpEntry(NotificationData.Entry entry) {
+ boolean wasEmpty = mHeadsUpEntries.isEmpty();
+ HeadsUpEntry headsUpEntry = mEntryPool.acquire();
+ headsUpEntry.setEntry(entry);
+ mHeadsUpEntries.put(entry.key, headsUpEntry);
+ for (OnHeadsUpChangedListener listener : mListeners) {
+ listener.OnHeadsUpStateChanged(entry, true);
+ }
+ entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ entry.row.setHeadsUp(true);
+ }
+
+ private void removeHeadsUpEntry(NotificationData.Entry entry) {
+ HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
+ mEntryPool.release(remove);
+ entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ entry.row.setHeadsUp(false);
+ for (OnHeadsUpChangedListener listener : mListeners) {
+ listener.OnHeadsUpStateChanged(entry, false);
+ }
+ updatePinnedHeadsUpState(false);
+ }
+
+ private void updatePinnedHeadsUpState(boolean forceImmediate) {
+ boolean hasPinnedHeadsUp = hasPinnedHeadsUpInternal();
+ if (hasPinnedHeadsUp == mHasPinnedHeadsUp) {
+ return;
+ }
+ mHasPinnedHeadsUp = hasPinnedHeadsUp;
+ for (OnHeadsUpChangedListener listener :mListeners) {
+ listener.OnPinnedHeadsUpExistChanged(hasPinnedHeadsUp, forceImmediate);
+ }
+ }
+
+ /**
+ * React to the removal of the notification in the heads up.
+ *
+ * @return true if the notification was removed and false if it still needs to be kept around
+ * for a bit since it wasn't shown long enough
+ */
+ public boolean removeNotification(String key) {
+ if (DEBUG) Log.v(TAG, "remove");
+ if (wasShownLongEnough(key)) {
+ releaseImmediately(key);
+ return true;
+ } else {
+ getHeadsUpEntry(key).hideAsSoonAsPossible();
+ return false;
+ }
+ }
+
+ private boolean wasShownLongEnough(String key) {
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+ HeadsUpEntry topEntry = getTopEntry();
+ if (mSwipedOutKeys.contains(key)) {
+ // We always instantly dismiss views being manually swiped out.
+ mSwipedOutKeys.remove(key);
+ return true;
+ }
+ if (headsUpEntry != topEntry) {
+ return true;
+ }
+ return headsUpEntry.wasShownLongEnough();
+ }
+
+ public boolean isHeadsUp(String key) {
+ return mHeadsUpEntries.containsKey(key);
+ }
+
+
+ /**
+ * Push any current Heads Up notification down into the shade.
+ */
+ public void releaseAllImmediately() {
+ if (DEBUG) Log.v(TAG, "releaseAllImmediately");
+ for (String key: mHeadsUpEntries.keySet()) {
+ releaseImmediately(key);
+ }
+ }
+
+ public void releaseImmediately(String key) {
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+ if (headsUpEntry == null) {
+ return;
+ }
+ NotificationData.Entry shadeEntry = headsUpEntry.entry;
+ removeHeadsUpEntry(shadeEntry);
+ }
+
+ public boolean isSnoozed(String packageName) {
+ final String key = snoozeKey(packageName, mUser);
+ Long snoozedUntil = mSnoozedPackages.get(key);
+ if (snoozedUntil != null) {
+ if (snoozedUntil > SystemClock.elapsedRealtime()) {
+ if (DEBUG) Log.v(TAG, key + " snoozed");
+ return true;
+ }
+ mSnoozedPackages.remove(packageName);
+ }
+ return false;
+ }
+
+ public void snooze() {
+ for (String key: mHeadsUpEntries.keySet()) {
+ HeadsUpEntry entry = mHeadsUpEntries.get(key);
+ String packageName = entry.entry.notification.getPackageName();
+ mSnoozedPackages.put(snoozeKey(packageName, mUser),
+ SystemClock.elapsedRealtime() + mSnoozeLengthMs);
+ }
+ mReleaseOnExpandFinish = true;
+ }
+
+ private static String snoozeKey(String packageName, int user) {
+ return user + "," + packageName;
+ }
+
+ private HeadsUpEntry getHeadsUpEntry(String key) {
+ return mHeadsUpEntries.get(key);
+ }
+
+ public NotificationData.Entry getEntry(String key) {
+ return mHeadsUpEntries.get(key).entry;
+ }
+
+ public TreeMap<String, HeadsUpEntry> getEntries() {
+ return mHeadsUpEntries;
+ }
+
+ public HeadsUpEntry getTopEntry() {
+ return mHeadsUpEntries.isEmpty() ? null : mHeadsUpEntries.lastEntry().getValue();
+ }
+
+ /**
+ * @param key the key of the touched notification
+ * @return whether the touch is valid and should not be discarded
+ */
+ public boolean shouldSwallowClick(String key) {
+ if (mClock.currentTimeMillis() < mHeadsUpEntries.get(key).postTime) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
+ // TODO: handle the shadow
+ //getBackground().setAlpha((int) (255 * swipeProgress));
+ return false;
+ }
+
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+ // TODO: Look into touchable region
+// mContentHolder.getLocationOnScreen(mTmpTwoArray);
+//
+// info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+// info.touchableRegion.set(mTmpTwoArray[0], mTmpTwoArray[1],
+// mTmpTwoArray[0] + mContentHolder.getWidth(),
+// mTmpTwoArray[1] + mContentHolder.getHeight());
+ }
+
+ public void setUser(int user) {
+ mUser = user;
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("HeadsUpManager state:");
+ pw.print(" mTouchSensitivityDelay="); pw.println(mTouchSensitivityDelay);
+ pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
+ pw.print(" now="); pw.println(SystemClock.elapsedRealtime());
+ pw.print(" mUser="); pw.println(mUser);
+ for (String key: mHeadsUpEntries.keySet()) {
+ pw.print(" HeadsUpEntry="); pw.println(mHeadsUpEntries.get(key));
+ }
+ int N = mSnoozedPackages.size();
+ pw.println(" snoozed packages: " + N);
+ for (int i = 0; i < N; i++) {
+ pw.print(" "); pw.print(mSnoozedPackages.valueAt(i));
+ pw.print(", "); pw.println(mSnoozedPackages.keyAt(i));
+ }
+ }
+
+ public boolean hasPinnedHeadsUp() {
+ return mHasPinnedHeadsUp;
+ }
+
+ private boolean hasPinnedHeadsUpInternal() {
+ for (String key: mHeadsUpEntries.keySet()) {
+ HeadsUpEntry entry = mHeadsUpEntries.get(key);
+ if (!entry.entry.row.isInShade()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void addSwipedOutKey(String key) {
+ mSwipedOutKeys.add(key);
+ }
+
+ public float getHighestPinnedHeadsUp() {
+ float max = 0;
+ for (String key: mHeadsUpEntries.keySet()) {
+ HeadsUpEntry entry = mHeadsUpEntries.get(key);
+ if (!entry.entry.row.isInShade()) {
+ max = Math.max(max, entry.entry.row.getActualHeight());
+ }
+ }
+ return max;
+ }
+
+ public void releaseAllToShade() {
+ for (String key: mHeadsUpEntries.keySet()) {
+ HeadsUpEntry entry = mHeadsUpEntries.get(key);
+ entry.entry.row.setInShade(true);
+ }
+ updatePinnedHeadsUpState(true);
+ }
+
+ public void onExpandingFinished() {
+ if (mReleaseOnExpandFinish) {
+ releaseAllImmediately();
+ mReleaseOnExpandFinish = false;
+ } else {
+ for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
+ removeHeadsUpEntry(entry);
+ }
+ mEntriesToRemoveAfterExpand.clear();
+ }
+ }
+
+ public void setTrackingHeadsUp(boolean trackingHeadsUp) {
+ mTrackingHeadsUp = trackingHeadsUp;
+ }
+
+ public void setIsExpanded(boolean isExpanded) {
+ mIsExpanded = isExpanded;
+ }
+
+ public int getTopHeadsUpHeight() {
+ HeadsUpEntry topEntry = getTopEntry();
+ return topEntry != null ? topEntry.entry.row.getHeadsUpHeight() : 0;
+ }
+
+ public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
+ public NotificationData.Entry entry;
+ public long postTime;
+ public long earliestRemovaltime;
+ private Runnable mRemoveHeadsUpRunnable;
+
+ public void setEntry(final NotificationData.Entry entry) {
+ this.entry = entry;
+
+ // The actual post time will be just after the heads-up really slided in
+ postTime = mClock.currentTimeMillis() + mTouchSensitivityDelay;
+ mRemoveHeadsUpRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (!mTrackingHeadsUp) {
+ removeHeadsUpEntry(entry);
+ } else {
+ mEntriesToRemoveAfterExpand.add(entry);
+ }
+ }
+ };
+ updateEntry();
+ }
+
+ public void updateEntry() {
+ long currentTime = mClock.currentTimeMillis();
+ postTime = Math.max(postTime, currentTime);
+ long finishTime = postTime + mHeadsUpNotificationDecay;
+ long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
+ earliestRemovaltime = currentTime + mMinimumDisplayTime;
+ removeAutoCancelCallbacks();
+ mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
+ }
+
+ @Override
+ public int compareTo(HeadsUpEntry o) {
+ return postTime < o.postTime ? -1
+ : postTime == o.postTime ? 0
+ : 1;
+ }
+
+ public void removeAutoCancelCallbacks() {
+ mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
+ }
+
+ public boolean wasShownLongEnough() {
+ return earliestRemovaltime < mClock.currentTimeMillis();
+ }
+
+ public void hideAsSoonAsPossible() {
+ removeAutoCancelCallbacks();
+ mHandler.postDelayed(mRemoveHeadsUpRunnable,
+ earliestRemovaltime - mClock.currentTimeMillis());
+ }
+ }
+
+ public static class Clock {
+ public long currentTimeMillis() {
+ return SystemClock.elapsedRealtime();
+ }
+ }
+
+ public interface OnHeadsUpChangedListener {
+ void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly);
+ void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
deleted file mode 100644
index 1e40bab..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- * Copyright (C) 2011 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.policy;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.ArrayMap;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.ViewTreeObserver;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.FrameLayout;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.ExpandHelper;
-import com.android.systemui.Gefingerpoken;
-import com.android.systemui.R;
-import com.android.systemui.SwipeHelper;
-import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback,
- ViewTreeObserver.OnComputeInternalInsetsListener {
- private static final String TAG = "HeadsUpNotificationView";
- private static final boolean DEBUG = false;
- private static final boolean SPEW = DEBUG;
- private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
-
- Rect mTmpRect = new Rect();
- int[] mTmpTwoArray = new int[2];
-
- private final int mHeadsUpNotificationDecay;
- private final int mMinimumDisplayTime;
-
- private final int mTouchSensitivityDelay;
- private final float mMaxAlpha = 1f;
- private final ArrayMap<String, Long> mSnoozedPackages;
- private final int mDefaultSnoozeLengthMs;
-
- private SwipeHelper mSwipeHelper;
- private EdgeSwipeHelper mEdgeSwipeHelper;
-
- private PhoneStatusBar mBar;
-
- private long mLingerUntilMs;
- private long mStartTouchTime;
- private ViewGroup mContentHolder;
- private int mSnoozeLengthMs;
- private ContentObserver mSettingsObserver;
-
- private NotificationData.Entry mHeadsUp;
- private int mUser;
- private String mMostRecentPackageName;
- private boolean mTouched;
- private Clock mClock;
-
- public static class Clock {
- public long currentTimeMillis() {
- return SystemClock.elapsedRealtime();
- }
- }
-
- public HeadsUpNotificationView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public HeadsUpNotificationView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- Resources resources = context.getResources();
- mTouchSensitivityDelay = resources.getInteger(R.integer.heads_up_sensitivity_delay);
- if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
- mSnoozedPackages = new ArrayMap<>();
- mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
- mSnoozeLengthMs = mDefaultSnoozeLengthMs;
- mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
- mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
- mClock = new Clock();
- }
-
- @VisibleForTesting
- public HeadsUpNotificationView(Context context, Clock clock, SwipeHelper swipeHelper,
- EdgeSwipeHelper edgeSwipeHelper, int headsUpNotificationDecay, int minimumDisplayTime,
- int touchSensitivityDelay, int snoozeLength) {
- super(context, null);
- mClock = clock;
- mSwipeHelper = swipeHelper;
- mEdgeSwipeHelper = edgeSwipeHelper;
- mMinimumDisplayTime = minimumDisplayTime;
- mHeadsUpNotificationDecay = headsUpNotificationDecay;
- mTouchSensitivityDelay = touchSensitivityDelay;
- mSnoozedPackages = new ArrayMap<>();
- mDefaultSnoozeLengthMs = snoozeLength;
- }
-
- public void updateResources() {
- if (mContentHolder != null) {
- final LayoutParams lp = (LayoutParams) mContentHolder.getLayoutParams();
- lp.width = getResources().getDimensionPixelSize(R.dimen.notification_panel_width);
- lp.gravity = getResources().getInteger(R.integer.notification_panel_layout_gravity);
- mContentHolder.setLayoutParams(lp);
- }
- }
-
- public void setBar(PhoneStatusBar bar) {
- mBar = bar;
- }
-
- public PhoneStatusBar getBar() {
- return mBar;
- }
-
- public ViewGroup getHolder() {
- return mContentHolder;
- }
-
- /**
- * Called when posting a new notification to the heads up.
- */
- public void showNotification(NotificationData.Entry headsUp) {
- if (DEBUG) Log.v(TAG, "showNotification");
- if (mHeadsUp != null) {
- // bump any previous heads up back to the shade
- releaseImmediately();
- }
- mTouched = false;
- updateNotification(headsUp, true);
- mLingerUntilMs = mClock.currentTimeMillis() + mMinimumDisplayTime;
- }
-
- /**
- * Called when updating or posting a notification to the heads up.
- */
- public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
- if (DEBUG) Log.v(TAG, "updateNotification");
-
- if (mHeadsUp == headsUp) {
- resetViewForHeadsup();
- // This is an in-place update. Noting more to do.
- return;
- }
-
- mHeadsUp = headsUp;
-
- if (mContentHolder != null) {
- mContentHolder.removeAllViews();
- }
-
- if (mHeadsUp != null) {
- mMostRecentPackageName = mHeadsUp.notification.getPackageName();
- if (mHeadsUp.row != null) {
- resetViewForHeadsup();
- }
-
- mStartTouchTime = SystemClock.elapsedRealtime() + mTouchSensitivityDelay;
- if (mContentHolder != null) { // only null in tests and before we are attached to a window
- mContentHolder.setX(0);
- mContentHolder.setVisibility(View.VISIBLE);
- mContentHolder.setAlpha(mMaxAlpha);
- mContentHolder.addView(mHeadsUp.row);
- sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-
- mSwipeHelper.snapChild(mContentHolder, 1f);
- }
-
- mHeadsUp.setInterruption();
- }
- if (alert) {
- // Make sure the heads up window is open.
- mBar.scheduleHeadsUpOpen();
- mBar.scheduleHeadsUpDecay(mHeadsUpNotificationDecay);
- }
- }
-
- private void resetViewForHeadsup() {
- if (mHeadsUp.row.areChildrenExpanded()) {
- mHeadsUp.row.setChildrenExpanded(false /* expanded */, false /* animated */);
- }
- mHeadsUp.row.setSystemExpanded(true);
- mHeadsUp.row.setSensitive(false);
- mHeadsUp.row.setHeadsUp(true);
- mHeadsUp.row.setTranslationY(0);
- mHeadsUp.row.setTranslationZ(0);
- mHeadsUp.row.setHideSensitive(
- false, false /* animated */, 0 /* delay */, 0 /* duration */);
- }
-
- /**
- * Possibly enter the lingering state by delaying the closing of the window.
- *
- * @return true if the notification has entered the lingering state.
- */
- private boolean startLingering(boolean removed) {
- final long now = mClock.currentTimeMillis();
- if (!mTouched && mHeadsUp != null && now < mLingerUntilMs) {
- if (removed) {
- mHeadsUp = null;
- }
- mBar.scheduleHeadsUpDecay(mLingerUntilMs - now);
- return true;
- }
- return false;
- }
-
- /**
- * React to the removal of the notification in the heads up.
- */
- public void removeNotification(String key) {
- if (DEBUG) Log.v(TAG, "remove");
- if (mHeadsUp == null || !mHeadsUp.key.equals(key)) {
- return;
- }
- if (!startLingering(/* removed */ true)) {
- mHeadsUp = null;
- releaseImmediately();
- }
- }
-
- /**
- * Ask for any current Heads Up notification to be pushed down into the shade.
- */
- public void release() {
- if (DEBUG) Log.v(TAG, "release");
- if (!startLingering(/* removed */ false)) {
- releaseImmediately();
- }
- }
-
- /**
- * Push any current Heads Up notification down into the shade.
- */
- public void releaseImmediately() {
- if (DEBUG) Log.v(TAG, "releaseImmediately");
- if (mHeadsUp != null) {
- mContentHolder.removeView(mHeadsUp.row);
- mBar.displayNotificationFromHeadsUp(mHeadsUp);
- }
- mHeadsUp = null;
- mBar.scheduleHeadsUpClose();
- }
-
- @Override
- protected void onVisibilityChanged(View changedView, int visibility) {
- super.onVisibilityChanged(changedView, visibility);
- if (DEBUG) Log.v(TAG, "onVisibilityChanged: " + visibility);
- if (changedView.getVisibility() == VISIBLE) {
- mStartTouchTime = mClock.currentTimeMillis() + mTouchSensitivityDelay;
- sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- }
- }
-
- public boolean isSnoozed(String packageName) {
- final String key = snoozeKey(packageName, mUser);
- Long snoozedUntil = mSnoozedPackages.get(key);
- if (snoozedUntil != null) {
- if (snoozedUntil > SystemClock.elapsedRealtime()) {
- if (DEBUG) Log.v(TAG, key + " snoozed");
- return true;
- }
- mSnoozedPackages.remove(packageName);
- }
- return false;
- }
-
- private void snooze() {
- if (mMostRecentPackageName != null) {
- mSnoozedPackages.put(snoozeKey(mMostRecentPackageName, mUser),
- SystemClock.elapsedRealtime() + mSnoozeLengthMs);
- }
- releaseImmediately();
- }
-
- private static String snoozeKey(String packageName, int user) {
- return user + "," + packageName;
- }
-
- public boolean isShowing(String key) {
- return mHeadsUp != null && mHeadsUp.key.equals(key);
- }
-
- public NotificationData.Entry getEntry() {
- return mHeadsUp;
- }
-
- public boolean isClearable() {
- return mHeadsUp == null || mHeadsUp.notification.isClearable();
- }
-
- // ViewGroup methods
-
-private static final ViewOutlineProvider CONTENT_HOLDER_OUTLINE_PROVIDER =
- new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- int outlineLeft = view.getPaddingLeft();
- int outlineTop = view.getPaddingTop();
-
- // Apply padding to shadow.
- outline.setRect(outlineLeft, outlineTop,
- view.getWidth() - outlineLeft - view.getPaddingRight(),
- view.getHeight() - outlineTop - view.getPaddingBottom());
- }
- };
-
- @Override
- public void onAttachedToWindow() {
- final ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
- float touchSlop = viewConfiguration.getScaledTouchSlop();
- mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, getContext());
- mSwipeHelper.setMaxSwipeProgress(mMaxAlpha);
- mEdgeSwipeHelper = new EdgeSwipeHelper(this, touchSlop);
-
- int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
- int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
-
- mContentHolder = (ViewGroup) findViewById(R.id.content_holder);
- mContentHolder.setOutlineProvider(CONTENT_HOLDER_OUTLINE_PROVIDER);
-
- mSnoozeLengthMs = Settings.Global.getInt(mContext.getContentResolver(),
- SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
- mSettingsObserver = new ContentObserver(getHandler()) {
- @Override
- public void onChange(boolean selfChange) {
- final int packageSnoozeLengthMs = Settings.Global.getInt(
- mContext.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
- if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) {
- mSnoozeLengthMs = packageSnoozeLengthMs;
- if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
- }
- }
- };
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
- mSettingsObserver);
- if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
-
- if (mHeadsUp != null) {
- // whoops, we're on already!
- showNotification(mHeadsUp);
- }
-
- getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- }
-
-
- @Override
- protected void onDetachedFromWindow() {
- mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
- if (mClock.currentTimeMillis() < mStartTouchTime) {
- return true;
- }
- mTouched = true;
- return mEdgeSwipeHelper.onInterceptTouchEvent(ev)
- || mSwipeHelper.onInterceptTouchEvent(ev)
- || mHeadsUp == null // lingering
- || super.onInterceptTouchEvent(ev);
- }
-
- // View methods
-
- @Override
- public void onDraw(android.graphics.Canvas c) {
- super.onDraw(c);
- if (DEBUG) {
- //Log.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
- // + getMeasuredHeight() + "px");
- c.save();
- c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
- android.graphics.Region.Op.DIFFERENCE);
- c.drawColor(0xFFcc00cc);
- c.restore();
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (mClock.currentTimeMillis() < mStartTouchTime) {
- return false;
- }
-
- final boolean wasRemoved = mHeadsUp == null;
- if (!wasRemoved) {
- mBar.scheduleHeadsUpDecay(mHeadsUpNotificationDecay);
- }
- return mEdgeSwipeHelper.onTouchEvent(ev)
- || mSwipeHelper.onTouchEvent(ev)
- || wasRemoved
- || super.onTouchEvent(ev);
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- float densityScale = getResources().getDisplayMetrics().density;
- mSwipeHelper.setDensityScale(densityScale);
- float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
- mSwipeHelper.setPagingTouchSlop(pagingTouchSlop);
- }
-
- // ExpandHelper.Callback methods
-
- @Override
- public ExpandableView getChildAtRawPosition(float x, float y) {
- return getChildAtPosition(x, y);
- }
-
- @Override
- public ExpandableView getChildAtPosition(float x, float y) {
- return mHeadsUp == null ? null : mHeadsUp.row;
- }
-
- @Override
- public boolean canChildBeExpanded(View v) {
- return mHeadsUp != null && mHeadsUp.row == v && mHeadsUp.row.isExpandable();
- }
-
- @Override
- public void setUserExpandedChild(View v, boolean userExpanded) {
- if (mHeadsUp != null && mHeadsUp.row == v) {
- mHeadsUp.row.setUserExpanded(userExpanded);
- }
- }
-
- @Override
- public void setUserLockedChild(View v, boolean userLocked) {
- if (mHeadsUp != null && mHeadsUp.row == v) {
- mHeadsUp.row.setUserLocked(userLocked);
- }
- }
-
- @Override
- public void expansionStateChanged(boolean isExpanding) {
-
- }
-
- // SwipeHelper.Callback methods
-
- @Override
- public boolean canChildBeDismissed(View v) {
- return true;
- }
-
- @Override
- public boolean isAntiFalsingNeeded() {
- return false;
- }
-
- @Override
- public float getFalsingThresholdFactor() {
- return 1.0f;
- }
-
- @Override
- public void onChildDismissed(View v) {
- Log.v(TAG, "User swiped heads up to dismiss");
- if (mHeadsUp != null && mHeadsUp.notification.isClearable()) {
- mBar.onNotificationClear(mHeadsUp.notification);
- mHeadsUp = null;
- }
- releaseImmediately();
- }
-
- @Override
- public void onBeginDrag(View v) {
- }
-
- @Override
- public void onDragCancelled(View v) {
- mContentHolder.setAlpha(mMaxAlpha); // sometimes this isn't quite reset
- }
-
- @Override
- public void onChildSnappedBack(View animView) {
- }
-
- @Override
- public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
- getBackground().setAlpha((int) (255 * swipeProgress));
- return false;
- }
-
- @Override
- public View getChildAtPosition(MotionEvent ev) {
- return mContentHolder;
- }
-
- @Override
- public View getChildContentView(View v) {
- return mContentHolder;
- }
-
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- mContentHolder.getLocationOnScreen(mTmpTwoArray);
-
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(mTmpTwoArray[0], mTmpTwoArray[1],
- mTmpTwoArray[0] + mContentHolder.getWidth(),
- mTmpTwoArray[1] + mContentHolder.getHeight());
- }
-
- public void escalate() {
- mBar.scheduleHeadsUpEscalation();
- }
-
- public String getKey() {
- return mHeadsUp == null ? null : mHeadsUp.notification.getKey();
- }
-
- public void setUser(int user) {
- mUser = user;
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("HeadsUpNotificationView state:");
- pw.print(" mTouchSensitivityDelay="); pw.println(mTouchSensitivityDelay);
- pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
- pw.print(" mLingerUntilMs="); pw.println(mLingerUntilMs);
- pw.print(" mTouched="); pw.println(mTouched);
- pw.print(" mMostRecentPackageName="); pw.println(mMostRecentPackageName);
- pw.print(" mStartTouchTime="); pw.println(mStartTouchTime);
- pw.print(" now="); pw.println(SystemClock.elapsedRealtime());
- pw.print(" mUser="); pw.println(mUser);
- if (mHeadsUp == null) {
- pw.println(" mHeadsUp=null");
- } else {
- pw.print(" mHeadsUp="); pw.println(mHeadsUp.notification.getKey());
- }
- int N = mSnoozedPackages.size();
- pw.println(" snoozed packages: " + N);
- for (int i = 0; i < N; i++) {
- pw.print(" "); pw.print(mSnoozedPackages.valueAt(i));
- pw.print(", "); pw.println(mSnoozedPackages.keyAt(i));
- }
- }
-
- public static class EdgeSwipeHelper implements Gefingerpoken {
- private static final boolean DEBUG_EDGE_SWIPE = false;
- private final float mTouchSlop;
- private final HeadsUpNotificationView mHeadsUpView;
- private boolean mConsuming;
- private float mFirstY;
- private float mFirstX;
-
- public EdgeSwipeHelper(HeadsUpNotificationView headsUpView, float touchSlop) {
- mHeadsUpView = headsUpView;
- mTouchSlop = touchSlop;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- if (DEBUG_EDGE_SWIPE) Log.d(TAG, "action down " + ev.getY());
- mFirstX = ev.getX();
- mFirstY = ev.getY();
- mConsuming = false;
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (DEBUG_EDGE_SWIPE) Log.d(TAG, "action move " + ev.getY());
- final float dY = ev.getY() - mFirstY;
- final float daX = Math.abs(ev.getX() - mFirstX);
- final float daY = Math.abs(dY);
- if (!mConsuming && daX < daY && daY > mTouchSlop) {
- mHeadsUpView.snooze();
- if (dY > 0) {
- if (DEBUG_EDGE_SWIPE) Log.d(TAG, "found an open");
- mHeadsUpView.getBar().animateExpandNotificationsPanel();
- }
- mConsuming = true;
- }
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (DEBUG_EDGE_SWIPE) Log.d(TAG, "action done");
- mConsuming = false;
- break;
- }
- return mConsuming;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return mConsuming;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 8e677f1..824ba94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -18,8 +18,10 @@ package com.android.systemui.statusbar.stack;
import android.view.View;
import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.ArrayList;
+import java.util.TreeMap;
/**
* A global state to track all input states for the algorithm.
@@ -34,6 +36,12 @@ public class AmbientState {
private int mSpeedBumpIndex = -1;
private boolean mDark;
private boolean mHideSensitive;
+ private HeadsUpManager mHeadsUpManager;
+ private float mPaddingOffset;
+ private int mLayoutHeight;
+ private int mTopPadding;
+ private boolean mShadeExpanded;
+ private float mMaxHeadsUpTranslation;
public int getScrollY() {
return mScrollY;
@@ -115,4 +123,56 @@ public class AmbientState {
public void setSpeedBumpIndex(int speedBumpIndex) {
mSpeedBumpIndex = speedBumpIndex;
}
+
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ mHeadsUpManager = headsUpManager;
+ }
+
+ public TreeMap<String, HeadsUpManager.HeadsUpEntry> getHeadsUpEntries() {
+ return mHeadsUpManager.getEntries();
+ }
+
+ public float getPaddingOffset() {
+ return mPaddingOffset;
+ }
+
+ public void setPaddingOffset(float paddingOffset) {
+ mPaddingOffset = paddingOffset;
+ }
+
+ public int getLayoutHeight() {
+ return mLayoutHeight;
+ }
+
+ public void setLayoutHeight(int layoutHeight) {
+ mLayoutHeight = layoutHeight;
+ }
+
+ public int getTopPadding() {
+ return mTopPadding;
+ }
+
+ public void setTopPadding(int topPadding) {
+ mTopPadding = topPadding;
+ }
+
+ public int getInnerHeight() {
+ return mLayoutHeight - mTopPadding;
+ }
+
+ public boolean isShadeExpanded() {
+ return mShadeExpanded;
+ }
+
+ public void setShadeExpanded(boolean shadeExpanded) {
+ mShadeExpanded = shadeExpanded;
+ }
+
+ public void setMaxHeadsUpTranslation(float maxHeadsUpTranslation) {
+ mMaxHeadsUpTranslation = maxHeadsUpTranslation;
+ }
+
+ public float getMaxHeadsUpTranslation() {
+ return mMaxHeadsUpTranslation;
+ }
}
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 2eafd57..cac1b8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -24,6 +24,7 @@ import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pair;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -47,6 +48,7 @@ import com.android.systemui.statusbar.StackScrollerDecorView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import java.util.ArrayList;
@@ -179,19 +181,21 @@ public class NotificationStackScrollLayout extends ViewGroup
private float mMinTopOverScrollToEscape;
private int mIntrinsicPadding;
private int mNotificationTopPadding;
+ private float mPaddingOffset;
private float mTopPaddingOverflow;
private boolean mDontReportNextOverScroll;
private boolean mRequestViewResizeAnimationOnLayout;
private boolean mNeedViewResizeAnimation;
private View mExpandedGroupView;
- private boolean mEverythingNeedsAnimation;
+ private boolean mEverythingNeedsAnimation;
/**
* The maximum scrollPosition which we are allowed to reach when a notification was expanded.
* This is needed to avoid scrolling too far after the notification was collapsed in the same
* motion.
*/
private int mMaxScrollAfterExpand;
+
private SwipeHelper.LongPressListener mLongPressListener;
/**
@@ -203,8 +207,8 @@ public class NotificationStackScrollLayout extends ViewGroup
private boolean mInterceptDelegateEnabled;
private boolean mDelegateToScrollView;
private boolean mDisallowScrollingInThisMotion;
- private long mGoToFullShadeDelay;
+ private long mGoToFullShadeDelay;
private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
= new ViewTreeObserver.OnPreDrawListener() {
@Override
@@ -219,6 +223,11 @@ public class NotificationStackScrollLayout extends ViewGroup
private int[] mTempInt2 = new int[2];
private boolean mGenerateChildOrderChangedEvent;
private boolean mRemoveAnimationEnabled;
+ private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
+ private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
+ = new HashSet<>();
+ private HeadsUpManager mHeadsUpManager;
+ private boolean mTrackingHeadsUp;
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -404,8 +413,8 @@ public class NotificationStackScrollLayout extends ViewGroup
}
private void updateAlgorithmHeightAndPadding() {
- mStackScrollAlgorithm.setLayoutHeight(getLayoutHeight());
- mStackScrollAlgorithm.setTopPadding(mTopPadding);
+ mAmbientState.setLayoutHeight(getLayoutHeight());
+ mAmbientState.setTopPadding(mTopPadding);
}
/**
@@ -478,9 +487,13 @@ public class NotificationStackScrollLayout extends ViewGroup
int newStackHeight = (int) height;
int minStackHeight = getMinStackHeight();
int stackHeight;
- if (newStackHeight - mTopPadding - mTopPaddingOverflow >= minStackHeight
+ float paddingOffset;
+ boolean trackingHeadsUp = mTrackingHeadsUp;
+ int normalExpandPositionStart = trackingHeadsUp ? mHeadsUpManager.getTopHeadsUpHeight()
+ : minStackHeight;
+ if (newStackHeight - mTopPadding - mTopPaddingOverflow >= normalExpandPositionStart
|| getNotGoneChildCount() == 0) {
- setTranslationY(mTopPaddingOverflow);
+ paddingOffset = mTopPaddingOverflow;
stackHeight = newStackHeight;
} else {
@@ -492,9 +505,13 @@ public class NotificationStackScrollLayout extends ViewGroup
float partiallyThere = (newStackHeight - mTopPadding - mTopPaddingOverflow)
/ minStackHeight;
partiallyThere = Math.max(0, partiallyThere);
- translationY += (1 - partiallyThere) * (mBottomStackPeekSize +
- mCollapseSecondCardPadding);
- setTranslationY(translationY - mTopPadding);
+ if (!trackingHeadsUp) {
+ translationY += (1 - partiallyThere) * (mBottomStackPeekSize +
+ mCollapseSecondCardPadding);
+ } else {
+ translationY = (int) (height - mHeadsUpManager.getTopHeadsUpHeight());
+ }
+ paddingOffset = translationY - mTopPadding;
stackHeight = (int) (height - (translationY - mTopPadding));
}
if (stackHeight != mCurrentStackHeight) {
@@ -502,6 +519,19 @@ public class NotificationStackScrollLayout extends ViewGroup
updateAlgorithmHeightAndPadding();
requestChildrenUpdate();
}
+ setPaddingOffset(paddingOffset);
+ }
+
+ public float getPaddingOffset() {
+ return mPaddingOffset;
+ }
+
+ private void setPaddingOffset(float paddingOffset) {
+ if (paddingOffset != mPaddingOffset) {
+ mPaddingOffset = paddingOffset;
+ mAmbientState.setPaddingOffset(paddingOffset);
+ requestChildrenUpdate();
+ }
}
/**
@@ -543,11 +573,6 @@ public class NotificationStackScrollLayout extends ViewGroup
if (mDismissAllInProgress) {
return;
}
- if (DEBUG) Log.v(TAG, "onChildDismissed: " + v);
- final View veto = v.findViewById(R.id.veto);
- if (veto != null && veto.getVisibility() != View.GONE) {
- veto.performClick();
- }
setSwipingInProgress(false);
if (mDragAnimPendingChildren.contains(v)) {
// We start the swipe and finish it in the same frame, we don't want any animation
@@ -556,6 +581,17 @@ public class NotificationStackScrollLayout extends ViewGroup
}
mSwipedOutViews.add(v);
mAmbientState.onDragFinished(v);
+ if (v instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (row.isHeadsUp()) {
+ mHeadsUpManager.addSwipedOutKey(row.getStatusBarNotification().getKey());
+ }
+ }
+ final View veto = v.findViewById(R.id.veto);
+ if (veto != null && veto.getVisibility() != View.GONE) {
+ veto.performClick();
+ }
+ if (DEBUG) Log.v(TAG, "onChildDismissed: " + v);
}
@Override
@@ -578,11 +614,6 @@ public class NotificationStackScrollLayout extends ViewGroup
return false;
}
- @Override
- public float getFalsingThresholdFactor() {
- return mPhoneStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
- }
-
public void onBeginDrag(View v) {
setSwipingInProgress(true);
mAmbientState.onBeginDrag(v);
@@ -597,6 +628,11 @@ public class NotificationStackScrollLayout extends ViewGroup
setSwipingInProgress(false);
}
+ @Override
+ public float getFalsingThresholdFactor() {
+ return mPhoneStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
+ }
+
public View getChildAtPosition(MotionEvent ev) {
return getChildAtPosition(ev.getX(), ev.getY());
}
@@ -1803,10 +1839,21 @@ public class NotificationStackScrollLayout extends ViewGroup
generateGoToFullShadeEvent();
generateViewResizeEvent();
generateGroupExpansionEvent();
+ generateHeadsUpAnimationEvents();
generateAnimateEverythingEvent();
mNeedsAnimation = false;
}
+ private void generateHeadsUpAnimationEvents() {
+ for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
+ int type = eventPair.second ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR
+ : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
+ mAnimationEvents.add(new AnimationEvent(eventPair.first,
+ type));
+ }
+ mHeadsUpChangeAnimations.clear();
+ }
+
private void generateGroupExpansionEvent() {
// Generate a group expansion/collapsing event if there is such a group at all
if (mExpandedGroupView != null) {
@@ -2182,6 +2229,10 @@ public class NotificationStackScrollLayout extends ViewGroup
public void onChildAnimationFinished() {
requestChildrenUpdate();
+ for (Runnable runnable : mAnimationFinishedRunnables) {
+ runnable.run();
+ }
+ mAnimationFinishedRunnables.clear();
}
/**
@@ -2579,6 +2630,36 @@ public class NotificationStackScrollLayout extends ViewGroup
}
}
+ public void performOnAnimationFinished(Runnable runnable) {
+ mAnimationFinishedRunnables.add(runnable);
+ }
+
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ mHeadsUpManager = headsUpManager;
+ mAmbientState.setHeadsUpManager(headsUpManager);
+ }
+
+ public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
+ if (mAnimationsEnabled) {
+ mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
+ mNeedsAnimation = true;
+ requestChildrenUpdate();
+ }
+ }
+
+ public void setShadeExpanded(boolean shadeExpanded) {
+ mAmbientState.setShadeExpanded(shadeExpanded);
+ }
+
+ public void setMaxHeadsUpTranslation(int maxTranslation) {
+ mAmbientState.setMaxHeadsUpTranslation(maxTranslation);
+ requestChildrenUpdate();
+ }
+
+ public void setTrackingHeadsUp(boolean trackingHeadsUp) {
+ mTrackingHeadsUp = trackingHeadsUp;
+ }
+
/**
* A listener that is notified when some child locations might have changed.
*/
@@ -2723,6 +2804,22 @@ public class NotificationStackScrollLayout extends ViewGroup
.animateY()
.animateZ(),
+ // ANIMATION_TYPE_HEADS_UP_APPEAR
+ new AnimationFilter()
+ .animateAlpha()
+ .animateHeight()
+ .animateTopInset()
+ .animateY()
+ .animateZ(),
+
+ // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
+ new AnimationFilter()
+ .animateAlpha()
+ .animateHeight()
+ .animateTopInset()
+ .animateY()
+ .animateZ(),
+
// ANIMATION_TYPE_EVERYTHING
new AnimationFilter()
.animateAlpha()
@@ -2780,6 +2877,12 @@ public class NotificationStackScrollLayout extends ViewGroup
// ANIMATION_TYPE_GROUP_EXPANSION_CHANGED
StackStateAnimator.ANIMATION_DURATION_EXPAND_CLICKED,
+ // ANIMATION_TYPE_HEADS_UP_APPEAR
+ StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR,
+
+ // ANIMATION_TYPE_HEADS_UP_DISAPPEAR
+ StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
+
// ANIMATION_TYPE_EVERYTHING
StackStateAnimator.ANIMATION_DURATION_STANDARD,
};
@@ -2798,7 +2901,9 @@ public class NotificationStackScrollLayout extends ViewGroup
static final int ANIMATION_TYPE_HIDE_SENSITIVE = 11;
static final int ANIMATION_TYPE_VIEW_RESIZE = 12;
static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
- static final int ANIMATION_TYPE_EVERYTHING = 14;
+ static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
+ static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
+ static final int ANIMATION_TYPE_EVERYTHING = 16;
static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index e7bf47b..b0f287f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -25,9 +25,11 @@ import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.ArrayList;
import java.util.List;
+import java.util.TreeMap;
/**
* The Algorithm of the {@link com.android.systemui.statusbar.stack
@@ -54,11 +56,6 @@ public class StackScrollAlgorithm {
private StackIndentationFunctor mTopStackIndentationFunctor;
private StackIndentationFunctor mBottomStackIndentationFunctor;
- private int mLayoutHeight;
-
- /** mLayoutHeight - mTopPadding */
- private int mInnerHeight;
- private int mTopPadding;
private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
private boolean mIsExpansionChanging;
private int mFirstChildMaxHeight;
@@ -157,13 +154,13 @@ public class StackScrollAlgorithm {
scrollY = Math.max(0, scrollY);
algorithmState.scrollY = (int) (scrollY + mCollapsedSize + bottomOverScroll);
- updateVisibleChildren(resultState, algorithmState);
+ updateVisibleChildren(resultState, algorithmState, ambientState);
// Phase 1:
- findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState);
+ findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState, ambientState);
// Phase 2:
- updatePositionsForState(resultState, algorithmState);
+ updatePositionsForState(resultState, algorithmState, ambientState);
// Phase 3:
updateZValuesForState(resultState, algorithmState);
@@ -329,23 +326,30 @@ public class StackScrollAlgorithm {
* Update the visible children on the state.
*/
private void updateVisibleChildren(StackScrollState resultState,
- StackScrollAlgorithmState state) {
+ StackScrollAlgorithmState state, AmbientState ambientState) {
ViewGroup hostView = resultState.getHostView();
int childCount = hostView.getChildCount();
state.visibleChildren.clear();
state.visibleChildren.ensureCapacity(childCount);
int notGoneIndex = 0;
+ TreeMap<String, HeadsUpManager.HeadsUpEntry> headsUpEntries =
+ ambientState.getHeadsUpEntries();
+ for (String key: headsUpEntries.keySet()) {
+ ExpandableView v = headsUpEntries.get(key).entry.row;
+ notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
+ }
for (int i = 0; i < childCount; i++) {
ExpandableView v = (ExpandableView) hostView.getChildAt(i);
if (v.getVisibility() != View.GONE) {
- StackViewState viewState = resultState.getViewStateForView(v);
- viewState.notGoneIndex = notGoneIndex;
- state.visibleChildren.add(v);
- notGoneIndex++;
- // handle the notgoneIndex for the children as well
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (row.isHeadsUp()) {
+ continue;
+ }
+ notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
+
+ // handle the notgoneIndex for the children as well
List<ExpandableNotificationRow> children =
row.getNotificationChildren();
if (row.areChildrenExpanded() && children != null) {
@@ -358,22 +362,35 @@ public class StackScrollAlgorithm {
}
}
}
+ } else {
+ notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
}
}
}
}
+ private int updateNotGoneIndex(StackScrollState resultState,
+ StackScrollAlgorithmState state, int notGoneIndex,
+ ExpandableView v) {
+ StackViewState viewState = resultState.getViewStateForView(v);
+ viewState.notGoneIndex = notGoneIndex;
+ state.visibleChildren.add(v);
+ notGoneIndex++;
+ return notGoneIndex;
+ }
+
/**
* Determine the positions for the views. This is the main part of the algorithm.
*
- * @param resultState The result state to update if a change to the properties of a child occurs
+ * @param resultState The result state to update if a change to the properties of a child occurs
* @param algorithmState The state in which the current pass of the algorithm is currently in
+ * @param ambientState The current ambient state
*/
private void updatePositionsForState(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState) {
+ StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
// The starting position of the bottom stack peek
- float bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
+ float bottomPeekStart = ambientState.getInnerHeight() - mBottomStackPeekSize;
// The position where the bottom stack starts.
float bottomStackStart = bottomPeekStart - mBottomStackSlowDownLength;
@@ -427,7 +444,8 @@ public class StackScrollAlgorithm {
bottomPeekStart, childViewState.yTranslation, childViewState,
childHeight);
}
- clampPositionToBottomStackStart(childViewState, childViewState.height);
+ clampPositionToBottomStackStart(childViewState, childViewState.height,
+ ambientState);
} else if (nextYPosition >= bottomStackStart) {
// Case 2:
// We are in the bottom stack.
@@ -435,7 +453,7 @@ public class StackScrollAlgorithm {
// According to the regular scroll view we are fully translated out of the
// bottom of the screen so we are fully in the bottom stack
updateStateForChildFullyInBottomStack(algorithmState,
- bottomStackStart, childViewState, childHeight);
+ bottomStackStart, childViewState, childHeight, ambientState);
} else {
// According to the regular scroll view we are currently translating out of /
// into the bottom of the screen
@@ -447,7 +465,7 @@ public class StackScrollAlgorithm {
// Case 3:
// We are in the regular scroll area.
childViewState.location = StackViewState.LOCATION_MAIN_AREA;
- clampYTranslation(childViewState, childHeight);
+ clampYTranslation(childViewState, childHeight, ambientState);
}
// The first card is always rendered.
@@ -468,7 +486,44 @@ public class StackScrollAlgorithm {
currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
yPositionInScrollView = yPositionInScrollViewAfterElement;
- childViewState.yTranslation += mTopPadding;
+ childViewState.yTranslation += ambientState.getTopPadding()
+ + ambientState.getPaddingOffset();
+
+ updateHeadsUpStates(resultState, algorithmState, ambientState);
+ }
+ }
+
+ private void updateHeadsUpStates(StackScrollState resultState,
+ StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+ TreeMap<String, HeadsUpManager.HeadsUpEntry> headsUpEntries =
+ ambientState.getHeadsUpEntries();
+ boolean hasPinnedHeadsUp = false;
+ for (String key: headsUpEntries.keySet()) {
+ ExpandableNotificationRow row = headsUpEntries.get(key).entry.row;
+ StackViewState childState = resultState.getViewStateForView(row);
+ if (!row.isInShade()) {
+ childState.yTranslation = Math.max(childState.yTranslation, 0);
+ hasPinnedHeadsUp = true;
+ }
+ childState.height = Math.max(childState.height, row.getHeadsUpHeight());
+ childState.yTranslation = Math.min(childState.yTranslation,
+ ambientState.getMaxHeadsUpTranslation() - childState.height);
+ }
+ if (hasPinnedHeadsUp && !ambientState.isShadeExpanded()) {
+ // Let's hide all normal views
+ int childCount = algorithmState.visibleChildren.size();
+ for (int i = 0; i < childCount; i++) {
+ ExpandableView child = algorithmState.visibleChildren.get(i);
+ StackViewState state = resultState.getViewStateForView(child);
+ boolean hideView = true;
+ if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ hideView = !row.isHeadsUp();
+ }
+ if (hideView) {
+ state.alpha = 0.0f;
+ }
+ }
}
}
@@ -478,8 +533,9 @@ public class StackScrollAlgorithm {
* @param childViewState the view state of the child
* @param childHeight the height of this child
*/
- private void clampYTranslation(StackViewState childViewState, int childHeight) {
- clampPositionToBottomStackStart(childViewState, childHeight);
+ private void clampYTranslation(StackViewState childViewState, int childHeight,
+ AmbientState ambientState) {
+ clampPositionToBottomStackStart(childViewState, childHeight, ambientState);
clampPositionToTopStackEnd(childViewState, childHeight);
}
@@ -491,9 +547,10 @@ public class StackScrollAlgorithm {
* @param childHeight the height of this child
*/
private void clampPositionToBottomStackStart(StackViewState childViewState,
- int childHeight) {
+ int childHeight, AmbientState ambientState) {
childViewState.yTranslation = Math.min(childViewState.yTranslation,
- mInnerHeight - mBottomStackPeekSize - mCollapseSecondCardPadding - childHeight);
+ ambientState.getInnerHeight() - mBottomStackPeekSize - mCollapseSecondCardPadding
+ - childHeight);
}
/**
@@ -548,8 +605,7 @@ public class StackScrollAlgorithm {
private void updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState,
float transitioningPositionStart, StackViewState childViewState,
- int childHeight) {
-
+ int childHeight, AmbientState ambientState) {
float currentYPosition;
algorithmState.itemsInBottomStack += 1.0f;
if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) {
@@ -567,7 +623,7 @@ public class StackScrollAlgorithm {
childViewState.alpha = 1.0f - algorithmState.partialInBottom;
}
childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_HIDDEN;
- currentYPosition = mInnerHeight;
+ currentYPosition = ambientState.getInnerHeight();
}
childViewState.yTranslation = currentYPosition - childHeight;
clampPositionToTopStackEnd(childViewState, childHeight);
@@ -629,7 +685,7 @@ public class StackScrollAlgorithm {
* @param algorithmState The state in which the current pass of the algorithm is currently in
*/
private void findNumberOfItemsInTopStackAndUpdateState(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState) {
+ StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
// The y Position if the element would be in a regular scrollView
float yPositionInScrollView = 0.0f;
@@ -647,7 +703,7 @@ public class StackScrollAlgorithm {
if (i == 0 && algorithmState.scrollY <= mCollapsedSize) {
// The starting position of the bottom stack peek
- int bottomPeekStart = mInnerHeight - mBottomStackPeekSize -
+ int bottomPeekStart = ambientState.getInnerHeight() - mBottomStackPeekSize -
mCollapseSecondCardPadding;
// Collapse and expand the first child while the shade is being expanded
float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding
@@ -744,21 +800,6 @@ public class StackScrollAlgorithm {
}
}
- public void setLayoutHeight(int layoutHeight) {
- this.mLayoutHeight = layoutHeight;
- updateInnerHeight();
- }
-
- public void setTopPadding(int topPadding) {
- mTopPadding = topPadding;
- updateInnerHeight();
- }
-
- private void updateInnerHeight() {
- mInnerHeight = mLayoutHeight - mTopPadding;
- }
-
-
/**
* Update whether the device is very small, i.e. Notifications can be in both the top and the
* bottom stack at the same time
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 b249fbf..9640b84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -45,6 +45,8 @@ public class StackStateAnimator {
public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
public static final int ANIMATION_DURATION_EXPAND_CLICKED = 360;
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
+ public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 280;
+ public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 220;
public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
public static final int ANIMATION_DELAY_PER_ELEMENT_EXPAND_CHILDREN = 54;
public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
@@ -90,6 +92,7 @@ public class StackStateAnimator {
private ValueAnimator mTopOverScrollAnimator;
private ValueAnimator mBottomOverScrollAnimator;
private ExpandableNotificationRow mChildExpandingView;
+ private StackViewState mTmpState = new StackViewState();
public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
mHostLayout = hostLayout;
@@ -828,6 +831,13 @@ public class StackStateAnimator {
ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
row.prepareExpansionChanged(finalState);
mChildExpandingView = row;
+ } else if (event.animationType == NotificationStackScrollLayout
+ .AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
+ // This item is added, initialize it's properties.
+ StackViewState viewState = finalState.getViewStateForView(changingView);
+ mTmpState.copyFrom(viewState);
+ mTmpState.yTranslation = -mTmpState.height;
+ finalState.applyState(changingView, mTmpState);
}
mNewEvents.add(event);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index c272e48..78122d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -123,19 +123,7 @@ public class TvStatusBar extends BaseStatusBar {
}
@Override
- public void scheduleHeadsUpDecay(long delay) {
- }
-
- @Override
- public void scheduleHeadsUpOpen() {
- }
-
- @Override
- public void scheduleHeadsUpEscalation() {
- }
-
- @Override
- public void scheduleHeadsUpClose() {
+ public void escalateHeadsUp() {
}
@Override