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:
Chris Wren
2013-07-16 20:49:17 -04:00
parent bc668ac4b7
commit 51c7510e49
15 changed files with 275 additions and 191 deletions

View File

@ -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

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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");
}

View File

@ -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);
}
}
}

View File

@ -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) {

View File

@ -136,6 +136,10 @@ public class TvStatusBar extends BaseStatusBar {
return null;
}
@Override
public void resetHeadsUpDecayTimer() {
}
@Override
public void animateExpandSettingsPanel() {
}