blob: b8c068efc5713a40d2bbfc4baa48fb760c766c02 [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
Joe Onoratof3c3c4f2010-10-21 11:09:02 -040019import java.io.FileDescriptor;
20import java.io.PrintWriter;
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -040021import java.util.ArrayList;
Daniel Sandlerb9606992010-11-19 14:47:59 -050022import java.util.Map;
23import java.util.IdentityHashMap;
Joe Onoratof3c3c4f2010-10-21 11:09:02 -040024
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -040025import android.animation.LayoutTransition;
26import android.animation.ObjectAnimator;
Daniel Sandlerb9606992010-11-19 14:47:59 -050027import android.animation.AnimatorSet;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040028import android.app.ActivityManagerNative;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040029import android.app.PendingIntent;
Joe Onoratoef1e7762010-09-17 18:38:38 -040030import android.app.Notification;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040031import android.app.StatusBarManager;
Joe Onorato808182d2010-07-09 18:52:06 -040032import android.content.Context;
33import android.content.Intent;
Daniel Sandler0ad460b2010-12-14 12:14:53 -050034import android.content.res.Configuration;
Joe Onorato808182d2010-07-09 18:52:06 -040035import android.content.res.Resources;
Daniel Sandler9120d552010-07-23 09:11:14 -040036import android.graphics.PixelFormat;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040037import android.graphics.Rect;
Joe Onorato4daaeaf2010-11-17 20:43:12 -080038import android.graphics.drawable.Drawable;
39import android.graphics.drawable.LayerDrawable;
Daniel Sandler9120d552010-07-23 09:11:14 -040040import android.os.Handler;
Joe Onorato808182d2010-07-09 18:52:06 -040041import android.os.IBinder;
Daniel Sandler3eebd1f2010-07-27 08:39:33 -040042import android.os.Message;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040043import android.os.RemoteException;
Daniel Sandler10163c62010-12-08 11:51:05 -050044import android.os.ServiceManager;
Joe Onoratoef1e7762010-09-17 18:38:38 -040045import android.text.TextUtils;
Daniel Sandler3eebd1f2010-07-27 08:39:33 -040046import android.util.Slog;
Joe Onorato55d2d762010-09-26 13:02:01 -070047import android.view.animation.Animation;
Daniel Sandlerce70d912010-09-02 11:59:41 -040048import android.view.animation.AnimationUtils;
Joe Onorato808182d2010-07-09 18:52:06 -040049import android.view.Gravity;
Daniel Sandler10163c62010-12-08 11:51:05 -050050import android.view.IWindowManager;
51import android.view.KeyEvent;
Daniel Sandler3eebd1f2010-07-27 08:39:33 -040052import android.view.LayoutInflater;
Joe Onoratob62ac122010-09-20 16:16:32 -040053import android.view.MotionEvent;
Daniel Sandler8304da42010-10-25 15:53:25 -040054import android.view.VelocityTracker;
Joe Onorato808182d2010-07-09 18:52:06 -040055import android.view.View;
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -040056import android.view.ViewConfiguration;
Daniel Sandler9120d552010-07-23 09:11:14 -040057import android.view.ViewGroup;
58import android.view.WindowManager;
59import android.view.WindowManagerImpl;
Joe Onoratoef1e7762010-09-17 18:38:38 -040060import android.widget.FrameLayout;
Daniel Sandler3eebd1f2010-07-27 08:39:33 -040061import android.widget.ImageView;
Joe Onorato808182d2010-07-09 18:52:06 -040062import android.widget.LinearLayout;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -040063import android.widget.RemoteViews;
64import android.widget.ScrollView;
65import android.widget.TextSwitcher;
Daniel Sandler9120d552010-07-23 09:11:14 -040066import android.widget.TextView;
Joe Onorato808182d2010-07-09 18:52:06 -040067
68import com.android.internal.statusbar.StatusBarIcon;
Joe Onorato808182d2010-07-09 18:52:06 -040069import com.android.internal.statusbar.StatusBarNotification;
70
Joe Onorato808182d2010-07-09 18:52:06 -040071import com.android.systemui.R;
Joe Onoratofd52b182010-11-10 18:00:52 -080072import com.android.systemui.statusbar.*;
73import com.android.systemui.statusbar.policy.BatteryController;
74import com.android.systemui.statusbar.policy.NetworkController;
75import com.android.systemui.recent.RecentApplicationsActivity;
Joe Onorato808182d2010-07-09 18:52:06 -040076
Joe Onoratofd52b182010-11-10 18:00:52 -080077public class TabletStatusBar extends StatusBar {
Daniel Sandlerfb970e92010-08-20 10:57:17 -040078 public static final boolean DEBUG = false;
Joe Onoratofd52b182010-11-10 18:00:52 -080079 public static final String TAG = "TabletStatusBar";
Joe Onorato808182d2010-07-09 18:52:06 -040080
Daniel Sandler0ad460b2010-12-14 12:14:53 -050081 public static final int MAX_NOTIFICATION_ICONS = 5;
82
Joe Onoratob62ac122010-09-20 16:16:32 -040083 public static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
84 public static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -040085 public static final int MSG_OPEN_NOTIFICATION_PEEK = 1002;
86 public static final int MSG_CLOSE_NOTIFICATION_PEEK = 1003;
Jim Miller44c66fe2010-10-20 18:32:52 -070087 public static final int MSG_OPEN_RECENTS_PANEL = 1020;
88 public static final int MSG_CLOSE_RECENTS_PANEL = 1021;
Daniel Sandler0ad460b2010-12-14 12:14:53 -050089 public static final int MSG_SHOW_CHROME = 1030;
90 public static final int MSG_HIDE_CHROME = 1031;
Jim Miller44c66fe2010-10-20 18:32:52 -070091
Daniel Sandler10163c62010-12-08 11:51:05 -050092 // Fitts' Law assistance for LatinIME; TODO: replace with a more general approach
93 private static final boolean FAKE_SPACE_BAR = true;
94
Daniel Sandlerd39e3882010-08-31 14:16:13 -040095 private static final int MAX_IMAGE_LEVEL = 10000;
Jim Miller44c66fe2010-10-20 18:32:52 -070096 private static final boolean USE_2D_RECENTS = true;
Daniel Sandlerd39e3882010-08-31 14:16:13 -040097
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -050098 public static final int LIGHTS_ON_DELAY = 5000;
99
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500100 int mBarHeight = -1;
101 int mIconSize = -1;
102 int mIconHPadding = -1;
Daniel Sandler9120d552010-07-23 09:11:14 -0400103
104 H mHandler = new H();
105
Daniel Sandler10163c62010-12-08 11:51:05 -0500106 IWindowManager mWindowManager;
107
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400108 // tracking all current notifications
109 private NotificationData mNotns = new NotificationData();
Jim Miller44c66fe2010-10-20 18:32:52 -0700110
Joe Onoratob62ac122010-09-20 16:16:32 -0400111 TabletStatusBarView mStatusBarView;
Joe Onoratofd52b182010-11-10 18:00:52 -0800112 View mNotificationArea;
113 View mNotificationTrigger;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400114 NotificationIconArea mNotificationIconArea;
Joe Onorato091e1b82010-09-26 18:04:44 -0700115 View mNavigationArea;
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -0500116
Joe Onorato536c58f2010-11-28 17:52:28 -0800117 ImageView mBackButton;
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -0500118 View mHomeButton;
Daniel Sandlere02d8082010-10-08 15:13:22 -0400119 View mMenuButton;
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400120 View mRecentButton;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400121
satokcd7cd292010-11-20 15:46:23 +0900122 InputMethodButton mInputMethodSwitchButton;
satok06487a52010-10-29 11:37:18 +0900123
Joe Onoratoddf680b2010-09-26 13:59:40 -0700124 NotificationPanel mNotificationPanel;
Joe Onorato8a576712010-11-15 16:50:34 -0800125 NotificationPeekPanel mNotificationPeekWindow;
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400126 ViewGroup mNotificationPeekRow;
127 int mNotificationPeekIndex;
Daniel Sandlera8e5b062010-12-01 13:53:08 -0500128 IBinder mNotificationPeekKey;
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400129 LayoutTransition mNotificationPeekScrubLeft, mNotificationPeekScrubRight;
130
131 int mNotificationPeekTapDuration;
132 int mNotificationFlingVelocity;
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400133
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400134 ViewGroup mPile;
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400135
Joe Onoratofd52b182010-11-10 18:00:52 -0800136 BatteryController mBatteryController;
137 NetworkController mNetworkController;
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400138
Daniel Sandlerce70d912010-09-02 11:59:41 -0400139 View mBarContents;
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -0500140
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500141 // hide system chrome ("lights out") support
142 View mShadow;
Daniel Sandlerce70d912010-09-02 11:59:41 -0400143
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400144 NotificationIconArea.IconLayout mIconLayout;
145
Joe Onoratoef1e7762010-09-17 18:38:38 -0400146 TabletTicker mTicker;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400147
Daniel Sandler10163c62010-12-08 11:51:05 -0500148 View mFakeSpaceBar;
149 KeyEvent mSpaceBarKeyEvent = null;
150
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400151 // for disabling the status bar
152 int mDisabled = 0;
153
Joe Onorato55d2d762010-09-26 13:02:01 -0700154 boolean mNotificationsOn = true;
Jim Miller44c66fe2010-10-20 18:32:52 -0700155 private RecentAppsPanel mRecentsPanel;
Joe Onorato55d2d762010-09-26 13:02:01 -0700156
Daniel Sandler9120d552010-07-23 09:11:14 -0400157 protected void addPanelWindows() {
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400158 final Context context = mContext;
159
Jim Miller44c66fe2010-10-20 18:32:52 -0700160 // Notification Panel
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400161 mNotificationPanel = (NotificationPanel)View.inflate(context,
Joe Onorato755cc742010-11-27 15:45:35 -0800162 R.layout.status_bar_notification_panel, null);
Joe Onorato7c270fa2010-12-08 17:31:42 -0800163 mNotificationPanel.show(false, false);
Joe Onoratob62ac122010-09-20 16:16:32 -0400164 mNotificationPanel.setOnTouchListener(
Joe Onoratoddf680b2010-09-26 13:59:40 -0700165 new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PANEL, mNotificationPanel));
Joe Onoratob62ac122010-09-20 16:16:32 -0400166
Joe Onoratofd52b182010-11-10 18:00:52 -0800167 // the battery and network icons
168 mBatteryController.addIconView((ImageView)mNotificationPanel.findViewById(R.id.battery));
169 mBatteryController.addLabelView(
170 (TextView)mNotificationPanel.findViewById(R.id.battery_text));
Joe Onoratoddbba422010-11-23 15:42:28 -0800171 mNetworkController.addCombinedSignalIconView(
Joe Onorato42f8e132010-11-29 15:52:43 -0800172 (ImageView)mNotificationPanel.findViewById(R.id.network_signal));
173 mNetworkController.addDataTypeIconView(
174 (ImageView)mNotificationPanel.findViewById(R.id.network_type));
Joe Onoratofd52b182010-11-10 18:00:52 -0800175 mNetworkController.addLabelView(
176 (TextView)mNotificationPanel.findViewById(R.id.network_text));
Daniel Sandler3e8f5a22010-12-03 14:52:10 -0500177 mNetworkController.addLabelView(
178 (TextView)mBarContents.findViewById(R.id.network_text));
Joe Onoratofd52b182010-11-10 18:00:52 -0800179
Joe Onorato55d2d762010-09-26 13:02:01 -0700180 mStatusBarView.setIgnoreChildren(0, mNotificationTrigger, mNotificationPanel);
Daniel Sandlerd39e3882010-08-31 14:16:13 -0400181
Daniel Sandler9120d552010-07-23 09:11:14 -0400182 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
Joe Onorato4cddc982010-11-21 11:48:15 -0800183 ViewGroup.LayoutParams.MATCH_PARENT,
Joe Onoratocf2b1992010-11-16 21:36:42 -0800184 ViewGroup.LayoutParams.MATCH_PARENT,
Daniel Sandler9120d552010-07-23 09:11:14 -0400185 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
186 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Jeff Brown46e75292010-11-10 16:53:45 -0800187 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
Joe Onorato7c270fa2010-12-08 17:31:42 -0800188 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
189 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
Daniel Sandler9120d552010-07-23 09:11:14 -0400190 PixelFormat.TRANSLUCENT);
Joe Onoratoea70e632010-09-27 17:59:37 -0700191 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
Daniel Sandler9120d552010-07-23 09:11:14 -0400192 lp.setTitle("NotificationPanel");
Joe Onorato7c270fa2010-12-08 17:31:42 -0800193 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
194 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
195 lp.windowAnimations = com.android.internal.R.style.Animation; // == no animation
Daniel Sandler9120d552010-07-23 09:11:14 -0400196
197 WindowManagerImpl.getDefault().addView(mNotificationPanel, lp);
198
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400199 // Notification preview window
Joe Onorato8a576712010-11-15 16:50:34 -0800200 mNotificationPeekWindow = (NotificationPeekPanel) View.inflate(context,
Joe Onorato755cc742010-11-27 15:45:35 -0800201 R.layout.status_bar_notification_peek, null);
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400202 mNotificationPeekRow = (ViewGroup) mNotificationPeekWindow.findViewById(R.id.content);
203 mNotificationPeekWindow.setVisibility(View.GONE);
204 mNotificationPeekWindow.setOnTouchListener(
Joe Onorato5e759462010-11-28 17:43:50 -0800205 new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PEEK, mNotificationPeekWindow));
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400206 mNotificationPeekScrubRight = new LayoutTransition();
207 mNotificationPeekScrubRight.setAnimator(LayoutTransition.APPEARING,
208 ObjectAnimator.ofInt(null, "left", -512, 0));
209 mNotificationPeekScrubRight.setAnimator(LayoutTransition.DISAPPEARING,
210 ObjectAnimator.ofInt(null, "left", -512, 0));
211 mNotificationPeekScrubRight.setDuration(500);
212
213 mNotificationPeekScrubLeft = new LayoutTransition();
214 mNotificationPeekScrubLeft.setAnimator(LayoutTransition.APPEARING,
215 ObjectAnimator.ofInt(null, "left", 512, 0));
216 mNotificationPeekScrubLeft.setAnimator(LayoutTransition.DISAPPEARING,
217 ObjectAnimator.ofInt(null, "left", 512, 0));
218 mNotificationPeekScrubLeft.setDuration(500);
219
220 // XXX: setIgnoreChildren?
221 lp = new WindowManager.LayoutParams(
222 512, // ViewGroup.LayoutParams.WRAP_CONTENT,
223 ViewGroup.LayoutParams.WRAP_CONTENT,
224 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
225 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Jeff Brown46e75292010-11-10 16:53:45 -0800226 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
227 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400228 PixelFormat.TRANSLUCENT);
229 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
230 lp.setTitle("NotificationPeekWindow");
231 lp.windowAnimations = com.android.internal.R.style.Animation_Toast;
232
233 WindowManagerImpl.getDefault().addView(mNotificationPeekWindow, lp);
234
Jim Miller44c66fe2010-10-20 18:32:52 -0700235 // Recents Panel
236 if (USE_2D_RECENTS) {
Joe Onorato755cc742010-11-27 15:45:35 -0800237 mRecentsPanel = (RecentAppsPanel) View.inflate(context,
238 R.layout.status_bar_recent_panel, null);
Jim Miller44c66fe2010-10-20 18:32:52 -0700239 mRecentsPanel.setVisibility(View.GONE);
240 mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
241 mRecentsPanel));
242 mStatusBarView.setIgnoreChildren(2, mRecentButton, mRecentsPanel);
243
244 lp = new WindowManager.LayoutParams(
245 ViewGroup.LayoutParams.WRAP_CONTENT,
246 ViewGroup.LayoutParams.WRAP_CONTENT,
247 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
248 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Jeff Brown46e75292010-11-10 16:53:45 -0800249 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
Jim Miller169a2112010-12-10 18:30:07 -0800250 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
251 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
Jim Miller44c66fe2010-10-20 18:32:52 -0700252 PixelFormat.TRANSLUCENT);
253 lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
254 lp.setTitle("RecentsPanel");
Joe Onoratofd52b182010-11-10 18:00:52 -0800255 lp.windowAnimations = R.style.Animation_RecentPanel;
Jim Miller44c66fe2010-10-20 18:32:52 -0700256
257 WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
258 mRecentsPanel.setBar(this);
259 }
Daniel Sandler9120d552010-07-23 09:11:14 -0400260 }
Joe Onorato808182d2010-07-09 18:52:06 -0400261
262 @Override
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400263 public void start() {
264 super.start(); // will add the main bar view
Joe Onorato808182d2010-07-09 18:52:06 -0400265 }
266
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500267 @Override
268 protected void onConfigurationChanged (Configuration newConfig) {
269 loadDimens();
270 }
271
272 protected void loadDimens() {
273 final Resources res = mContext.getResources();
274
275 mBarHeight = res.getDimensionPixelSize(
276 com.android.internal.R.dimen.status_bar_height);
277
278 int newIconSize = res.getDimensionPixelSize(
279 com.android.internal.R.dimen.status_bar_icon_size);
280 int newIconHPadding = res.getDimensionPixelSize(
281 R.dimen.status_bar_icon_padding);
282
283 if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
284// Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
285 mIconHPadding = newIconHPadding;
286 mIconSize = newIconSize;
287 reloadAllNotificationIcons(); // reload the tray
288 }
289 }
290
Joe Onorato808182d2010-07-09 18:52:06 -0400291 protected View makeStatusBarView() {
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400292 final Context context = mContext;
Daniel Sandler10163c62010-12-08 11:51:05 -0500293
294 mWindowManager = IWindowManager.Stub.asInterface(
295 ServiceManager.getService(Context.WINDOW_SERVICE));
Joe Onorato808182d2010-07-09 18:52:06 -0400296
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500297 loadDimens();
Joe Onorato808182d2010-07-09 18:52:06 -0400298
Joe Onoratob62ac122010-09-20 16:16:32 -0400299 final TabletStatusBarView sb = (TabletStatusBarView)View.inflate(
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400300 context, R.layout.status_bar, null);
Joe Onorato808182d2010-07-09 18:52:06 -0400301 mStatusBarView = sb;
302
Joe Onoratob62ac122010-09-20 16:16:32 -0400303 sb.setHandler(mHandler);
304
Daniel Sandlerce70d912010-09-02 11:59:41 -0400305 mBarContents = sb.findViewById(R.id.bar_contents);
Joe Onoratof63b0f42010-09-12 17:03:19 -0400306
Joe Onoratofd52b182010-11-10 18:00:52 -0800307 // the whole right-hand side of the bar
308 mNotificationArea = sb.findViewById(R.id.notificationArea);
309
Joe Onorato55d2d762010-09-26 13:02:01 -0700310 // the button to open the notification area
Joe Onoratofd52b182010-11-10 18:00:52 -0800311 mNotificationTrigger = sb.findViewById(R.id.notificationTrigger);
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400312 mNotificationTrigger.setOnClickListener(mOnClickListener);
Joe Onorato55d2d762010-09-26 13:02:01 -0700313
Joe Onorato808182d2010-07-09 18:52:06 -0400314 // the more notifications icon
315 mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
316
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400317 // where the icons go
318 mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400319 mIconLayout.setOnTouchListener(new NotificationIconTouchListener());
320
321 ViewConfiguration vc = ViewConfiguration.get(context);
322 mNotificationPeekTapDuration = vc.getTapTimeout();
323 mNotificationFlingVelocity = 300; // px/s
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400324
Joe Onorato52f80cd2010-11-21 15:34:48 -0800325 mTicker = new TabletTicker(context);
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400326
Joe Onoratofd52b182010-11-10 18:00:52 -0800327 // The icons
328 mBatteryController = new BatteryController(mContext);
329 mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery));
330 mNetworkController = new NetworkController(mContext);
Joe Onorato42f8e132010-11-29 15:52:43 -0800331 mNetworkController.addCombinedSignalIconView(
332 (ImageView)sb.findViewById(R.id.network_signal));
333 mNetworkController.addDataTypeIconView(
334 (ImageView)sb.findViewById(R.id.network_type));
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400335
Joe Onorato091e1b82010-09-26 18:04:44 -0700336 // The navigation buttons
337 mNavigationArea = sb.findViewById(R.id.navigationArea);
Joe Onorato536c58f2010-11-28 17:52:28 -0800338 mBackButton = (ImageView)mNavigationArea.findViewById(R.id.back);
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -0500339 mHomeButton = mNavigationArea.findViewById(R.id.home);
Daniel Sandlere02d8082010-10-08 15:13:22 -0400340 mMenuButton = mNavigationArea.findViewById(R.id.menu);
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -0500341 mRecentButton = mNavigationArea.findViewById(R.id.recent_apps);
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -0500342 mRecentButton.setOnClickListener(mOnClickListener);
Joe Onorato091e1b82010-09-26 18:04:44 -0700343
satok06487a52010-10-29 11:37:18 +0900344 // The bar contents buttons
satokcd7cd292010-11-20 15:46:23 +0900345 mInputMethodSwitchButton = (InputMethodButton) sb.findViewById(R.id.imeSwitchButton);
satok06487a52010-10-29 11:37:18 +0900346
Daniel Sandler10163c62010-12-08 11:51:05 -0500347 // for redirecting errant bar taps to the IME
348 mFakeSpaceBar = sb.findViewById(R.id.fake_space_bar);
349
Daniel Sandlerb9606992010-11-19 14:47:59 -0500350 // "shadows" of the status bar features, for lights-out mode
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500351 mShadow = sb.findViewById(R.id.bar_shadow);
352 mShadow.setOnTouchListener(
353 new View.OnTouchListener() {
354 public boolean onTouch(View v, MotionEvent ev) {
355 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
356 mShadow.setVisibility(View.GONE);
357 mBarContents.setVisibility(View.VISIBLE);
358 }
359 return false;
360 }
361 });
Daniel Sandlerb9606992010-11-19 14:47:59 -0500362
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500363 // tuning parameters
364 final int LIGHTS_GOING_OUT_SYSBAR_DURATION = 600;
365 final int LIGHTS_GOING_OUT_SHADOW_DURATION = 1000;
366 final int LIGHTS_GOING_OUT_SHADOW_DELAY = 500;
367
368 final int LIGHTS_COMING_UP_SYSBAR_DURATION = 200;
369// final int LIGHTS_COMING_UP_SYSBAR_DELAY = 50;
370 final int LIGHTS_COMING_UP_SHADOW_DURATION = 0;
371
372 LayoutTransition xition = new LayoutTransition();
373 xition.setAnimator(LayoutTransition.APPEARING,
374 ObjectAnimator.ofFloat(null, "alpha", 0.5f, 1f));
375 xition.setDuration(LayoutTransition.APPEARING, LIGHTS_COMING_UP_SYSBAR_DURATION);
376 xition.setStartDelay(LayoutTransition.APPEARING, 0);
377 xition.setAnimator(LayoutTransition.DISAPPEARING,
378 ObjectAnimator.ofFloat(null, "alpha", 1f, 0f));
379 xition.setDuration(LayoutTransition.DISAPPEARING, LIGHTS_GOING_OUT_SYSBAR_DURATION);
380 xition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
381 ((ViewGroup)sb.findViewById(R.id.bar_contents_holder)).setLayoutTransition(xition);
382
383 xition = new LayoutTransition();
384 xition.setAnimator(LayoutTransition.APPEARING,
385 ObjectAnimator.ofFloat(null, "alpha", 0f, 1f));
386 xition.setDuration(LayoutTransition.APPEARING, LIGHTS_GOING_OUT_SHADOW_DURATION);
387 xition.setStartDelay(LayoutTransition.APPEARING, LIGHTS_GOING_OUT_SHADOW_DELAY);
388 xition.setAnimator(LayoutTransition.DISAPPEARING,
389 ObjectAnimator.ofFloat(null, "alpha", 1f, 0f));
390 xition.setDuration(LayoutTransition.DISAPPEARING, LIGHTS_COMING_UP_SHADOW_DURATION);
391 xition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
392 ((ViewGroup)sb.findViewById(R.id.bar_shadow_holder)).setLayoutTransition(xition);
Daniel Sandlerb9606992010-11-19 14:47:59 -0500393
Joe Onorato5dd11692010-09-27 15:34:04 -0700394 // set the initial view visibility
395 setAreThereNotifications();
Daniel Sandler271ea122010-10-22 14:06:10 -0400396 refreshNotificationTrigger();
Joe Onorato5dd11692010-09-27 15:34:04 -0700397
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400398 // Add the windows
399 addPanelWindows();
400
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400401 mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
402 mPile.removeAllViews();
Jim Miller44c66fe2010-10-20 18:32:52 -0700403
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400404 ScrollView scroller = (ScrollView)mPile.getParent();
405 scroller.setFillViewport(true);
406
Joe Onorato808182d2010-07-09 18:52:06 -0400407 return sb;
408 }
409
410 protected int getStatusBarGravity() {
411 return Gravity.BOTTOM | Gravity.FILL_HORIZONTAL;
412 }
413
Daniel Sandler9120d552010-07-23 09:11:14 -0400414 private class H extends Handler {
Daniel Sandler9120d552010-07-23 09:11:14 -0400415 public void handleMessage(Message m) {
416 switch (m.what) {
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400417 case MSG_OPEN_NOTIFICATION_PEEK:
418 if (DEBUG) Slog.d(TAG, "opening notification peek window; arg=" + m.arg1);
419 if (m.arg1 >= 0) {
420 final int N = mNotns.size();
Daniel Sandlera8e5b062010-12-01 13:53:08 -0500421 if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) {
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400422 NotificationData.Entry entry = mNotns.get(N-1-mNotificationPeekIndex);
423 entry.icon.setBackgroundColor(0);
Daniel Sandlera8e5b062010-12-01 13:53:08 -0500424 mNotificationPeekIndex = -1;
425 mNotificationPeekKey = null;
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400426 }
427
428 final int peekIndex = m.arg1;
429 if (peekIndex < N) {
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500430 //Slog.d(TAG, "loading peek: " + peekIndex);
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400431 NotificationData.Entry entry = mNotns.get(N-1-peekIndex);
432 NotificationData.Entry copy = new NotificationData.Entry(
433 entry.key,
434 entry.notification,
435 entry.icon);
436 inflateViews(copy, mNotificationPeekRow);
437
438 entry.icon.setBackgroundColor(0x20FFFFFF);
439
440// mNotificationPeekRow.setLayoutTransition(
441// peekIndex < mNotificationPeekIndex
442// ? mNotificationPeekScrubLeft
443// : mNotificationPeekScrubRight);
444
445 mNotificationPeekRow.removeAllViews();
446 mNotificationPeekRow.addView(copy.row);
447
448 mNotificationPeekWindow.setVisibility(View.VISIBLE);
Joe Onorato7c270fa2010-12-08 17:31:42 -0800449 mNotificationPanel.show(false, true);
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400450
451 mNotificationPeekIndex = peekIndex;
Daniel Sandlera8e5b062010-12-01 13:53:08 -0500452 mNotificationPeekKey = entry.key;
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400453 }
454 }
455 break;
456 case MSG_CLOSE_NOTIFICATION_PEEK:
457 if (DEBUG) Slog.d(TAG, "closing notification peek window");
458 mNotificationPeekWindow.setVisibility(View.GONE);
459 mNotificationPeekRow.removeAllViews();
460 final int N = mNotns.size();
Daniel Sandlera8e5b062010-12-01 13:53:08 -0500461 if (mNotificationPeekIndex >= 0 && mNotificationPeekIndex < N) {
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400462 NotificationData.Entry entry = mNotns.get(N-1-mNotificationPeekIndex);
463 entry.icon.setBackgroundColor(0);
464 }
Daniel Sandlera8e5b062010-12-01 13:53:08 -0500465
466 mNotificationPeekIndex = -1;
467 mNotificationPeekKey = null;
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400468 break;
Daniel Sandler9120d552010-07-23 09:11:14 -0400469 case MSG_OPEN_NOTIFICATION_PANEL:
470 if (DEBUG) Slog.d(TAG, "opening notifications panel");
Joe Onorato7c270fa2010-12-08 17:31:42 -0800471 if (!mNotificationPanel.isShowing()) {
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400472 mNotificationPeekWindow.setVisibility(View.GONE);
Joe Onorato7c270fa2010-12-08 17:31:42 -0800473 mNotificationPanel.show(true, true);
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500474 mNotificationArea.setVisibility(View.GONE);
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800475 mTicker.halt();
Joe Onorato091e1b82010-09-26 18:04:44 -0700476 }
Daniel Sandler9120d552010-07-23 09:11:14 -0400477 break;
478 case MSG_CLOSE_NOTIFICATION_PANEL:
479 if (DEBUG) Slog.d(TAG, "closing notifications panel");
Joe Onorato7c270fa2010-12-08 17:31:42 -0800480 if (mNotificationPanel.isShowing()) {
481 mNotificationPanel.show(false, true);
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500482 mNotificationArea.setVisibility(View.VISIBLE);
Joe Onorato091e1b82010-09-26 18:04:44 -0700483 }
Daniel Sandler9120d552010-07-23 09:11:14 -0400484 break;
Jim Miller44c66fe2010-10-20 18:32:52 -0700485 case MSG_OPEN_RECENTS_PANEL:
486 if (DEBUG) Slog.d(TAG, "opening recents panel");
487 if (mRecentsPanel != null) mRecentsPanel.setVisibility(View.VISIBLE);
488 break;
489 case MSG_CLOSE_RECENTS_PANEL:
490 if (DEBUG) Slog.d(TAG, "closing recents panel");
491 if (mRecentsPanel != null) mRecentsPanel.setVisibility(View.GONE);
Daniel Sandler9120d552010-07-23 09:11:14 -0400492 break;
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500493 case MSG_SHOW_CHROME:
Daniel Sandlere03d1bc2010-11-17 21:36:40 -0500494 if (DEBUG) Slog.d(TAG, "hiding shadows (lights on)");
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500495 mBarContents.setVisibility(View.VISIBLE);
496 mShadow.setVisibility(View.GONE);
Daniel Sandler06e66302010-11-05 15:00:06 -0400497 break;
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500498 case MSG_HIDE_CHROME:
Daniel Sandlere03d1bc2010-11-17 21:36:40 -0500499 if (DEBUG) Slog.d(TAG, "showing shadows (lights out)");
Daniel Sandler06e66302010-11-05 15:00:06 -0400500 animateCollapse();
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500501 mBarContents.setVisibility(View.GONE);
502 mShadow.setVisibility(View.VISIBLE);
Daniel Sandlere03d1bc2010-11-17 21:36:40 -0500503 break;
Daniel Sandler9120d552010-07-23 09:11:14 -0400504 }
505 }
506 }
Daniel Sandler271ea122010-10-22 14:06:10 -0400507
508 public void refreshNotificationTrigger() {
Joe Onoratofd52b182010-11-10 18:00:52 -0800509 /*
Daniel Sandlercf3c7cf2010-10-22 15:30:12 -0400510 if (mNotificationTrigger == null) return;
511
Daniel Sandler271ea122010-10-22 14:06:10 -0400512 int resId;
Joe Onorato7c270fa2010-12-08 17:31:42 -0800513 boolean panel = (mNotificationPanel != null && mNotificationPanel.isShowing();
Daniel Sandler271ea122010-10-22 14:06:10 -0400514 if (!mNotificationsOn) {
515 resId = R.drawable.ic_sysbar_noti_dnd;
516 } else if (mNotns.size() > 0) {
517 resId = panel ? R.drawable.ic_sysbar_noti_avail_open : R.drawable.ic_sysbar_noti_avail;
518 } else {
519 resId = panel ? R.drawable.ic_sysbar_noti_none_open : R.drawable.ic_sysbar_noti_none;
520 }
Joe Onoratofd52b182010-11-10 18:00:52 -0800521 //mNotificationTrigger.setImageResource(resId);
522 */
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400523 }
Daniel Sandler9120d552010-07-23 09:11:14 -0400524
Joe Onorato808182d2010-07-09 18:52:06 -0400525 public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400526 if (DEBUG) Slog.d(TAG, "addIcon(" + slot + ") -> " + icon);
Joe Onorato808182d2010-07-09 18:52:06 -0400527 }
528
529 public void updateIcon(String slot, int index, int viewIndex,
530 StatusBarIcon old, StatusBarIcon icon) {
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400531 if (DEBUG) Slog.d(TAG, "updateIcon(" + slot + ") -> " + icon);
Joe Onorato808182d2010-07-09 18:52:06 -0400532 }
533
534 public void removeIcon(String slot, int index, int viewIndex) {
Daniel Sandler1e3ed8f2010-08-13 10:12:48 -0400535 if (DEBUG) Slog.d(TAG, "removeIcon(" + slot + ")");
Joe Onorato808182d2010-07-09 18:52:06 -0400536 }
537
538 public void addNotification(IBinder key, StatusBarNotification notification) {
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400539 if (DEBUG) Slog.d(TAG, "addNotification(" + key + " -> " + notification + ")");
540 addNotificationViews(key, notification);
Daniel Sandlerfb970e92010-08-20 10:57:17 -0400541
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800542 final boolean immersive = isImmersive();
Joe Onoratocf2b1992010-11-16 21:36:42 -0800543 if (false && immersive) {
Daniel Sandlerfb970e92010-08-20 10:57:17 -0400544 // TODO: immersive mode popups for tablet
545 } else if (notification.notification.fullScreenIntent != null) {
546 // not immersive & a full-screen alert should be shown
547 Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;"
548 + " sending fullScreenIntent");
549 try {
550 notification.notification.fullScreenIntent.send();
551 } catch (PendingIntent.CanceledException e) {
552 }
553 } else {
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800554 tick(key, notification);
Daniel Sandlerfb970e92010-08-20 10:57:17 -0400555 }
Joe Onorato5dd11692010-09-27 15:34:04 -0700556
557 setAreThereNotifications();
Joe Onorato808182d2010-07-09 18:52:06 -0400558 }
559
560 public void updateNotification(IBinder key, StatusBarNotification notification) {
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400561 if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ") // TODO");
Jim Miller44c66fe2010-10-20 18:32:52 -0700562
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400563 final NotificationData.Entry oldEntry = mNotns.findByKey(key);
Daniel Sandler379020a2010-07-29 16:20:06 -0400564 if (oldEntry == null) {
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400565 Slog.w(TAG, "updateNotification for unknown key: " + key);
566 return;
567 }
568
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400569 final StatusBarNotification oldNotification = oldEntry.notification;
570 final RemoteViews oldContentView = oldNotification.notification.contentView;
571
572 final RemoteViews contentView = notification.notification.contentView;
573
Daniel Sandler373a9982010-11-30 12:03:59 -0500574 if (DEBUG) {
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400575 Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
576 + " ongoing=" + oldNotification.isOngoing()
577 + " expanded=" + oldEntry.expanded
578 + " contentView=" + oldContentView);
579 Slog.d(TAG, "new notification: when=" + notification.notification.when
580 + " ongoing=" + oldNotification.isOngoing()
581 + " contentView=" + contentView);
582 }
583
584 // Can we just reapply the RemoteViews in place? If when didn't change, the order
585 // didn't change.
Daniel Sandler373a9982010-11-30 12:03:59 -0500586 boolean orderUnchanged = (notification.notification.when == oldNotification.notification.when
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400587 && notification.isOngoing() == oldNotification.isOngoing()
588 && oldEntry.expanded != null
589 && contentView != null
590 && oldContentView != null
591 && contentView.getPackage() != null
592 && oldContentView.getPackage() != null
593 && oldContentView.getPackage().equals(contentView.getPackage())
Daniel Sandler373a9982010-11-30 12:03:59 -0500594 && oldContentView.getLayoutId() == contentView.getLayoutId());
595 ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
596 boolean isLastAnyway = rowParent.indexOfChild(oldEntry.row) == rowParent.getChildCount() - 1;
597 if (orderUnchanged || isLastAnyway) {
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400598 if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
599 oldEntry.notification = notification;
600 try {
601 // Reapply the RemoteViews
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400602 contentView.reapply(mContext, oldEntry.content);
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400603 // update the contentIntent
604 final PendingIntent contentIntent = notification.notification.contentIntent;
605 if (contentIntent != null) {
606 oldEntry.content.setOnClickListener(new NotificationClicker(contentIntent,
607 notification.pkg, notification.tag, notification.id));
Joe Onorato184498c2010-10-08 17:57:18 -0400608 } else {
609 oldEntry.content.setOnClickListener(null);
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400610 }
611 // Update the icon.
612 final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
613 notification.notification.icon, notification.notification.iconLevel,
614 notification.notification.number);
615 if (!oldEntry.icon.set(ic)) {
616 handleNotificationError(key, notification, "Couldn't update icon: " + ic);
617 return;
618 }
619 }
620 catch (RuntimeException e) {
621 // It failed to add cleanly. Log, and remove the view from the panel.
622 Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
623 removeNotificationViews(key);
624 addNotificationViews(key, notification);
625 }
626 } else {
627 if (DEBUG) Slog.d(TAG, "not reusing notification for key: " + key);
628 removeNotificationViews(key);
629 addNotificationViews(key, notification);
630 }
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800631 // fullScreenIntent doesn't happen on updates. You need to clear & repost a new
632 // notification.
633 final boolean immersive = isImmersive();
634 if (false && immersive) {
635 // TODO: immersive mode
636 } else {
637 tick(key, notification);
638 }
Joe Onorato5dd11692010-09-27 15:34:04 -0700639
640 setAreThereNotifications();
Joe Onorato808182d2010-07-09 18:52:06 -0400641 }
642
643 public void removeNotification(IBinder key) {
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400644 if (DEBUG) Slog.d(TAG, "removeNotification(" + key + ") // TODO");
645 removeNotificationViews(key);
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800646 mTicker.remove(key);
Joe Onorato5dd11692010-09-27 15:34:04 -0700647 setAreThereNotifications();
Joe Onorato808182d2010-07-09 18:52:06 -0400648 }
649
Daniel Sandler3e8f5a22010-12-03 14:52:10 -0500650 public void showClock(boolean show) {
651 View clock = mBarContents.findViewById(R.id.clock);
652 View network_text = mBarContents.findViewById(R.id.network_text);
653 if (clock != null) {
654 clock.setVisibility(show ? View.VISIBLE : View.GONE);
655 }
656 if (network_text != null) {
657 network_text.setVisibility((!show) ? View.VISIBLE : View.GONE);
658 }
659 }
660
Joe Onorato808182d2010-07-09 18:52:06 -0400661 public void disable(int state) {
Joe Onorato091e1b82010-09-26 18:04:44 -0700662 int old = mDisabled;
663 int diff = state ^ old;
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400664 mDisabled = state;
665
Joe Onorato091e1b82010-09-26 18:04:44 -0700666 // act accordingly
Daniel Sandler3e8f5a22010-12-03 14:52:10 -0500667 if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
668 boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
669 Slog.d(TAG, "DISABLE_CLOCK: " + (show ? "no" : "yes"));
670 showClock(show);
671 }
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400672 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
673 if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
674 Slog.d(TAG, "DISABLE_EXPAND: yes");
675 animateCollapse();
676 }
677 }
678 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
679 if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
680 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
Daniel Sandlerb9606992010-11-19 14:47:59 -0500681 // synchronize with current shadow state
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500682 mNotificationIconArea.setVisibility(View.GONE);
Joe Onorato091e1b82010-09-26 18:04:44 -0700683 mTicker.halt();
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400684 } else {
685 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
Daniel Sandlerb9606992010-11-19 14:47:59 -0500686 // synchronize with current shadow state
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500687 mNotificationIconArea.setVisibility(View.VISIBLE);
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400688 }
689 } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
Joe Onorato091e1b82010-09-26 18:04:44 -0700690 if ((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
Joe Onoratoef1e7762010-09-17 18:38:38 -0400691 mTicker.halt();
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400692 }
693 }
Joe Onorato091e1b82010-09-26 18:04:44 -0700694 if ((diff & StatusBarManager.DISABLE_NAVIGATION) != 0) {
695 if ((state & StatusBarManager.DISABLE_NAVIGATION) != 0) {
696 Slog.d(TAG, "DISABLE_NAVIGATION: yes");
Joe Onoratofd52b182010-11-10 18:00:52 -0800697 mNavigationArea.setVisibility(View.GONE);
Joe Onorato091e1b82010-09-26 18:04:44 -0700698 } else {
699 Slog.d(TAG, "DISABLE_NAVIGATION: no");
Joe Onoratofd52b182010-11-10 18:00:52 -0800700 mNavigationArea.setVisibility(View.VISIBLE);
Joe Onorato091e1b82010-09-26 18:04:44 -0700701 }
702 }
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400703 }
704
Joe Onoratoef1e7762010-09-17 18:38:38 -0400705 private boolean hasTicker(Notification n) {
Joe Onorato46439ce2010-11-19 13:56:21 -0800706 return n.tickerView != null || !TextUtils.isEmpty(n.tickerText);
Joe Onoratoef1e7762010-09-17 18:38:38 -0400707 }
708
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800709 private void tick(IBinder key, StatusBarNotification n) {
Joe Onorato55d2d762010-09-26 13:02:01 -0700710 // Don't show the ticker when the windowshade is open.
Joe Onorato7c270fa2010-12-08 17:31:42 -0800711 if (mNotificationPanel.isShowing()) {
Joe Onorato55d2d762010-09-26 13:02:01 -0700712 return;
713 }
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400714 // Show the ticker if one is requested. Also don't do this
715 // until status bar window is attached to the window manager,
716 // because... well, what's the point otherwise? And trying to
717 // run a ticker without being attached will crash!
Joe Onoratoef1e7762010-09-17 18:38:38 -0400718 if (hasTicker(n.notification) && mStatusBarView.getWindowToken() != null) {
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400719 if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
720 | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800721 mTicker.add(key, n);
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400722 }
723 }
724 }
725
Joe Onorato808182d2010-07-09 18:52:06 -0400726 public void animateExpand() {
Joe Onoratob62ac122010-09-20 16:16:32 -0400727 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL);
728 mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL);
Joe Onorato808182d2010-07-09 18:52:06 -0400729 }
730
731 public void animateCollapse() {
Joe Onoratob62ac122010-09-20 16:16:32 -0400732 mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PANEL);
733 mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PANEL);
Jim Miller44c66fe2010-10-20 18:32:52 -0700734 mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
735 mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
Daniel Sandler9120d552010-07-23 09:11:14 -0400736 }
737
Joe Onoratofd52b182010-11-10 18:00:52 -0800738 // called by StatusBar
Daniel Sandler06e66302010-11-05 15:00:06 -0400739 @Override
Joe Onorato93056472010-09-10 10:30:46 -0400740 public void setLightsOn(boolean on) {
Daniel Sandler59485d742010-12-02 00:11:33 -0500741 // Policy note: if the frontmost activity needs the menu key, we assume it is a legacy app
742 // that can't handle lights-out mode.
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500743 if (mMenuButton.getVisibility() == View.VISIBLE) {
Daniel Sandler59485d742010-12-02 00:11:33 -0500744 on = true;
745 }
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500746 mHandler.removeMessages(MSG_HIDE_CHROME);
747 mHandler.removeMessages(MSG_SHOW_CHROME);
748 mHandler.sendEmptyMessage(on ? MSG_SHOW_CHROME : MSG_HIDE_CHROME);
Joe Onorato93056472010-09-10 10:30:46 -0400749 }
750
Daniel Sandlere02d8082010-10-08 15:13:22 -0400751 public void setMenuKeyVisible(boolean visible) {
752 if (DEBUG) {
753 Slog.d(TAG, (visible?"showing":"hiding") + " the MENU button");
754 }
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -0500755 mMenuButton.setVisibility(visible ? View.VISIBLE : View.GONE);
Daniel Sandler59485d742010-12-02 00:11:33 -0500756
757 // See above re: lights-out policy for legacy apps.
758 if (visible) setLightsOn(true);
Daniel Sandlere02d8082010-10-08 15:13:22 -0400759 }
760
satokcd7cd292010-11-20 15:46:23 +0900761 public void setIMEButtonVisible(IBinder token, boolean visible) {
satok06487a52010-10-29 11:37:18 +0900762 if (DEBUG) {
763 Slog.d(TAG, (visible?"showing":"hiding") + " the IME button");
764 }
satokcd7cd292010-11-20 15:46:23 +0900765 mInputMethodSwitchButton.setIMEButtonVisible(token, visible);
Joe Onorato536c58f2010-11-28 17:52:28 -0800766 mBackButton.setImageResource(
767 visible ? R.drawable.ic_sysbar_back_ime : R.drawable.ic_sysbar_back);
Daniel Sandler10163c62010-12-08 11:51:05 -0500768 if (FAKE_SPACE_BAR) {
769 mFakeSpaceBar.setVisibility(visible ? View.VISIBLE : View.GONE);
770 }
satok06487a52010-10-29 11:37:18 +0900771 }
772
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800773 private boolean isImmersive() {
774 try {
775 return ActivityManagerNative.getDefault().isTopActivityImmersive();
776 //Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
777 } catch (RemoteException ex) {
778 // the end is nigh
779 return false;
780 }
781 }
782
Joe Onorato5dd11692010-09-27 15:34:04 -0700783 private void setAreThereNotifications() {
784 final boolean hasClearable = mNotns.hasClearableItems();
Joe Onorato5dd11692010-09-27 15:34:04 -0700785 }
786
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400787 /**
788 * Cancel this notification and tell the status bar service about the failure. Hold no locks.
789 */
790 void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
791 removeNotification(key);
792 try {
793 mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
794 } catch (RemoteException ex) {
795 // The end is nigh.
796 }
797 }
798
Daniel Sandler10163c62010-12-08 11:51:05 -0500799 private void sendKey(KeyEvent key) {
800 try {
801 if (DEBUG) Slog.d(TAG, "injecting key event: " + key);
802 mWindowManager.injectInputEventNoWait(key);
803 } catch (RemoteException ex) {
804 }
805 }
806
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400807 private View.OnClickListener mOnClickListener = new View.OnClickListener() {
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400808 public void onClick(View v) {
Joe Onoratofd52b182010-11-10 18:00:52 -0800809 if (v == mNotificationTrigger) {
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400810 onClickNotificationTrigger();
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400811 } else if (v == mRecentButton) {
812 onClickRecentButton();
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400813 }
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400814 }
815 };
816
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400817 public void onClickNotificationTrigger() {
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -0500818 if (DEBUG) Slog.d(TAG, "clicked notification icons; disabled=" + mDisabled);
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400819 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
Daniel Sandler271ea122010-10-22 14:06:10 -0400820 if (!mNotificationsOn) {
821 mNotificationsOn = true;
Daniel Sandlercf3c7cf2010-10-22 15:30:12 -0400822 mIconLayout.setVisibility(View.VISIBLE); // TODO: animation
Daniel Sandler271ea122010-10-22 14:06:10 -0400823 refreshNotificationTrigger();
824 } else {
Joe Onorato7c270fa2010-12-08 17:31:42 -0800825 int msg = !mNotificationPanel.isShowing()
Daniel Sandler271ea122010-10-22 14:06:10 -0400826 ? MSG_OPEN_NOTIFICATION_PANEL
827 : MSG_CLOSE_NOTIFICATION_PANEL;
828 mHandler.removeMessages(msg);
829 mHandler.sendEmptyMessage(msg);
830 }
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400831 }
832 }
833
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400834 public void onClickRecentButton() {
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -0500835 if (DEBUG) Slog.d(TAG, "clicked recent apps; disabled=" + mDisabled);
Jim Miller44c66fe2010-10-20 18:32:52 -0700836 if (mRecentsPanel == null) {
837 Intent intent = new Intent();
838 intent.setClass(mContext, RecentApplicationsActivity.class);
839 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
840 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
841 mContext.startActivity(intent);
842 } else {
843 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
844 int msg = (mRecentsPanel.getVisibility() == View.GONE)
845 ? MSG_OPEN_RECENTS_PANEL
846 : MSG_CLOSE_RECENTS_PANEL;
847 mHandler.removeMessages(msg);
848 mHandler.sendEmptyMessage(msg);
849 }
850 }
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400851 }
Joe Onorato55d2d762010-09-26 13:02:01 -0700852
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400853 private class NotificationClicker implements View.OnClickListener {
854 private PendingIntent mIntent;
855 private String mPkg;
856 private String mTag;
857 private int mId;
858
859 NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
860 mIntent = intent;
861 mPkg = pkg;
862 mTag = tag;
863 mId = id;
864 }
865
866 public void onClick(View v) {
867 try {
868 // The intent we are sending is for the application, which
869 // won't have permission to immediately start an activity after
870 // the user switches to home. We know it is safe to do at this
871 // point, so make sure new activity switches are now allowed.
872 ActivityManagerNative.getDefault().resumeAppSwitches();
873 } catch (RemoteException e) {
874 }
875
876 if (mIntent != null) {
877 int[] pos = new int[2];
878 v.getLocationOnScreen(pos);
879 Intent overlay = new Intent();
880 overlay.setSourceBounds(
881 new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
882 try {
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400883 mIntent.send(mContext, 0, overlay);
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400884 } catch (PendingIntent.CanceledException e) {
885 // the stack trace isn't very helpful here. Just log the exception message.
886 Slog.w(TAG, "Sending contentIntent failed: " + e);
887 }
888 }
889
890 try {
891 mBarService.onNotificationClick(mPkg, mTag, mId);
892 } catch (RemoteException ex) {
893 // system process is dead if we're here.
894 }
895
896 // close the shade if it was open
897 animateCollapse();
898
899 // If this click was on the intruder alert, hide that instead
900// mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
901 }
902 }
903
904 StatusBarNotification removeNotificationViews(IBinder key) {
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400905 NotificationData.Entry entry = mNotns.remove(key);
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400906 if (entry == null) {
907 Slog.w(TAG, "removeNotification for unknown key: " + key);
908 return null;
909 }
910 // Remove the expanded view.
911 ViewGroup rowParent = (ViewGroup)entry.row.getParent();
912 if (rowParent != null) rowParent.removeView(entry.row);
Daniel Sandlera8e5b062010-12-01 13:53:08 -0500913
914 if (key == mNotificationPeekKey) {
915 // must close the peek as well, since it's gone
916 mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK);
917 }
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400918 // Remove the icon.
Daniel Sandler0f0b11c2010-08-04 15:54:58 -0400919// ViewGroup iconParent = (ViewGroup)entry.icon.getParent();
920// if (iconParent != null) iconParent.removeView(entry.icon);
Daniel Sandler0ad460b2010-12-14 12:14:53 -0500921 updateNotificationIcons();
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400922
923 return entry.notification;
924 }
925
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400926 private class NotificationIconTouchListener implements View.OnTouchListener {
927 VelocityTracker mVT;
928
929 public NotificationIconTouchListener() {
930 }
931
932 public boolean onTouch(View v, MotionEvent event) {
933 boolean peeking = mNotificationPeekWindow.getVisibility() != View.GONE;
Joe Onorato7c270fa2010-12-08 17:31:42 -0800934 boolean panelShowing = mNotificationPanel.isShowing();
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400935 if (panelShowing) return false;
936
937 switch (event.getAction()) {
938 case MotionEvent.ACTION_DOWN:
939 mVT = VelocityTracker.obtain();
940
941 // fall through
942 case MotionEvent.ACTION_OUTSIDE:
943 case MotionEvent.ACTION_MOVE:
944 // peek and switch icons if necessary
945 int numIcons = mIconLayout.getChildCount();
Joe Onorato5e759462010-11-28 17:43:50 -0800946 int peekIndex = (int)((float)event.getX() * numIcons / mIconLayout.getWidth());
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400947 if (peekIndex > numIcons - 1) peekIndex = numIcons - 1;
948 else if (peekIndex < 0) peekIndex = 0;
949
950 if (!peeking || mNotificationPeekIndex != peekIndex) {
951 if (DEBUG) Slog.d(TAG, "will peek at notification #" + peekIndex);
952 Message peekMsg = mHandler.obtainMessage(MSG_OPEN_NOTIFICATION_PEEK);
953 peekMsg.arg1 = peekIndex;
954
955 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
956
957 // no delay if we're scrubbing left-right
Joe Onorato5e759462010-11-28 17:43:50 -0800958 mHandler.sendMessage(peekMsg);
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400959 }
960
961 // check for fling
962 if (mVT != null) {
963 mVT.addMovement(event);
964 mVT.computeCurrentVelocity(1000);
965 // require a little more oomph once we're already in peekaboo mode
966 if (!panelShowing && (
967 (peeking && mVT.getYVelocity() < -mNotificationFlingVelocity*3)
968 || (mVT.getYVelocity() < -mNotificationFlingVelocity))) {
969 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
970 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL);
971 mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK);
972 mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL);
973 }
974 }
975 return true;
976 case MotionEvent.ACTION_UP:
977 case MotionEvent.ACTION_CANCEL:
978 mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
979 if (peeking) {
Joe Onorato5e759462010-11-28 17:43:50 -0800980 mHandler.sendEmptyMessageDelayed(MSG_CLOSE_NOTIFICATION_PEEK, 5000);
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -0400981 }
982 mVT.recycle();
983 mVT = null;
984 return true;
985 }
986 return false;
987 }
988 }
989
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400990 StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
Daniel Sandlerdfa08db2010-08-05 16:18:42 -0400991 if (DEBUG) {
992 Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
993 }
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400994 // Construct the icon.
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400995 final StatusBarIconView iconView = new StatusBarIconView(mContext,
Daniel Sandler3eebd1f2010-07-27 08:39:33 -0400996 notification.pkg + "/0x" + Integer.toHexString(notification.id));
997 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
998
999 final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
1000 notification.notification.icon,
1001 notification.notification.iconLevel,
1002 notification.notification.number);
1003 if (!iconView.set(ic)) {
1004 handleNotificationError(key, notification, "Couldn't attach StatusBarIcon: " + ic);
1005 return null;
1006 }
1007 // Construct the expanded view.
1008 NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
Daniel Sandlerdfa08db2010-08-05 16:18:42 -04001009 if (!inflateViews(entry, mPile)) {
Daniel Sandler3eebd1f2010-07-27 08:39:33 -04001010 handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
1011 + notification);
1012 return null;
1013 }
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -04001014
Daniel Sandler3eebd1f2010-07-27 08:39:33 -04001015 // Add the icon.
Daniel Sandlerdfa08db2010-08-05 16:18:42 -04001016 mNotns.add(entry);
Daniel Sandler0ad460b2010-12-14 12:14:53 -05001017 updateNotificationIcons();
Daniel Sandler0f0b11c2010-08-04 15:54:58 -04001018
Daniel Sandler3eebd1f2010-07-27 08:39:33 -04001019 return iconView;
1020 }
1021
Daniel Sandler0ad460b2010-12-14 12:14:53 -05001022 private void reloadAllNotificationIcons() {
1023 if (mIconLayout == null) return;
1024 mIconLayout.removeAllViews();
1025 updateNotificationIcons();
1026 }
1027
1028 private void updateNotificationIcons() {
Daniel Sandler0f0b11c2010-08-04 15:54:58 -04001029 // XXX: need to implement a new limited linear layout class
1030 // to avoid removing & readding everything
Daniel Sandlerdfa08db2010-08-05 16:18:42 -04001031
Daniel Sandler0ad460b2010-12-14 12:14:53 -05001032 if (mIconLayout == null) return;
1033
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -04001034 final LinearLayout.LayoutParams params
Daniel Sandler0ad460b2010-12-14 12:14:53 -05001035 = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mBarHeight);
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -04001036
Daniel Sandler0f0b11c2010-08-04 15:54:58 -04001037 int N = mNotns.size();
Daniel Sandlerdfa08db2010-08-05 16:18:42 -04001038
1039 if (DEBUG) {
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -04001040 Slog.d(TAG, "refreshing icons: " + N + " notifications, mIconLayout=" + mIconLayout);
Daniel Sandlerdfa08db2010-08-05 16:18:42 -04001041 }
1042
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -04001043 ArrayList<View> toShow = new ArrayList<View>();
1044
Daniel Sandler0ad460b2010-12-14 12:14:53 -05001045 for (int i=0; i<MAX_NOTIFICATION_ICONS; i++) {
Daniel Sandler0f0b11c2010-08-04 15:54:58 -04001046 if (i>=N) break;
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -04001047 toShow.add(mNotns.get(N-i-1).icon);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -04001048 }
Daniel Sandlerdfa08db2010-08-05 16:18:42 -04001049
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -04001050 ArrayList<View> toRemove = new ArrayList<View>();
1051 for (int i=0; i<mIconLayout.getChildCount(); i++) {
1052 View child = mIconLayout.getChildAt(i);
1053 if (!toShow.contains(child)) {
1054 toRemove.add(child);
1055 }
Daniel Sandlerdfa08db2010-08-05 16:18:42 -04001056 }
Daniel Sandler271ea122010-10-22 14:06:10 -04001057
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -04001058 for (View remove : toRemove) {
1059 mIconLayout.removeView(remove);
1060 }
1061
1062 for (int i=0; i<toShow.size(); i++) {
1063 View v = toShow.get(i);
Daniel Sandler0ad460b2010-12-14 12:14:53 -05001064 v.setPadding(mIconHPadding, 0, mIconHPadding, 0);
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -04001065 if (v.getParent() == null) {
Daniel Sandler0ad460b2010-12-14 12:14:53 -05001066 mIconLayout.addView(v, i, params);
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -04001067 }
1068 }
1069
1070 loadNotificationPanel();
Daniel Sandler271ea122010-10-22 14:06:10 -04001071 refreshNotificationTrigger();
Daniel Sandler0f0b11c2010-08-04 15:54:58 -04001072 }
1073
Daniel Sandlerb0cc50d2010-10-26 16:55:56 -04001074 private void loadNotificationPanel() {
1075 int N = mNotns.size();
1076
1077 ArrayList<View> toShow = new ArrayList<View>();
1078
1079 for (int i=0; i<N; i++) {
1080 View row = mNotns.get(N-i-1).row;
1081 toShow.add(row);
1082 }
1083
1084 ArrayList<View> toRemove = new ArrayList<View>();
1085 for (int i=0; i<mPile.getChildCount(); i++) {
1086 View child = mPile.getChildAt(i);
1087 if (!toShow.contains(child)) {
1088 toRemove.add(child);
1089 }
1090 }
1091
1092 for (View remove : toRemove) {
1093 mPile.removeView(remove);
1094 }
1095
1096 for (int i=0; i<toShow.size(); i++) {
1097 View v = toShow.get(i);
1098 if (v.getParent() == null) {
1099 mPile.addView(toShow.get(i));
1100 }
1101 }
1102 }
1103
Joe Onorato4daaeaf2010-11-17 20:43:12 -08001104 void workAroundBadLayerDrawableOpacity(View v) {
1105 LayerDrawable d = (LayerDrawable)v.getBackground();
1106 v.setBackgroundDrawable(null);
1107 d.setOpacity(PixelFormat.TRANSLUCENT);
1108 v.setBackgroundDrawable(d);
1109 }
1110
Daniel Sandler3eebd1f2010-07-27 08:39:33 -04001111 private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
1112 StatusBarNotification sbn = entry.notification;
1113 RemoteViews remoteViews = sbn.notification.contentView;
1114 if (remoteViews == null) {
1115 return false;
1116 }
1117
1118 // create the row view
Joe Onoratof3c3c4f2010-10-21 11:09:02 -04001119 LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
1120 Context.LAYOUT_INFLATER_SERVICE);
Joe Onorato755cc742010-11-27 15:45:35 -08001121 View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
Joe Onorato4daaeaf2010-11-17 20:43:12 -08001122 workAroundBadLayerDrawableOpacity(row);
Daniel Sandler0f0b11c2010-08-04 15:54:58 -04001123 View vetoButton = row.findViewById(R.id.veto);
Joe Onoratoa4a65032010-09-27 15:53:44 -07001124 if (entry.notification.isClearable()) {
1125 final String _pkg = sbn.pkg;
1126 final String _tag = sbn.tag;
1127 final int _id = sbn.id;
Jim Miller44c66fe2010-10-20 18:32:52 -07001128 vetoButton.setOnClickListener(new View.OnClickListener() {
Joe Onoratoa4a65032010-09-27 15:53:44 -07001129 public void onClick(View v) {
1130 try {
1131 mBarService.onNotificationClear(_pkg, _tag, _id);
1132 } catch (RemoteException ex) {
1133 // system process is dead if we're here.
1134 }
Daniel Sandler0f0b11c2010-08-04 15:54:58 -04001135 }
Joe Onoratoa4a65032010-09-27 15:53:44 -07001136 });
1137 } else {
1138 vetoButton.setVisibility(View.INVISIBLE);
1139 }
Daniel Sandler3eebd1f2010-07-27 08:39:33 -04001140
Joe Onorato561d3852010-11-20 18:09:34 -08001141 // the large icon
1142 ImageView largeIcon = (ImageView)row.findViewById(R.id.large_icon);
1143 if (sbn.notification.largeIcon != null) {
1144 largeIcon.setImageBitmap(sbn.notification.largeIcon);
1145 } else {
1146 largeIcon.getLayoutParams().width = 0;
1147 largeIcon.setVisibility(View.INVISIBLE);
1148 }
1149
Daniel Sandler3eebd1f2010-07-27 08:39:33 -04001150 // bind the click event to the content area
1151 ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
1152 // XXX: update to allow controls within notification views
1153 content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
1154// content.setOnFocusChangeListener(mFocusChangeListener);
1155 PendingIntent contentIntent = sbn.notification.contentIntent;
1156 if (contentIntent != null) {
1157 content.setOnClickListener(new NotificationClicker(contentIntent,
1158 sbn.pkg, sbn.tag, sbn.id));
Joe Onorato184498c2010-10-08 17:57:18 -04001159 } else {
1160 content.setOnClickListener(null);
Daniel Sandler3eebd1f2010-07-27 08:39:33 -04001161 }
1162
1163 View expanded = null;
1164 Exception exception = null;
1165 try {
Joe Onoratof3c3c4f2010-10-21 11:09:02 -04001166 expanded = remoteViews.apply(mContext, content);
Daniel Sandler3eebd1f2010-07-27 08:39:33 -04001167 }
1168 catch (RuntimeException e) {
1169 exception = e;
1170 }
1171 if (expanded == null) {
Joe Onorato46439ce2010-11-19 13:56:21 -08001172 final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
Daniel Sandler3eebd1f2010-07-27 08:39:33 -04001173 Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
1174 return false;
1175 } else {
1176 content.addView(expanded);
1177 row.setDrawingCacheEnabled(true);
1178 }
1179
1180 entry.row = row;
1181 entry.content = content;
1182 entry.expanded = expanded;
1183
1184 return true;
1185 }
Daniel Sandlerce70d912010-09-02 11:59:41 -04001186
Daniel Sandler0ad460b2010-12-14 12:14:53 -05001187/*
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001188 public class ShadowController {
1189 boolean mShowShadows;
Daniel Sandlerb9606992010-11-19 14:47:59 -05001190 Map<View, View> mShadowsForElements = new IdentityHashMap<View, View>(7);
1191 Map<View, View> mElementsForShadows = new IdentityHashMap<View, View>(7);
1192 LayoutTransition mElementTransition, mShadowTransition;
1193
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001194 View mTouchTarget;
Daniel Sandlerce70d912010-09-02 11:59:41 -04001195
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001196 ShadowController(boolean showShadows) {
1197 mShowShadows = showShadows;
1198 mTouchTarget = null;
Daniel Sandlerb9606992010-11-19 14:47:59 -05001199
1200 mElementTransition = new LayoutTransition();
1201// AnimatorSet s = new AnimatorSet();
1202// s.play(ObjectAnimator.ofInt(null, "top", 48, 0))
1203// .with(ObjectAnimator.ofFloat(null, "scaleY", 0.5f, 1f))
1204// .with(ObjectAnimator.ofFloat(null, "alpha", 0.5f, 1f))
1205// ;
1206 mElementTransition.setAnimator(LayoutTransition.APPEARING, //s);
1207 ObjectAnimator.ofInt(null, "top", 48, 0));
1208 mElementTransition.setDuration(LayoutTransition.APPEARING, 100);
1209 mElementTransition.setStartDelay(LayoutTransition.APPEARING, 0);
1210
1211// s = new AnimatorSet();
1212// s.play(ObjectAnimator.ofInt(null, "top", 0, 48))
1213// .with(ObjectAnimator.ofFloat(null, "scaleY", 1f, 0.5f))
1214// .with(ObjectAnimator.ofFloat(null, "alpha", 1f, 0.5f))
1215// ;
1216 mElementTransition.setAnimator(LayoutTransition.DISAPPEARING, //s);
1217 ObjectAnimator.ofInt(null, "top", 0, 48));
1218 mElementTransition.setDuration(LayoutTransition.DISAPPEARING, 400);
1219
1220 mShadowTransition = new LayoutTransition();
1221 mShadowTransition.setAnimator(LayoutTransition.APPEARING,
1222 ObjectAnimator.ofFloat(null, "alpha", 0f, 1f));
1223 mShadowTransition.setDuration(LayoutTransition.APPEARING, 200);
1224 mShadowTransition.setStartDelay(LayoutTransition.APPEARING, 100);
1225 mShadowTransition.setAnimator(LayoutTransition.DISAPPEARING,
1226 ObjectAnimator.ofFloat(null, "alpha", 1f, 0f));
1227 mShadowTransition.setDuration(LayoutTransition.DISAPPEARING, 100);
1228
1229 ViewGroup bar = (ViewGroup) TabletStatusBar.this.mBarContents;
1230 bar.setLayoutTransition(mElementTransition);
1231 ViewGroup nav = (ViewGroup) TabletStatusBar.this.mNavigationArea;
1232 nav.setLayoutTransition(mElementTransition);
1233 ViewGroup shadowGroup = (ViewGroup) bar.findViewById(R.id.shadows);
1234 shadowGroup.setLayoutTransition(mShadowTransition);
1235 }
1236
1237 public void add(View element, View shadow) {
1238 shadow.setOnTouchListener(makeTouchListener());
1239 mShadowsForElements.put(element, shadow);
1240 mElementsForShadows.put(shadow, element);
Joe Onoratof63b0f42010-09-12 17:03:19 -04001241 }
1242
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001243 public boolean getShadowState() {
1244 return mShowShadows;
Joe Onoratof63b0f42010-09-12 17:03:19 -04001245 }
1246
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001247 public View.OnTouchListener makeTouchListener() {
1248 return new View.OnTouchListener() {
1249 public boolean onTouch(View v, MotionEvent ev) {
1250 final int action = ev.getAction();
1251
1252 if (DEBUG) Slog.d(TAG, "ShadowController: v=" + v + ", ev=" + ev);
1253
1254 // currently redirecting events?
1255 if (mTouchTarget == null) {
Daniel Sandlerb9606992010-11-19 14:47:59 -05001256 mTouchTarget = mElementsForShadows.get(v);
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001257 }
1258
1259 if (mTouchTarget != null && mTouchTarget.getVisibility() != View.GONE) {
1260 boolean last = false;
1261 switch (action) {
1262 case MotionEvent.ACTION_CANCEL:
1263 case MotionEvent.ACTION_UP:
Daniel Sandlere03d1bc2010-11-17 21:36:40 -05001264 mHandler.removeMessages(MSG_RESTORE_SHADOWS);
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001265 if (mShowShadows) {
Daniel Sandlere03d1bc2010-11-17 21:36:40 -05001266 mHandler.sendEmptyMessageDelayed(MSG_RESTORE_SHADOWS,
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001267 v == mNotificationShadow ? 5000 : 500);
1268 }
1269 last = true;
1270 break;
1271 case MotionEvent.ACTION_DOWN:
Daniel Sandlere03d1bc2010-11-17 21:36:40 -05001272 mHandler.removeMessages(MSG_RESTORE_SHADOWS);
Daniel Sandlerb9606992010-11-19 14:47:59 -05001273 setElementShadow(mTouchTarget, false);
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001274 break;
1275 }
1276 mTouchTarget.dispatchTouchEvent(ev);
1277 if (last) mTouchTarget = null;
1278 return true;
1279 }
1280
1281 return false;
1282 }
1283 };
Joe Onoratof63b0f42010-09-12 17:03:19 -04001284 }
1285
Daniel Sandlere03d1bc2010-11-17 21:36:40 -05001286 public void refresh() {
Daniel Sandlerb9606992010-11-19 14:47:59 -05001287 for (View element : mShadowsForElements.keySet()) {
1288 setElementShadow(element, mShowShadows);
1289 }
Daniel Sandlere03d1bc2010-11-17 21:36:40 -05001290 }
1291
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001292 public void showAllShadows() {
1293 mShowShadows = true;
Daniel Sandlere03d1bc2010-11-17 21:36:40 -05001294 refresh();
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001295 }
1296
1297 public void hideAllShadows() {
1298 mShowShadows = false;
Daniel Sandlere03d1bc2010-11-17 21:36:40 -05001299 refresh();
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001300 }
1301
1302 // Use View.INVISIBLE for things hidden due to shadowing, and View.GONE for things that are
1303 // disabled (and should not be shadowed or re-shown)
Daniel Sandlerb9606992010-11-19 14:47:59 -05001304 public void setElementShadow(View button, boolean shade) {
1305 View shadow = mShadowsForElements.get(button);
Daniel Sandlerb6d3dc62010-11-17 16:51:26 -05001306 if (shadow != null) {
1307 if (button.getVisibility() != View.GONE) {
1308 shadow.setVisibility(shade ? View.VISIBLE : View.INVISIBLE);
1309 button.setVisibility(shade ? View.INVISIBLE : View.VISIBLE);
1310 }
1311 }
1312 }
Daniel Sandlerb9606992010-11-19 14:47:59 -05001313
1314 // Hide both element and shadow, using default layout animations.
1315 public void hideElement(View button) {
1316 Slog.d(TAG, "hiding: " + button);
1317 View shadow = mShadowsForElements.get(button);
1318 if (shadow != null) {
1319 shadow.setVisibility(View.GONE);
1320 }
1321 button.setVisibility(View.GONE);
1322 }
1323
1324 // Honoring the current shadow state.
1325 public void showElement(View button) {
1326 Slog.d(TAG, "showing: " + button);
1327 View shadow = mShadowsForElements.get(button);
1328 if (shadow != null) {
1329 shadow.setVisibility(mShowShadows ? View.VISIBLE : View.INVISIBLE);
1330 }
1331 button.setVisibility(mShowShadows ? View.INVISIBLE : View.VISIBLE);
1332 }
Daniel Sandlerce70d912010-09-02 11:59:41 -04001333 }
Daniel Sandler0ad460b2010-12-14 12:14:53 -05001334 */
Joe Onoratob62ac122010-09-20 16:16:32 -04001335
1336 public class TouchOutsideListener implements View.OnTouchListener {
1337 private int mMsg;
Joe Onoratoddf680b2010-09-26 13:59:40 -07001338 private StatusBarPanel mPanel;
Joe Onoratob62ac122010-09-20 16:16:32 -04001339
Joe Onoratoddf680b2010-09-26 13:59:40 -07001340 public TouchOutsideListener(int msg, StatusBarPanel panel) {
Joe Onoratob62ac122010-09-20 16:16:32 -04001341 mMsg = msg;
Joe Onoratoddf680b2010-09-26 13:59:40 -07001342 mPanel = panel;
Joe Onoratob62ac122010-09-20 16:16:32 -04001343 }
1344
1345 public boolean onTouch(View v, MotionEvent ev) {
Joe Onoratoddf680b2010-09-26 13:59:40 -07001346 final int action = ev.getAction();
1347 if (action == MotionEvent.ACTION_OUTSIDE
1348 || (action == MotionEvent.ACTION_DOWN
1349 && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
Joe Onoratob62ac122010-09-20 16:16:32 -04001350 mHandler.removeMessages(mMsg);
1351 mHandler.sendEmptyMessage(mMsg);
1352 return true;
1353 }
1354 return false;
1355 }
1356 }
Joe Onorato091e1b82010-09-26 18:04:44 -07001357
Joe Onoratof3c3c4f2010-10-21 11:09:02 -04001358 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1359 pw.print("mDisabled=0x");
1360 pw.println(Integer.toHexString(mDisabled));
1361 }
Joe Onorato808182d2010-07-09 18:52:06 -04001362}
Daniel Sandlerd39e3882010-08-31 14:16:13 -04001363
1364