blob: 6f74924d6d7cf437ffb20efae8558f02a9b58f57 [file] [log] [blame]
Joe Onorato808182d2010-07-09 18:52:06 -04001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.statusbar.tablet;
18
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040019import android.app.ActivityManagerNative;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040020import android.app.PendingIntent;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040021import android.app.StatusBarManager;
Joe Onorato808182d2010-07-09 18:52:06 -040022import android.content.Context;
23import android.content.Intent;
24import android.content.res.Resources;
Daniel Sandler9120d552010-07-23 09:11:14 -040025import android.graphics.PixelFormat;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040026import android.graphics.Rect;
Daniel Sandler9120d552010-07-23 09:11:14 -040027import android.os.Handler;
Joe Onorato808182d2010-07-09 18:52:06 -040028import android.os.IBinder;
Daniel Sandler3eebd1f2010-07-27 08:39:33 -040029import android.os.Message;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040030import android.os.RemoteException;
Daniel Sandler3eebd1f2010-07-27 08:39:33 -040031import android.util.Slog;
Daniel Sandlerce70d912010-09-02 11:59:41 -040032import android.view.animation.AnimationUtils;
Joe Onorato808182d2010-07-09 18:52:06 -040033import android.view.Gravity;
Daniel Sandler3eebd1f2010-07-27 08:39:33 -040034import android.view.LayoutInflater;
Joe Onorato808182d2010-07-09 18:52:06 -040035import android.view.View;
Daniel Sandler9120d552010-07-23 09:11:14 -040036import android.view.ViewGroup;
37import android.view.WindowManager;
38import android.view.WindowManagerImpl;
Daniel Sandler3eebd1f2010-07-27 08:39:33 -040039import android.widget.ImageView;
Joe Onorato808182d2010-07-09 18:52:06 -040040import android.widget.LinearLayout;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040041import android.widget.RemoteViews;
42import android.widget.ScrollView;
43import android.widget.TextSwitcher;
Daniel Sandler9120d552010-07-23 09:11:14 -040044import android.widget.TextView;
Joe Onorato808182d2010-07-09 18:52:06 -040045
46import com.android.internal.statusbar.StatusBarIcon;
Joe Onorato808182d2010-07-09 18:52:06 -040047import com.android.internal.statusbar.StatusBarNotification;
48
49import com.android.systemui.statusbar.*;
Joe Onorato013cfc12010-09-08 15:23:18 -040050import com.android.systemui.recent.RecentApplicationsActivity;
Joe Onorato808182d2010-07-09 18:52:06 -040051import com.android.systemui.R;
52
53public class TabletStatusBarService extends StatusBarService {
Daniel Sandlerfb970e92010-08-20 10:57:17 -040054 public static final boolean DEBUG = false;
Daniel Sandler9120d552010-07-23 09:11:14 -040055 public static final String TAG = "TabletStatusBar";
Joe Onorato808182d2010-07-09 18:52:06 -040056
Daniel Sandlerd39e3882010-08-31 14:16:13 -040057 private static final int MAX_IMAGE_LEVEL = 10000;
58
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040059
Joe Onorato808182d2010-07-09 18:52:06 -040060
61 int mIconSize;
Daniel Sandler9120d552010-07-23 09:11:14 -040062
63 H mHandler = new H();
64
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040065 // tracking all current notifications
66 private NotificationData mNotns = new NotificationData();
67
68 View mStatusBarView;
69 NotificationIconArea mNotificationIconArea;
70
Daniel Sandler0f0b11c2010-08-04 15:54:58 -040071 View mNotificationPanel;
Daniel Sandler6425ef92010-08-11 16:10:19 -040072 SystemPanel mSystemPanel;
Daniel Sandler3eebd1f2010-07-27 08:39:33 -040073
Daniel Sandler0f0b11c2010-08-04 15:54:58 -040074 ViewGroup mPile;
75 TextView mClearButton;
Daniel Sandler3eebd1f2010-07-27 08:39:33 -040076
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -040077 ImageView mBatteryMeter;
78 ImageView mSignalMeter;
Daniel Sandlerd39e3882010-08-31 14:16:13 -040079 ImageView mSignalIcon;
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -040080
Daniel Sandlerce70d912010-09-02 11:59:41 -040081 View mBarContents;
82 View mCurtains;
83
Daniel Sandler0f0b11c2010-08-04 15:54:58 -040084 NotificationIconArea.IconLayout mIconLayout;
85
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040086 KickerController mKicker;
87 View mKickerView;
88 boolean mTicking;
89 boolean mExpandedVisible;
90
91 // for disabling the status bar
92 int mDisabled = 0;
93
Daniel Sandler9120d552010-07-23 09:11:14 -040094 protected void addPanelWindows() {
Daniel Sandler9120d552010-07-23 09:11:14 -040095 final Resources res = getResources();
96 final int barHeight= res.getDimensionPixelSize(
97 com.android.internal.R.dimen.status_bar_height);
98
Daniel Sandlerd39e3882010-08-31 14:16:13 -040099 mNotificationPanel = View.inflate(this, R.layout.sysbar_panel_notifications, null);
100 mNotificationPanel.setVisibility(View.GONE);
101
Daniel Sandler9120d552010-07-23 09:11:14 -0400102 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400103 400, // ViewGroup.LayoutParams.WRAP_CONTENT,
Daniel Sandler9120d552010-07-23 09:11:14 -0400104 ViewGroup.LayoutParams.WRAP_CONTENT,
105 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
106 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Daniel Sandler9120d552010-07-23 09:11:14 -0400107 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400108 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
Daniel Sandler9120d552010-07-23 09:11:14 -0400109 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
110 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
111 PixelFormat.TRANSLUCENT);
112 lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
113 lp.setTitle("NotificationPanel");
114 lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard;
115
116 WindowManagerImpl.getDefault().addView(mNotificationPanel, lp);
117
Daniel Sandlerd39e3882010-08-31 14:16:13 -0400118 mSystemPanel = (SystemPanel) View.inflate(this, R.layout.sysbar_panel_system, null);
119 mSystemPanel.setVisibility(View.GONE);
120
Daniel Sandler9120d552010-07-23 09:11:14 -0400121 lp = new WindowManager.LayoutParams(
Daniel Sandlerd39e3882010-08-31 14:16:13 -0400122 800,
Daniel Sandler6425ef92010-08-11 16:10:19 -0400123 ViewGroup.LayoutParams.WRAP_CONTENT,
Daniel Sandler9120d552010-07-23 09:11:14 -0400124 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
125 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Daniel Sandler9120d552010-07-23 09:11:14 -0400126 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400127 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
Daniel Sandler9120d552010-07-23 09:11:14 -0400128 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
129 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
130 PixelFormat.TRANSLUCENT);
131 lp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
132 lp.setTitle("SystemPanel");
133 lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard;
134
135 WindowManagerImpl.getDefault().addView(mSystemPanel, lp);
Daniel Sandler6425ef92010-08-11 16:10:19 -0400136 mSystemPanel.setBar(this);
Daniel Sandler9120d552010-07-23 09:11:14 -0400137 }
Joe Onorato808182d2010-07-09 18:52:06 -0400138
139 @Override
140 public void onCreate() {
Daniel Sandler9120d552010-07-23 09:11:14 -0400141 super.onCreate(); // will add the main bar view
Joe Onorato808182d2010-07-09 18:52:06 -0400142 }
143
144 protected View makeStatusBarView() {
145 Resources res = getResources();
146
147 mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
148
149 final View sb = View.inflate(this, R.layout.status_bar, null);
150 mStatusBarView = sb;
151
Daniel Sandlerce70d912010-09-02 11:59:41 -0400152 mBarContents = sb.findViewById(R.id.bar_contents);
153 mCurtains = sb.findViewById(R.id.lights_out);
154 View systemInfo = sb.findViewById(R.id.systemInfo);
155 View.OnLongClickListener toggle = new View.OnLongClickListener() {
156 public boolean onLongClick(View v) {
157 toggleLightsOut(v);
158 return true;
159 }
160 };
161
162 systemInfo.setOnLongClickListener(toggle);
163 mCurtains.setOnLongClickListener(toggle);
164
Joe Onorato808182d2010-07-09 18:52:06 -0400165 // the more notifications icon
166 mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
167
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400168 // where the icons go
169 mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
170
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400171 mKicker = new KickerController((Context)this, mStatusBarView);
172
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400173 // System info (center)
174 mBatteryMeter = (ImageView) sb.findViewById(R.id.battery);
175 mSignalMeter = (ImageView) sb.findViewById(R.id.signal);
Daniel Sandlerd39e3882010-08-31 14:16:13 -0400176 mSignalIcon = (ImageView) sb.findViewById(R.id.signal_icon);
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400177
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400178 // Add the windows
179 addPanelWindows();
180
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400181 mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
182 mPile.removeAllViews();
183
184 ScrollView scroller = (ScrollView)mPile.getParent();
185 scroller.setFillViewport(true);
186
187 mClearButton = (TextView)mNotificationPanel.findViewById(R.id.clear_all_button);
188 mClearButton.setOnClickListener(mClearButtonListener);
189
Joe Onorato808182d2010-07-09 18:52:06 -0400190 return sb;
191 }
192
193 protected int getStatusBarGravity() {
194 return Gravity.BOTTOM | Gravity.FILL_HORIZONTAL;
195 }
196
Daniel Sandler9120d552010-07-23 09:11:14 -0400197 private class H extends Handler {
198 public static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
199 public static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
200 public static final int MSG_OPEN_SYSTEM_PANEL = 1010;
201 public static final int MSG_CLOSE_SYSTEM_PANEL = 1011;
202 public void handleMessage(Message m) {
203 switch (m.what) {
204 case MSG_OPEN_NOTIFICATION_PANEL:
205 if (DEBUG) Slog.d(TAG, "opening notifications panel");
206 mNotificationPanel.setVisibility(View.VISIBLE);
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400207 mExpandedVisible = true;
Daniel Sandler9120d552010-07-23 09:11:14 -0400208 break;
209 case MSG_CLOSE_NOTIFICATION_PANEL:
210 if (DEBUG) Slog.d(TAG, "closing notifications panel");
211 mNotificationPanel.setVisibility(View.GONE);
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400212 mExpandedVisible = false;
Daniel Sandler9120d552010-07-23 09:11:14 -0400213 break;
214 case MSG_OPEN_SYSTEM_PANEL:
215 if (DEBUG) Slog.d(TAG, "opening system panel");
216 mSystemPanel.setVisibility(View.VISIBLE);
217 break;
218 case MSG_CLOSE_SYSTEM_PANEL:
219 if (DEBUG) Slog.d(TAG, "closing system panel");
220 mSystemPanel.setVisibility(View.GONE);
221 break;
222 }
223 }
224 }
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400225
Daniel Sandlerd7db7b12010-08-13 16:26:39 -0400226 public void setBatteryMeter(int level, boolean plugged) {
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400227 if (DEBUG) Slog.d(TAG, "battery=" + level + (plugged ? " - plugged" : " - unplugged"));
Daniel Sandlerd39e3882010-08-31 14:16:13 -0400228 mBatteryMeter.setImageResource(R.drawable.sysbar_batterymini);
229 // adjust percent to permyriad for ClipDrawable's sake
230 mBatteryMeter.setImageLevel(level * (MAX_IMAGE_LEVEL / 100));
Daniel Sandlerd7db7b12010-08-13 16:26:39 -0400231 }
232
Daniel Sandler764b4da2010-08-24 16:24:35 -0400233 public void setSignalMeter(int level, boolean isWifi) {
234 if (DEBUG) Slog.d(TAG, "signal=" + level);
235 if (level < 0) {
Daniel Sandlerd39e3882010-08-31 14:16:13 -0400236 mSignalMeter.setImageDrawable(null);
Daniel Sandler764b4da2010-08-24 16:24:35 -0400237 mSignalMeter.setImageLevel(0);
Daniel Sandlerd39e3882010-08-31 14:16:13 -0400238 mSignalIcon.setImageDrawable(null);
Daniel Sandler764b4da2010-08-24 16:24:35 -0400239 } else {
Daniel Sandlerd39e3882010-08-31 14:16:13 -0400240 mSignalMeter.setImageResource(R.drawable.sysbar_wifimini);
241 // adjust to permyriad
242 mSignalMeter.setImageLevel(level * (MAX_IMAGE_LEVEL / 100));
243 mSignalIcon.setImageResource(isWifi ? R.drawable.ic_sysbar_wifi_mini
244 : R.drawable.ic_sysbar_wifi_mini); // XXX
Daniel Sandler764b4da2010-08-24 16:24:35 -0400245 }
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400246 }
Daniel Sandler9120d552010-07-23 09:11:14 -0400247
Joe Onorato808182d2010-07-09 18:52:06 -0400248 public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400249 if (DEBUG) Slog.d(TAG, "addIcon(" + slot + ") -> " + icon);
Joe Onorato808182d2010-07-09 18:52:06 -0400250 }
251
252 public void updateIcon(String slot, int index, int viewIndex,
253 StatusBarIcon old, StatusBarIcon icon) {
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400254 if (DEBUG) Slog.d(TAG, "updateIcon(" + slot + ") -> " + icon);
Joe Onorato808182d2010-07-09 18:52:06 -0400255 }
256
257 public void removeIcon(String slot, int index, int viewIndex) {
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400258 if (DEBUG) Slog.d(TAG, "removeIcon(" + slot + ")");
Joe Onorato808182d2010-07-09 18:52:06 -0400259 }
260
261 public void addNotification(IBinder key, StatusBarNotification notification) {
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400262 if (DEBUG) Slog.d(TAG, "addNotification(" + key + " -> " + notification + ")");
263 addNotificationViews(key, notification);
Daniel Sandlerfb970e92010-08-20 10:57:17 -0400264
265 boolean immersive = false;
266 try {
267 immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
268 Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
269 } catch (RemoteException ex) {
270 }
271 if (immersive) {
272 // TODO: immersive mode popups for tablet
273 } else if (notification.notification.fullScreenIntent != null) {
274 // not immersive & a full-screen alert should be shown
275 Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;"
276 + " sending fullScreenIntent");
277 try {
278 notification.notification.fullScreenIntent.send();
279 } catch (PendingIntent.CanceledException e) {
280 }
281 } else {
282 // tick()
283 }
Joe Onorato808182d2010-07-09 18:52:06 -0400284 }
285
286 public void updateNotification(IBinder key, StatusBarNotification notification) {
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400287 if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ") // TODO");
Daniel Sandler379020a2010-07-29 16:20:06 -0400288
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400289 final NotificationData.Entry oldEntry = mNotns.findByKey(key);
Daniel Sandler379020a2010-07-29 16:20:06 -0400290 if (oldEntry == null) {
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400291 Slog.w(TAG, "updateNotification for unknown key: " + key);
292 return;
293 }
294
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400295 final StatusBarNotification oldNotification = oldEntry.notification;
296 final RemoteViews oldContentView = oldNotification.notification.contentView;
297
298 final RemoteViews contentView = notification.notification.contentView;
299
300 if (false) {
301 Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
302 + " ongoing=" + oldNotification.isOngoing()
303 + " expanded=" + oldEntry.expanded
304 + " contentView=" + oldContentView);
305 Slog.d(TAG, "new notification: when=" + notification.notification.when
306 + " ongoing=" + oldNotification.isOngoing()
307 + " contentView=" + contentView);
308 }
309
310 // Can we just reapply the RemoteViews in place? If when didn't change, the order
311 // didn't change.
312 if (notification.notification.when == oldNotification.notification.when
313 && notification.isOngoing() == oldNotification.isOngoing()
314 && oldEntry.expanded != null
315 && contentView != null
316 && oldContentView != null
317 && contentView.getPackage() != null
318 && oldContentView.getPackage() != null
319 && oldContentView.getPackage().equals(contentView.getPackage())
320 && oldContentView.getLayoutId() == contentView.getLayoutId()) {
321 if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
322 oldEntry.notification = notification;
323 try {
324 // Reapply the RemoteViews
325 contentView.reapply(this, oldEntry.content);
326 // update the contentIntent
327 final PendingIntent contentIntent = notification.notification.contentIntent;
328 if (contentIntent != null) {
329 oldEntry.content.setOnClickListener(new NotificationClicker(contentIntent,
330 notification.pkg, notification.tag, notification.id));
331 }
332 // Update the icon.
333 final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
334 notification.notification.icon, notification.notification.iconLevel,
335 notification.notification.number);
336 if (!oldEntry.icon.set(ic)) {
337 handleNotificationError(key, notification, "Couldn't update icon: " + ic);
338 return;
339 }
340 }
341 catch (RuntimeException e) {
342 // It failed to add cleanly. Log, and remove the view from the panel.
343 Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
344 removeNotificationViews(key);
345 addNotificationViews(key, notification);
346 }
347 } else {
348 if (DEBUG) Slog.d(TAG, "not reusing notification for key: " + key);
349 removeNotificationViews(key);
350 addNotificationViews(key, notification);
351 }
352 // TODO: kicker; immersive mode
Joe Onorato808182d2010-07-09 18:52:06 -0400353 }
354
355 public void removeNotification(IBinder key) {
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400356 if (DEBUG) Slog.d(TAG, "removeNotification(" + key + ") // TODO");
357 removeNotificationViews(key);
Joe Onorato808182d2010-07-09 18:52:06 -0400358 }
359
360 public void disable(int state) {
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400361 /*
362 final int old = mDisabled;
363 final int diff = state ^ old;
364 mDisabled = state;
365
366 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
367 if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
368 Slog.d(TAG, "DISABLE_EXPAND: yes");
369 animateCollapse();
370 }
371 }
372 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
373 if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
374 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
375 if (mTicking) {
376 mKicker.halt();
377 } else {
378 mNotificationIconArea.setVisibility(View.INVISIBLE);
379 }
380 } else {
381 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
382 if (!mExpandedVisible) {
383 mNotificationIconArea.setVisibility(View.VISIBLE);
384 }
385 }
386 } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
387 if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
388 Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
389 mKicker.halt();
390 }
391 }
392 */
393 }
394
395 void performDisableActions(int net) {
396 /*
397 int old = mDisabled;
398 int diff = net ^ old;
399 mDisabled = net;
400
401 // act accordingly
402 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
403 if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
404 Slog.d(TAG, "DISABLE_EXPAND: yes");
405 animateCollapse();
406 }
407 }
408 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
409 if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
410 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
411 if (mTicking) {
412 mNotificationIconArea.setVisibility(View.INVISIBLE);
413 mKicker.halt();
414 } else {
415 mNotificationIconArea.setVisibility(View.INVISIBLE);
416 }
417 } else {
418 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
419 if (!mExpandedVisible) {
420 mNotificationIconArea.setVisibility(View.VISIBLE);
421 }
422 }
423 } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
424 if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
425 mKicker.halt();
426 }
427 }
428 */
429 }
430
431 private void tick(StatusBarNotification n) {
432 // Show the ticker if one is requested. Also don't do this
433 // until status bar window is attached to the window manager,
434 // because... well, what's the point otherwise? And trying to
435 // run a ticker without being attached will crash!
436 if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
437 if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
438 | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
439 mKicker.addEntry(n);
440 }
441 }
442 }
443
444 private class KickerController {
445 View mView;
446 ImageView mKickerIcon;
447 TextSwitcher mKickerText;
448
449 public KickerController(Context context, View sb) {
450 mView = sb.findViewById(R.id.ticker);
451 mKickerIcon = (ImageView) mView.findViewById(R.id.tickerIcon);
452 mKickerText = (TextSwitcher) mView.findViewById(R.id.tickerText);
453 }
454
455 public void halt() {
456 tickerHalting();
457 }
458
459 public void addEntry(StatusBarNotification n) {
460 mKickerIcon.setImageResource(n.notification.icon);
461 mKickerText.setCurrentText(n.notification.tickerText);
462 tickerStarting();
463 }
464
465 public void tickerStarting() {
466 mTicking = true;
467 mIconLayout.setVisibility(View.GONE);
468 mKickerView.setVisibility(View.VISIBLE);
469 }
470
471 public void tickerDone() {
472 mIconLayout.setVisibility(View.VISIBLE);
473 mKickerView.setVisibility(View.GONE);
474 mTicking = false;
475 }
476
477 public void tickerHalting() {
478 mIconLayout.setVisibility(View.VISIBLE);
479 mKickerView.setVisibility(View.GONE);
480 mTicking = false;
481 }
Joe Onorato808182d2010-07-09 18:52:06 -0400482 }
483
484 public void animateExpand() {
Daniel Sandler9120d552010-07-23 09:11:14 -0400485 mHandler.removeMessages(H.MSG_OPEN_NOTIFICATION_PANEL);
486 mHandler.sendEmptyMessage(H.MSG_OPEN_NOTIFICATION_PANEL);
Joe Onorato808182d2010-07-09 18:52:06 -0400487 }
488
489 public void animateCollapse() {
Daniel Sandler9120d552010-07-23 09:11:14 -0400490 mHandler.removeMessages(H.MSG_CLOSE_NOTIFICATION_PANEL);
491 mHandler.sendEmptyMessage(H.MSG_CLOSE_NOTIFICATION_PANEL);
492 mHandler.removeMessages(H.MSG_CLOSE_SYSTEM_PANEL);
493 mHandler.sendEmptyMessage(H.MSG_CLOSE_SYSTEM_PANEL);
494 }
495
496 public void notificationIconsClicked(View v) {
497 if (DEBUG) Slog.d(TAG, "clicked notification icons");
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400498 mHandler.removeMessages(H.MSG_CLOSE_SYSTEM_PANEL);
499 mHandler.sendEmptyMessage(H.MSG_CLOSE_SYSTEM_PANEL);
500
Daniel Sandler9120d552010-07-23 09:11:14 -0400501 int msg = (mNotificationPanel.getVisibility() == View.GONE)
502 ? H.MSG_OPEN_NOTIFICATION_PANEL
503 : H.MSG_CLOSE_NOTIFICATION_PANEL;
504 mHandler.removeMessages(msg);
505 mHandler.sendEmptyMessage(msg);
506 }
507
508 public void systemInfoClicked(View v) {
509 if (DEBUG) Slog.d(TAG, "clicked system info");
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400510 mHandler.removeMessages(H.MSG_CLOSE_NOTIFICATION_PANEL);
511 mHandler.sendEmptyMessage(H.MSG_CLOSE_NOTIFICATION_PANEL);
512
Daniel Sandler9120d552010-07-23 09:11:14 -0400513 int msg = (mSystemPanel.getVisibility() == View.GONE)
514 ? H.MSG_OPEN_SYSTEM_PANEL
515 : H.MSG_CLOSE_SYSTEM_PANEL;
516 mHandler.removeMessages(msg);
517 mHandler.sendEmptyMessage(msg);
Joe Onorato808182d2010-07-09 18:52:06 -0400518 }
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400519
Jim Millera1019492010-08-30 17:14:54 -0700520 public void recentButtonClicked(View v) {
521 if (DEBUG) Slog.d(TAG, "clicked recent apps");
522 Intent intent = new Intent();
523 intent.setClass(this, RecentApplicationsActivity.class);
524 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
525 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
526 startActivity(intent);
527 }
528
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400529 /**
530 * Cancel this notification and tell the status bar service about the failure. Hold no locks.
531 */
532 void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
533 removeNotification(key);
534 try {
535 mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
536 } catch (RemoteException ex) {
537 // The end is nigh.
538 }
539 }
540
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400541 private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
542 public void onClick(View v) {
543 try {
544 mBarService.onClearAllNotifications();
545 } catch (RemoteException ex) {
546 // system process is dead if we're here.
547 }
548 animateCollapse();
549 }
550 };
551
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400552 private class NotificationClicker implements View.OnClickListener {
553 private PendingIntent mIntent;
554 private String mPkg;
555 private String mTag;
556 private int mId;
557
558 NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
559 mIntent = intent;
560 mPkg = pkg;
561 mTag = tag;
562 mId = id;
563 }
564
565 public void onClick(View v) {
566 try {
567 // The intent we are sending is for the application, which
568 // won't have permission to immediately start an activity after
569 // the user switches to home. We know it is safe to do at this
570 // point, so make sure new activity switches are now allowed.
571 ActivityManagerNative.getDefault().resumeAppSwitches();
572 } catch (RemoteException e) {
573 }
574
575 if (mIntent != null) {
576 int[] pos = new int[2];
577 v.getLocationOnScreen(pos);
578 Intent overlay = new Intent();
579 overlay.setSourceBounds(
580 new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
581 try {
582 mIntent.send(TabletStatusBarService.this, 0, overlay);
583 } catch (PendingIntent.CanceledException e) {
584 // the stack trace isn't very helpful here. Just log the exception message.
585 Slog.w(TAG, "Sending contentIntent failed: " + e);
586 }
587 }
588
589 try {
590 mBarService.onNotificationClick(mPkg, mTag, mId);
591 } catch (RemoteException ex) {
592 // system process is dead if we're here.
593 }
594
595 // close the shade if it was open
596 animateCollapse();
597
598 // If this click was on the intruder alert, hide that instead
599// mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
600 }
601 }
602
603 StatusBarNotification removeNotificationViews(IBinder key) {
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400604 NotificationData.Entry entry = mNotns.remove(key);
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400605 if (entry == null) {
606 Slog.w(TAG, "removeNotification for unknown key: " + key);
607 return null;
608 }
609 // Remove the expanded view.
610 ViewGroup rowParent = (ViewGroup)entry.row.getParent();
611 if (rowParent != null) rowParent.removeView(entry.row);
612 // Remove the icon.
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400613// ViewGroup iconParent = (ViewGroup)entry.icon.getParent();
614// if (iconParent != null) iconParent.removeView(entry.icon);
615 refreshIcons();
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400616
617 return entry.notification;
618 }
619
620 StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400621 if (DEBUG) {
622 Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
623 }
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400624 // Construct the icon.
625 final StatusBarIconView iconView = new StatusBarIconView(this,
626 notification.pkg + "/0x" + Integer.toHexString(notification.id));
627 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
628
629 final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
630 notification.notification.icon,
631 notification.notification.iconLevel,
632 notification.notification.number);
633 if (!iconView.set(ic)) {
634 handleNotificationError(key, notification, "Couldn't attach StatusBarIcon: " + ic);
635 return null;
636 }
637 // Construct the expanded view.
638 NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400639 if (!inflateViews(entry, mPile)) {
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400640 handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
641 + notification);
642 return null;
643 }
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400644 // Add the icon.
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400645 mNotns.add(entry);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400646 refreshIcons();
647
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400648 return iconView;
649 }
650
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400651 private void refreshIcons() {
652 // XXX: need to implement a new limited linear layout class
653 // to avoid removing & readding everything
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400654
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400655 int N = mNotns.size();
656 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mIconSize, mIconSize);
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400657
658 if (DEBUG) {
659 Slog.d(TAG, "refreshing icons (" + N + " notifications, mIconLayout="
660 + mIconLayout + ", mPile=" + mPile);
661 }
662
663 mIconLayout.removeAllViews();
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400664 for (int i=0; i<4; i++) {
665 if (i>=N) break;
666 mIconLayout.addView(mNotns.get(N-i-1).icon, i, params);
667 }
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400668
669 mPile.removeAllViews();
670 for (int i=0; i<N; i++) {
671 mPile.addView(mNotns.get(N-i-1).row);
672 }
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400673 }
674
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400675 private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
676 StatusBarNotification sbn = entry.notification;
677 RemoteViews remoteViews = sbn.notification.contentView;
678 if (remoteViews == null) {
679 return false;
680 }
681
682 // create the row view
683 LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
684 View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400685 View vetoButton = row.findViewById(R.id.veto);
686 final String _pkg = sbn.pkg;
687 final String _tag = sbn.tag;
688 final int _id = sbn.id;
689 vetoButton.setOnClickListener(new View.OnClickListener() {
690 public void onClick(View v) {
691 try {
692 mBarService.onNotificationClear(_pkg, _tag, _id);
693 } catch (RemoteException ex) {
694 // system process is dead if we're here.
695 }
696// animateCollapse();
697 }
698 });
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400699
700 // bind the click event to the content area
701 ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
702 // XXX: update to allow controls within notification views
703 content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
704// content.setOnFocusChangeListener(mFocusChangeListener);
705 PendingIntent contentIntent = sbn.notification.contentIntent;
706 if (contentIntent != null) {
707 content.setOnClickListener(new NotificationClicker(contentIntent,
708 sbn.pkg, sbn.tag, sbn.id));
709 }
710
711 View expanded = null;
712 Exception exception = null;
713 try {
714 expanded = remoteViews.apply(this, content);
715 }
716 catch (RuntimeException e) {
717 exception = e;
718 }
719 if (expanded == null) {
720 String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
721 Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
722 return false;
723 } else {
724 content.addView(expanded);
725 row.setDrawingCacheEnabled(true);
726 }
727
728 entry.row = row;
729 entry.content = content;
730 entry.expanded = expanded;
731
732 return true;
733 }
Daniel Sandlerce70d912010-09-02 11:59:41 -0400734
735 protected void setLightsOut(boolean out) {
736 if (out) {
737 mCurtains.setAnimation(AnimationUtils.loadAnimation((Context)this,
738 R.anim.lights_out_in));
739 mCurtains.setVisibility(View.VISIBLE);
740 mBarContents.setAnimation(AnimationUtils.loadAnimation((Context)this,
741 R.anim.status_bar_out));
742 mBarContents.setVisibility(View.GONE);
743 } else {
744 mCurtains.setAnimation(AnimationUtils.loadAnimation((Context)this,
745 R.anim.lights_out_out));
746 mCurtains.setVisibility(View.GONE);
747 mBarContents.setAnimation(AnimationUtils.loadAnimation((Context)this,
748 R.anim.status_bar_in));
749 mBarContents.setVisibility(View.VISIBLE);
750 }
751 }
752
753 public void toggleLightsOut(View v) {
754 setLightsOut(mCurtains.getVisibility() != View.VISIBLE);
755 }
Joe Onorato808182d2010-07-09 18:52:06 -0400756}
Daniel Sandlerd39e3882010-08-31 14:16:13 -0400757
758