summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorDaniel Sandler <dsandler@android.com>2012-03-12 14:38:58 -0400
committerChris Wren <cwren@android.com>2012-04-18 11:21:19 -0400
commit6a858c347f4d4e5db4c8f00d5e285967631b71ca (patch)
treed84fd54c5ffff04165ede028ed205a0781002da1 /packages
parent3d100d97a55c5aba2cac5599a158fe3759d278ca (diff)
downloadframeworks_base-6a858c347f4d4e5db4c8f00d5e285967631b71ca.zip
frameworks_base-6a858c347f4d4e5db4c8f00d5e285967631b71ca.tar.gz
frameworks_base-6a858c347f4d4e5db4c8f00d5e285967631b71ca.tar.bz2
Gestures for expanding notifications.
Change-Id: I104c157ffcc2d60b3f0a95c59d4322b07103b69f
Diffstat (limited to 'packages')
-rw-r--r--packages/SystemUI/res/layout/notification_adaptive_wrapper.xml20
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_row.xml11
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/src/com/android/systemui/ExpandHelper.java199
-rw-r--r--packages/SystemUI/src/com/android/systemui/Gefingerpoken.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java203
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java111
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java161
11 files changed, 525 insertions, 278 deletions
diff --git a/packages/SystemUI/res/layout/notification_adaptive_wrapper.xml b/packages/SystemUI/res/layout/notification_adaptive_wrapper.xml
new file mode 100644
index 0000000..15d0890
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_adaptive_wrapper.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<SizeAdaptiveLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="@android:color/background_dark"
+ android:id="@+id/notification_adaptive_wrapper"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index c307c6e..a0d1b08 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -1,6 +1,6 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="@dimen/notification_height"
+ android:layout_height="wrap_content"
>
<Button
@@ -21,7 +21,14 @@
android:focusable="true"
android:clickable="true"
android:background="@drawable/notification_row_bg"
- />
+ >
+
+ <com.android.internal.widget.SizeAdaptiveLayout android:id="@+id/adaptive"
+ android:background="@android:color/background_dark"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </com.android.systemui.statusbar.LatestItemView>
<View
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 4441ca6..f786e86 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -55,6 +55,13 @@
<!-- Height of notification icons in the status bar -->
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
+ <!-- Height of a small notification in the status bar -->
+ <dimen name="notification_min_height">@android:dimen/notification_large_icon_height</dimen>
+
+ <!-- Height of a small notification in the status bar -->
+ <!-- TODO: change this back to 256dp once we deal with actions. -->
+ <dimen name="notification_max_height">320dp</dimen>
+
<!-- size at which Notification icons will be drawn in the status bar -->
<dimen name="status_bar_icon_drawing_size">18dip</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
new file mode 100644
index 0000000..aa289da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2012 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.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.RectF;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import com.android.internal.widget.SizeAdaptiveLayout;
+
+public class ExpandHelper implements Gefingerpoken, OnClickListener {
+ public interface Callback {
+ View getChildAtPosition(MotionEvent ev);
+ View getChildAtPosition(float x, float y);
+ }
+
+ private static final String TAG = "ExpandHelper";
+ protected static final boolean DEBUG = false;
+ private static final long EXPAND_DURATION = 250;
+
+ @SuppressWarnings("unused")
+ private Context mContext;
+
+ private boolean mStretching;
+ private View mCurrView;
+ private float mOldHeight;
+ private float mNaturalHeight;
+ private float mInitialTouchSpan;
+ private Callback mCallback;
+ private ScaleGestureDetector mDetector;
+ private ViewScaler mScaler;
+ private ObjectAnimator mAnimation;
+
+ private int mSmallSize;
+ private int mLargeSize;
+
+
+ private class ViewScaler {
+ View mView;
+ public ViewScaler() {}
+ public void setView(View v) {
+ mView = v;
+ }
+ public void setHeight(float h) {
+ Log.v(TAG, "SetHeight: setting to " + h);
+ ViewGroup.LayoutParams lp = mView.getLayoutParams();
+ lp.height = (int)h;
+ mView.setLayoutParams(lp);
+ mView.requestLayout();
+ }
+ public float getHeight() {
+ int height = mView.getLayoutParams().height;
+ if (height < 0) {
+ height = mView.getMeasuredHeight();
+ }
+ return (float) height;
+ }
+ public int getNaturalHeight(int maximum) {
+ ViewGroup.LayoutParams lp = mView.getLayoutParams();
+ if (DEBUG) Log.v(TAG, "Inspecting a child of type: " + mView.getClass().getName());
+ int oldHeight = lp.height;
+ lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ mView.setLayoutParams(lp);
+ mView.measure(
+ View.MeasureSpec.makeMeasureSpec(mView.getMeasuredWidth(),
+ View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(maximum,
+ View.MeasureSpec.AT_MOST));
+ lp.height = oldHeight;
+ mView.setLayoutParams(lp);
+ return mView.getMeasuredHeight();
+ }
+ }
+
+ public ExpandHelper(Context context, Callback callback, int small, int large) {
+ mSmallSize = small;
+ mLargeSize = large;
+ mContext = context;
+ mCallback = callback;
+ mScaler = new ViewScaler();
+ mDetector =
+ new ScaleGestureDetector(context,
+ new ScaleGestureDetector.SimpleOnScaleGestureListener() {
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ if (DEBUG) Log.v(TAG, "onscalebegin()");
+ View v = mCallback.getChildAtPosition(detector.getFocusX(), detector.getFocusY());
+
+ // your fingers have to be somewhat close to the bounds of the view in question
+ mInitialTouchSpan = Math.abs(detector.getCurrentSpanY());
+ if (DEBUG) Log.d(TAG, "got mInitialTouchSpan: " + mInitialTouchSpan);
+
+ mStretching = initScale(v);
+ return mStretching;
+ }
+
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ if (DEBUG) Log.v(TAG, "onscale() on " + mCurrView);
+ float h = Math.abs(detector.getCurrentSpanY());
+ if (DEBUG) Log.d(TAG, "current span is: " + h);
+ h = h + mOldHeight - mInitialTouchSpan;
+ h = h<mSmallSize?mSmallSize:(h>mLargeSize?mLargeSize:h);
+ h = h>mNaturalHeight?mNaturalHeight:h;
+ if (DEBUG) Log.d(TAG, "scale continues: h=" + h);
+ mScaler.setHeight(h);
+ return true;
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ if (DEBUG) Log.v(TAG, "onscaleend()");
+ // I guess we're alone now
+ if (DEBUG) Log.d(TAG, "scale end");
+ finishScale(false);
+ }
+ });
+ }
+
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (DEBUG) Log.d(TAG, "interceptTouch: act=" + (ev.getAction()) +
+ " stretching=" + mStretching);
+ mDetector.onTouchEvent(ev);
+ return mStretching;
+ }
+
+ public boolean onTouchEvent(MotionEvent ev) {
+ final int action = ev.getAction();
+ if (DEBUG) Log.d(TAG, "touch: act=" + (action) + " stretching=" + mStretching);
+ if (mStretching) {
+ mDetector.onTouchEvent(ev);
+ }
+ switch (action) {
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mStretching = false;
+ mCurrView = null;
+ break;
+ }
+ return true;
+ }
+ private boolean initScale(View v) {
+ if (v != null) {
+ if (DEBUG) Log.d(TAG, "scale begins on view: " + v);
+ mStretching = true;
+ mCurrView = v;
+ mScaler.setView(v);
+ mOldHeight = mScaler.getHeight();
+ mNaturalHeight = mScaler.getNaturalHeight(mLargeSize);
+ if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight +
+ " mNaturalHeight: " + mNaturalHeight);
+ v.getParent().requestDisallowInterceptTouchEvent(true);
+ if (DEBUG) v.setBackgroundColor(0xFFFFFF00);
+ }
+ return mStretching;
+ }
+
+ private void finishScale(boolean force) {
+ float h = mScaler.getHeight();
+ final boolean wasClosed = (mOldHeight == mSmallSize);
+ if (wasClosed) {
+ h = (force || h > mSmallSize) ? mNaturalHeight : mSmallSize;
+ } else {
+ h = (force || h < mNaturalHeight) ? mSmallSize : mNaturalHeight;
+ }
+ if (DEBUG) mCurrView.setBackgroundColor(0);
+ mAnimation = ObjectAnimator.ofFloat(mScaler, "height", h).setDuration(EXPAND_DURATION);
+ mAnimation.start();
+ mStretching = false;
+ mCurrView = null;
+ }
+
+ @Override
+ public void onClick(View v) {
+ initScale(v);
+ finishScale(true);
+
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Gefingerpoken.java b/packages/SystemUI/src/com/android/systemui/Gefingerpoken.java
new file mode 100644
index 0000000..b2d5c21
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/Gefingerpoken.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 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.view.MotionEvent;
+
+// ACHTUNG!
+public interface Gefingerpoken {
+ boolean onInterceptTouchEvent(MotionEvent ev);
+ boolean onTouchEvent(MotionEvent ev);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 276ca21..19657a9 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -29,7 +29,7 @@ import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
-public class SwipeHelper {
+public class SwipeHelper implements Gefingerpoken {
static final String TAG = "com.android.systemui.SwipeHelper";
private static final boolean DEBUG = false;
private static final boolean DEBUG_INVALIDATE = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 8d7afc8..ede8e7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -18,9 +18,14 @@ package com.android.systemui.statusbar;
import java.util.ArrayList;
+import android.app.ActivityManagerNative;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -34,15 +39,18 @@ import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.widget.LinearLayout;
+import android.widget.RemoteViews;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
import com.android.internal.statusbar.StatusBarNotification;
+import com.android.internal.widget.SizeAdaptiveLayout;
import com.android.systemui.SystemUI;
import com.android.systemui.recent.RecentsPanelView;
import com.android.systemui.recent.RecentTasksLoader;
@@ -66,12 +74,15 @@ public abstract class BaseStatusBar extends SystemUI implements
protected IStatusBarService mBarService;
protected H mHandler = createHandler();
+ // used to notify status bar for suppressing notification LED
+ protected boolean mPanelSlightlyVisible;
+
// Recent apps
protected RecentsPanelView mRecentsPanel;
protected RecentTasksLoader mRecentTasksLoader;
// UI-specific methods
-
+
/**
* Create all windows necessary for the status bar (including navigation, overlay panels, etc)
* and add them to the window manager.
@@ -81,15 +92,15 @@ public abstract class BaseStatusBar extends SystemUI implements
protected Display mDisplay;
private IWindowManager mWindowManager;
-
+
public IWindowManager getWindowManager() {
return mWindowManager;
}
-
+
public Display getDisplay() {
return mDisplay;
}
-
+
public IStatusBarService getStatusBarService() {
return mBarService;
}
@@ -109,7 +120,7 @@ public abstract class BaseStatusBar extends SystemUI implements
ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
mCommandQueue = new CommandQueue(this, iconList);
-
+
int[] switches = new int[7];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
try {
@@ -118,7 +129,7 @@ public abstract class BaseStatusBar extends SystemUI implements
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
-
+
createAndAddWindows();
disable(switches[0]);
@@ -152,7 +163,7 @@ public abstract class BaseStatusBar extends SystemUI implements
if (DEBUG) {
Slog.d(TAG, String.format(
- "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
+ "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
iconList.size(),
switches[0],
switches[1],
@@ -161,7 +172,7 @@ public abstract class BaseStatusBar extends SystemUI implements
));
}
}
-
+
protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
View vetoButton = row.findViewById(R.id.veto);
if (n.isClearable()) {
@@ -183,7 +194,7 @@ public abstract class BaseStatusBar extends SystemUI implements
}
return vetoButton;
}
-
+
protected void applyLegacyRowBackground(StatusBarNotification sbn, View content) {
if (sbn.notification.contentView.getLayoutId() !=
@@ -323,4 +334,178 @@ public abstract class BaseStatusBar extends SystemUI implements
return false;
}
}
+
+ protected void workAroundBadLayerDrawableOpacity(View v) {
+ }
+
+ protected boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
+ int minHeight =
+ mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height);
+ int maxHeight =
+ mContext.getResources().getDimensionPixelSize(R.dimen.notification_max_height);
+ StatusBarNotification sbn = entry.notification;
+ RemoteViews oneU = sbn.notification.contentView;
+ RemoteViews large = sbn.notification.bigContentView;
+ if (oneU == null) {
+ return false;
+ }
+
+ // 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);
+ // XXX: temporary: while testing big notifications, auto-expand all of them
+ ViewGroup.LayoutParams lp = row.getLayoutParams();
+ if (sbn.notification.bigContentView != null) {
+ lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ } else {
+ lp.height = minHeight;
+ }
+ row.setLayoutParams(lp);
+ workAroundBadLayerDrawableOpacity(row);
+ View vetoButton = updateNotificationVetoButton(row, sbn);
+ vetoButton.setContentDescription(mContext.getString(
+ R.string.accessibility_remove_notification));
+
+ // NB: the large icon is now handled entirely by the template
+
+ // bind the click event to the content area
+ ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
+ ViewGroup adaptive = (ViewGroup)row.findViewById(R.id.adaptive);
+ // XXX: update to allow controls within notification views
+ content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+// content.setOnFocusChangeListener(mFocusChangeListener);
+ PendingIntent contentIntent = sbn.notification.contentIntent;
+ if (contentIntent != null) {
+ final View.OnClickListener listener = new NotificationClicker(contentIntent,
+ sbn.pkg, sbn.tag, sbn.id);
+ content.setOnClickListener(listener);
+ } else {
+ content.setOnClickListener(null);
+ }
+
+ View expandedOneU = null;
+ View expandedLarge = null;
+ Exception exception = null;
+ try {
+ expandedOneU = oneU.apply(mContext, adaptive);
+ if (large != null) {
+ expandedLarge = large.apply(mContext, adaptive);
+ }
+ }
+ catch (RuntimeException e) {
+ exception = e;
+ }
+ if (expandedOneU == null && expandedLarge == null) {
+ final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
+ Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
+ return false;
+ } else {
+ if (expandedOneU != null) {
+ SizeAdaptiveLayout.LayoutParams params =
+ new SizeAdaptiveLayout.LayoutParams(expandedOneU.getLayoutParams());
+ params.minHeight = minHeight;
+ params.maxHeight = minHeight;
+ adaptive.addView(expandedOneU, params);
+ }
+ if (expandedLarge != null) {
+ SizeAdaptiveLayout.LayoutParams params =
+ new SizeAdaptiveLayout.LayoutParams(expandedLarge.getLayoutParams());
+ params.minHeight = minHeight+1;
+ params.maxHeight = SizeAdaptiveLayout.LayoutParams.UNBOUNDED;
+ adaptive.addView(expandedLarge, params);
+ }
+ row.setDrawingCacheEnabled(true);
+ }
+
+ applyLegacyRowBackground(sbn, content);
+
+ entry.row = row;
+ entry.content = content;
+ entry.expanded = expandedOneU;
+ entry.expandedLarge = expandedOneU;
+
+ return true;
+ }
+
+ public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) {
+ return new NotificationClicker(intent, pkg, tag, id);
+ }
+
+ private class NotificationClicker implements View.OnClickListener {
+ private PendingIntent mIntent;
+ private String mPkg;
+ private String mTag;
+ private int mId;
+
+ NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
+ mIntent = intent;
+ mPkg = pkg;
+ mTag = tag;
+ mId = id;
+ }
+
+ public void onClick(View v) {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManagerNative.getDefault().resumeAppSwitches();
+ // Also, notifications can be launched from the lock screen,
+ // so dismiss the lock screen when the activity starts.
+ ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+ } catch (RemoteException e) {
+ }
+
+ if (mIntent != null) {
+ int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+ Intent overlay = new Intent();
+ overlay.setSourceBounds(
+ new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+ try {
+ mIntent.send(mContext, 0, overlay);
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here. Just log the exception message.
+ Slog.w(TAG, "Sending contentIntent failed: " + e);
+ }
+
+ KeyguardManager kgm =
+ (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ if (kgm != null) kgm.exitKeyguardSecurely(null);
+ }
+
+ try {
+ mBarService.onNotificationClick(mPkg, mTag, mId);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+
+ // close the shade if it was open
+ animateCollapse();
+ visibilityChanged(false);
+
+ // If this click was on the intruder alert, hide that instead
+// mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
+ }
+ }
+ /**
+ * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
+ * This was added last-minute and is inconsistent with the way the rest of the notifications
+ * are handled, because the notification isn't really cancelled. The lights are just
+ * turned off. If any other notifications happen, the lights will turn back on. Steve says
+ * this is what he wants. (see bug 1131461)
+ */
+ protected void visibilityChanged(boolean visible) {
+ if (mPanelSlightlyVisible != visible) {
+ mPanelSlightlyVisible = visible;
+ try {
+ mBarService.onPanelRevealed();
+ } catch (RemoteException ex) {
+ // Won't fail unless the world has ended.
+ }
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 6fbcd64..3ff85d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -38,6 +38,7 @@ public class NotificationData {
public View content; // takes the click events and sends the PendingIntent
public View expanded; // the inflated RemoteViews
public ImageView largeIcon;
+ public View expandedLarge;
public Entry() {}
public Entry(IBinder key, StatusBarNotification n, StatusBarIconView ic) {
this.key = key;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 3f611fc..f45b3ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -28,14 +28,11 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
-import android.os.Build;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
@@ -50,11 +47,9 @@ import android.view.Display;
import android.view.Gravity;
import android.view.IWindowManager;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
@@ -68,26 +63,25 @@ import android.widget.RemoteViews;
import android.widget.ScrollView;
import android.widget.TextView;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarNotification;
import com.android.systemui.R;
-import com.android.systemui.SwipeHelper;
import com.android.systemui.recent.RecentTasksLoader;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.IntruderAlertView;
import com.android.systemui.statusbar.policy.DateView;
+import com.android.systemui.statusbar.policy.IntruderAlertView;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NotificationRowLayout;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
public class PhoneStatusBar extends BaseStatusBar {
static final String TAG = "PhoneStatusBar";
public static final boolean DEBUG = false;
@@ -832,73 +826,6 @@ public class PhoneStatusBar extends BaseStatusBar {
}
}
- private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
- StatusBarNotification sbn = entry.notification;
- // XXX: temporary: while testing big notifications, auto-expand all of them
- final boolean big = (sbn.notification.bigContentView != null);
- RemoteViews remoteViews = big ? sbn.notification.bigContentView
- : sbn.notification.contentView;
- if (remoteViews == null) {
- return false;
- }
-
- // 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);
- ViewGroup.LayoutParams lp = row.getLayoutParams();
- if (big) {
- lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- } else {
- lp.height = mContext.getResources().getDimensionPixelSize(R.dimen.notification_height);
- }
- row.setLayoutParams(lp);
- View vetoButton = updateNotificationVetoButton(row, sbn);
- vetoButton.setContentDescription(mContext.getString(
- R.string.accessibility_remove_notification));
-
- // NB: the large icon is now handled entirely by the template
-
- // bind the click event to the content area
- ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
- // XXX: update to allow controls within notification views
- content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-// content.setOnFocusChangeListener(mFocusChangeListener);
- PendingIntent contentIntent = sbn.notification.contentIntent;
- if (contentIntent != null) {
- final View.OnClickListener listener = new NotificationClicker(contentIntent,
- sbn.pkg, sbn.tag, sbn.id);
- content.setOnClickListener(listener);
- } else {
- content.setOnClickListener(null);
- }
-
- View expanded = null;
- Exception exception = null;
- try {
- expanded = remoteViews.apply(mContext, content);
- }
- catch (RuntimeException e) {
- exception = e;
- }
- if (expanded == null) {
- final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
- Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
- return false;
- } else {
- content.addView(expanded);
- row.setDrawingCacheEnabled(true);
- }
-
- applyLegacyRowBackground(sbn, content);
-
- entry.row = row;
- entry.content = content;
- entry.expanded = expanded;
-
- return true;
- }
-
StatusBarNotification removeNotificationViews(IBinder key) {
NotificationData.Entry entry = mNotificationData.remove(key);
if (entry == null) {
@@ -1520,10 +1447,6 @@ public class PhoneStatusBar extends BaseStatusBar {
@Override
public void setHardKeyboardStatus(boolean available, boolean enabled) { }
- public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) {
- return new NotificationClicker(intent, pkg, tag, id);
- }
-
private class NotificationClicker implements View.OnClickListener {
private PendingIntent mIntent;
private String mPkg;
@@ -1538,12 +1461,6 @@ public class PhoneStatusBar extends BaseStatusBar {
}
public void onClick(View v) {
- if (DEBUG) {
- Slog.v(TAG, "NotificationClicker: intent=" + mIntent
- + " pkg=" + mPkg
- + " tag=" + mTag
- + " id=" + mId);
- }
try {
// The intent we are sending is for the application, which
// won't have permission to immediately start an activity after
@@ -1976,24 +1893,6 @@ public class PhoneStatusBar extends BaseStatusBar {
}
}
- /**
- * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
- * This was added last-minute and is inconsistent with the way the rest of the notifications
- * are handled, because the notification isn't really cancelled. The lights are just
- * turned off. If any other notifications happen, the lights will turn back on. Steve says
- * this is what he wants. (see bug 1131461)
- */
- void visibilityChanged(boolean visible) {
- if (mPanelSlightlyVisible != visible) {
- mPanelSlightlyVisible = visible;
- try {
- mBarService.onPanelRevealed();
- } catch (RemoteException ex) {
- // Won't fail unless the world has ended.
- }
- }
- }
-
void performDisableActions(int net) {
int old = mDisabled;
int diff = net ^ old;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index 9fd89ed..5369317 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -34,12 +34,17 @@ import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+import com.android.systemui.ExpandHelper;
+import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
import java.util.HashMap;
-public class NotificationRowLayout extends LinearLayout implements SwipeHelper.Callback {
+public class NotificationRowLayout
+ extends LinearLayout
+ implements SwipeHelper.Callback, ExpandHelper.Callback
+{
private static final String TAG = "NotificationRowLayout";
private static final boolean DEBUG = false;
private static final boolean SLOW_ANIMATIONS = DEBUG;
@@ -55,6 +60,9 @@ public class NotificationRowLayout extends LinearLayout implements SwipeHelper.C
HashMap<View, ValueAnimator> mDisappearingViews = new HashMap<View, ValueAnimator>();
private SwipeHelper mSwipeHelper;
+ private ExpandHelper mExpandHelper;
+
+ private Gefingerpoken mCurrentHelper;
// Flag set during notification removal animation to avoid causing too much work until
// animation is done
@@ -71,6 +79,8 @@ public class NotificationRowLayout extends LinearLayout implements SwipeHelper.C
setOrientation(LinearLayout.VERTICAL);
+ setMotionEventSplittingEnabled(false);
+
if (DEBUG) {
setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
@Override
@@ -89,23 +99,61 @@ public class NotificationRowLayout extends LinearLayout implements SwipeHelper.C
float densityScale = getResources().getDisplayMetrics().density;
float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
+ int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
+ int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
+ mExpandHelper = new ExpandHelper(mContext, this, minHeight, maxHeight);
}
public void setAnimateBounds(boolean anim) {
mAnimateBounds = anim;
}
+ private void logLayoutTransition() {
+ Log.v(TAG, "layout " +
+ (getLayoutTransition().isChangingLayout() ? "is " : "is not ") +
+ "in transition and animations " +
+ (getLayoutTransition().isRunning() ? "are " : "are not ") +
+ "running.");
+ }
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
- return mSwipeHelper.onInterceptTouchEvent(ev) ||
- super.onInterceptTouchEvent(ev);
+ if (DEBUG) logLayoutTransition();
+
+ MotionEvent cancellation = MotionEvent.obtain(ev);
+ cancellation.setAction(MotionEvent.ACTION_CANCEL);
+
+ if (mSwipeHelper.onInterceptTouchEvent(ev)) {
+ if (DEBUG) Log.v(TAG, "will swipe");
+ mCurrentHelper = mSwipeHelper;
+ mExpandHelper.onInterceptTouchEvent(cancellation);
+ return true;
+ } else if (mExpandHelper.onInterceptTouchEvent(ev)) {
+ if (DEBUG) Log.v(TAG, "will stretch");
+ mCurrentHelper = mExpandHelper;
+ mSwipeHelper.onInterceptTouchEvent(cancellation);
+ return true;
+ } else {
+ mCurrentHelper = null;
+ if (super.onInterceptTouchEvent(ev)) {
+ if (DEBUG) Log.v(TAG, "intercepting ourselves");
+ mSwipeHelper.onInterceptTouchEvent(cancellation);
+ mExpandHelper.onInterceptTouchEvent(cancellation);
+ return true;
+ }
+ }
+ return false;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
- return mSwipeHelper.onTouchEvent(ev) ||
- super.onTouchEvent(ev);
+ if (DEBUG) Log.v(TAG, "onTouchEvent()");
+ if (DEBUG) logLayoutTransition();
+ if (mCurrentHelper != null) {
+ return mCurrentHelper.onTouchEvent(ev);
+ }
+ return super.onTouchEvent(ev);
}
public boolean canChildBeDismissed(View v) {
@@ -130,10 +178,12 @@ public class NotificationRowLayout extends LinearLayout implements SwipeHelper.C
}
public View getChildAtPosition(MotionEvent ev) {
+ return getChildAtPosition(ev.getX(), ev.getY());
+ }
+ public View getChildAtPosition(float touchX, float touchY) {
// find the view under the pointer, accounting for GONE views
final int count = getChildCount();
int y = 0;
- int touchY = (int) ev.getY();
int childIdx = 0;
View slidingChild;
for (; childIdx < count; childIdx++) {
@@ -188,6 +238,7 @@ public class NotificationRowLayout extends LinearLayout implements SwipeHelper.C
@Override
public void onDraw(android.graphics.Canvas c) {
super.onDraw(c);
+ if (DEBUG) logLayoutTransition();
if (DEBUG) {
//Slog.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
// + getMeasuredHeight() + "px");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index ba51108..8c1509b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -16,14 +16,9 @@
package com.android.systemui.statusbar.tablet;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.app.ActivityManagerNative;
-import android.app.KeyguardManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
@@ -32,17 +27,13 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.inputmethodservice.InputMethodService;
-import android.os.Build;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
@@ -53,7 +44,6 @@ import android.view.Display;
import android.view.Gravity;
import android.view.IWindowManager;
import android.view.KeyEvent;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.VelocityTracker;
@@ -86,6 +76,10 @@ import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.Prefs;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
public class TabletStatusBar extends BaseStatusBar implements
InputMethodsPanel.OnHardKeyboardEnabledChangeListener,
RecentsPanelView.OnRecentsPanelVisibilityChangedListener {
@@ -187,8 +181,6 @@ public class TabletStatusBar extends BaseStatusBar implements
private CompatModePanel mCompatModePanel;
private int mSystemUiVisibility = 0;
- // used to notify status bar for suppressing notification LED
- private boolean mPanelSlightlyVisible;
private int mNavigationIconHints = 0;
@@ -912,7 +904,7 @@ public class TabletStatusBar extends BaseStatusBar implements
// update the contentIntent
final PendingIntent contentIntent = notification.notification.contentIntent;
if (contentIntent != null) {
- final View.OnClickListener listener = new NotificationClicker(contentIntent,
+ final View.OnClickListener listener = makeClicker(contentIntent,
notification.pkg, notification.tag, notification.id);
oldEntry.content.setOnClickListener(listener);
} else {
@@ -1105,24 +1097,6 @@ public class TabletStatusBar extends BaseStatusBar implements
}
}
- /**
- * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
- * This was added last-minute and is inconsistent with the way the rest of the notifications
- * are handled, because the notification isn't really cancelled. The lights are just
- * turned off. If any other notifications happen, the lights will turn back on. Steve says
- * this is what he wants. (see bug 1131461)
- */
- void visibilityChanged(boolean visible) {
- if (mPanelSlightlyVisible != visible) {
- mPanelSlightlyVisible = visible;
- try {
- mBarService.onPanelRevealed();
- } catch (RemoteException ex) {
- // Won't fail unless the world has ended.
- }
- }
- }
-
@Override // CommandQueue
public void setNavigationIconHints(int hints) {
if (hints == mNavigationIconHints) return;
@@ -1364,70 +1338,6 @@ public class TabletStatusBar extends BaseStatusBar implements
mHandler.sendEmptyMessage(msg);
}
- public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) {
- return new NotificationClicker(intent, pkg, tag, id);
- }
-
- private class NotificationClicker implements View.OnClickListener {
- private PendingIntent mIntent;
- private String mPkg;
- private String mTag;
- private int mId;
-
- NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
- mIntent = intent;
- mPkg = pkg;
- mTag = tag;
- mId = id;
- }
-
- public void onClick(View v) {
- try {
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- ActivityManagerNative.getDefault().resumeAppSwitches();
- // Also, notifications can be launched from the lock screen,
- // so dismiss the lock screen when the activity starts.
- ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
- } catch (RemoteException e) {
- }
-
- if (mIntent != null) {
- int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- Intent overlay = new Intent();
- overlay.setSourceBounds(
- new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
- try {
- mIntent.send(mContext, 0, overlay);
-
- } catch (PendingIntent.CanceledException e) {
- // the stack trace isn't very helpful here. Just log the exception message.
- Slog.w(TAG, "Sending contentIntent failed: " + e);
- }
-
- KeyguardManager kgm =
- (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- if (kgm != null) kgm.exitKeyguardSecurely(null);
- }
-
- try {
- mBarService.onNotificationClick(mPkg, mTag, mId);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
-
- // close the shade if it was open
- animateCollapse();
- visibilityChanged(false);
-
- // If this click was on the intruder alert, hide that instead
-// mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
- }
- }
-
StatusBarNotification removeNotificationViews(IBinder key) {
NotificationData.Entry entry = mNotificationData.remove(key);
if (entry == null) {
@@ -1801,7 +1711,8 @@ public class TabletStatusBar extends BaseStatusBar implements
mNotificationPanel.setNotificationCount(N);
}
- void workAroundBadLayerDrawableOpacity(View v) {
+ @Override
+ protected void workAroundBadLayerDrawableOpacity(View v) {
Drawable bgd = v.getBackground();
if (!(bgd instanceof LayerDrawable)) return;
@@ -1811,64 +1722,6 @@ public class TabletStatusBar extends BaseStatusBar implements
v.setBackgroundDrawable(d);
}
- private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
- StatusBarNotification sbn = entry.notification;
- RemoteViews remoteViews = sbn.notification.contentView;
- if (remoteViews == null) {
- return false;
- }
-
- // 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);
- workAroundBadLayerDrawableOpacity(row);
- View vetoButton = updateNotificationVetoButton(row, entry.notification);
- vetoButton.setContentDescription(mContext.getString(
- R.string.accessibility_remove_notification));
-
- // NB: the large icon is now handled entirely by the template
-
- // bind the click event to the content area
- ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
- // XXX: update to allow controls within notification views
- content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-// content.setOnFocusChangeListener(mFocusChangeListener);
- PendingIntent contentIntent = sbn.notification.contentIntent;
- if (contentIntent != null) {
- final View.OnClickListener listener = new NotificationClicker(
- contentIntent, sbn.pkg, sbn.tag, sbn.id);
- content.setOnClickListener(listener);
- } else {
- content.setOnClickListener(null);
- }
-
- View expanded = null;
- Exception exception = null;
- try {
- expanded = remoteViews.apply(mContext, content);
- }
- catch (RuntimeException e) {
- exception = e;
- }
- if (expanded == null) {
- final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
- Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
- return false;
- } else {
- content.addView(expanded);
- row.setDrawingCacheEnabled(true);
- }
-
- applyLegacyRowBackground(sbn, content);
-
- entry.row = row;
- entry.content = content;
- entry.expanded = expanded;
-
- return true;
- }
-
public void clearAll() {
try {
mBarService.onClearAllNotifications();