blob: b1e38d865c5f87b07c88eca1bd9004d4bf7bd823 [file] [log] [blame]
Joe Onorato2314aab2010-04-08 16:41:23 -05001/*
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
Joe Onorato79de0c52010-05-26 17:03:26 -040017package com.android.systemui.statusbar;
Joe Onorato2314aab2010-04-08 16:41:23 -050018
Christopher Tate5e08af02012-09-21 17:17:22 -070019import android.app.ActivityManager;
Daniel Sandler6a858c32012-03-12 14:38:58 -040020import android.app.ActivityManagerNative;
21import android.app.KeyguardManager;
22import android.app.PendingIntent;
Adam Powell0fc5b2b2012-07-18 18:20:29 -070023import android.app.TaskStackBuilder;
Daniel Sandlerb9301c32012-08-14 15:08:24 -040024import android.content.BroadcastReceiver;
Joe Onorato808182d2010-07-09 18:52:06 -040025import android.content.Context;
Daniel Sandler6a858c32012-03-12 14:38:58 -040026import android.content.Intent;
Daniel Sandlerb9301c32012-08-14 15:08:24 -040027import android.content.IntentFilter;
Daniel Sandler96fd7c12012-03-30 16:37:36 -040028import android.content.pm.ApplicationInfo;
29import android.content.pm.PackageManager.NameNotFoundException;
John Spurlockde84f0e2013-06-12 12:41:00 -040030import android.content.res.Configuration;
Daniel Sandler26cda272012-05-22 15:44:08 -040031import android.database.ContentObserver;
Daniel Sandler6a858c32012-03-12 14:38:58 -040032import android.graphics.Rect;
Daniel Sandlerf7a19562012-04-04 14:04:21 -040033import android.net.Uri;
Daniel Sandler96fd7c12012-03-30 16:37:36 -040034import android.os.Build;
Michael Jurka7f2668c2012-03-27 07:49:52 -070035import android.os.Handler;
Joe Onorato808182d2010-07-09 18:52:06 -040036import android.os.IBinder;
Michael Jurka7f2668c2012-03-27 07:49:52 -070037import android.os.Message;
Chris Wren157026f2013-06-28 16:54:01 -040038import android.os.PowerManager;
Joe Onorato808182d2010-07-09 18:52:06 -040039import android.os.RemoteException;
40import android.os.ServiceManager;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070041import android.os.UserHandle;
Daniel Sandlerf7a19562012-04-04 14:04:21 -040042import android.provider.Settings;
Chris Wren157026f2013-06-28 16:54:01 -040043import android.service.dreams.DreamService;
44import android.service.dreams.IDreamManager;
John Spurlockde84f0e2013-06-12 12:41:00 -040045import android.service.notification.StatusBarNotification;
Chris Wren0c8275b2012-05-08 13:36:48 -040046import android.text.TextUtils;
Joe Onorato808182d2010-07-09 18:52:06 -040047import android.util.Log;
Daniel Sandlerc638c1e2011-08-24 16:19:23 -070048import android.view.Display;
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -050049import android.view.IWindowManager;
Michael Jurka7f2668c2012-03-27 07:49:52 -070050import android.view.LayoutInflater;
Daniel Sandlerf7a19562012-04-04 14:04:21 -040051import android.view.MenuItem;
Michael Jurka7f2668c2012-03-27 07:49:52 -070052import android.view.MotionEvent;
Joe Onorato808182d2010-07-09 18:52:06 -040053import android.view.View;
Daniel Sandler6a858c32012-03-12 14:38:58 -040054import android.view.ViewGroup;
Michael Jurka7f2668c2012-03-27 07:49:52 -070055import android.view.ViewGroup.LayoutParams;
Joe Onorato808182d2010-07-09 18:52:06 -040056import android.view.WindowManager;
Michael Jurka80343f62012-10-18 13:13:46 +020057import android.view.WindowManagerGlobal;
Chris Wren0c8275b2012-05-08 13:36:48 -040058import android.widget.ImageView;
Michael Jurka7f2668c2012-03-27 07:49:52 -070059import android.widget.LinearLayout;
Daniel Sandlerf7a19562012-04-04 14:04:21 -040060import android.widget.PopupMenu;
Michael Jurkaa600fd92012-06-25 15:57:05 -070061import android.widget.RemoteViews;
Daniel Sandlerb9301c32012-08-14 15:08:24 -040062import android.widget.TextView;
Joe Onorato808182d2010-07-09 18:52:06 -040063
John Spurlockde84f0e2013-06-12 12:41:00 -040064import com.android.internal.statusbar.IStatusBarService;
65import com.android.internal.statusbar.StatusBarIcon;
66import com.android.internal.statusbar.StatusBarIconList;
67import com.android.internal.widget.SizeAdaptiveLayout;
68import com.android.systemui.R;
John Spurlockd08de372013-06-24 13:06:08 -040069import com.android.systemui.RecentsComponent;
John Spurlockde84f0e2013-06-12 12:41:00 -040070import com.android.systemui.SearchPanelView;
71import com.android.systemui.SystemUI;
John Spurlockde84f0e2013-06-12 12:41:00 -040072import com.android.systemui.statusbar.policy.NotificationRowLayout;
John Spurlockde84f0e2013-06-12 12:41:00 -040073
Michael Jurkaa600fd92012-06-25 15:57:05 -070074import java.util.ArrayList;
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -070075import java.util.Locale;
Joe Onorato1c95ecb2010-06-28 17:19:12 -040076
Michael Jurka7f2668c2012-03-27 07:49:52 -070077public abstract class BaseStatusBar extends SystemUI implements
Michael Jurkacb2522c2012-04-13 09:32:47 -070078 CommandQueue.Callbacks {
Daniel Sandler198a0302012-08-17 16:04:31 -040079 public static final String TAG = "StatusBar";
80 public static final boolean DEBUG = false;
Daniel Sandlerb9301c32012-08-14 15:08:24 -040081 public static final boolean MULTIUSER_DEBUG = false;
Joe Onorato1c95ecb2010-06-28 17:19:12 -040082
Michael Jurkacb2522c2012-04-13 09:32:47 -070083 protected static final int MSG_TOGGLE_RECENTS_PANEL = 1020;
Michael Jurka7f2668c2012-03-27 07:49:52 -070084 protected static final int MSG_CLOSE_RECENTS_PANEL = 1021;
85 protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
86 protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
Jim Millere898ac52012-04-06 17:10:57 -070087 protected static final int MSG_OPEN_SEARCH_PANEL = 1024;
88 protected static final int MSG_CLOSE_SEARCH_PANEL = 1025;
Chris Wren157026f2013-06-28 16:54:01 -040089 protected static final int MSG_SHOW_HEADS_UP = 1026;
90 protected static final int MSG_HIDE_HEADS_UP = 1027;
Chris Wren0c8275b2012-05-08 13:36:48 -040091
Chris Wren157026f2013-06-28 16:54:01 -040092 protected static final boolean ENABLE_HEADS_UP = true;
93 // scores above this threshold should be displayed in heads up mode.
94 private static final int INTERRUPTION_THRESHOLD = 10;
Chris Wren0c8275b2012-05-08 13:36:48 -040095
Michael Jurka56a57832012-05-14 13:24:43 -070096 // Should match the value in PhoneWindowManager
97 public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
98
Chris Wren0c8275b2012-05-08 13:36:48 -040099 public static final int EXPANDED_LEAVE_ALONE = -10000;
100 public static final int EXPANDED_FULL_OPEN = -10001;
Chris Wren157026f2013-06-28 16:54:01 -0400101 private static final String SETTING_HEADS_UP = "heads_up_enabled";
Michael Jurka7f2668c2012-03-27 07:49:52 -0700102
Joe Onorato808182d2010-07-09 18:52:06 -0400103 protected CommandQueue mCommandQueue;
104 protected IStatusBarService mBarService;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700105 protected H mHandler = createHandler();
106
Chris Wren0c8275b2012-05-08 13:36:48 -0400107 // all notifications
108 protected NotificationData mNotificationData = new NotificationData();
109 protected NotificationRowLayout mPile;
110
Chris Wren157026f2013-06-28 16:54:01 -0400111 protected StatusBarNotification mCurrentlyInterruptingNotification;
Chris Wren0c8275b2012-05-08 13:36:48 -0400112
Daniel Sandler6a858c32012-03-12 14:38:58 -0400113 // used to notify status bar for suppressing notification LED
114 protected boolean mPanelSlightlyVisible;
115
Jim Millere898ac52012-04-06 17:10:57 -0700116 // Search panel
117 protected SearchPanelView mSearchPanelView;
118
Daniel Sandler469e96e2012-05-04 15:56:19 -0400119 protected PopupMenu mNotificationBlamePopup;
120
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400121 protected int mCurrentUserId = 0;
122
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700123 protected int mLayoutDirection;
124 private Locale mLocale;
Chris Wren157026f2013-06-28 16:54:01 -0400125 protected boolean mUseHeadsUp = true;
126
127 protected IDreamManager mDreamManager;
128 KeyguardManager mKeyguardManager;
129 PowerManager mPowerManager;
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700130
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500131 // UI-specific methods
Daniel Sandler6a858c32012-03-12 14:38:58 -0400132
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500133 /**
134 * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
135 * and add them to the window manager.
136 */
137 protected abstract void createAndAddWindows();
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400138
Jeff Brown98365d72012-08-19 20:30:52 -0700139 protected WindowManager mWindowManager;
140 protected IWindowManager mWindowManagerService;
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700141 protected abstract void refreshLayout(int layoutDirection);
142
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500143 protected Display mDisplay;
Jeff Brown98365d72012-08-19 20:30:52 -0700144
Daniel Sandler26cda272012-05-22 15:44:08 -0400145 private boolean mDeviceProvisioned = false;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400146
John Spurlockd08de372013-06-24 13:06:08 -0400147 private RecentsComponent mRecents;
148
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500149 public IStatusBarService getStatusBarService() {
150 return mBarService;
151 }
Joe Onorato2039e482010-11-29 14:54:24 -0800152
Daniel Sandler3ffdcc72012-09-23 14:31:48 -0400153 public boolean isDeviceProvisioned() {
Daniel Sandler26cda272012-05-22 15:44:08 -0400154 return mDeviceProvisioned;
155 }
156
157 private ContentObserver mProvisioningObserver = new ContentObserver(new Handler()) {
158 @Override
159 public void onChange(boolean selfChange) {
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700160 final boolean provisioned = 0 != Settings.Global.getInt(
161 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
Daniel Sandler26cda272012-05-22 15:44:08 -0400162 if (provisioned != mDeviceProvisioned) {
163 mDeviceProvisioned = provisioned;
164 updateNotificationIcons();
165 }
166 }
167 };
168
Chris Wren157026f2013-06-28 16:54:01 -0400169 final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
170 @Override
171 public void onChange(boolean selfChange) {
172 mUseHeadsUp = ENABLE_HEADS_UP && 0 != Settings.Global.getInt(
173 mContext.getContentResolver(), SETTING_HEADS_UP, 0);
174 Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
175 if (!mUseHeadsUp) {
176 Log.d(TAG, "dismissing any existing heads up notification on disable event");
177 mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
178 }
179 }
180 };
181
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700182 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
183 @Override
184 public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) {
Daniel Sandler198a0302012-08-17 16:04:31 -0400185 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400186 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
Daniel Sandler198a0302012-08-17 16:04:31 -0400187 }
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700188 final boolean isActivity = pendingIntent.isActivity();
189 if (isActivity) {
190 try {
191 // The intent we are sending is for the application, which
192 // won't have permission to immediately start an activity after
193 // the user switches to home. We know it is safe to do at this
194 // point, so make sure new activity switches are now allowed.
195 ActivityManagerNative.getDefault().resumeAppSwitches();
196 // Also, notifications can be launched from the lock screen,
197 // so dismiss the lock screen when the activity starts.
198 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
199 } catch (RemoteException e) {
200 }
201 }
202
203 boolean handled = super.onClickHandler(view, pendingIntent, fillInIntent);
204
205 if (isActivity && handled) {
206 // close the shade if it was open
Daniel Sandler11cf1782012-09-27 14:03:08 -0400207 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700208 visibilityChanged(false);
209 }
210 return handled;
211 }
212 };
213
John Spurlock5c454122013-06-17 07:35:46 -0400214 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
215 @Override
216 public void onReceive(Context context, Intent intent) {
217 String action = intent.getAction();
218 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
219 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
220 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
221 userSwitched(mCurrentUserId);
222 }
223 }
224 };
225
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400226 public void start() {
Jeff Brown98365d72012-08-19 20:30:52 -0700227 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
228 mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
229 mDisplay = mWindowManager.getDefaultDisplay();
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500230
Chris Wren157026f2013-06-28 16:54:01 -0400231 mDreamManager = IDreamManager.Stub.asInterface(
232 ServiceManager.checkService(DreamService.DREAM_SERVICE));
233 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
234 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
235
Daniel Sandler26cda272012-05-22 15:44:08 -0400236 mProvisioningObserver.onChange(false); // set up
237 mContext.getContentResolver().registerContentObserver(
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700238 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
Daniel Sandler26cda272012-05-22 15:44:08 -0400239 mProvisioningObserver);
240
Chris Wren157026f2013-06-28 16:54:01 -0400241 mHeadsUpObserver.onChange(false); // set up
242 mContext.getContentResolver().registerContentObserver(
243 Settings.Global.getUriFor(SETTING_HEADS_UP), true,
244 mHeadsUpObserver);
245
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500246 mBarService = IStatusBarService.Stub.asInterface(
247 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400248
John Spurlockd08de372013-06-24 13:06:08 -0400249 mRecents = getComponent(RecentsComponent.class);
250
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700251 mLocale = mContext.getResources().getConfiguration().locale;
252 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
253
Joe Onorato2314aab2010-04-08 16:41:23 -0500254 // Connect in to the status bar manager service
Joe Onorato0cbda992010-05-02 16:28:15 -0700255 StatusBarIconList iconList = new StatusBarIconList();
Joe Onorato75199e32010-05-29 17:22:51 -0400256 ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
257 ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
Joe Onorato66d7d012010-05-14 10:05:10 -0700258 mCommandQueue = new CommandQueue(this, iconList);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400259
Jeff Brown2992ea72011-01-28 22:04:14 -0800260 int[] switches = new int[7];
satokcd7cd292010-11-20 15:46:23 +0900261 ArrayList<IBinder> binders = new ArrayList<IBinder>();
Joe Onorato2314aab2010-04-08 16:41:23 -0500262 try {
Joe Onorato93056472010-09-10 10:30:46 -0400263 mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
satokcd7cd292010-11-20 15:46:23 +0900264 switches, binders);
Joe Onorato2314aab2010-04-08 16:41:23 -0500265 } catch (RemoteException ex) {
266 // If the system process isn't there we're doomed anyway.
267 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400268
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500269 createAndAddWindows();
Joe Onorato0cbda992010-05-02 16:28:15 -0700270
Joe Onoratoe4c7b3f2010-10-30 12:15:03 -0700271 disable(switches[0]);
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700272 setSystemUiVisibility(switches[1], 0xffffffff);
Dianne Hackborn7d049322011-06-14 15:00:32 -0700273 topAppWindowChanged(switches[2] != 0);
satokcd7cd292010-11-20 15:46:23 +0900274 // StatusBarManagerService has a back up of IME token and it's restored here.
Joe Onorato857fd9b2011-01-27 15:08:35 -0800275 setImeWindowStatus(binders.get(0), switches[3], switches[4]);
Jeff Brown2992ea72011-01-28 22:04:14 -0800276 setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
Joe Onorato93056472010-09-10 10:30:46 -0400277
Joe Onorato0cbda992010-05-02 16:28:15 -0700278 // Set up the initial icon state
Joe Onorato75199e32010-05-29 17:22:51 -0400279 int N = iconList.size();
Joe Onorato0cbda992010-05-02 16:28:15 -0700280 int viewIndex = 0;
281 for (int i=0; i<N; i++) {
282 StatusBarIcon icon = iconList.getIcon(i);
283 if (icon != null) {
284 addIcon(iconList.getSlot(i), i, viewIndex, icon);
285 viewIndex++;
286 }
287 }
288
Joe Onorato75199e32010-05-29 17:22:51 -0400289 // Set up the initial notification state
290 N = notificationKeys.size();
Joe Onorato75199e32010-05-29 17:22:51 -0400291 if (N == notifications.size()) {
292 for (int i=0; i<N; i++) {
293 addNotification(notificationKeys.get(i), notifications.get(i));
294 }
295 } else {
296 Log.wtf(TAG, "Notification list length mismatch: keys=" + N
297 + " notifications=" + notifications.size());
298 }
299
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500300 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400301 Log.d(TAG, String.format(
Daniel Sandler6a858c32012-03-12 14:38:58 -0400302 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500303 iconList.size(),
304 switches[0],
305 switches[1],
306 switches[2],
307 switches[3]
308 ));
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400309 }
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400310
Christopher Tate5e08af02012-09-21 17:17:22 -0700311 mCurrentUserId = ActivityManager.getCurrentUser();
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400312
313 IntentFilter filter = new IntentFilter();
314 filter.addAction(Intent.ACTION_USER_SWITCHED);
John Spurlock5c454122013-06-17 07:35:46 -0400315 mContext.registerReceiver(mBroadcastReceiver, filter);
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700316
317 mLocale = mContext.getResources().getConfiguration().locale;
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400318 }
319
320 public void userSwitched(int newUserId) {
321 // should be overridden
322 }
323
324 public boolean notificationIsForCurrentUser(StatusBarNotification n) {
325 final int thisUserId = mCurrentUserId;
326 final int notificationUserId = n.getUserId();
327 if (DEBUG && MULTIUSER_DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400328 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400329 n, thisUserId, notificationUserId));
330 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700331 return notificationUserId == UserHandle.USER_ALL
332 || thisUserId == notificationUserId;
Joe Onorato2314aab2010-04-08 16:41:23 -0500333 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400334
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700335 @Override
336 protected void onConfigurationChanged(Configuration newConfig) {
337 final Locale newLocale = mContext.getResources().getConfiguration().locale;
338 if (! newLocale.equals(mLocale)) {
339 mLocale = newLocale;
340 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
341 refreshLayout(mLayoutDirection);
342 }
343 }
344
Winson Chungc57ccf02011-10-13 15:04:59 -0700345 protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
346 View vetoButton = row.findViewById(R.id.veto);
347 if (n.isClearable()) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400348 final String _pkg = n.getPackageName();
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400349 final String _tag = n.getTag();
350 final int _id = n.getId();
Winson Chungc57ccf02011-10-13 15:04:59 -0700351 vetoButton.setOnClickListener(new View.OnClickListener() {
352 public void onClick(View v) {
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700353 // Accessibility feedback
354 v.announceForAccessibility(
355 mContext.getString(R.string.accessibility_notification_dismissed));
Winson Chungc57ccf02011-10-13 15:04:59 -0700356 try {
357 mBarService.onNotificationClear(_pkg, _tag, _id);
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700358
Winson Chungc57ccf02011-10-13 15:04:59 -0700359 } catch (RemoteException ex) {
360 // system process is dead if we're here.
361 }
362 }
363 });
364 vetoButton.setVisibility(View.VISIBLE);
365 } else {
366 vetoButton.setVisibility(View.GONE);
367 }
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700368 vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
Winson Chungc57ccf02011-10-13 15:04:59 -0700369 return vetoButton;
370 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400371
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400372
373 protected void applyLegacyRowBackground(StatusBarNotification sbn, View content) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400374 if (sbn.getNotification().contentView.getLayoutId() !=
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400375 com.android.internal.R.layout.notification_template_base) {
376 int version = 0;
377 try {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400378 ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.getPackageName(), 0);
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400379 version = info.targetSdkVersion;
380 } catch (NameNotFoundException ex) {
John Spurlockcd686b52013-06-05 10:13:46 -0400381 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400382 }
383 if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) {
384 content.setBackgroundResource(R.drawable.notification_row_legacy_bg);
385 } else {
Chris Wrend84e5932012-04-19 17:58:03 -0400386 content.setBackgroundResource(com.android.internal.R.drawable.notification_bg);
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400387 }
388 }
389 }
390
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400391 private void startApplicationDetailsActivity(String packageName) {
392 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
393 Uri.fromParts("package", packageName, null));
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700394 intent.setComponent(intent.resolveActivity(mContext.getPackageManager()));
Amith Yamasaniea7e9152012-09-24 16:11:18 -0700395 TaskStackBuilder.create(mContext).addNextIntentWithParentStack(intent).startActivities(
396 null, UserHandle.CURRENT);
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400397 }
398
Jim Miller9a720f52012-05-30 03:19:43 -0700399 protected View.OnLongClickListener getNotificationLongClicker() {
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400400 return new View.OnLongClickListener() {
401 @Override
402 public boolean onLongClick(View v) {
403 final String packageNameF = (String) v.getTag();
404 if (packageNameF == null) return false;
Jeff Sharkeyaf232ed2012-04-30 15:19:39 -0700405 if (v.getWindowToken() == null) return false;
Daniel Sandler469e96e2012-05-04 15:56:19 -0400406 mNotificationBlamePopup = new PopupMenu(mContext, v);
407 mNotificationBlamePopup.getMenuInflater().inflate(
408 R.menu.notification_popup_menu,
409 mNotificationBlamePopup.getMenu());
410 mNotificationBlamePopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400411 public boolean onMenuItemClick(MenuItem item) {
412 if (item.getItemId() == R.id.notification_inspect_item) {
413 startApplicationDetailsActivity(packageNameF);
Daniel Sandler11cf1782012-09-27 14:03:08 -0400414 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400415 } else {
416 return false;
417 }
418 return true;
419 }
420 });
Daniel Sandler469e96e2012-05-04 15:56:19 -0400421 mNotificationBlamePopup.show();
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400422
423 return true;
424 }
425 };
426 }
427
Daniel Sandler469e96e2012-05-04 15:56:19 -0400428 public void dismissPopups() {
429 if (mNotificationBlamePopup != null) {
430 mNotificationBlamePopup.dismiss();
431 mNotificationBlamePopup = null;
432 }
433 }
434
Chris Wren157026f2013-06-28 16:54:01 -0400435 public void dismissHeadsUp() {
Daniel Sandlerfa7887b2012-03-26 09:43:31 -0400436 // pass
437 }
Michael Jurka7f2668c2012-03-27 07:49:52 -0700438
439 @Override
440 public void toggleRecentApps() {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700441 int msg = MSG_TOGGLE_RECENTS_PANEL;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700442 mHandler.removeMessages(msg);
443 mHandler.sendEmptyMessage(msg);
444 }
445
446 @Override
447 public void preloadRecentApps() {
448 int msg = MSG_PRELOAD_RECENT_APPS;
449 mHandler.removeMessages(msg);
450 mHandler.sendEmptyMessage(msg);
451 }
452
453 @Override
454 public void cancelPreloadRecentApps() {
455 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
456 mHandler.removeMessages(msg);
457 mHandler.sendEmptyMessage(msg);
458 }
459
460 @Override
Jim Millere898ac52012-04-06 17:10:57 -0700461 public void showSearchPanel() {
462 int msg = MSG_OPEN_SEARCH_PANEL;
463 mHandler.removeMessages(msg);
464 mHandler.sendEmptyMessage(msg);
465 }
466
467 @Override
468 public void hideSearchPanel() {
469 int msg = MSG_CLOSE_SEARCH_PANEL;
470 mHandler.removeMessages(msg);
471 mHandler.sendEmptyMessage(msg);
472 }
473
Jim Millere898ac52012-04-06 17:10:57 -0700474 protected abstract WindowManager.LayoutParams getSearchLayoutParams(
475 LayoutParams layoutParams);
476
Jim Millere898ac52012-04-06 17:10:57 -0700477 protected void updateSearchPanel() {
478 // Search Panel
479 boolean visible = false;
480 if (mSearchPanelView != null) {
481 visible = mSearchPanelView.isShowing();
Jeff Brown98365d72012-08-19 20:30:52 -0700482 mWindowManager.removeView(mSearchPanelView);
Jim Millere898ac52012-04-06 17:10:57 -0700483 }
484
485 // Provide SearchPanel with a temporary parent to allow layout params to work.
486 LinearLayout tmpRoot = new LinearLayout(mContext);
487 mSearchPanelView = (SearchPanelView) LayoutInflater.from(mContext).inflate(
488 R.layout.status_bar_search_panel, tmpRoot, false);
489 mSearchPanelView.setOnTouchListener(
490 new TouchOutsideListener(MSG_CLOSE_SEARCH_PANEL, mSearchPanelView));
491 mSearchPanelView.setVisibility(View.GONE);
492
493 WindowManager.LayoutParams lp = getSearchLayoutParams(mSearchPanelView.getLayoutParams());
494
Jeff Brown98365d72012-08-19 20:30:52 -0700495 mWindowManager.addView(mSearchPanelView, lp);
Jim Millere898ac52012-04-06 17:10:57 -0700496 mSearchPanelView.setBar(this);
497 if (visible) {
498 mSearchPanelView.show(true, false);
499 }
500 }
501
Michael Jurkaecc395a2012-03-30 05:31:46 -0700502 protected H createHandler() {
Michael Jurka7f2668c2012-03-27 07:49:52 -0700503 return new H();
504 }
505
Michael Jurka56a57832012-05-14 13:24:43 -0700506 static void sendCloseSystemWindows(Context context, String reason) {
507 if (ActivityManagerNative.isSystemReady()) {
508 try {
509 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
510 } catch (RemoteException e) {
511 }
512 }
513 }
514
Michael Jurkacb2522c2012-04-13 09:32:47 -0700515 protected abstract View getStatusBarView();
516
Michael Jurka80343f62012-10-18 13:13:46 +0200517 protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
518 // additional optimization when we have software system buttons - start loading the recent
519 // tasks on touch down
520 @Override
521 public boolean onTouch(View v, MotionEvent event) {
522 int action = event.getAction() & MotionEvent.ACTION_MASK;
523 if (action == MotionEvent.ACTION_DOWN) {
524 preloadRecentTasksList();
525 } else if (action == MotionEvent.ACTION_CANCEL) {
526 cancelPreloadingRecentTasksList();
527 } else if (action == MotionEvent.ACTION_UP) {
528 if (!v.isPressed()) {
529 cancelPreloadingRecentTasksList();
530 }
531
532 }
533 return false;
534 }
535 };
536
John Spurlockd08de372013-06-24 13:06:08 -0400537 protected void toggleRecentsActivity() {
538 if (mRecents != null) {
539 mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
540 }
541 }
Michael Jurka80343f62012-10-18 13:13:46 +0200542
John Spurlockd08de372013-06-24 13:06:08 -0400543 protected void preloadRecentTasksList() {
544 if (mRecents != null) {
545 mRecents.preloadRecentTasksList();
546 }
Michael Jurka80343f62012-10-18 13:13:46 +0200547 }
548
549 protected void cancelPreloadingRecentTasksList() {
John Spurlockd08de372013-06-24 13:06:08 -0400550 if (mRecents != null) {
551 mRecents.cancelPreloadingRecentTasksList();
552 }
553 }
Michael Jurka80343f62012-10-18 13:13:46 +0200554
John Spurlockd08de372013-06-24 13:06:08 -0400555 protected void closeRecents() {
556 if (mRecents != null) {
557 mRecents.closeRecents();
558 }
Michael Jurka80343f62012-10-18 13:13:46 +0200559 }
560
Michael Jurka7f2668c2012-03-27 07:49:52 -0700561 protected class H extends Handler {
562 public void handleMessage(Message m) {
Michael Jurka80343f62012-10-18 13:13:46 +0200563 Intent intent;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700564 switch (m.what) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700565 case MSG_TOGGLE_RECENTS_PANEL:
Michael Jurkacb2522c2012-04-13 09:32:47 -0700566 toggleRecentsActivity();
567 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700568 case MSG_CLOSE_RECENTS_PANEL:
John Spurlockd08de372013-06-24 13:06:08 -0400569 closeRecents();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700570 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700571 case MSG_PRELOAD_RECENT_APPS:
Michael Jurka80343f62012-10-18 13:13:46 +0200572 preloadRecentTasksList();
Michael Jurka7f2668c2012-03-27 07:49:52 -0700573 break;
574 case MSG_CANCEL_PRELOAD_RECENT_APPS:
Michael Jurka80343f62012-10-18 13:13:46 +0200575 cancelPreloadingRecentTasksList();
Michael Jurka7f2668c2012-03-27 07:49:52 -0700576 break;
Jim Millere898ac52012-04-06 17:10:57 -0700577 case MSG_OPEN_SEARCH_PANEL:
John Spurlockcd686b52013-06-05 10:13:46 -0400578 if (DEBUG) Log.d(TAG, "opening search panel");
Jim Miller07994402012-05-02 14:22:27 -0700579 if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) {
Jim Millere898ac52012-04-06 17:10:57 -0700580 mSearchPanelView.show(true, true);
581 }
582 break;
583 case MSG_CLOSE_SEARCH_PANEL:
John Spurlockcd686b52013-06-05 10:13:46 -0400584 if (DEBUG) Log.d(TAG, "closing search panel");
Jim Millere898ac52012-04-06 17:10:57 -0700585 if (mSearchPanelView != null && mSearchPanelView.isShowing()) {
586 mSearchPanelView.show(false, true);
587 }
588 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700589 }
590 }
591 }
592
593 public class TouchOutsideListener implements View.OnTouchListener {
594 private int mMsg;
595 private StatusBarPanel mPanel;
596
597 public TouchOutsideListener(int msg, StatusBarPanel panel) {
598 mMsg = msg;
599 mPanel = panel;
600 }
601
602 public boolean onTouch(View v, MotionEvent ev) {
603 final int action = ev.getAction();
604 if (action == MotionEvent.ACTION_OUTSIDE
605 || (action == MotionEvent.ACTION_DOWN
606 && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
607 mHandler.removeMessages(mMsg);
608 mHandler.sendEmptyMessage(mMsg);
609 return true;
610 }
611 return false;
612 }
613 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400614
615 protected void workAroundBadLayerDrawableOpacity(View v) {
616 }
617
618 protected boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
619 int minHeight =
620 mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height);
621 int maxHeight =
622 mContext.getResources().getDimensionPixelSize(R.dimen.notification_max_height);
623 StatusBarNotification sbn = entry.notification;
Chris Wren574a55e2013-07-15 18:48:37 -0400624 RemoteViews contentView = sbn.getNotification().contentView;
625 RemoteViews bigContentView = sbn.getNotification().bigContentView;
626 if (contentView == null) {
Daniel Sandler6a858c32012-03-12 14:38:58 -0400627 return false;
628 }
629
630 // create the row view
631 LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
632 Context.LAYOUT_INFLATER_SERVICE);
633 View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
Daniel Sandlerbc5559f2012-04-19 01:08:15 -0400634
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400635 // for blaming (see SwipeHelper.setLongPressListener)
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400636 row.setTag(sbn.getPackageName());
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400637
Daniel Sandler6a858c32012-03-12 14:38:58 -0400638 workAroundBadLayerDrawableOpacity(row);
639 View vetoButton = updateNotificationVetoButton(row, sbn);
640 vetoButton.setContentDescription(mContext.getString(
641 R.string.accessibility_remove_notification));
642
643 // NB: the large icon is now handled entirely by the template
644
645 // bind the click event to the content area
646 ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
647 ViewGroup adaptive = (ViewGroup)row.findViewById(R.id.adaptive);
Daniel Sandlerbc5559f2012-04-19 01:08:15 -0400648
Daniel Sandler6a858c32012-03-12 14:38:58 -0400649 content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
Daniel Sandlerbc5559f2012-04-19 01:08:15 -0400650
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400651 PendingIntent contentIntent = sbn.getNotification().contentIntent;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400652 if (contentIntent != null) {
653 final View.OnClickListener listener = new NotificationClicker(contentIntent,
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400654 sbn.getPackageName(), sbn.getTag(), sbn.getId());
Daniel Sandler6a858c32012-03-12 14:38:58 -0400655 content.setOnClickListener(listener);
656 } else {
657 content.setOnClickListener(null);
658 }
659
Chris Wren574a55e2013-07-15 18:48:37 -0400660 View contentViewLocal = null;
661 View bigContentViewLocal = null;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400662 try {
Chris Wren574a55e2013-07-15 18:48:37 -0400663 contentViewLocal = contentView.apply(mContext, adaptive, mOnClickHandler);
664 if (bigContentView != null) {
665 bigContentViewLocal = bigContentView.apply(mContext, adaptive, mOnClickHandler);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400666 }
667 }
668 catch (RuntimeException e) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400669 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
John Spurlockcd686b52013-06-05 10:13:46 -0400670 Log.e(TAG, "couldn't inflate view for notification " + ident, e);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400671 return false;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400672 }
673
Chris Wren574a55e2013-07-15 18:48:37 -0400674 if (contentViewLocal != null) {
Daniel Sandler8680bf82012-05-15 16:52:52 -0400675 SizeAdaptiveLayout.LayoutParams params =
Chris Wren574a55e2013-07-15 18:48:37 -0400676 new SizeAdaptiveLayout.LayoutParams(contentViewLocal.getLayoutParams());
Chris Wrened5cc0e2012-06-21 11:25:58 -0400677 params.minHeight = minHeight;
678 params.maxHeight = minHeight;
Chris Wren574a55e2013-07-15 18:48:37 -0400679 adaptive.addView(contentViewLocal, params);
Daniel Sandler8680bf82012-05-15 16:52:52 -0400680 }
Chris Wren574a55e2013-07-15 18:48:37 -0400681 if (bigContentViewLocal != null) {
Daniel Sandler8680bf82012-05-15 16:52:52 -0400682 SizeAdaptiveLayout.LayoutParams params =
Chris Wren574a55e2013-07-15 18:48:37 -0400683 new SizeAdaptiveLayout.LayoutParams(bigContentViewLocal.getLayoutParams());
Chris Wrened5cc0e2012-06-21 11:25:58 -0400684 params.minHeight = minHeight+1;
Daniel Sandler8680bf82012-05-15 16:52:52 -0400685 params.maxHeight = maxHeight;
Chris Wren574a55e2013-07-15 18:48:37 -0400686 adaptive.addView(bigContentViewLocal, params);
Daniel Sandler8680bf82012-05-15 16:52:52 -0400687 }
688 row.setDrawingCacheEnabled(true);
689
Daniel Sandler6a858c32012-03-12 14:38:58 -0400690 applyLegacyRowBackground(sbn, content);
691
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400692 if (MULTIUSER_DEBUG) {
693 TextView debug = (TextView) row.findViewById(R.id.debug_info);
694 if (debug != null) {
695 debug.setVisibility(View.VISIBLE);
696 debug.setText("U " + entry.notification.getUserId());
697 }
698 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400699 entry.row = row;
700 entry.content = content;
Chris Wren574a55e2013-07-15 18:48:37 -0400701 entry.expanded = contentViewLocal;
702 entry.setBigContentView(bigContentViewLocal);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400703
704 return true;
705 }
706
707 public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) {
708 return new NotificationClicker(intent, pkg, tag, id);
709 }
710
Chris Wren157026f2013-06-28 16:54:01 -0400711 protected class NotificationClicker implements View.OnClickListener {
Daniel Sandler6a858c32012-03-12 14:38:58 -0400712 private PendingIntent mIntent;
713 private String mPkg;
714 private String mTag;
715 private int mId;
716
Chris Wren157026f2013-06-28 16:54:01 -0400717 public NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
Daniel Sandler6a858c32012-03-12 14:38:58 -0400718 mIntent = intent;
719 mPkg = pkg;
720 mTag = tag;
721 mId = id;
722 }
723
724 public void onClick(View v) {
725 try {
726 // The intent we are sending is for the application, which
727 // won't have permission to immediately start an activity after
728 // the user switches to home. We know it is safe to do at this
729 // point, so make sure new activity switches are now allowed.
730 ActivityManagerNative.getDefault().resumeAppSwitches();
731 // Also, notifications can be launched from the lock screen,
732 // so dismiss the lock screen when the activity starts.
733 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
734 } catch (RemoteException e) {
735 }
736
737 if (mIntent != null) {
738 int[] pos = new int[2];
739 v.getLocationOnScreen(pos);
740 Intent overlay = new Intent();
741 overlay.setSourceBounds(
742 new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
743 try {
744 mIntent.send(mContext, 0, overlay);
745 } catch (PendingIntent.CanceledException e) {
746 // the stack trace isn't very helpful here. Just log the exception message.
John Spurlockcd686b52013-06-05 10:13:46 -0400747 Log.w(TAG, "Sending contentIntent failed: " + e);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400748 }
749
750 KeyguardManager kgm =
751 (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
752 if (kgm != null) kgm.exitKeyguardSecurely(null);
753 }
754
755 try {
756 mBarService.onNotificationClick(mPkg, mTag, mId);
757 } catch (RemoteException ex) {
758 // system process is dead if we're here.
759 }
760
761 // close the shade if it was open
Daniel Sandler11cf1782012-09-27 14:03:08 -0400762 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400763 visibilityChanged(false);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400764 }
765 }
766 /**
767 * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
768 * This was added last-minute and is inconsistent with the way the rest of the notifications
769 * are handled, because the notification isn't really cancelled. The lights are just
770 * turned off. If any other notifications happen, the lights will turn back on. Steve says
771 * this is what he wants. (see bug 1131461)
772 */
773 protected void visibilityChanged(boolean visible) {
774 if (mPanelSlightlyVisible != visible) {
775 mPanelSlightlyVisible = visible;
776 try {
777 mBarService.onPanelRevealed();
778 } catch (RemoteException ex) {
779 // Won't fail unless the world has ended.
780 }
781 }
782 }
783
Chris Wren0c8275b2012-05-08 13:36:48 -0400784 /**
785 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
786 * about the failure.
787 *
788 * WARNING: this will call back into us. Don't hold any locks.
789 */
790 void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
791 removeNotification(key);
792 try {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400793 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(), n.getInitialPid(), message);
Chris Wren0c8275b2012-05-08 13:36:48 -0400794 } catch (RemoteException ex) {
795 // The end is nigh.
796 }
797 }
798
799 protected StatusBarNotification removeNotificationViews(IBinder key) {
800 NotificationData.Entry entry = mNotificationData.remove(key);
801 if (entry == null) {
John Spurlockcd686b52013-06-05 10:13:46 -0400802 Log.w(TAG, "removeNotification for unknown key: " + key);
Chris Wren0c8275b2012-05-08 13:36:48 -0400803 return null;
804 }
805 // Remove the expanded view.
806 ViewGroup rowParent = (ViewGroup)entry.row.getParent();
807 if (rowParent != null) rowParent.removeView(entry.row);
Chris Wren8fd12652012-05-09 21:25:57 -0400808 updateExpansionStates();
Chris Wren0c8275b2012-05-08 13:36:48 -0400809 updateNotificationIcons();
810
811 return entry.notification;
812 }
813
814 protected StatusBarIconView addNotificationViews(IBinder key,
815 StatusBarNotification notification) {
816 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400817 Log.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
Chris Wren0c8275b2012-05-08 13:36:48 -0400818 }
819 // Construct the icon.
820 final StatusBarIconView iconView = new StatusBarIconView(mContext,
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400821 notification.getPackageName() + "/0x" + Integer.toHexString(notification.getId()),
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400822 notification.getNotification());
Chris Wren0c8275b2012-05-08 13:36:48 -0400823 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
824
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400825 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400826 notification.getUser(),
827 notification.getNotification().icon,
828 notification.getNotification().iconLevel,
829 notification.getNotification().number,
830 notification.getNotification().tickerText);
Chris Wren0c8275b2012-05-08 13:36:48 -0400831 if (!iconView.set(ic)) {
832 handleNotificationError(key, notification, "Couldn't create icon: " + ic);
833 return null;
834 }
835 // Construct the expanded view.
836 NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
837 if (!inflateViews(entry, mPile)) {
838 handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
839 + notification);
840 return null;
841 }
842
843 // Add the expanded view and icon.
844 int pos = mNotificationData.add(entry);
845 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400846 Log.d(TAG, "addNotificationViews: added at " + pos);
Chris Wren0c8275b2012-05-08 13:36:48 -0400847 }
Chris Wren8fd12652012-05-09 21:25:57 -0400848 updateExpansionStates();
Chris Wren0c8275b2012-05-08 13:36:48 -0400849 updateNotificationIcons();
850
851 return iconView;
852 }
853
Chris Wren8fd12652012-05-09 21:25:57 -0400854 protected boolean expandView(NotificationData.Entry entry, boolean expand) {
Chris Wren66757212012-05-14 10:05:42 -0400855 int rowHeight =
856 mContext.getResources().getDimensionPixelSize(R.dimen.notification_row_min_height);
857 ViewGroup.LayoutParams lp = entry.row.getLayoutParams();
858 if (entry.expandable() && expand) {
John Spurlockcd686b52013-06-05 10:13:46 -0400859 if (DEBUG) Log.d(TAG, "setting expanded row height to WRAP_CONTENT");
Chris Wren66757212012-05-14 10:05:42 -0400860 lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
Chris Wren8fd12652012-05-09 21:25:57 -0400861 } else {
John Spurlockcd686b52013-06-05 10:13:46 -0400862 if (DEBUG) Log.d(TAG, "setting collapsed row height to " + rowHeight);
Chris Wren66757212012-05-14 10:05:42 -0400863 lp.height = rowHeight;
Chris Wren8fd12652012-05-09 21:25:57 -0400864 }
Chris Wren66757212012-05-14 10:05:42 -0400865 entry.row.setLayoutParams(lp);
866 return expand;
Chris Wren8fd12652012-05-09 21:25:57 -0400867 }
868
869 protected void updateExpansionStates() {
870 int N = mNotificationData.size();
871 for (int i = 0; i < N; i++) {
872 NotificationData.Entry entry = mNotificationData.get(i);
Chris Wren3ddab0d2012-08-02 16:52:21 -0400873 if (!entry.userLocked()) {
874 if (i == (N-1)) {
John Spurlockcd686b52013-06-05 10:13:46 -0400875 if (DEBUG) Log.d(TAG, "expanding top notification at " + i);
Chris Wren3ddab0d2012-08-02 16:52:21 -0400876 expandView(entry, true);
Chris Wren8fd12652012-05-09 21:25:57 -0400877 } else {
Chris Wren3ddab0d2012-08-02 16:52:21 -0400878 if (!entry.userExpanded()) {
John Spurlockcd686b52013-06-05 10:13:46 -0400879 if (DEBUG) Log.d(TAG, "collapsing notification at " + i);
Chris Wren3ddab0d2012-08-02 16:52:21 -0400880 expandView(entry, false);
881 } else {
John Spurlockcd686b52013-06-05 10:13:46 -0400882 if (DEBUG) Log.d(TAG, "ignoring user-modified notification at " + i);
Chris Wren3ddab0d2012-08-02 16:52:21 -0400883 }
Chris Wren8fd12652012-05-09 21:25:57 -0400884 }
Chris Wren3ddab0d2012-08-02 16:52:21 -0400885 } else {
John Spurlockcd686b52013-06-05 10:13:46 -0400886 if (DEBUG) Log.d(TAG, "ignoring notification being held by user at " + i);
Chris Wren8fd12652012-05-09 21:25:57 -0400887 }
888 }
889 }
890
Chris Wren0c8275b2012-05-08 13:36:48 -0400891 protected abstract void haltTicker();
892 protected abstract void setAreThereNotifications();
893 protected abstract void updateNotificationIcons();
894 protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime);
895 protected abstract void updateExpandedViewPos(int expandedPosition);
Chris Wren8fd12652012-05-09 21:25:57 -0400896 protected abstract int getExpandedViewMaxHeight();
Jim Millerb4238e02012-05-14 15:26:20 -0700897 protected abstract boolean shouldDisableNavbarGestures();
Chris Wren0c8275b2012-05-08 13:36:48 -0400898
899 protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
Daniel Sandler26cda272012-05-22 15:44:08 -0400900 return parent != null && parent.indexOfChild(entry.row) == 0;
Chris Wren0c8275b2012-05-08 13:36:48 -0400901 }
902
903 public void updateNotification(IBinder key, StatusBarNotification notification) {
John Spurlockcd686b52013-06-05 10:13:46 -0400904 if (DEBUG) Log.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
Chris Wren0c8275b2012-05-08 13:36:48 -0400905
906 final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
907 if (oldEntry == null) {
John Spurlockcd686b52013-06-05 10:13:46 -0400908 Log.w(TAG, "updateNotification for unknown key: " + key);
Chris Wren0c8275b2012-05-08 13:36:48 -0400909 return;
910 }
911
912 final StatusBarNotification oldNotification = oldEntry.notification;
913
914 // XXX: modify when we do something more intelligent with the two content views
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400915 final RemoteViews oldContentView = oldNotification.getNotification().contentView;
916 final RemoteViews contentView = notification.getNotification().contentView;
917 final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
918 final RemoteViews bigContentView = notification.getNotification().bigContentView;
Chris Wren0c8275b2012-05-08 13:36:48 -0400919
920 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400921 Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
Chris Wren0c8275b2012-05-08 13:36:48 -0400922 + " ongoing=" + oldNotification.isOngoing()
923 + " expanded=" + oldEntry.expanded
924 + " contentView=" + oldContentView
Chris Wrene9e37722012-05-11 17:40:14 -0400925 + " bigContentView=" + oldBigContentView
Chris Wren0c8275b2012-05-08 13:36:48 -0400926 + " rowParent=" + oldEntry.row.getParent());
John Spurlockcd686b52013-06-05 10:13:46 -0400927 Log.d(TAG, "new notification: when=" + notification.getNotification().when
Chris Wren0c8275b2012-05-08 13:36:48 -0400928 + " ongoing=" + oldNotification.isOngoing()
Chris Wrene9e37722012-05-11 17:40:14 -0400929 + " contentView=" + contentView
930 + " bigContentView=" + bigContentView);
Chris Wren0c8275b2012-05-08 13:36:48 -0400931 }
932
933 // Can we just reapply the RemoteViews in place? If when didn't change, the order
934 // didn't change.
Chris Wrene9e37722012-05-11 17:40:14 -0400935
936 // 1U is never null
Chris Wren0c8275b2012-05-08 13:36:48 -0400937 boolean contentsUnchanged = oldEntry.expanded != null
Chris Wren0c8275b2012-05-08 13:36:48 -0400938 && contentView.getPackage() != null
939 && oldContentView.getPackage() != null
940 && oldContentView.getPackage().equals(contentView.getPackage())
941 && oldContentView.getLayoutId() == contentView.getLayoutId();
Chris Wrene9e37722012-05-11 17:40:14 -0400942 // large view may be null
943 boolean bigContentsUnchanged =
Chris Wren574a55e2013-07-15 18:48:37 -0400944 (oldEntry.getBigContentView() == null && bigContentView == null)
945 || ((oldEntry.getBigContentView() != null && bigContentView != null)
Chris Wrene9e37722012-05-11 17:40:14 -0400946 && bigContentView.getPackage() != null
947 && oldBigContentView.getPackage() != null
948 && oldBigContentView.getPackage().equals(bigContentView.getPackage())
949 && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
Chris Wren0c8275b2012-05-08 13:36:48 -0400950 ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400951 boolean orderUnchanged = notification.getNotification().when== oldNotification.getNotification().when
952 && notification.getScore() == oldNotification.getScore();
Chris Wren0c8275b2012-05-08 13:36:48 -0400953 // score now encompasses/supersedes isOngoing()
954
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400955 boolean updateTicker = notification.getNotification().tickerText != null
956 && !TextUtils.equals(notification.getNotification().tickerText,
957 oldEntry.notification.getNotification().tickerText);
Chris Wren0c8275b2012-05-08 13:36:48 -0400958 boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
Chris Wrene9e37722012-05-11 17:40:14 -0400959 if (contentsUnchanged && bigContentsUnchanged && (orderUnchanged || isTopAnyway)) {
John Spurlockcd686b52013-06-05 10:13:46 -0400960 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
Chris Wren0c8275b2012-05-08 13:36:48 -0400961 oldEntry.notification = notification;
962 try {
963 // Reapply the RemoteViews
Dianne Hackborna1940212012-06-28 16:07:22 -0700964 contentView.reapply(mContext, oldEntry.expanded, mOnClickHandler);
Chris Wren574a55e2013-07-15 18:48:37 -0400965 if (bigContentView != null && oldEntry.getBigContentView() != null) {
966 bigContentView.reapply(mContext, oldEntry.getBigContentView(), mOnClickHandler);
Chris Wrene9e37722012-05-11 17:40:14 -0400967 }
Chris Wren0c8275b2012-05-08 13:36:48 -0400968 // update the contentIntent
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400969 final PendingIntent contentIntent = notification.getNotification().contentIntent;
Chris Wren0c8275b2012-05-08 13:36:48 -0400970 if (contentIntent != null) {
971 final View.OnClickListener listener = makeClicker(contentIntent,
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400972 notification.getPackageName(), notification.getTag(), notification.getId());
Chris Wren0c8275b2012-05-08 13:36:48 -0400973 oldEntry.content.setOnClickListener(listener);
974 } else {
975 oldEntry.content.setOnClickListener(null);
976 }
977 // Update the icon.
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400978 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400979 notification.getUser(),
980 notification.getNotification().icon, notification.getNotification().iconLevel,
981 notification.getNotification().number,
982 notification.getNotification().tickerText);
Chris Wren0c8275b2012-05-08 13:36:48 -0400983 if (!oldEntry.icon.set(ic)) {
984 handleNotificationError(key, notification, "Couldn't update icon: " + ic);
985 return;
986 }
Chris Wren8fd12652012-05-09 21:25:57 -0400987 updateExpansionStates();
Chris Wren0c8275b2012-05-08 13:36:48 -0400988 }
989 catch (RuntimeException e) {
990 // It failed to add cleanly. Log, and remove the view from the panel.
John Spurlockcd686b52013-06-05 10:13:46 -0400991 Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
Chris Wren0c8275b2012-05-08 13:36:48 -0400992 removeNotificationViews(key);
993 addNotificationViews(key, notification);
994 }
995 } else {
John Spurlockcd686b52013-06-05 10:13:46 -0400996 if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
997 if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
998 if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
999 if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
Chris Wrenc9433ecb2012-06-27 17:37:03 -04001000 final boolean wasExpanded = oldEntry.userExpanded();
Chris Wren0c8275b2012-05-08 13:36:48 -04001001 removeNotificationViews(key);
1002 addNotificationViews(key, notification);
Chris Wrenc9433ecb2012-06-27 17:37:03 -04001003 if (wasExpanded) {
1004 final NotificationData.Entry newEntry = mNotificationData.findByKey(key);
1005 expandView(newEntry, true);
Chris Wren5ae1ea82012-06-28 14:35:43 -04001006 newEntry.setUserExpanded(true);
Chris Wrenc9433ecb2012-06-27 17:37:03 -04001007 }
Chris Wren0c8275b2012-05-08 13:36:48 -04001008 }
1009
1010 // Update the veto button accordingly (and as a result, whether this row is
1011 // swipe-dismissable)
1012 updateNotificationVetoButton(oldEntry.row, notification);
1013
John Spurlock61e36832012-09-10 09:43:27 -04001014 // Is this for you?
1015 boolean isForCurrentUser = notificationIsForCurrentUser(notification);
John Spurlockcd686b52013-06-05 10:13:46 -04001016 if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
John Spurlock61e36832012-09-10 09:43:27 -04001017
Chris Wren0c8275b2012-05-08 13:36:48 -04001018 // Restart the ticker if it's still running
John Spurlock61e36832012-09-10 09:43:27 -04001019 if (updateTicker && isForCurrentUser) {
Chris Wren0c8275b2012-05-08 13:36:48 -04001020 haltTicker();
1021 tick(key, notification, false);
1022 }
1023
1024 // Recalculate the position of the sliding windows and the titles.
1025 setAreThereNotifications();
1026 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1027
Chris Wren157026f2013-06-28 16:54:01 -04001028 // See if we need to update the heads up.
1029 if (ENABLE_HEADS_UP && oldNotification == mCurrentlyInterruptingNotification) {
1030 if (DEBUG) Log.d(TAG, "updating the current heads up:" + notification);
Chris Wren0c8275b2012-05-08 13:36:48 -04001031 // XXX: this is a hack for Alarms. The real implementation will need to *update*
Chris Wren157026f2013-06-28 16:54:01 -04001032 // the heads up.
1033 if (!shouldInterrupt(notification)) {
1034 if (DEBUG) Log.d(TAG, "no longer interrupts!");
1035 mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
Chris Wren0c8275b2012-05-08 13:36:48 -04001036 }
1037 }
1038 }
John Spurlock36231282012-06-23 17:11:27 -04001039
Chris Wren157026f2013-06-28 16:54:01 -04001040 protected boolean shouldInterrupt(StatusBarNotification notification) {
1041 boolean interrupt = notification.getNotification().fullScreenIntent == null
1042 && notification.getScore() >= INTERRUPTION_THRESHOLD
1043 && mPowerManager.isScreenOn() && !mKeyguardManager.isKeyguardLocked();
1044 try {
1045 interrupt = interrupt && !mDreamManager.isDreaming();
1046 } catch (RemoteException e) {
1047 Log.d(TAG, "failed to query dream manager", e);
1048 }
1049 return interrupt;
1050 }
1051
John Spurlock36231282012-06-23 17:11:27 -04001052 // Q: What kinds of notifications should show during setup?
1053 // A: Almost none! Only things coming from the system (package is "android") that also
1054 // have special "kind" tags marking them as relevant for setup (see below).
1055 protected boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001056 if ("android".equals(sbn.getPackageName())) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001057 if (sbn.getNotification().kind != null) {
1058 for (String aKind : sbn.getNotification().kind) {
John Spurlock36231282012-06-23 17:11:27 -04001059 // IME switcher, created by InputMethodManagerService
1060 if ("android.system.imeswitcher".equals(aKind)) return true;
1061 // OTA availability & errors, created by SystemUpdateService
1062 if ("android.system.update".equals(aKind)) return true;
1063 }
1064 }
1065 }
1066 return false;
1067 }
John Spurlock67ad3682012-06-26 17:42:00 -04001068
1069 public boolean inKeyguardRestrictedInputMode() {
1070 KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
1071 return km.inKeyguardRestrictedInputMode();
1072 }
John Spurlockad3e6cb2013-04-30 08:47:43 -04001073
1074 public void suspendAutohide() {
1075 // hook for subclasses
1076 }
1077
1078 public void resumeAutohide() {
1079 // hook for subclasses
1080 }
John Spurlock5c454122013-06-17 07:35:46 -04001081
1082 public void destroy() {
1083 if (mSearchPanelView != null) {
1084 mWindowManager.removeViewImmediate(mSearchPanelView);
1085 }
1086 mContext.unregisterReceiver(mBroadcastReceiver);
1087 }
Joe Onorato2314aab2010-04-08 16:41:23 -05001088}