Merge "Don't let the heads up close too quickly."
This commit is contained in:
@@ -146,6 +146,9 @@
|
||||
before the app can interrupt again. -->
|
||||
<integer name="heads_up_default_snooze_length_ms">60000</integer>
|
||||
|
||||
<!-- Minimum display time for a heads up notification, in milliseconds. -->
|
||||
<integer name="heads_up_notification_minimum_time">3000</integer>
|
||||
|
||||
<!-- milliseconds before the heads up notification accepts touches. -->
|
||||
<integer name="heads_up_sensitivity_delay">700</integer>
|
||||
|
||||
|
||||
@@ -127,7 +127,6 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
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 int MSG_DECAY_HEADS_UP = 1031;
|
||||
|
||||
protected static final boolean ENABLE_HEADS_UP = true;
|
||||
// scores above this threshold should be displayed in heads up mode.
|
||||
@@ -1153,7 +1152,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public abstract void resetHeadsUpDecayTimer();
|
||||
public abstract void scheduleHeadsUpDecay(long delay);
|
||||
|
||||
public abstract void scheduleHeadsUpOpen();
|
||||
|
||||
@@ -1353,8 +1352,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
|
||||
PendingIntent contentIntent = sbn.getNotification().contentIntent;
|
||||
if (contentIntent != null) {
|
||||
final View.OnClickListener listener = makeClicker(contentIntent, sbn.getKey(),
|
||||
isHeadsUp);
|
||||
final View.OnClickListener listener = makeClicker(contentIntent, sbn.getKey());
|
||||
row.setOnClickListener(listener);
|
||||
} else {
|
||||
row.setOnClickListener(null);
|
||||
@@ -1515,20 +1513,17 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
return true;
|
||||
}
|
||||
|
||||
public NotificationClicker makeClicker(PendingIntent intent, String notificationKey,
|
||||
boolean forHun) {
|
||||
return new NotificationClicker(intent, notificationKey, forHun);
|
||||
public NotificationClicker makeClicker(PendingIntent intent, String notificationKey) {
|
||||
return new NotificationClicker(intent, notificationKey);
|
||||
}
|
||||
|
||||
protected class NotificationClicker implements View.OnClickListener {
|
||||
private PendingIntent mIntent;
|
||||
private final String mNotificationKey;
|
||||
private boolean mIsHeadsUp;
|
||||
|
||||
public NotificationClicker(PendingIntent intent, String notificationKey, boolean forHun) {
|
||||
public NotificationClicker(PendingIntent intent, String notificationKey) {
|
||||
mIntent = intent;
|
||||
mNotificationKey = notificationKey;
|
||||
mIsHeadsUp = forHun;
|
||||
}
|
||||
|
||||
public void onClick(final View v) {
|
||||
@@ -1541,12 +1536,12 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
mCurrentUserId);
|
||||
dismissKeyguardThenExecute(new OnDismissAction() {
|
||||
public boolean onDismiss() {
|
||||
if (mIsHeadsUp) {
|
||||
if (mNotificationKey.equals(mHeadsUpNotificationView.getKey())) {
|
||||
// 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.releaseAndClose();
|
||||
mHeadsUpNotificationView.releaseImmediately();
|
||||
}
|
||||
new Thread() {
|
||||
@Override
|
||||
@@ -1893,7 +1888,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
&& oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
|
||||
|
||||
final boolean shouldInterrupt = shouldInterrupt(notification);
|
||||
final boolean alertAgain = alertAgain(oldEntry, n);
|
||||
final boolean alertAgain = shouldInterrupt && alertAgain(oldEntry, n);
|
||||
boolean updateSuccessful = false;
|
||||
if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
|
||||
&& publicUnchanged) {
|
||||
@@ -1916,14 +1911,12 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
}
|
||||
|
||||
if (wasHeadsUp) {
|
||||
if (shouldInterrupt) {
|
||||
updateHeadsUpViews(oldEntry, notification);
|
||||
if (alertAgain) {
|
||||
resetHeadsUpDecayTimer();
|
||||
}
|
||||
} else {
|
||||
// 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.releaseAndClose();
|
||||
mHeadsUpNotificationView.release();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@@ -1946,23 +1939,19 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
if (!updateSuccessful) {
|
||||
if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
|
||||
if (wasHeadsUp) {
|
||||
if (shouldInterrupt) {
|
||||
if (DEBUG) Log.d(TAG, "rebuilding heads up for key: " + key);
|
||||
Entry newEntry = new Entry(notification, null);
|
||||
ViewGroup holder = mHeadsUpNotificationView.getHolder();
|
||||
if (inflateViewsForHeadsUp(newEntry, holder)) {
|
||||
mHeadsUpNotificationView.showNotification(newEntry);
|
||||
if (alertAgain) {
|
||||
resetHeadsUpDecayTimer();
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Couldn't create new updated headsup for package "
|
||||
+ contentView.getPackage());
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "rebuilding heads up for key: " + key);
|
||||
Entry newEntry = new Entry(notification, null);
|
||||
ViewGroup holder = mHeadsUpNotificationView.getHolder();
|
||||
if (inflateViewsForHeadsUp(newEntry, holder)) {
|
||||
mHeadsUpNotificationView.updateNotification(newEntry, 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;
|
||||
mHeadsUpNotificationView.releaseAndClose();
|
||||
mHeadsUpNotificationView.release();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@@ -2032,8 +2021,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
// update the contentIntent
|
||||
final PendingIntent contentIntent = notification.getNotification().contentIntent;
|
||||
if (contentIntent != null) {
|
||||
final View.OnClickListener listener = makeClicker(contentIntent, notification.getKey(),
|
||||
isHeadsUp);
|
||||
final View.OnClickListener listener = makeClicker(contentIntent, notification.getKey());
|
||||
entry.row.setOnClickListener(listener);
|
||||
} else {
|
||||
entry.row.setOnClickListener(null);
|
||||
|
||||
@@ -361,7 +361,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
if (!mUseHeadsUp) {
|
||||
Log.d(TAG, "dismissing any existing heads up notification on disable event");
|
||||
setHeadsUpVisibility(false);
|
||||
mHeadsUpNotificationView.release();
|
||||
mHeadsUpNotificationView.releaseImmediately();
|
||||
removeHeadsUpView();
|
||||
} else {
|
||||
addHeadsUpView();
|
||||
@@ -1212,33 +1212,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
setAreThereNotifications();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetHeadsUpDecayTimer() {
|
||||
mHandler.removeMessages(MSG_DECAY_HEADS_UP);
|
||||
if (mUseHeadsUp && mHeadsUpNotificationDecay > 0
|
||||
&& mHeadsUpNotificationView.isClearable()) {
|
||||
mHandler.sendEmptyMessageDelayed(MSG_DECAY_HEADS_UP, mHeadsUpNotificationDecay);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleHeadsUpOpen() {
|
||||
mHandler.removeMessages(MSG_SHOW_HEADS_UP);
|
||||
mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleHeadsUpClose() {
|
||||
mHandler.removeMessages(MSG_HIDE_HEADS_UP);
|
||||
mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleHeadsUpEscalation() {
|
||||
mHandler.removeMessages(MSG_ESCALATE_HEADS_UP);
|
||||
mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateNotificationRanking(RankingMap ranking) {
|
||||
mNotificationData.updateRanking(ranking);
|
||||
@@ -1247,9 +1220,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
|
||||
@Override
|
||||
public void removeNotification(String key, RankingMap ranking) {
|
||||
if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null
|
||||
&& key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) {
|
||||
mHeadsUpNotificationView.clear();
|
||||
if (ENABLE_HEADS_UP) {
|
||||
mHeadsUpNotificationView.removeNotification(key);
|
||||
}
|
||||
|
||||
StatusBarNotification old = removeNotificationViews(key, ranking);
|
||||
@@ -1870,16 +1842,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
case MSG_SHOW_HEADS_UP:
|
||||
setHeadsUpVisibility(true);
|
||||
break;
|
||||
case MSG_DECAY_HEADS_UP:
|
||||
mHeadsUpNotificationView.release();
|
||||
setHeadsUpVisibility(false);
|
||||
break;
|
||||
case MSG_HIDE_HEADS_UP:
|
||||
mHeadsUpNotificationView.release();
|
||||
setHeadsUpVisibility(false);
|
||||
break;
|
||||
case MSG_ESCALATE_HEADS_UP:
|
||||
escalateHeadsUp();
|
||||
case MSG_HIDE_HEADS_UP:
|
||||
mHeadsUpNotificationView.releaseImmediately();
|
||||
setHeadsUpVisibility(false);
|
||||
break;
|
||||
case MSG_LAUNCH_TRANSITION_TIMEOUT:
|
||||
@@ -1889,11 +1855,41 @@ 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.release();
|
||||
mHeadsUpNotificationView.releaseImmediately();
|
||||
final Notification notification = sbn.getNotification();
|
||||
if (notification.fullScreenIntent != null) {
|
||||
if (DEBUG)
|
||||
@@ -2734,10 +2730,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
public void onHeadsUpDismissed() {
|
||||
mHeadsUpNotificationView.dismiss();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload some of our resources when the configuration changes.
|
||||
*
|
||||
@@ -2772,7 +2764,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
|
||||
mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
|
||||
|
||||
mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay);
|
||||
mRowMinHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
|
||||
mRowMaxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ import android.graphics.Rect;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.Settings;
|
||||
import android.util.ArrayMap;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Rect;
|
||||
import android.os.SystemClock;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
@@ -36,6 +39,7 @@ 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;
|
||||
@@ -58,6 +62,9 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
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;
|
||||
@@ -68,6 +75,7 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
|
||||
private PhoneStatusBar mBar;
|
||||
|
||||
private long mLingerUntilMs;
|
||||
private long mStartTouchTime;
|
||||
private ViewGroup mContentHolder;
|
||||
private int mSnoozeLengthMs;
|
||||
@@ -76,6 +84,14 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
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);
|
||||
@@ -89,6 +105,24 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
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() {
|
||||
@@ -104,90 +138,141 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
mBar = bar;
|
||||
}
|
||||
|
||||
public PhoneStatusBar getBar() {
|
||||
return mBar;
|
||||
}
|
||||
|
||||
public ViewGroup getHolder() {
|
||||
return mContentHolder;
|
||||
}
|
||||
|
||||
public boolean showNotification(NotificationData.Entry headsUp) {
|
||||
if (mHeadsUp != null && headsUp != null && !mHeadsUp.key.equals(headsUp.key)) {
|
||||
/**
|
||||
* 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
|
||||
release();
|
||||
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 (alert) {
|
||||
mBar.scheduleHeadsUpDecay(mHeadsUpNotificationDecay);
|
||||
}
|
||||
invalidate();
|
||||
|
||||
if (mHeadsUp == headsUp) {
|
||||
// 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();
|
||||
mHeadsUp.row.setSystemExpanded(true);
|
||||
mHeadsUp.row.setSensitive(false);
|
||||
mHeadsUp.row.setHeadsUp(true);
|
||||
mHeadsUp.row.setHideSensitive(
|
||||
false, false /* animated */, 0 /* delay */, 0 /* duration */);
|
||||
if (mContentHolder == null) {
|
||||
// too soon!
|
||||
return false;
|
||||
if (mHeadsUp.row != null) { // only null in tests
|
||||
mHeadsUp.row.setSystemExpanded(true);
|
||||
mHeadsUp.row.setSensitive(false);
|
||||
mHeadsUp.row.setHeadsUp(true);
|
||||
mHeadsUp.row.setHideSensitive(
|
||||
false, false /* animated */, 0 /* delay */, 0 /* duration */);
|
||||
}
|
||||
mContentHolder.setX(0);
|
||||
mContentHolder.setVisibility(View.VISIBLE);
|
||||
mContentHolder.setAlpha(mMaxAlpha);
|
||||
mContentHolder.addView(mHeadsUp.row);
|
||||
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
|
||||
|
||||
mSwipeHelper.snapChild(mContentHolder, 1f);
|
||||
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();
|
||||
|
||||
// 2. Animate mHeadsUpNotificationView in
|
||||
// Make sure the heads up window is open.
|
||||
mBar.scheduleHeadsUpOpen();
|
||||
|
||||
// 3. Set alarm to age the notification off
|
||||
mBar.resetHeadsUpDecayTimer();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
mBar.displayNotificationFromHeadsUp(mHeadsUp.notification);
|
||||
}
|
||||
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 isShowing(String key) {
|
||||
return mHeadsUp != null && mHeadsUp.key.equals(key);
|
||||
}
|
||||
|
||||
/** Discard the Heads Up notification. */
|
||||
public void clear() {
|
||||
mHeadsUp = null;
|
||||
mBar.scheduleHeadsUpClose();
|
||||
}
|
||||
|
||||
/** Respond to dismissal of the Heads Up window. */
|
||||
public void dismiss() {
|
||||
if (mHeadsUp == null) return;
|
||||
if (mHeadsUp.notification.isClearable()) {
|
||||
mBar.onNotificationClear(mHeadsUp.notification);
|
||||
} else {
|
||||
release();
|
||||
}
|
||||
mHeadsUp = null;
|
||||
mBar.scheduleHeadsUpClose();
|
||||
}
|
||||
|
||||
/** Push any current Heads Up notification down into the shade. */
|
||||
public void release() {
|
||||
if (mHeadsUp != null) {
|
||||
mBar.displayNotificationFromHeadsUp(mHeadsUp.notification);
|
||||
}
|
||||
mHeadsUp = null;
|
||||
}
|
||||
|
||||
public boolean isSnoozed(String packageName) {
|
||||
final String key = snoozeKey(packageName, mUser);
|
||||
Long snoozedUntil = mSnoozedPackages.get(key);
|
||||
@@ -206,16 +291,15 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
mSnoozedPackages.put(snoozeKey(mMostRecentPackageName, mUser),
|
||||
SystemClock.elapsedRealtime() + mSnoozeLengthMs);
|
||||
}
|
||||
releaseAndClose();
|
||||
releaseImmediately();
|
||||
}
|
||||
|
||||
private static String snoozeKey(String packageName, int user) {
|
||||
return user + "," + packageName;
|
||||
}
|
||||
|
||||
public void releaseAndClose() {
|
||||
release();
|
||||
mBar.scheduleHeadsUpClose();
|
||||
public boolean isShowing(String key) {
|
||||
return mHeadsUp != null && mHeadsUp.key.equals(key);
|
||||
}
|
||||
|
||||
public NotificationData.Entry getEntry() {
|
||||
@@ -228,19 +312,19 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
|
||||
// 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();
|
||||
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());
|
||||
}
|
||||
};
|
||||
// Apply padding to shadow.
|
||||
outline.setRect(outlineLeft, outlineTop,
|
||||
view.getWidth() - outlineLeft - view.getPaddingRight(),
|
||||
view.getHeight() - outlineTop - view.getPaddingBottom());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
@@ -248,7 +332,7 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
float touchSlop = viewConfiguration.getScaledTouchSlop();
|
||||
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, getContext());
|
||||
mSwipeHelper.setMaxSwipeProgress(mMaxAlpha);
|
||||
mEdgeSwipeHelper = new EdgeSwipeHelper(touchSlop);
|
||||
mEdgeSwipeHelper = new EdgeSwipeHelper(this, touchSlop);
|
||||
|
||||
int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
|
||||
int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
|
||||
@@ -282,6 +366,7 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
|
||||
@@ -290,11 +375,13 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
|
||||
if (SystemClock.elapsedRealtime() < mStartTouchTime) {
|
||||
if (mClock.currentTimeMillis() < mStartTouchTime) {
|
||||
return true;
|
||||
}
|
||||
mTouched = true;
|
||||
return mEdgeSwipeHelper.onInterceptTouchEvent(ev)
|
||||
|| mSwipeHelper.onInterceptTouchEvent(ev)
|
||||
|| mHeadsUp == null // lingering
|
||||
|| super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
@@ -316,12 +403,17 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
if (SystemClock.elapsedRealtime() < mStartTouchTime) {
|
||||
if (mClock.currentTimeMillis() < mStartTouchTime) {
|
||||
return false;
|
||||
}
|
||||
mBar.resetHeadsUpDecayTimer();
|
||||
|
||||
final boolean wasRemoved = mHeadsUp == null;
|
||||
if (!wasRemoved) {
|
||||
mBar.scheduleHeadsUpDecay(mHeadsUpNotificationDecay);
|
||||
}
|
||||
return mEdgeSwipeHelper.onTouchEvent(ev)
|
||||
|| mSwipeHelper.onTouchEvent(ev)
|
||||
|| wasRemoved
|
||||
|| super.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
@@ -390,7 +482,11 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
@Override
|
||||
public void onChildDismissed(View v) {
|
||||
Log.v(TAG, "User swiped heads up to dismiss");
|
||||
mBar.onHeadsUpDismissed();
|
||||
if (mHeadsUp != null && mHeadsUp.notification.isClearable()) {
|
||||
mBar.onNotificationClear(mHeadsUp.notification);
|
||||
mHeadsUp = null;
|
||||
}
|
||||
releaseImmediately();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -448,6 +544,8 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
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());
|
||||
@@ -465,14 +563,16 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
}
|
||||
}
|
||||
|
||||
private class EdgeSwipeHelper implements Gefingerpoken {
|
||||
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(float touchSlop) {
|
||||
public EdgeSwipeHelper(HeadsUpNotificationView headsUpView, float touchSlop) {
|
||||
mHeadsUpView = headsUpView;
|
||||
mTouchSlop = touchSlop;
|
||||
}
|
||||
|
||||
@@ -492,10 +592,10 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
final float daX = Math.abs(ev.getX() - mFirstX);
|
||||
final float daY = Math.abs(dY);
|
||||
if (!mConsuming && daX < daY && daY > mTouchSlop) {
|
||||
snooze();
|
||||
mHeadsUpView.snooze();
|
||||
if (dY > 0) {
|
||||
if (DEBUG_EDGE_SWIPE) Log.d(TAG, "found an open");
|
||||
mBar.animateExpandNotificationsPanel();
|
||||
mHeadsUpView.getBar().animateExpandNotificationsPanel();
|
||||
}
|
||||
mConsuming = true;
|
||||
}
|
||||
@@ -503,7 +603,7 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
if (DEBUG_EDGE_SWIPE) Log.d(TAG, "action done" );
|
||||
if (DEBUG_EDGE_SWIPE) Log.d(TAG, "action done");
|
||||
mConsuming = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ public class TvStatusBar extends BaseStatusBar {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetHeadsUpDecayTimer() {
|
||||
public void scheduleHeadsUpDecay(long delay) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.systemui;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
/**
|
||||
* Base class that does System UI specific setup.
|
||||
*/
|
||||
public class SysuiTestCase extends AndroidTestCase {
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
// Mockito stuff.
|
||||
System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath());
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.systemui.statusbar.policy;
|
||||
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.os.*;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import com.android.systemui.SwipeHelper;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.NotificationData;
|
||||
import com.android.systemui.statusbar.phone.PhoneStatusBar;
|
||||
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Test the Heads Up Notification.
|
||||
*
|
||||
* Specifically the policy that a notificaiton must remain visibile for a minimum period of time.
|
||||
*/
|
||||
public class HeadsUpNotificationTest extends SysuiTestCase {
|
||||
private static final String TAG = "HeadsUpNotificationTest";
|
||||
|
||||
private static int TOUCH_SENSITIVITY = 100;
|
||||
private static int NOTIFICATION_DECAY = 10000;
|
||||
private static int MINIMUM_DISPLAY_TIME = 3000;
|
||||
private static int SNOOZE_TIME = 60000;
|
||||
private static long TOO_SOON = 1000L; // less than MINIMUM_DISPLAY_TIME
|
||||
private static long LATER = 5000L; // more than MINIMUM_DISPLAY_TIME
|
||||
private static long REMAINING_VISIBILITY = MINIMUM_DISPLAY_TIME - TOO_SOON;
|
||||
|
||||
protected HeadsUpNotificationView mHeadsUp;
|
||||
|
||||
@Mock protected PhoneStatusBar mMockStatusBar;
|
||||
@Mock private HeadsUpNotificationView.Clock mClock;
|
||||
@Mock private SwipeHelper mMockSwipeHelper;
|
||||
@Mock private HeadsUpNotificationView.EdgeSwipeHelper mMockEdgeSwipeHelper;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mHeadsUp = new HeadsUpNotificationView(mContext,
|
||||
mClock, mMockSwipeHelper, mMockEdgeSwipeHelper,
|
||||
NOTIFICATION_DECAY, MINIMUM_DISPLAY_TIME, TOUCH_SENSITIVITY, SNOOZE_TIME);
|
||||
mHeadsUp.setBar(mMockStatusBar);
|
||||
}
|
||||
|
||||
private NotificationData.Entry makeNotification(String key) {
|
||||
StatusBarNotification sbn = mock(StatusBarNotification.class);
|
||||
when(sbn.getKey()).thenReturn(key);
|
||||
return new NotificationData.Entry(sbn, null);
|
||||
}
|
||||
|
||||
public void testPostAndDecay() {
|
||||
NotificationData.Entry a = makeNotification("a");
|
||||
mHeadsUp.showNotification(a);
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
|
||||
Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpOpen();
|
||||
ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
|
||||
Mockito.verify(mMockStatusBar).scheduleHeadsUpDecay(decayArg.capture());
|
||||
// New notification gets a full decay time.
|
||||
assertEquals(NOTIFICATION_DECAY, (long) decayArg.getValue());
|
||||
}
|
||||
|
||||
public void testPostAndDeleteTooSoon() {
|
||||
when(mClock.currentTimeMillis()).thenReturn(0L);
|
||||
NotificationData.Entry a = makeNotification("a");
|
||||
mHeadsUp.showNotification(a);
|
||||
reset(mMockStatusBar);
|
||||
|
||||
when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
|
||||
mHeadsUp.removeNotification(a.key);
|
||||
ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
|
||||
Mockito.verify(mMockStatusBar).scheduleHeadsUpDecay(decayArg.capture());
|
||||
// Leave the window up for the balance of the minumum time.
|
||||
assertEquals(REMAINING_VISIBILITY, (long) decayArg.getValue());
|
||||
}
|
||||
|
||||
public void testPostAndDeleteLater() {
|
||||
when(mClock.currentTimeMillis()).thenReturn(0L);
|
||||
NotificationData.Entry a = makeNotification("a");
|
||||
mHeadsUp.showNotification(a);
|
||||
reset(mMockStatusBar);
|
||||
|
||||
when(mClock.currentTimeMillis()).thenReturn(LATER);
|
||||
mHeadsUp.removeNotification(a.key);
|
||||
// Delete closes immediately if the minimum time window is satisfied.
|
||||
Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpClose();
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpDecay(anyInt());
|
||||
}
|
||||
|
||||
// This is a bad test. It should not care that there is a call to scheduleHeadsUpClose(),
|
||||
// but it happens that there will be one, so it is important that it happen before the
|
||||
// call to scheduleHeadsUpOpen(), so that the final state is open.
|
||||
// Maybe mMockStatusBar should instead be a fake that tracks the open/closed state.
|
||||
public void testPostAndReplaceTooSoon() {
|
||||
InOrder callOrder = inOrder(mMockStatusBar);
|
||||
when(mClock.currentTimeMillis()).thenReturn(0L);
|
||||
NotificationData.Entry a = makeNotification("a");
|
||||
mHeadsUp.showNotification(a);
|
||||
reset(mMockStatusBar);
|
||||
|
||||
when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
|
||||
NotificationData.Entry b = makeNotification("b");
|
||||
mHeadsUp.showNotification(b);
|
||||
Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpClose();
|
||||
ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
|
||||
Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpDecay(decayArg.capture());
|
||||
// New notification gets a full decay time.
|
||||
assertEquals(NOTIFICATION_DECAY, (long) decayArg.getValue());
|
||||
|
||||
// Make sure close was called before open, so that the heads up stays open.
|
||||
callOrder.verify(mMockStatusBar).scheduleHeadsUpClose();
|
||||
callOrder.verify(mMockStatusBar).scheduleHeadsUpOpen();
|
||||
}
|
||||
|
||||
public void testPostAndUpdateAlertAgain() {
|
||||
when(mClock.currentTimeMillis()).thenReturn(0L);
|
||||
NotificationData.Entry a = makeNotification("a");
|
||||
mHeadsUp.showNotification(a);
|
||||
reset(mMockStatusBar);
|
||||
|
||||
when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
|
||||
mHeadsUp.updateNotification(a, true);
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
|
||||
ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
|
||||
Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpDecay(decayArg.capture());
|
||||
// Alert again gets a full decay time.
|
||||
assertEquals(NOTIFICATION_DECAY, (long) decayArg.getValue());
|
||||
}
|
||||
|
||||
public void testPostAndUpdateAlertAgainFastFail() {
|
||||
when(mClock.currentTimeMillis()).thenReturn(0L);
|
||||
NotificationData.Entry a = makeNotification("a");
|
||||
mHeadsUp.showNotification(a);
|
||||
reset(mMockStatusBar);
|
||||
|
||||
when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
|
||||
NotificationData.Entry a_prime = makeNotification("a");
|
||||
mHeadsUp.updateNotification(a_prime, true);
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
|
||||
ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
|
||||
Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpDecay(decayArg.capture());
|
||||
// Alert again gets a full decay time.
|
||||
assertEquals(NOTIFICATION_DECAY, (long) decayArg.getValue());
|
||||
}
|
||||
|
||||
public void testPostAndUpdateNoAlertAgain() {
|
||||
when(mClock.currentTimeMillis()).thenReturn(0L);
|
||||
NotificationData.Entry a = makeNotification("a");
|
||||
mHeadsUp.showNotification(a);
|
||||
reset(mMockStatusBar);
|
||||
|
||||
when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
|
||||
mHeadsUp.updateNotification(a, false);
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpDecay(anyInt());
|
||||
}
|
||||
|
||||
public void testPostAndUpdateNoAlertAgainFastFail() {
|
||||
when(mClock.currentTimeMillis()).thenReturn(0L);
|
||||
NotificationData.Entry a = makeNotification("a");
|
||||
mHeadsUp.showNotification(a);
|
||||
reset(mMockStatusBar);
|
||||
|
||||
when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
|
||||
NotificationData.Entry a_prime = makeNotification("a");
|
||||
mHeadsUp.updateNotification(a_prime, false);
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpDecay(anyInt());
|
||||
}
|
||||
|
||||
public void testPostAndUpdateLowPriorityTooSoon() {
|
||||
when(mClock.currentTimeMillis()).thenReturn(0L);
|
||||
NotificationData.Entry a = makeNotification("a");
|
||||
mHeadsUp.showNotification(a);
|
||||
reset(mMockStatusBar);
|
||||
|
||||
when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
|
||||
mHeadsUp.release();
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
|
||||
ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
|
||||
Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpDecay(decayArg.capture());
|
||||
// Down grade on update leaves the window up for the balance of the minumum time.
|
||||
assertEquals(REMAINING_VISIBILITY, (long) decayArg.getValue());
|
||||
}
|
||||
|
||||
public void testPostAndUpdateLowPriorityTooSoonFastFail() {
|
||||
when(mClock.currentTimeMillis()).thenReturn(0L);
|
||||
NotificationData.Entry a = makeNotification("a");
|
||||
mHeadsUp.showNotification(a);
|
||||
reset(mMockStatusBar);
|
||||
|
||||
when(mClock.currentTimeMillis()).thenReturn(TOO_SOON);
|
||||
NotificationData.Entry a_prime = makeNotification("a");
|
||||
mHeadsUp.updateNotification(a_prime, false);
|
||||
mHeadsUp.release();
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpClose();
|
||||
ArgumentCaptor<Long> decayArg = ArgumentCaptor.forClass(Long.class);
|
||||
Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpDecay(decayArg.capture());
|
||||
// Down grade on update leaves the window up for the balance of the minumum time.
|
||||
assertEquals(REMAINING_VISIBILITY, (long) decayArg.getValue());
|
||||
}
|
||||
|
||||
public void testPostAndUpdateLowPriorityLater() {
|
||||
when(mClock.currentTimeMillis()).thenReturn(0L);
|
||||
NotificationData.Entry a = makeNotification("a");
|
||||
mHeadsUp.showNotification(a);
|
||||
reset(mMockStatusBar);
|
||||
|
||||
when(mClock.currentTimeMillis()).thenReturn(LATER);
|
||||
mHeadsUp.release();
|
||||
// Down grade on update closes immediately if the minimum time window is satisfied.
|
||||
Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpClose();
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpDecay(anyInt());
|
||||
}
|
||||
|
||||
public void testPostAndUpdateLowPriorityLaterFastFail() {
|
||||
when(mClock.currentTimeMillis()).thenReturn(0L);
|
||||
NotificationData.Entry a = makeNotification("a");
|
||||
mHeadsUp.showNotification(a);
|
||||
reset(mMockStatusBar);
|
||||
|
||||
when(mClock.currentTimeMillis()).thenReturn(LATER);
|
||||
NotificationData.Entry a_prime = makeNotification("a");
|
||||
mHeadsUp.updateNotification(a_prime, false);
|
||||
mHeadsUp.release();
|
||||
// Down grade on update closes immediately if the minimum time window is satisfied.
|
||||
Mockito.verify(mMockStatusBar, times(1)).scheduleHeadsUpClose();
|
||||
Mockito.verify(mMockStatusBar, never()).scheduleHeadsUpDecay(anyInt());
|
||||
}
|
||||
}
|
||||
@@ -29,11 +29,11 @@ import android.telephony.SignalStrength;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.telephony.IccCardConstants;
|
||||
import com.android.internal.telephony.cdma.EriInfo;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
|
||||
import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
|
||||
import com.android.systemui.statusbar.policy.NetworkControllerImpl.SignalCluster;
|
||||
@@ -46,7 +46,7 @@ import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NetworkControllerBaseTest extends AndroidTestCase {
|
||||
public class NetworkControllerBaseTest extends SysuiTestCase {
|
||||
private static final String TAG = "NetworkControllerBaseTest";
|
||||
protected static final int DEFAULT_LEVEL = 2;
|
||||
protected static final int DEFAULT_SIGNAL_STRENGTH =
|
||||
@@ -76,9 +76,6 @@ public class NetworkControllerBaseTest extends AndroidTestCase {
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
// Mockito stuff.
|
||||
System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath());
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
|
||||
mMockWm = mock(WifiManager.class);
|
||||
mMockTm = mock(TelephonyManager.class);
|
||||
|
||||
Reference in New Issue
Block a user