allow rich notifications in the heads up.
new shouldInterrupt logic: screen on, not locked, not dreaming, and priority above HIGH and noisy, or has fullscreen intent draft of API allowing devs to give hints about head up display reuse inflateViews() add an expand helper to the heads up space move some things into Entry for reuse don't allow touches in first second delay decay if touched make decay time a resource add a custom viewgroup for notification rows to get view management out of the NotificationData class. Change-Id: I36464f110cfa0dabc3f35db7db6c35c27e8ee2ba
This commit is contained in:
@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
>
|
||||
<scale
|
||||
<translate
|
||||
android:interpolator="@android:interpolator/overshoot"
|
||||
android:fromXScale="0.7" android:toXScale="1.0"
|
||||
android:fromYScale="0.7" android:toYScale="1.0"
|
||||
android:pivotX="50%" android:pivotY="50%"
|
||||
android:fromYDelta="-50%" android:toYDelta="0"
|
||||
android:duration="@android:integer/config_shortAnimTime" />
|
||||
<alpha
|
||||
android:interpolator="@android:interpolator/decelerate_quad"
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 272 B After Width: | Height: | Size: 500 B |
@ -24,6 +24,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@*android:dimen/status_bar_height"
|
||||
>
|
||||
<FrameLayout
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -1,4 +1,5 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.android.systemui.statusbar.ExpandableNotificationRow
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
@ -62,4 +63,4 @@
|
||||
android:padding="2dp"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
</com.android.systemui.statusbar.ExpandableNotificationRow>
|
||||
|
@ -101,5 +101,11 @@
|
||||
|
||||
<!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
|
||||
<bool name="config_show4GForLTE">true</bool>
|
||||
|
||||
<!-- milliseconds before the heads up notification auto-dismisses. -->
|
||||
<integer name="heads_up_notification_decay">3700</integer>
|
||||
|
||||
<!-- milliseconds before the heads up notification accepts touches. -->
|
||||
<integer name="heads_up_sensitivity_delay">700</integer>
|
||||
</resources>
|
||||
|
||||
|
@ -16,8 +16,5 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<item type="id" name="expandable_tag" />
|
||||
<item type="id" name="user_expanded_tag" />
|
||||
<item type="id" name="user_lock_tag" />
|
||||
<item type="id" name="status_bar_cling_stub" />
|
||||
</resources>
|
||||
|
@ -38,8 +38,8 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
|
||||
View getChildAtRawPosition(float x, float y);
|
||||
View getChildAtPosition(float x, float y);
|
||||
boolean canChildBeExpanded(View v);
|
||||
boolean setUserExpandedChild(View v, boolean userExpanded);
|
||||
boolean setUserLockedChild(View v, boolean userLocked);
|
||||
void setUserExpandedChild(View v, boolean userExpanded);
|
||||
void setUserLockedChild(View v, boolean userLocked);
|
||||
}
|
||||
|
||||
private static final String TAG = "ExpandHelper";
|
||||
@ -181,7 +181,6 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
|
||||
* @param callback the container that holds the items to be manipulated
|
||||
* @param small the smallest allowable size for the manuipulated items.
|
||||
* @param large the largest allowable size for the manuipulated items.
|
||||
* @param scoller if non-null also manipulate the scroll position to obey the gravity.
|
||||
*/
|
||||
public ExpandHelper(Context context, Callback callback, int small, int large) {
|
||||
mSmallSize = small;
|
||||
|
@ -19,6 +19,7 @@ package com.android.systemui.statusbar;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerNative;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.TaskStackBuilder;
|
||||
import android.content.BroadcastReceiver;
|
||||
@ -91,7 +92,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
|
||||
protected static final boolean ENABLE_HEADS_UP = true;
|
||||
// scores above this threshold should be displayed in heads up mode.
|
||||
private static final int INTERRUPTION_THRESHOLD = 10;
|
||||
private static final int INTERRUPTION_THRESHOLD = 11;
|
||||
|
||||
// Should match the value in PhoneWindowManager
|
||||
public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
|
||||
@ -108,7 +109,8 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
protected NotificationData mNotificationData = new NotificationData();
|
||||
protected NotificationRowLayout mPile;
|
||||
|
||||
protected StatusBarNotification mCurrentlyInterruptingNotification;
|
||||
protected NotificationData.Entry mInterruptingNotificationEntry;
|
||||
protected long mInterruptingNotificationTime;
|
||||
|
||||
// used to notify status bar for suppressing notification LED
|
||||
protected boolean mPanelSlightlyVisible;
|
||||
@ -127,6 +129,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
protected IDreamManager mDreamManager;
|
||||
KeyguardManager mKeyguardManager;
|
||||
PowerManager mPowerManager;
|
||||
protected int mRowHeight;
|
||||
|
||||
// UI-specific methods
|
||||
|
||||
@ -432,7 +435,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
}
|
||||
}
|
||||
|
||||
public void dismissHeadsUp() {
|
||||
public void onHeadsUpDismissed() {
|
||||
// pass
|
||||
}
|
||||
|
||||
@ -558,6 +561,8 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void resetHeadsUpDecayTimer();
|
||||
|
||||
protected class H extends Handler {
|
||||
public void handleMessage(Message m) {
|
||||
Intent intent;
|
||||
@ -615,7 +620,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
protected void workAroundBadLayerDrawableOpacity(View v) {
|
||||
}
|
||||
|
||||
protected boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
|
||||
public boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
|
||||
int minHeight =
|
||||
mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height);
|
||||
int maxHeight =
|
||||
@ -630,7 +635,8 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
// create the row view
|
||||
LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) inflater.inflate(
|
||||
R.layout.status_bar_notification_row, parent, false);
|
||||
|
||||
// for blaming (see SwipeHelper.setLongPressListener)
|
||||
row.setTag(sbn.getPackageName());
|
||||
@ -697,6 +703,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
}
|
||||
}
|
||||
entry.row = row;
|
||||
entry.row.setRowHeight(mRowHeight);
|
||||
entry.content = content;
|
||||
entry.expanded = contentViewLocal;
|
||||
entry.setBigContentView(bigContentViewLocal);
|
||||
@ -851,33 +858,18 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
return iconView;
|
||||
}
|
||||
|
||||
protected boolean expandView(NotificationData.Entry entry, boolean expand) {
|
||||
int rowHeight =
|
||||
mContext.getResources().getDimensionPixelSize(R.dimen.notification_row_min_height);
|
||||
ViewGroup.LayoutParams lp = entry.row.getLayoutParams();
|
||||
if (entry.expandable() && expand) {
|
||||
if (DEBUG) Log.d(TAG, "setting expanded row height to WRAP_CONTENT");
|
||||
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "setting collapsed row height to " + rowHeight);
|
||||
lp.height = rowHeight;
|
||||
}
|
||||
entry.row.setLayoutParams(lp);
|
||||
return expand;
|
||||
}
|
||||
|
||||
protected void updateExpansionStates() {
|
||||
int N = mNotificationData.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
NotificationData.Entry entry = mNotificationData.get(i);
|
||||
if (!entry.userLocked()) {
|
||||
if (!entry.row.isUserLocked()) {
|
||||
if (i == (N-1)) {
|
||||
if (DEBUG) Log.d(TAG, "expanding top notification at " + i);
|
||||
expandView(entry, true);
|
||||
entry.row.setExpanded(true);
|
||||
} else {
|
||||
if (!entry.userExpanded()) {
|
||||
if (!entry.row.isUserExpanded()) {
|
||||
if (DEBUG) Log.d(TAG, "collapsing notification at " + i);
|
||||
expandView(entry, false);
|
||||
entry.row.setExpanded(false);
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "ignoring user-modified notification at " + i);
|
||||
}
|
||||
@ -997,13 +989,13 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
|
||||
if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
|
||||
if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
|
||||
final boolean wasExpanded = oldEntry.userExpanded();
|
||||
final boolean wasExpanded = oldEntry.row.isUserExpanded();
|
||||
removeNotificationViews(key);
|
||||
addNotificationViews(key, notification);
|
||||
if (wasExpanded) {
|
||||
final NotificationData.Entry newEntry = mNotificationData.findByKey(key);
|
||||
expandView(newEntry, true);
|
||||
newEntry.setUserExpanded(true);
|
||||
newEntry.row.setExpanded(true);
|
||||
newEntry.row.setUserExpanded(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1026,7 +1018,8 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
|
||||
|
||||
// See if we need to update the heads up.
|
||||
if (ENABLE_HEADS_UP && oldNotification == mCurrentlyInterruptingNotification) {
|
||||
if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null
|
||||
&& oldNotification == mInterruptingNotificationEntry.notification) {
|
||||
if (DEBUG) Log.d(TAG, "updating the current heads up:" + notification);
|
||||
// XXX: this is a hack for Alarms. The real implementation will need to *update*
|
||||
// the heads up.
|
||||
@ -1037,15 +1030,28 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean shouldInterrupt(StatusBarNotification notification) {
|
||||
boolean interrupt = notification.getNotification().fullScreenIntent == null
|
||||
&& notification.getScore() >= INTERRUPTION_THRESHOLD
|
||||
&& mPowerManager.isScreenOn() && !mKeyguardManager.isKeyguardLocked();
|
||||
protected boolean shouldInterrupt(StatusBarNotification sbn) {
|
||||
Notification notification = sbn.getNotification();
|
||||
// some predicates to make the boolean logic legible
|
||||
boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
|
||||
|| (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
|
||||
|| notification.sound != null
|
||||
|| notification.vibrate != null;
|
||||
boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
|
||||
boolean isFullscreen = notification.fullScreenIntent != null;
|
||||
boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
|
||||
Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
|
||||
|
||||
boolean interrupt = (isFullscreen || (isHighPriority && isNoisy))
|
||||
&& isAllowed
|
||||
&& mPowerManager.isScreenOn()
|
||||
&& !mKeyguardManager.isKeyguardLocked();
|
||||
try {
|
||||
interrupt = interrupt && !mDreamManager.isDreaming();
|
||||
} catch (RemoteException e) {
|
||||
Log.d(TAG, "failed to query dream manager", e);
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
|
||||
return interrupt;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
public class ExpandableNotificationRow extends FrameLayout {
|
||||
private int mRowHeight;
|
||||
|
||||
/** does this row contain layouts that can adapt to row expansion */
|
||||
private boolean mExpandable;
|
||||
/** has the user manually expanded this row */
|
||||
private boolean mUserExpanded;
|
||||
/** is the user touching this row */
|
||||
private boolean mUserLocked;
|
||||
|
||||
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public int getRowHeight() {
|
||||
return mRowHeight;
|
||||
}
|
||||
|
||||
public void setRowHeight(int rowHeight) {
|
||||
this.mRowHeight = rowHeight;
|
||||
}
|
||||
|
||||
public boolean isExpandable() {
|
||||
return mExpandable;
|
||||
}
|
||||
|
||||
public void setExpandable(boolean expandable) {
|
||||
mExpandable = expandable;
|
||||
}
|
||||
|
||||
public boolean isUserExpanded() {
|
||||
return mUserExpanded;
|
||||
}
|
||||
|
||||
public void setUserExpanded(boolean userExpanded) {
|
||||
mUserExpanded = userExpanded;
|
||||
}
|
||||
|
||||
public boolean isUserLocked() {
|
||||
return mUserLocked;
|
||||
}
|
||||
|
||||
public void setUserLocked(boolean userLocked) {
|
||||
mUserLocked = userLocked;
|
||||
}
|
||||
|
||||
public void setExpanded(boolean expand) {
|
||||
ViewGroup.LayoutParams lp = getLayoutParams();
|
||||
if (expand && mExpandable) {
|
||||
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
} else {
|
||||
lp.height = mRowHeight;
|
||||
}
|
||||
setLayoutParams(lp);
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ public class NotificationData {
|
||||
public IBinder key;
|
||||
public StatusBarNotification notification;
|
||||
public StatusBarIconView icon;
|
||||
public View row; // the outer expanded view
|
||||
public ExpandableNotificationRow row; // the outer expanded view
|
||||
public View content; // takes the click events and sends the PendingIntent
|
||||
public View expanded; // the inflated RemoteViews
|
||||
public ImageView largeIcon;
|
||||
@ -47,40 +47,16 @@ public class NotificationData {
|
||||
}
|
||||
public void setBigContentView(View bigContentView) {
|
||||
this.expandedBig = bigContentView;
|
||||
writeBooleanTag(row, R.id.expandable_tag, bigContentView != null);
|
||||
row.setExpandable(bigContentView != null);
|
||||
}
|
||||
public View getBigContentView() {
|
||||
return expandedBig;
|
||||
}
|
||||
/**
|
||||
* Return whether the entry can be expanded.
|
||||
*/
|
||||
public boolean expandable() {
|
||||
return NotificationData.getIsExpandable(row);
|
||||
}
|
||||
/**
|
||||
* Return whether the entry has been manually expanded by the user.
|
||||
*/
|
||||
public boolean userExpanded() {
|
||||
return NotificationData.getUserExpanded(row);
|
||||
}
|
||||
/**
|
||||
* Set the flag indicating that this was manually expanded by the user.
|
||||
*/
|
||||
public boolean setUserExpanded(boolean userExpanded) {
|
||||
return NotificationData.setUserExpanded(row, userExpanded);
|
||||
}
|
||||
/**
|
||||
* Return whether the entry is being touched by the user.
|
||||
*/
|
||||
public boolean userLocked() {
|
||||
return NotificationData.getUserLocked(row);
|
||||
}
|
||||
/**
|
||||
* Set the flag indicating that this is being touched by the user.
|
||||
*/
|
||||
public boolean setUserLocked(boolean userLocked) {
|
||||
return NotificationData.setUserLocked(row, userLocked);
|
||||
public void setUserLocked(boolean userLocked) {
|
||||
row.setUserLocked(userLocked);
|
||||
}
|
||||
}
|
||||
private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
|
||||
@ -125,8 +101,8 @@ public class NotificationData {
|
||||
return i;
|
||||
}
|
||||
|
||||
public int add(IBinder key, StatusBarNotification notification, View row, View content,
|
||||
View expanded, StatusBarIconView icon) {
|
||||
public int add(IBinder key, StatusBarNotification notification, ExpandableNotificationRow row,
|
||||
View content, View expanded, StatusBarIconView icon) {
|
||||
Entry entry = new Entry();
|
||||
entry.key = key;
|
||||
entry.notification = notification;
|
||||
@ -171,55 +147,4 @@ public class NotificationData {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static boolean readBooleanTag(View view, int id) {
|
||||
if (view != null) {
|
||||
Object value = view.getTag(id);
|
||||
return value != null && value instanceof Boolean && ((Boolean) value).booleanValue();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static boolean writeBooleanTag(View view, int id, boolean value) {
|
||||
if (view != null) {
|
||||
view.setTag(id, Boolean.valueOf(value));
|
||||
return value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the entry can be expanded.
|
||||
*/
|
||||
public static boolean getIsExpandable(View row) {
|
||||
return readBooleanTag(row, R.id.expandable_tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the entry has been manually expanded by the user.
|
||||
*/
|
||||
public static boolean getUserExpanded(View row) {
|
||||
return readBooleanTag(row, R.id.user_expanded_tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the entry has been manually expanded by the user.
|
||||
*/
|
||||
public static boolean setUserExpanded(View row, boolean userExpanded) {
|
||||
return writeBooleanTag(row, R.id.user_expanded_tag, userExpanded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the entry is being touched by the user.
|
||||
*/
|
||||
public static boolean getUserLocked(View row) {
|
||||
return readBooleanTag(row, R.id.user_lock_tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the entry is being touched by the user.
|
||||
*/
|
||||
public static boolean setUserLocked(View row, boolean userLocked) {
|
||||
return writeBooleanTag(row, R.id.user_lock_tag, userLocked);
|
||||
}
|
||||
}
|
||||
|
@ -122,9 +122,6 @@ public class PhoneStatusBar extends BaseStatusBar {
|
||||
private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
|
||||
// 1020-1030 reserved for BaseStatusBar
|
||||
|
||||
// will likely move to a resource or other tunable param at some point
|
||||
private static final int HEADS_UP_DECAY_MS = 0; // disabled, was 10000;
|
||||
|
||||
private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
|
||||
|
||||
private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
|
||||
@ -226,6 +223,7 @@ public class PhoneStatusBar extends BaseStatusBar {
|
||||
|
||||
// for heads up notifications
|
||||
private HeadsUpNotificationView mHeadsUpNotificationView;
|
||||
private int mHeadsUpNotificationDecay;
|
||||
|
||||
// on-screen navigation buttons
|
||||
private NavigationBarView mNavigationBarView = null;
|
||||
@ -829,7 +827,7 @@ public class PhoneStatusBar extends BaseStatusBar {
|
||||
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
|
||||
WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL, // above the status bar!
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
@ -837,8 +835,8 @@ public class PhoneStatusBar extends BaseStatusBar {
|
||||
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
|
||||
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
|
||||
lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
|
||||
//lp.y += height * 1.5; // FIXME
|
||||
lp.setTitle("Heads Up");
|
||||
lp.packageName = mContext.getPackageName();
|
||||
lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
|
||||
@ -890,26 +888,19 @@ public class PhoneStatusBar extends BaseStatusBar {
|
||||
if (mUseHeadsUp && shouldInterrupt(notification)) {
|
||||
if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
|
||||
// 1. Populate mHeadsUpNotificationView
|
||||
mInterruptingNotificationTime = System.currentTimeMillis();
|
||||
mInterruptingNotificationEntry = new Entry(key, notification, null);
|
||||
|
||||
// bind the click event to the content area
|
||||
PendingIntent contentIntent = notification.getNotification().contentIntent;
|
||||
final View.OnClickListener listener = (contentIntent != null)
|
||||
? new NotificationClicker(contentIntent,
|
||||
notification.getPackageName(), notification.getTag(), notification.getId())
|
||||
: null;
|
||||
|
||||
if (mHeadsUpNotificationView.applyContent(notification.getNotification(), listener)) {
|
||||
|
||||
mCurrentlyInterruptingNotification = notification;
|
||||
|
||||
if (inflateViews(mInterruptingNotificationEntry,
|
||||
mHeadsUpNotificationView.getHolder())) {
|
||||
mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry);
|
||||
// 2. Animate mHeadsUpNotificationView in
|
||||
mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
|
||||
|
||||
// 3. Set alarm to age the notification off (TODO)
|
||||
mHandler.removeMessages(MSG_HIDE_HEADS_UP);
|
||||
if (HEADS_UP_DECAY_MS > 0) {
|
||||
mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, HEADS_UP_DECAY_MS);
|
||||
}
|
||||
// 3. Set alarm to age the notification off
|
||||
resetHeadsUpDecayTimer();
|
||||
} else {
|
||||
mInterruptingNotificationEntry = null;
|
||||
}
|
||||
} else if (notification.getNotification().fullScreenIntent != null) {
|
||||
// Stop screensaver if the notification has a full-screen intent.
|
||||
@ -926,7 +917,7 @@ public class PhoneStatusBar extends BaseStatusBar {
|
||||
// usual case: status bar visible & not immersive
|
||||
|
||||
// show the ticker if there isn't already a heads up
|
||||
if (mCurrentlyInterruptingNotification == null) {
|
||||
if (mInterruptingNotificationEntry == null) {
|
||||
tick(null, notification, true);
|
||||
}
|
||||
}
|
||||
@ -936,6 +927,14 @@ public class PhoneStatusBar extends BaseStatusBar {
|
||||
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetHeadsUpDecayTimer() {
|
||||
mHandler.removeMessages(MSG_HIDE_HEADS_UP);
|
||||
if (mHeadsUpNotificationDecay > 0) {
|
||||
mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, mHeadsUpNotificationDecay);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeNotification(IBinder key) {
|
||||
StatusBarNotification old = removeNotificationViews(key);
|
||||
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
|
||||
@ -947,7 +946,8 @@ public class PhoneStatusBar extends BaseStatusBar {
|
||||
// Recalculate the position of the sliding windows and the titles.
|
||||
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
|
||||
|
||||
if (ENABLE_HEADS_UP && old == mCurrentlyInterruptingNotification) {
|
||||
if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null
|
||||
&& old == mInterruptingNotificationEntry.notification) {
|
||||
mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
|
||||
}
|
||||
|
||||
@ -1335,7 +1335,7 @@ public class PhoneStatusBar extends BaseStatusBar {
|
||||
break;
|
||||
case MSG_HIDE_HEADS_UP:
|
||||
setHeadsUpVisibility(false);
|
||||
mCurrentlyInterruptingNotification = null;
|
||||
mInterruptingNotificationEntry = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2464,14 +2464,14 @@ public class PhoneStatusBar extends BaseStatusBar {
|
||||
mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
public void dismissHeadsUp() {
|
||||
if (mCurrentlyInterruptingNotification == null) return;
|
||||
public void onHeadsUpDismissed() {
|
||||
if (mInterruptingNotificationEntry == null) return;
|
||||
|
||||
try {
|
||||
mBarService.onNotificationClear(
|
||||
mCurrentlyInterruptingNotification.getPackageName(),
|
||||
mCurrentlyInterruptingNotification.getTag(),
|
||||
mCurrentlyInterruptingNotification.getId());
|
||||
mInterruptingNotificationEntry.notification.getPackageName(),
|
||||
mInterruptingNotificationEntry.notification.getTag(),
|
||||
mInterruptingNotificationEntry.notification.getId());
|
||||
} catch (android.os.RemoteException ex) {
|
||||
// oh well
|
||||
}
|
||||
@ -2555,6 +2555,9 @@ public class PhoneStatusBar extends BaseStatusBar {
|
||||
mNotificationPanelMinHeightFrac = 0f;
|
||||
}
|
||||
|
||||
mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay);
|
||||
mRowHeight = res.getDimensionPixelSize(R.dimen.notification_row_min_height);
|
||||
|
||||
if (false) Log.v(TAG, "updateResources");
|
||||
}
|
||||
|
||||
|
@ -16,11 +16,9 @@
|
||||
|
||||
package com.android.systemui.statusbar.policy;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
@ -29,23 +27,32 @@ import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.android.systemui.ExpandHelper;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SwipeHelper;
|
||||
import com.android.systemui.statusbar.BaseStatusBar;
|
||||
import com.android.systemui.statusbar.NotificationData;
|
||||
|
||||
public class HeadsUpNotificationView extends LinearLayout implements SwipeHelper.Callback {
|
||||
public class HeadsUpNotificationView extends LinearLayout implements SwipeHelper.Callback, ExpandHelper.Callback {
|
||||
private static final String TAG = "HeadsUpNotificationView";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
Rect mTmpRect = new Rect();
|
||||
|
||||
private final int mTouchSensitivityDelay;
|
||||
private SwipeHelper mSwipeHelper;
|
||||
|
||||
BaseStatusBar mBar;
|
||||
private BaseStatusBar mBar;
|
||||
private ExpandHelper mExpandHelper;
|
||||
private long mStartTouchTime;
|
||||
|
||||
public ViewGroup getHolder() {
|
||||
return mContentHolder;
|
||||
}
|
||||
|
||||
private ViewGroup mContentHolder;
|
||||
|
||||
private Notification mHeadsUp;
|
||||
private OnClickListener mOnClickListener;
|
||||
private NotificationData.Entry mHeadsUp;
|
||||
|
||||
public HeadsUpNotificationView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
@ -53,8 +60,9 @@ public class HeadsUpNotificationView extends LinearLayout implements SwipeHelper
|
||||
|
||||
public HeadsUpNotificationView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
setOrientation(LinearLayout.VERTICAL);
|
||||
mTouchSensitivityDelay = getResources().getInteger(R.integer.heads_up_sensitivity_delay);
|
||||
if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -63,10 +71,14 @@ public class HeadsUpNotificationView extends LinearLayout implements SwipeHelper
|
||||
float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
|
||||
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
|
||||
|
||||
int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_min_height);
|
||||
int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height);
|
||||
mExpandHelper = new ExpandHelper(mContext, this, minHeight, maxHeight);
|
||||
|
||||
mContentHolder = (ViewGroup) findViewById(R.id.contentHolder);
|
||||
if (mHeadsUp != null) {
|
||||
// whoops, we're on already!
|
||||
applyContent(mHeadsUp, mOnClickListener);
|
||||
setNotification(mHeadsUp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,14 +89,23 @@ public class HeadsUpNotificationView extends LinearLayout implements SwipeHelper
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
|
||||
return mSwipeHelper.onInterceptTouchEvent(ev) ||
|
||||
super.onInterceptTouchEvent(ev);
|
||||
if (System.currentTimeMillis() < mStartTouchTime) {
|
||||
return true;
|
||||
}
|
||||
return mSwipeHelper.onInterceptTouchEvent(ev)
|
||||
|| mExpandHelper.onInterceptTouchEvent(ev)
|
||||
|| super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
return mSwipeHelper.onTouchEvent(ev) ||
|
||||
super.onTouchEvent(ev);
|
||||
if (System.currentTimeMillis() < mStartTouchTime) {
|
||||
return false;
|
||||
}
|
||||
mBar.resetHeadsUpDecayTimer();
|
||||
return mSwipeHelper.onTouchEvent(ev)
|
||||
|| mExpandHelper.onTouchEvent(ev)
|
||||
|| super.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
public boolean canChildBeDismissed(View v) {
|
||||
@ -93,7 +114,7 @@ public class HeadsUpNotificationView extends LinearLayout implements SwipeHelper
|
||||
|
||||
public void onChildDismissed(View v) {
|
||||
Log.v(TAG, "User swiped heads up to dismiss");
|
||||
mBar.dismissHeadsUp();
|
||||
mBar.onHeadsUpDismissed();
|
||||
}
|
||||
|
||||
public void onBeginDrag(View v) {
|
||||
@ -134,34 +155,48 @@ public class HeadsUpNotificationView extends LinearLayout implements SwipeHelper
|
||||
}
|
||||
}
|
||||
|
||||
public boolean applyContent(Notification headsUp, OnClickListener listener) {
|
||||
public boolean setNotification(NotificationData.Entry headsUp) {
|
||||
mHeadsUp = headsUp;
|
||||
mOnClickListener = listener;
|
||||
mHeadsUp.row.setExpanded(false);
|
||||
if (mContentHolder == null) {
|
||||
// too soon!
|
||||
return false;
|
||||
}
|
||||
if (headsUp.contentView == null) {
|
||||
// bad data
|
||||
return false;
|
||||
}
|
||||
mContentHolder.setX(0);
|
||||
mContentHolder.setVisibility(View.VISIBLE);
|
||||
mContentHolder.setAlpha(1f);
|
||||
mContentHolder.removeAllViews();
|
||||
final View content = headsUp.contentView.apply(getContext(), mContentHolder);
|
||||
if (listener != null) {
|
||||
content.setOnClickListener(listener);
|
||||
|
||||
Drawable bg = getResources().getDrawable(R.drawable.heads_up_notification_row_bg);
|
||||
if (bg == null) {
|
||||
Log.e(TAG, String.format("Can't find background drawable id=0x%08x",
|
||||
R.drawable.heads_up_notification_row_bg));
|
||||
} else {
|
||||
content.setBackgroundDrawable(bg);
|
||||
}
|
||||
}
|
||||
mContentHolder.addView(content);
|
||||
mContentHolder.addView(mHeadsUp.row);
|
||||
mStartTouchTime = System.currentTimeMillis() + mTouchSensitivityDelay;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getChildAtRawPosition(float x, float y) {
|
||||
return getChildAtPosition(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ import android.widget.LinearLayout;
|
||||
import com.android.systemui.ExpandHelper;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SwipeHelper;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.NotificationData;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -149,15 +150,20 @@ public class NotificationRowLayout
|
||||
}
|
||||
|
||||
public boolean canChildBeExpanded(View v) {
|
||||
return NotificationData.getIsExpandable(v);
|
||||
return v instanceof ExpandableNotificationRow
|
||||
&& ((ExpandableNotificationRow) v).isExpandable();
|
||||
}
|
||||
|
||||
public boolean setUserExpandedChild(View v, boolean userExpanded) {
|
||||
return NotificationData.setUserExpanded(v, userExpanded);
|
||||
public void setUserExpandedChild(View v, boolean userExpanded) {
|
||||
if (v instanceof ExpandableNotificationRow) {
|
||||
((ExpandableNotificationRow) v).setUserExpanded(userExpanded);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setUserLockedChild(View v, boolean userLocked) {
|
||||
return NotificationData.setUserLocked(v, userLocked);
|
||||
public void setUserLockedChild(View v, boolean userLocked) {
|
||||
if (v instanceof ExpandableNotificationRow) {
|
||||
((ExpandableNotificationRow) v).setUserLocked(userLocked);
|
||||
}
|
||||
}
|
||||
|
||||
public void onChildDismissed(View v) {
|
||||
|
@ -136,6 +136,10 @@ public class TvStatusBar extends BaseStatusBar {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetHeadsUpDecayTimer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void animateExpandSettingsPanel() {
|
||||
}
|
||||
|
Reference in New Issue
Block a user