diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-xlarge/config.xml
new file mode 100644
index 0000000000000..2640662e627fd
--- /dev/null
+++ b/core/res/res/values-xlarge/config.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ com.android.systemui/com.android.systemui.statusbar.tablet.TabletStatusBarService
+
+
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 689f73a9e7c6d..ec0d83c031892 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -22,7 +22,7 @@
- com.android.systemui/com.android.systemui.statusbar.StatusBarService
+ com.android.systemui/com.android.systemui.statusbar.PhoneStatusBarService
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 75045d77e9d78..6d77a3a28bc51 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -12,7 +12,12 @@
android:icon="@drawable/ic_launcher_settings">
+
+
diff --git a/packages/SystemUI/res/drawable/notification_dragger.png b/packages/SystemUI/res/drawable/notification_dragger.png
new file mode 100644
index 0000000000000..fad1f3226c6db
Binary files /dev/null and b/packages/SystemUI/res/drawable/notification_dragger.png differ
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
new file mode 100644
index 0000000000000..1e93bee0ac1ab
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -0,0 +1,155 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 46671495c8c7c..2f1b36e8a8a35 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -60,33 +60,6 @@
android:textStyle="bold"
android:gravity="center_vertical|left"
/>
-
-
-
-
-
-
-
-
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
index f45caf51662b7..0f6723e06e1b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
@@ -23,7 +23,7 @@ import android.widget.LinearLayout;
public class CloseDragHandle extends LinearLayout {
- StatusBarService mService;
+ PhoneStatusBarService mService;
public CloseDragHandle(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index f9347b1377ac6..2c0af65fc8c09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -32,7 +32,7 @@ import com.android.internal.statusbar.StatusBarNotification;
* coalescing these calls so they don't stack up. For the calls
* are coalesced, note that they are all idempotent.
*/
-class CommandQueue extends IStatusBar.Stub {
+public class CommandQueue extends IStatusBar.Stub {
private static final String TAG = "StatusBar.CommandQueue";
private static final int MSG_MASK = 0xffff0000;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
index 3d85f27519db5..a2d4b95d3422a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
@@ -27,7 +27,7 @@ import android.util.Slog;
public class ExpandedView extends LinearLayout {
- StatusBarService mService;
+ PhoneStatusBarService mService;
int mPrevHeight = -1;
public ExpandedView(Context context, AttributeSet attrs) {
@@ -53,7 +53,7 @@ public class ExpandedView extends LinearLayout {
//Slog.d(StatusBarService.TAG, "height changed old=" + mPrevHeight
// + " new=" + height);
mPrevHeight = height;
- mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
+ mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
new file mode 100644
index 0000000000000..eff9a1d5ded2d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -0,0 +1,1552 @@
+/*
+ * Copyright (C) 2010 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.app.Service;
+import android.app.ActivityManagerNative;
+import android.app.Dialog;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.Log;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.FrameLayout;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Set;
+
+import com.android.internal.statusbar.IStatusBar;
+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.systemui.R;
+import com.android.systemui.statusbar.policy.StatusBarPolicy;
+
+
+
+public class PhoneStatusBarService extends StatusBarService {
+ static final String TAG = "PhoneStatusBarService";
+ static final boolean SPEW = false;
+
+ public static final String ACTION_STATUSBAR_START
+ = "com.android.internal.policy.statusbar.START";
+
+ static final int EXPANDED_LEAVE_ALONE = -10000;
+ static final int EXPANDED_FULL_OPEN = -10001;
+
+ private static final int MSG_ANIMATE = 1000;
+ private static final int MSG_ANIMATE_REVEAL = 1001;
+ private static final int MSG_SHOW_INTRUDER = 1002;
+ private static final int MSG_HIDE_INTRUDER = 1003;
+
+ // will likely move to a resource or other tunable param at some point
+ private static final int INTRUDER_ALERT_DECAY_MS = 10000;
+
+ StatusBarPolicy mIconPolicy;
+
+ int mIconSize;
+ Display mDisplay;
+
+ StatusBarView mStatusBarView;
+ int mPixelFormat;
+ H mHandler = new H();
+ Object mQueueLock = new Object();
+
+ // icons
+ LinearLayout mIcons;
+ IconMerger mNotificationIcons;
+ LinearLayout mStatusIcons;
+
+ // expanded notifications
+ Dialog mExpandedDialog;
+ ExpandedView mExpandedView;
+ WindowManager.LayoutParams mExpandedParams;
+ ScrollView mScrollView;
+ View mNotificationLinearLayout;
+ View mExpandedContents;
+ // top bar
+ TextView mNoNotificationsTitle;
+ TextView mClearButton;
+ // drag bar
+ CloseDragHandle mCloseView;
+ // ongoing
+ NotificationData mOngoing = new NotificationData();
+ TextView mOngoingTitle;
+ LinearLayout mOngoingItems;
+ // latest
+ NotificationData mLatest = new NotificationData();
+ TextView mLatestTitle;
+ LinearLayout mLatestItems;
+ // position
+ int[] mPositionTmp = new int[2];
+ boolean mExpanded;
+ boolean mExpandedVisible;
+
+ // the date view
+ DateView mDateView;
+
+ // for immersive activities
+ private View mIntruderAlertView;
+
+ // the tracker view
+ TrackingView mTrackingView;
+ WindowManager.LayoutParams mTrackingParams;
+ int mTrackingPosition; // the position of the top of the tracking view.
+ private boolean mPanelSlightlyVisible;
+
+ // ticker
+ private Ticker mTicker;
+ private View mTickerView;
+ private boolean mTicking;
+
+ // Tracking finger for opening/closing.
+ int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
+ boolean mTracking;
+ VelocityTracker mVelocityTracker;
+
+ static final int ANIM_FRAME_DURATION = (1000/60);
+
+ boolean mAnimating;
+ long mCurAnimationTime;
+ float mDisplayHeight;
+ float mAnimY;
+ float mAnimVel;
+ float mAnimAccel;
+ long mAnimLastTime;
+ boolean mAnimatingReveal = false;
+ int mViewDelta;
+ int[] mAbsPos = new int[2];
+
+ // for disabling the status bar
+ int mDisabled = 0;
+
+ private class ExpandedDialog extends Dialog {
+ ExpandedDialog(Context context) {
+ super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_BACK:
+ if (!down) {
+ animateCollapse();
+ }
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+
+ super.onCreate();
+
+ addIntruderView();
+
+ // Lastly, call to the icon policy to install/update all the icons.
+ mIconPolicy = new StatusBarPolicy(this);
+ }
+
+ @Override
+ public void onDestroy() {
+ // we're never destroyed
+ }
+
+ // ================================================================================
+ // Constructing the view
+ // ================================================================================
+ protected View makeStatusBarView() {
+ final Context context = this;
+
+ Resources res = context.getResources();
+
+ mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+
+ ExpandedView expanded = (ExpandedView)View.inflate(context,
+ R.layout.status_bar_expanded, null);
+ expanded.mService = this;
+
+ mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null);
+ mIntruderAlertView.setVisibility(View.GONE);
+ mIntruderAlertView.setClickable(true);
+
+ StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
+ sb.mService = this;
+
+ // figure out which pixel-format to use for the status bar.
+ mPixelFormat = PixelFormat.TRANSLUCENT;
+ Drawable bg = sb.getBackground();
+ if (bg != null) {
+ mPixelFormat = bg.getOpacity();
+ }
+
+ mStatusBarView = sb;
+ mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
+ mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
+ mIcons = (LinearLayout)sb.findViewById(R.id.icons);
+ mTickerView = sb.findViewById(R.id.ticker);
+ mDateView = (DateView)sb.findViewById(R.id.date);
+
+ mExpandedDialog = new ExpandedDialog(context);
+ mExpandedView = expanded;
+ mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
+ mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
+ mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
+ mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
+ mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
+ mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
+ mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
+ mClearButton.setOnClickListener(mClearButtonListener);
+ mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
+ mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
+
+ mOngoingTitle.setVisibility(View.GONE);
+ mLatestTitle.setVisibility(View.GONE);
+
+ mTicker = new MyTicker(context, sb);
+
+ TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
+ tickerView.mTicker = mTicker;
+
+ mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null);
+ mTrackingView.mService = this;
+ mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
+ mCloseView.mService = this;
+
+ mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
+ // the more notifications icon
+ StatusBarIconView moreView = new StatusBarIconView(this, "more");
+ moreView.set(new StatusBarIcon(null, R.drawable.stat_notify_more, 0));
+ mNotificationIcons.addMoreView(moreView,
+ new LinearLayout.LayoutParams(mIconSize, mIconSize));
+
+ // set the inital view visibility
+ setAreThereNotifications();
+ mDateView.setVisibility(View.INVISIBLE);
+
+ // receive broadcasts
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ context.registerReceiver(mBroadcastReceiver, filter);
+
+ return sb;
+ }
+
+ protected int getStatusBarGravity() {
+ return Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ }
+
+ private void addIntruderView() {
+ final Resources res = getResources();
+ final int height= res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ PixelFormat.TRANSLUCENT);
+ lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ lp.y += height * 1.5; // FIXME
+ lp.setTitle("IntruderAlert");
+ lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar_IntruderAlert;
+
+ WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp);
+ }
+
+ public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
+ if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+ + " icon=" + icon);
+ StatusBarIconView view = new StatusBarIconView(this, slot);
+ view.set(icon);
+ mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
+ }
+
+ public void updateIcon(String slot, int index, int viewIndex,
+ StatusBarIcon old, StatusBarIcon icon) {
+ if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+ + " old=" + old + " icon=" + icon);
+ StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
+ view.set(icon);
+ }
+
+ public void removeIcon(String slot, int index, int viewIndex) {
+ if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
+ mStatusIcons.removeViewAt(viewIndex);
+ }
+
+ public void addNotification(IBinder key, StatusBarNotification notification) {
+ StatusBarIconView iconView = addNotificationViews(key, notification);
+ if (iconView == null) return;
+
+ boolean immersive = false;
+ try {
+ immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
+ Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
+ } catch (RemoteException ex) {
+ }
+ if (immersive) {
+ if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
+ Slog.d(TAG, "Presenting high-priority notification in immersive activity");
+ // @@@ special new transient ticker mode
+ // 1. Populate mIntruderAlertView
+
+ ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon);
+ TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText);
+ alertIcon.setImageDrawable(StatusBarIconView.getIcon(
+ alertIcon.getContext(),
+ iconView.getStatusBarIcon()));
+ alertText.setText(notification.notification.tickerText);
+
+ View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content);
+ button.setOnClickListener(
+ new Launcher(notification.notification.contentIntent,
+ notification.pkg, notification.tag, notification.id));
+
+ // 2. Animate mIntruderAlertView in
+ mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
+
+ // 3. Set alarm to age the notification off (TODO)
+ mHandler.removeMessages(MSG_HIDE_INTRUDER);
+ mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
+ }
+ } else if (notification.notification.fullScreenIntent != null) {
+ // not immersive & a full-screen alert should be shown
+ Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;"
+ + " sending fullScreenIntent");
+ try {
+ notification.notification.fullScreenIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ }
+ } else {
+ // usual case: status bar visible & not immersive
+
+ // show the ticker
+ tick(notification);
+ }
+
+ // Recalculate the position of the sliding windows and the titles.
+ setAreThereNotifications();
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ }
+
+ public void updateNotification(IBinder key, StatusBarNotification notification) {
+ Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification);
+
+ NotificationData oldList;
+ int oldIndex = mOngoing.findEntry(key);
+ if (oldIndex >= 0) {
+ oldList = mOngoing;
+ } else {
+ oldIndex = mLatest.findEntry(key);
+ if (oldIndex < 0) {
+ Slog.w(TAG, "updateNotification for unknown key: " + key);
+ return;
+ }
+ oldList = mLatest;
+ }
+ final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex);
+ final StatusBarNotification oldNotification = oldEntry.notification;
+ final RemoteViews oldContentView = oldNotification.notification.contentView;
+
+ final RemoteViews contentView = notification.notification.contentView;
+
+ if (false) {
+ Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
+ + " ongoing=" + oldNotification.isOngoing()
+ + " expanded=" + oldEntry.expanded
+ + " contentView=" + oldContentView);
+ Slog.d(TAG, "new notification: when=" + notification.notification.when
+ + " ongoing=" + oldNotification.isOngoing()
+ + " contentView=" + contentView);
+ }
+
+ // Can we just reapply the RemoteViews in place? If when didn't change, the order
+ // didn't change.
+ if (notification.notification.when == oldNotification.notification.when
+ && notification.isOngoing() == oldNotification.isOngoing()
+ && oldEntry.expanded != null
+ && contentView != null && oldContentView != null
+ && contentView.getPackage() != null
+ && oldContentView.getPackage() != null
+ && oldContentView.getPackage().equals(contentView.getPackage())
+ && oldContentView.getLayoutId() == contentView.getLayoutId()) {
+ if (SPEW) Slog.d(TAG, "reusing notification");
+ oldEntry.notification = notification;
+ try {
+ // Reapply the RemoteViews
+ contentView.reapply(this, oldEntry.content);
+ // update the contentIntent
+ final PendingIntent contentIntent = notification.notification.contentIntent;
+ if (contentIntent != null) {
+ oldEntry.content.setOnClickListener(new Launcher(contentIntent,
+ notification.pkg, notification.tag, notification.id));
+ }
+ // Update the icon.
+ final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
+ notification.notification.icon, notification.notification.iconLevel,
+ notification.notification.number);
+ if (!oldEntry.icon.set(ic)) {
+ handleNotificationError(key, notification, "Couldn't update icon: " + ic);
+ return;
+ }
+ }
+ catch (RuntimeException e) {
+ // It failed to add cleanly. Log, and remove the view from the panel.
+ Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
+ removeNotificationViews(key);
+ addNotificationViews(key, notification);
+ }
+ } else {
+ if (SPEW) Slog.d(TAG, "not reusing notification");
+ removeNotificationViews(key);
+ addNotificationViews(key, notification);
+ }
+
+ // Restart the ticker if it's still running
+ tick(notification);
+
+ // Recalculate the position of the sliding windows and the titles.
+ setAreThereNotifications();
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ }
+
+ public void removeNotification(IBinder key) {
+ if (SPEW) Slog.d(TAG, "removeNotification key=" + key);
+ StatusBarNotification old = removeNotificationViews(key);
+
+ if (old != null) {
+ // Cancel the ticker if it's still running
+ mTicker.removeEntry(old);
+
+ // Recalculate the position of the sliding windows and the titles.
+ setAreThereNotifications();
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ }
+ }
+
+ private int chooseIconIndex(boolean isOngoing, int viewIndex) {
+ final int latestSize = mLatest.size();
+ if (isOngoing) {
+ return latestSize + (mOngoing.size() - viewIndex);
+ } else {
+ return latestSize - viewIndex;
+ }
+ }
+
+ View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
+ Notification n = notification.notification;
+ RemoteViews remoteViews = n.contentView;
+ if (remoteViews == null) {
+ return null;
+ }
+
+ // create the row view
+ LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
+
+ // bind the click event to the content area
+ ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
+ content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ content.setOnFocusChangeListener(mFocusChangeListener);
+ PendingIntent contentIntent = n.contentIntent;
+ if (contentIntent != null) {
+ content.setOnClickListener(new Launcher(contentIntent, notification.pkg,
+ notification.tag, notification.id));
+ }
+
+ View expanded = null;
+ Exception exception = null;
+ try {
+ expanded = remoteViews.apply(this, content);
+ }
+ catch (RuntimeException e) {
+ exception = e;
+ }
+ if (expanded == null) {
+ String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id);
+ Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
+ return null;
+ } else {
+ content.addView(expanded);
+ row.setDrawingCacheEnabled(true);
+ }
+
+ return new View[] { row, content, expanded };
+ }
+
+ StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
+ NotificationData list;
+ ViewGroup parent;
+ final boolean isOngoing = notification.isOngoing();
+ if (isOngoing) {
+ list = mOngoing;
+ parent = mOngoingItems;
+ } else {
+ list = mLatest;
+ parent = mLatestItems;
+ }
+ // Construct the expanded view.
+ final View[] views = makeNotificationView(notification, parent);
+ if (views == null) {
+ handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+ + notification);
+ return null;
+ }
+ final View row = views[0];
+ final View content = views[1];
+ final View expanded = views[2];
+ // Construct the icon.
+ final StatusBarIconView iconView = new StatusBarIconView(this,
+ notification.pkg + "/0x" + Integer.toHexString(notification.id));
+ final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
+ notification.notification.iconLevel, notification.notification.number);
+ if (!iconView.set(ic)) {
+ handleNotificationError(key, notification, "Coulding create icon: " + ic);
+ return null;
+ }
+ // Add the expanded view.
+ final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
+ parent.addView(row, viewIndex);
+ // Add the icon.
+ final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
+ mNotificationIcons.addView(iconView, iconIndex,
+ new LinearLayout.LayoutParams(mIconSize, mIconSize));
+ return iconView;
+ }
+
+ StatusBarNotification removeNotificationViews(IBinder key) {
+ NotificationData.Entry entry = mOngoing.remove(key);
+ if (entry == null) {
+ entry = mLatest.remove(key);
+ if (entry == null) {
+ Slog.w(TAG, "removeNotification for unknown key: " + key);
+ return null;
+ }
+ }
+ // Remove the expanded view.
+ ((ViewGroup)entry.row.getParent()).removeView(entry.row);
+ // Remove the icon.
+ ((ViewGroup)entry.icon.getParent()).removeView(entry.icon);
+
+ return entry.notification;
+ }
+
+ private void setAreThereNotifications() {
+ boolean ongoing = mOngoing.hasVisibleItems();
+ boolean latest = mLatest.hasVisibleItems();
+
+ // (no ongoing notifications are clearable)
+ if (mLatest.hasClearableItems()) {
+ mClearButton.setVisibility(View.VISIBLE);
+ } else {
+ mClearButton.setVisibility(View.INVISIBLE);
+ }
+
+ mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
+ mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
+
+ if (ongoing || latest) {
+ mNoNotificationsTitle.setVisibility(View.GONE);
+ } else {
+ mNoNotificationsTitle.setVisibility(View.VISIBLE);
+ }
+ }
+
+
+ /**
+ * State is one or more of the DISABLE constants from StatusBarManager.
+ */
+ public void disable(int state) {
+ final int old = mDisabled;
+ final int diff = state ^ old;
+ mDisabled = state;
+
+ if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+ if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
+ Slog.d(TAG, "DISABLE_EXPAND: yes");
+ animateCollapse();
+ }
+ }
+ if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+ if (mTicking) {
+ mTicker.halt();
+ } else {
+ setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
+ }
+ } else {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+ if (!mExpandedVisible) {
+ setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
+ mTicker.halt();
+ }
+ }
+ }
+
+ /**
+ * All changes to the status bar and notifications funnel through here and are batched.
+ */
+ private class H extends Handler {
+ public void handleMessage(Message m) {
+ switch (m.what) {
+ case MSG_ANIMATE:
+ doAnimation();
+ break;
+ case MSG_ANIMATE_REVEAL:
+ doRevealAnimation();
+ break;
+ case MSG_SHOW_INTRUDER:
+ setIntruderAlertVisibility(true);
+ break;
+ case MSG_HIDE_INTRUDER:
+ setIntruderAlertVisibility(false);
+ break;
+ }
+ }
+ }
+
+ View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
+ public void onFocusChange(View v, boolean hasFocus) {
+ // Because 'v' is a ViewGroup, all its children will be (un)selected
+ // too, which allows marqueeing to work.
+ v.setSelected(hasFocus);
+ }
+ };
+
+ private void makeExpandedVisible() {
+ if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
+ if (mExpandedVisible) {
+ return;
+ }
+ mExpandedVisible = true;
+ visibilityChanged(true);
+
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ mExpandedView.requestFocus(View.FOCUS_FORWARD);
+ mTrackingView.setVisibility(View.VISIBLE);
+
+ if (!mTicking) {
+ setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+
+ public void animateExpand() {
+ if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
+ if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+ return ;
+ }
+ if (mExpanded) {
+ return;
+ }
+
+ prepareTracking(0, true);
+ performFling(0, 2000.0f, true);
+ }
+
+ public void animateCollapse() {
+ if (SPEW) {
+ Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
+ + " mExpandedVisible=" + mExpandedVisible
+ + " mExpanded=" + mExpanded
+ + " mAnimating=" + mAnimating
+ + " mAnimY=" + mAnimY
+ + " mAnimVel=" + mAnimVel);
+ }
+
+ if (!mExpandedVisible) {
+ return;
+ }
+
+ int y;
+ if (mAnimating) {
+ y = (int)mAnimY;
+ } else {
+ y = mDisplay.getHeight()-1;
+ }
+ // Let the fling think that we're open so it goes in the right direction
+ // and doesn't try to re-open the windowshade.
+ mExpanded = true;
+ prepareTracking(y, false);
+ performFling(y, -2000.0f, true);
+ }
+
+ void performExpand() {
+ if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
+ if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+ return ;
+ }
+ if (mExpanded) {
+ return;
+ }
+
+ mExpanded = true;
+ makeExpandedVisible();
+ updateExpandedViewPos(EXPANDED_FULL_OPEN);
+
+ if (false) postStartTracing();
+ }
+
+ void performCollapse() {
+ if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
+ + " mExpandedVisible=" + mExpandedVisible);
+
+ if (!mExpandedVisible) {
+ return;
+ }
+ mExpandedVisible = false;
+ visibilityChanged(false);
+ mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ mTrackingView.setVisibility(View.GONE);
+
+ if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
+ setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
+
+ if (!mExpanded) {
+ return;
+ }
+ mExpanded = false;
+ }
+
+ void doAnimation() {
+ if (mAnimating) {
+ if (SPEW) Slog.d(TAG, "doAnimation");
+ if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
+ incrementAnim();
+ if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY);
+ if (mAnimY >= mDisplay.getHeight()-1) {
+ if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
+ mAnimating = false;
+ updateExpandedViewPos(EXPANDED_FULL_OPEN);
+ performExpand();
+ }
+ else if (mAnimY < mStatusBarView.getHeight()) {
+ if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
+ mAnimating = false;
+ updateExpandedViewPos(0);
+ performCollapse();
+ }
+ else {
+ updateExpandedViewPos((int)mAnimY);
+ mCurAnimationTime += ANIM_FRAME_DURATION;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+ }
+ }
+ }
+
+ void stopTracking() {
+ mTracking = false;
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+
+ void incrementAnim() {
+ long now = SystemClock.uptimeMillis();
+ float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s
+ final float y = mAnimY;
+ final float v = mAnimVel; // px/s
+ final float a = mAnimAccel; // px/s/s
+ mAnimY = y + (v*t) + (0.5f*a*t*t); // px
+ mAnimVel = v + (a*t); // px/s
+ mAnimLastTime = now; // ms
+ //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
+ // + " mAnimAccel=" + mAnimAccel);
+ }
+
+ void doRevealAnimation() {
+ final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
+ if (mAnimatingReveal && mAnimating && mAnimY < h) {
+ incrementAnim();
+ if (mAnimY >= h) {
+ mAnimY = h;
+ updateExpandedViewPos((int)mAnimY);
+ } else {
+ updateExpandedViewPos((int)mAnimY);
+ mCurAnimationTime += ANIM_FRAME_DURATION;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+ mCurAnimationTime);
+ }
+ }
+ }
+
+ void prepareTracking(int y, boolean opening) {
+ mTracking = true;
+ mVelocityTracker = VelocityTracker.obtain();
+ if (opening) {
+ mAnimAccel = 2000.0f;
+ mAnimVel = 200;
+ mAnimY = mStatusBarView.getHeight();
+ updateExpandedViewPos((int)mAnimY);
+ mAnimating = true;
+ mAnimatingReveal = true;
+ mHandler.removeMessages(MSG_ANIMATE);
+ mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+ long now = SystemClock.uptimeMillis();
+ mAnimLastTime = now;
+ mCurAnimationTime = now + ANIM_FRAME_DURATION;
+ mAnimating = true;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+ mCurAnimationTime);
+ makeExpandedVisible();
+ } else {
+ // it's open, close it?
+ if (mAnimating) {
+ mAnimating = false;
+ mHandler.removeMessages(MSG_ANIMATE);
+ }
+ updateExpandedViewPos(y + mViewDelta);
+ }
+ }
+
+ void performFling(int y, float vel, boolean always) {
+ mAnimatingReveal = false;
+ mDisplayHeight = mDisplay.getHeight();
+
+ mAnimY = y;
+ mAnimVel = vel;
+
+ //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
+
+ if (mExpanded) {
+ if (!always && (
+ vel > 200.0f
+ || (y > (mDisplayHeight-25) && vel > -200.0f))) {
+ // We are expanded, but they didn't move sufficiently to cause
+ // us to retract. Animate back to the expanded position.
+ mAnimAccel = 2000.0f;
+ if (vel < 0) {
+ mAnimVel = 0;
+ }
+ }
+ else {
+ // We are expanded and are now going to animate away.
+ mAnimAccel = -2000.0f;
+ if (vel > 0) {
+ mAnimVel = 0;
+ }
+ }
+ } else {
+ if (always || (
+ vel > 200.0f
+ || (y > (mDisplayHeight/2) && vel > -200.0f))) {
+ // We are collapsed, and they moved enough to allow us to
+ // expand. Animate in the notifications.
+ mAnimAccel = 2000.0f;
+ if (vel < 0) {
+ mAnimVel = 0;
+ }
+ }
+ else {
+ // We are collapsed, but they didn't move sufficiently to cause
+ // us to retract. Animate back to the collapsed position.
+ mAnimAccel = -2000.0f;
+ if (vel > 0) {
+ mAnimVel = 0;
+ }
+ }
+ }
+ //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
+ // + " mAnimAccel=" + mAnimAccel);
+
+ long now = SystemClock.uptimeMillis();
+ mAnimLastTime = now;
+ mCurAnimationTime = now + ANIM_FRAME_DURATION;
+ mAnimating = true;
+ mHandler.removeMessages(MSG_ANIMATE);
+ mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+ stopTracking();
+ }
+
+ boolean interceptTouchEvent(MotionEvent event) {
+ if (SPEW) {
+ Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
+ + mDisabled);
+ }
+
+ if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+ return false;
+ }
+
+ final int statusBarSize = mStatusBarView.getHeight();
+ final int hitSize = statusBarSize*2;
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ final int y = (int)event.getRawY();
+
+ if (!mExpanded) {
+ mViewDelta = statusBarSize - y;
+ } else {
+ mTrackingView.getLocationOnScreen(mAbsPos);
+ mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
+ }
+ if ((!mExpanded && y < hitSize) ||
+ (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
+
+ // We drop events at the edge of the screen to make the windowshade come
+ // down by accident less, especially when pushing open a device with a keyboard
+ // that rotates (like g1 and droid)
+ int x = (int)event.getRawX();
+ final int edgeBorder = mEdgeBorder;
+ if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
+ prepareTracking(y, !mExpanded);// opening if we're not already fully visible
+ mVelocityTracker.addMovement(event);
+ }
+ }
+ } else if (mTracking) {
+ mVelocityTracker.addMovement(event);
+ final int minY = statusBarSize + mCloseView.getHeight();
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ int y = (int)event.getRawY();
+ if (mAnimatingReveal && y < minY) {
+ // nothing
+ } else {
+ mAnimatingReveal = false;
+ updateExpandedViewPos(y + mViewDelta);
+ }
+ } else if (event.getAction() == MotionEvent.ACTION_UP) {
+ mVelocityTracker.computeCurrentVelocity(1000);
+
+ float yVel = mVelocityTracker.getYVelocity();
+ boolean negative = yVel < 0;
+
+ float xVel = mVelocityTracker.getXVelocity();
+ if (xVel < 0) {
+ xVel = -xVel;
+ }
+ if (xVel > 150.0f) {
+ xVel = 150.0f; // limit how much we care about the x axis
+ }
+
+ float vel = (float)Math.hypot(yVel, xVel);
+ if (negative) {
+ vel = -vel;
+ }
+
+ performFling((int)event.getRawY(), vel, false);
+ }
+
+ }
+ return false;
+ }
+
+ private class Launcher implements View.OnClickListener {
+ private PendingIntent mIntent;
+ private String mPkg;
+ private String mTag;
+ private int mId;
+
+ Launcher(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();
+ } 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(PhoneStatusBarService.this, 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);
+ }
+ }
+
+ 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();
+
+ // If this click was on the intruder alert, hide that instead
+ mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
+ }
+ }
+
+ private void tick(StatusBarNotification n) {
+ // Show the ticker if one is requested. Also don't do this
+ // until status bar window is attached to the window manager,
+ // because... well, what's the point otherwise? And trying to
+ // run a ticker without being attached will crash!
+ if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
+ if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
+ | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
+ mTicker.addEntry(n);
+ }
+ }
+ }
+
+ /**
+ * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+ * about the failure.
+ *
+ * WARNING: this will call back into us. Don't hold any locks.
+ */
+ void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
+ removeNotification(key);
+ try {
+ mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
+ } catch (RemoteException ex) {
+ // The end is nigh.
+ }
+ }
+
+ private class MyTicker extends Ticker {
+ MyTicker(Context context, StatusBarView sb) {
+ super(context, sb);
+ }
+
+ @Override
+ void tickerStarting() {
+ mTicking = true;
+ mIcons.setVisibility(View.GONE);
+ mTickerView.setVisibility(View.VISIBLE);
+ mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
+ mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
+ if (mExpandedVisible) {
+ setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
+ }
+ }
+
+ @Override
+ void tickerDone() {
+ mIcons.setVisibility(View.VISIBLE);
+ mTickerView.setVisibility(View.GONE);
+ mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
+ mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
+ mTickingDoneListener));
+ if (mExpandedVisible) {
+ setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
+ }
+ }
+
+ void tickerHalting() {
+ mIcons.setVisibility(View.VISIBLE);
+ mTickerView.setVisibility(View.GONE);
+ mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
+ mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
+ mTickingDoneListener));
+ if (mExpandedVisible) {
+ setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+ }
+
+ Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
+ public void onAnimationEnd(Animation animation) {
+ mTicking = false;
+ }
+ public void onAnimationRepeat(Animation animation) {
+ }
+ public void onAnimationStart(Animation animation) {
+ }
+ };
+
+ private Animation loadAnim(int id, Animation.AnimationListener listener) {
+ Animation anim = AnimationUtils.loadAnimation(PhoneStatusBarService.this, id);
+ if (listener != null) {
+ anim.setAnimationListener(listener);
+ }
+ return anim;
+ }
+
+ public String viewInfo(View v) {
+ return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
+ + " " + v.getWidth() + "x" + v.getHeight() + ")";
+ }
+
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump StatusBar from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mQueueLock) {
+ pw.println("Current Status Bar state:");
+ pw.println(" mExpanded=" + mExpanded
+ + ", mExpandedVisible=" + mExpandedVisible);
+ pw.println(" mTicking=" + mTicking);
+ pw.println(" mTracking=" + mTracking);
+ pw.println(" mAnimating=" + mAnimating
+ + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
+ + ", mAnimAccel=" + mAnimAccel);
+ pw.println(" mCurAnimationTime=" + mCurAnimationTime
+ + " mAnimLastTime=" + mAnimLastTime);
+ pw.println(" mDisplayHeight=" + mDisplayHeight
+ + " mAnimatingReveal=" + mAnimatingReveal
+ + " mViewDelta=" + mViewDelta);
+ pw.println(" mDisplayHeight=" + mDisplayHeight);
+ pw.println(" mExpandedParams: " + mExpandedParams);
+ pw.println(" mExpandedView: " + viewInfo(mExpandedView));
+ pw.println(" mExpandedDialog: " + mExpandedDialog);
+ pw.println(" mTrackingParams: " + mTrackingParams);
+ pw.println(" mTrackingView: " + viewInfo(mTrackingView));
+ pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle));
+ pw.println(" mOngoingItems: " + viewInfo(mOngoingItems));
+ pw.println(" mLatestTitle: " + viewInfo(mLatestTitle));
+ pw.println(" mLatestItems: " + viewInfo(mLatestItems));
+ pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
+ pw.println(" mCloseView: " + viewInfo(mCloseView));
+ pw.println(" mTickerView: " + viewInfo(mTickerView));
+ pw.println(" mScrollView: " + viewInfo(mScrollView)
+ + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
+ pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
+ }
+ /*
+ synchronized (mNotificationData) {
+ int N = mNotificationData.ongoingCount();
+ pw.println(" ongoingCount.size=" + N);
+ for (int i=0; i s
- final float y = mAnimY;
- final float v = mAnimVel; // px/s
- final float a = mAnimAccel; // px/s/s
- mAnimY = y + (v*t) + (0.5f*a*t*t); // px
- mAnimVel = v + (a*t); // px/s
- mAnimLastTime = now; // ms
- //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
- // + " mAnimAccel=" + mAnimAccel);
- }
-
- void doRevealAnimation() {
- final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
- if (mAnimatingReveal && mAnimating && mAnimY < h) {
- incrementAnim();
- if (mAnimY >= h) {
- mAnimY = h;
- updateExpandedViewPos((int)mAnimY);
- } else {
- updateExpandedViewPos((int)mAnimY);
- mCurAnimationTime += ANIM_FRAME_DURATION;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
- mCurAnimationTime);
- }
- }
- }
-
- void prepareTracking(int y, boolean opening) {
- mTracking = true;
- mVelocityTracker = VelocityTracker.obtain();
- if (opening) {
- mAnimAccel = 2000.0f;
- mAnimVel = 200;
- mAnimY = mStatusBarView.getHeight();
- updateExpandedViewPos((int)mAnimY);
- mAnimating = true;
- mAnimatingReveal = true;
- mHandler.removeMessages(MSG_ANIMATE);
- mHandler.removeMessages(MSG_ANIMATE_REVEAL);
- long now = SystemClock.uptimeMillis();
- mAnimLastTime = now;
- mCurAnimationTime = now + ANIM_FRAME_DURATION;
- mAnimating = true;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
- mCurAnimationTime);
- makeExpandedVisible();
- } else {
- // it's open, close it?
- if (mAnimating) {
- mAnimating = false;
- mHandler.removeMessages(MSG_ANIMATE);
- }
- updateExpandedViewPos(y + mViewDelta);
- }
- }
-
- void performFling(int y, float vel, boolean always) {
- mAnimatingReveal = false;
- mDisplayHeight = mDisplay.getHeight();
-
- mAnimY = y;
- mAnimVel = vel;
-
- //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
-
- if (mExpanded) {
- if (!always && (
- vel > 200.0f
- || (y > (mDisplayHeight-25) && vel > -200.0f))) {
- // We are expanded, but they didn't move sufficiently to cause
- // us to retract. Animate back to the expanded position.
- mAnimAccel = 2000.0f;
- if (vel < 0) {
- mAnimVel = 0;
- }
- }
- else {
- // We are expanded and are now going to animate away.
- mAnimAccel = -2000.0f;
- if (vel > 0) {
- mAnimVel = 0;
- }
- }
- } else {
- if (always || (
- vel > 200.0f
- || (y > (mDisplayHeight/2) && vel > -200.0f))) {
- // We are collapsed, and they moved enough to allow us to
- // expand. Animate in the notifications.
- mAnimAccel = 2000.0f;
- if (vel < 0) {
- mAnimVel = 0;
- }
- }
- else {
- // We are collapsed, but they didn't move sufficiently to cause
- // us to retract. Animate back to the collapsed position.
- mAnimAccel = -2000.0f;
- if (vel > 0) {
- mAnimVel = 0;
- }
- }
- }
- //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
- // + " mAnimAccel=" + mAnimAccel);
-
- long now = SystemClock.uptimeMillis();
- mAnimLastTime = now;
- mCurAnimationTime = now + ANIM_FRAME_DURATION;
- mAnimating = true;
- mHandler.removeMessages(MSG_ANIMATE);
- mHandler.removeMessages(MSG_ANIMATE_REVEAL);
- mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
- stopTracking();
- }
-
- boolean interceptTouchEvent(MotionEvent event) {
- if (SPEW) {
- Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
- + mDisabled);
- }
-
- if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
- return false;
- }
-
- final int statusBarSize = mStatusBarView.getHeight();
- final int hitSize = statusBarSize*2;
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- final int y = (int)event.getRawY();
-
- if (!mExpanded) {
- mViewDelta = statusBarSize - y;
- } else {
- mTrackingView.getLocationOnScreen(mAbsPos);
- mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
- }
- if ((!mExpanded && y < hitSize) ||
- (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
-
- // We drop events at the edge of the screen to make the windowshade come
- // down by accident less, especially when pushing open a device with a keyboard
- // that rotates (like g1 and droid)
- int x = (int)event.getRawX();
- final int edgeBorder = mEdgeBorder;
- if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
- prepareTracking(y, !mExpanded);// opening if we're not already fully visible
- mVelocityTracker.addMovement(event);
- }
- }
- } else if (mTracking) {
- mVelocityTracker.addMovement(event);
- final int minY = statusBarSize + mCloseView.getHeight();
- if (event.getAction() == MotionEvent.ACTION_MOVE) {
- int y = (int)event.getRawY();
- if (mAnimatingReveal && y < minY) {
- // nothing
- } else {
- mAnimatingReveal = false;
- updateExpandedViewPos(y + mViewDelta);
- }
- } else if (event.getAction() == MotionEvent.ACTION_UP) {
- mVelocityTracker.computeCurrentVelocity(1000);
-
- float yVel = mVelocityTracker.getYVelocity();
- boolean negative = yVel < 0;
-
- float xVel = mVelocityTracker.getXVelocity();
- if (xVel < 0) {
- xVel = -xVel;
- }
- if (xVel > 150.0f) {
- xVel = 150.0f; // limit how much we care about the x axis
- }
-
- float vel = (float)Math.hypot(yVel, xVel);
- if (negative) {
- vel = -vel;
- }
-
- performFling((int)event.getRawY(), vel, false);
- }
-
- }
- return false;
- }
-
- private class Launcher implements View.OnClickListener {
- private PendingIntent mIntent;
- private String mPkg;
- private String mTag;
- private int mId;
-
- Launcher(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();
- } 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(StatusBarService.this, 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);
- }
- }
-
- 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();
-
- // If this click was on the intruder alert, hide that instead
- mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
- }
- }
-
- private void tick(StatusBarNotification n) {
- // Show the ticker if one is requested. Also don't do this
- // until status bar window is attached to the window manager,
- // because... well, what's the point otherwise? And trying to
- // run a ticker without being attached will crash!
- if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
- if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
- | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
- mTicker.addEntry(n);
- }
- }
- }
-
- /**
- * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
- * about the failure.
- *
- * WARNING: this will call back into us. Don't hold any locks.
- */
- void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
- removeNotification(key);
- try {
- mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
- } catch (RemoteException ex) {
- // The end is nigh.
- }
- }
-
- private class MyTicker extends Ticker {
- MyTicker(Context context, StatusBarView sb) {
- super(context, sb);
- }
-
- @Override
- void tickerStarting() {
- mTicking = true;
- mIcons.setVisibility(View.GONE);
- mTickerView.setVisibility(View.VISIBLE);
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
- if (mExpandedVisible) {
- setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
- }
- }
-
- @Override
- void tickerDone() {
- mIcons.setVisibility(View.VISIBLE);
- mTickerView.setVisibility(View.GONE);
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
- mTickingDoneListener));
- if (mExpandedVisible) {
- setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
- }
- }
-
- void tickerHalting() {
- mIcons.setVisibility(View.VISIBLE);
- mTickerView.setVisibility(View.GONE);
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
- mTickingDoneListener));
- if (mExpandedVisible) {
- setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
- }
-
- Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
- public void onAnimationEnd(Animation animation) {
- mTicking = false;
- }
- public void onAnimationRepeat(Animation animation) {
- }
- public void onAnimationStart(Animation animation) {
- }
- };
-
- private Animation loadAnim(int id, Animation.AnimationListener listener) {
- Animation anim = AnimationUtils.loadAnimation(StatusBarService.this, id);
- if (listener != null) {
- anim.setAnimationListener(listener);
- }
- return anim;
- }
-
- public String viewInfo(View v) {
- return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
- + " " + v.getWidth() + "x" + v.getHeight() + ")";
- }
-
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump StatusBar from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
- synchronized (mQueueLock) {
- pw.println("Current Status Bar state:");
- pw.println(" mExpanded=" + mExpanded
- + ", mExpandedVisible=" + mExpandedVisible);
- pw.println(" mTicking=" + mTicking);
- pw.println(" mTracking=" + mTracking);
- pw.println(" mAnimating=" + mAnimating
- + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
- + ", mAnimAccel=" + mAnimAccel);
- pw.println(" mCurAnimationTime=" + mCurAnimationTime
- + " mAnimLastTime=" + mAnimLastTime);
- pw.println(" mDisplayHeight=" + mDisplayHeight
- + " mAnimatingReveal=" + mAnimatingReveal
- + " mViewDelta=" + mViewDelta);
- pw.println(" mDisplayHeight=" + mDisplayHeight);
- pw.println(" mExpandedParams: " + mExpandedParams);
- pw.println(" mExpandedView: " + viewInfo(mExpandedView));
- pw.println(" mExpandedDialog: " + mExpandedDialog);
- pw.println(" mTrackingParams: " + mTrackingParams);
- pw.println(" mTrackingView: " + viewInfo(mTrackingView));
- pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle));
- pw.println(" mOngoingItems: " + viewInfo(mOngoingItems));
- pw.println(" mLatestTitle: " + viewInfo(mLatestTitle));
- pw.println(" mLatestItems: " + viewInfo(mLatestItems));
- pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
- pw.println(" mCloseView: " + viewInfo(mCloseView));
- pw.println(" mTickerView: " + viewInfo(mTickerView));
- pw.println(" mScrollView: " + viewInfo(mScrollView)
- + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
- pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
- }
- /*
- synchronized (mNotificationData) {
- int N = mNotificationData.ongoingCount();
- pw.println(" ongoingCount.size=" + N);
- for (int i=0; i