blob: ed00398b653181f6baba87ac28f8c38bd9868ec9 [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;
Chris Wren51c75102013-07-16 20:49:17 -040021import android.app.Notification;
Daniel Sandler6a858c32012-03-12 14:38:58 -040022import 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;
Jim Millera999d462013-10-30 13:58:11 -070072import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
John Spurlockde84f0e2013-06-12 12:41:00 -040073import com.android.systemui.statusbar.policy.NotificationRowLayout;
John Spurlockde84f0e2013-06-12 12:41:00 -040074
Michael Jurkaa600fd92012-06-25 15:57:05 -070075import java.util.ArrayList;
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -070076import java.util.Locale;
Joe Onorato1c95ecb2010-06-28 17:19:12 -040077
Michael Jurka7f2668c2012-03-27 07:49:52 -070078public abstract class BaseStatusBar extends SystemUI implements
Michael Jurkacb2522c2012-04-13 09:32:47 -070079 CommandQueue.Callbacks {
Daniel Sandler198a0302012-08-17 16:04:31 -040080 public static final String TAG = "StatusBar";
81 public static final boolean DEBUG = false;
Daniel Sandlerb9301c32012-08-14 15:08:24 -040082 public static final boolean MULTIUSER_DEBUG = false;
Joe Onorato1c95ecb2010-06-28 17:19:12 -040083
Michael Jurkacb2522c2012-04-13 09:32:47 -070084 protected static final int MSG_TOGGLE_RECENTS_PANEL = 1020;
Michael Jurka7f2668c2012-03-27 07:49:52 -070085 protected static final int MSG_CLOSE_RECENTS_PANEL = 1021;
86 protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
87 protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
Jim Millere898ac52012-04-06 17:10:57 -070088 protected static final int MSG_OPEN_SEARCH_PANEL = 1024;
89 protected static final int MSG_CLOSE_SEARCH_PANEL = 1025;
Chris Wren157026f2013-06-28 16:54:01 -040090 protected static final int MSG_SHOW_HEADS_UP = 1026;
91 protected static final int MSG_HIDE_HEADS_UP = 1027;
Chris Wrene97f90b2013-08-07 17:39:35 -040092 protected static final int MSG_ESCALATE_HEADS_UP = 1028;
Chris Wren0c8275b2012-05-08 13:36:48 -040093
Chris Wren157026f2013-06-28 16:54:01 -040094 protected static final boolean ENABLE_HEADS_UP = true;
95 // scores above this threshold should be displayed in heads up mode.
Chris Wrenf6e83f42013-09-11 14:02:59 -040096 protected static final int INTERRUPTION_THRESHOLD = 11;
97 protected static final String SETTING_HEADS_UP = "heads_up_enabled";
Chris Wren0c8275b2012-05-08 13:36:48 -040098
Michael Jurka56a57832012-05-14 13:24:43 -070099 // Should match the value in PhoneWindowManager
100 public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
101
Chris Wren0c8275b2012-05-08 13:36:48 -0400102 public static final int EXPANDED_LEAVE_ALONE = -10000;
103 public static final int EXPANDED_FULL_OPEN = -10001;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700104
Joe Onorato808182d2010-07-09 18:52:06 -0400105 protected CommandQueue mCommandQueue;
106 protected IStatusBarService mBarService;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700107 protected H mHandler = createHandler();
108
Chris Wren0c8275b2012-05-08 13:36:48 -0400109 // all notifications
110 protected NotificationData mNotificationData = new NotificationData();
111 protected NotificationRowLayout mPile;
112
Chris Wren51c75102013-07-16 20:49:17 -0400113 protected NotificationData.Entry mInterruptingNotificationEntry;
114 protected long mInterruptingNotificationTime;
Chris Wren0c8275b2012-05-08 13:36:48 -0400115
Daniel Sandler6a858c32012-03-12 14:38:58 -0400116 // used to notify status bar for suppressing notification LED
117 protected boolean mPanelSlightlyVisible;
118
Jim Millere898ac52012-04-06 17:10:57 -0700119 // Search panel
120 protected SearchPanelView mSearchPanelView;
121
Daniel Sandler469e96e2012-05-04 15:56:19 -0400122 protected PopupMenu mNotificationBlamePopup;
123
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400124 protected int mCurrentUserId = 0;
125
Daniel Sandler777dcde2013-09-30 10:21:45 -0400126 protected int mLayoutDirection = -1; // invalid
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700127 private Locale mLocale;
Chris Wrenf6e83f42013-09-11 14:02:59 -0400128 protected boolean mUseHeadsUp = false;
Chris Wren157026f2013-06-28 16:54:01 -0400129
130 protected IDreamManager mDreamManager;
Chris Wren157026f2013-06-28 16:54:01 -0400131 PowerManager mPowerManager;
Chris Wren51c75102013-07-16 20:49:17 -0400132 protected int mRowHeight;
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700133
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500134 // UI-specific methods
Daniel Sandler6a858c32012-03-12 14:38:58 -0400135
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500136 /**
137 * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
138 * and add them to the window manager.
139 */
140 protected abstract void createAndAddWindows();
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400141
Jeff Brown98365d72012-08-19 20:30:52 -0700142 protected WindowManager mWindowManager;
143 protected IWindowManager mWindowManagerService;
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700144 protected abstract void refreshLayout(int layoutDirection);
145
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500146 protected Display mDisplay;
Jeff Brown98365d72012-08-19 20:30:52 -0700147
Daniel Sandler26cda272012-05-22 15:44:08 -0400148 private boolean mDeviceProvisioned = false;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400149
John Spurlockd08de372013-06-24 13:06:08 -0400150 private RecentsComponent mRecents;
151
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500152 public IStatusBarService getStatusBarService() {
153 return mBarService;
154 }
Joe Onorato2039e482010-11-29 14:54:24 -0800155
Daniel Sandler3ffdcc72012-09-23 14:31:48 -0400156 public boolean isDeviceProvisioned() {
Daniel Sandler26cda272012-05-22 15:44:08 -0400157 return mDeviceProvisioned;
158 }
159
160 private ContentObserver mProvisioningObserver = new ContentObserver(new Handler()) {
161 @Override
162 public void onChange(boolean selfChange) {
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700163 final boolean provisioned = 0 != Settings.Global.getInt(
164 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
Daniel Sandler26cda272012-05-22 15:44:08 -0400165 if (provisioned != mDeviceProvisioned) {
166 mDeviceProvisioned = provisioned;
167 updateNotificationIcons();
168 }
169 }
170 };
171
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700172 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
173 @Override
174 public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) {
Daniel Sandler198a0302012-08-17 16:04:31 -0400175 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400176 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
Daniel Sandler198a0302012-08-17 16:04:31 -0400177 }
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700178 final boolean isActivity = pendingIntent.isActivity();
179 if (isActivity) {
180 try {
181 // The intent we are sending is for the application, which
182 // won't have permission to immediately start an activity after
183 // the user switches to home. We know it is safe to do at this
184 // point, so make sure new activity switches are now allowed.
185 ActivityManagerNative.getDefault().resumeAppSwitches();
186 // Also, notifications can be launched from the lock screen,
187 // so dismiss the lock screen when the activity starts.
188 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
189 } catch (RemoteException e) {
190 }
191 }
192
193 boolean handled = super.onClickHandler(view, pendingIntent, fillInIntent);
194
195 if (isActivity && handled) {
196 // close the shade if it was open
Daniel Sandler11cf1782012-09-27 14:03:08 -0400197 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700198 visibilityChanged(false);
199 }
200 return handled;
201 }
202 };
203
John Spurlock5c454122013-06-17 07:35:46 -0400204 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
205 @Override
206 public void onReceive(Context context, Intent intent) {
207 String action = intent.getAction();
208 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
209 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
210 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
211 userSwitched(mCurrentUserId);
212 }
213 }
214 };
215
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400216 public void start() {
Jeff Brown98365d72012-08-19 20:30:52 -0700217 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
218 mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
219 mDisplay = mWindowManager.getDefaultDisplay();
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500220
Chris Wren157026f2013-06-28 16:54:01 -0400221 mDreamManager = IDreamManager.Stub.asInterface(
222 ServiceManager.checkService(DreamService.DREAM_SERVICE));
Chris Wren157026f2013-06-28 16:54:01 -0400223 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
224
Daniel Sandler26cda272012-05-22 15:44:08 -0400225 mProvisioningObserver.onChange(false); // set up
226 mContext.getContentResolver().registerContentObserver(
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700227 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
Daniel Sandler26cda272012-05-22 15:44:08 -0400228 mProvisioningObserver);
229
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500230 mBarService = IStatusBarService.Stub.asInterface(
231 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400232
John Spurlockd08de372013-06-24 13:06:08 -0400233 mRecents = getComponent(RecentsComponent.class);
234
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700235 mLocale = mContext.getResources().getConfiguration().locale;
236 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
237
Joe Onorato2314aab2010-04-08 16:41:23 -0500238 // Connect in to the status bar manager service
Joe Onorato0cbda992010-05-02 16:28:15 -0700239 StatusBarIconList iconList = new StatusBarIconList();
Joe Onorato75199e32010-05-29 17:22:51 -0400240 ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
241 ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
Joe Onorato66d7d012010-05-14 10:05:10 -0700242 mCommandQueue = new CommandQueue(this, iconList);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400243
Jeff Brown2992ea72011-01-28 22:04:14 -0800244 int[] switches = new int[7];
satokcd7cd292010-11-20 15:46:23 +0900245 ArrayList<IBinder> binders = new ArrayList<IBinder>();
Joe Onorato2314aab2010-04-08 16:41:23 -0500246 try {
Joe Onorato93056472010-09-10 10:30:46 -0400247 mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
satokcd7cd292010-11-20 15:46:23 +0900248 switches, binders);
Joe Onorato2314aab2010-04-08 16:41:23 -0500249 } catch (RemoteException ex) {
250 // If the system process isn't there we're doomed anyway.
251 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400252
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500253 createAndAddWindows();
Joe Onorato0cbda992010-05-02 16:28:15 -0700254
Joe Onoratoe4c7b3f2010-10-30 12:15:03 -0700255 disable(switches[0]);
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700256 setSystemUiVisibility(switches[1], 0xffffffff);
Dianne Hackborn7d049322011-06-14 15:00:32 -0700257 topAppWindowChanged(switches[2] != 0);
satokcd7cd292010-11-20 15:46:23 +0900258 // StatusBarManagerService has a back up of IME token and it's restored here.
Joe Onorato857fd9b2011-01-27 15:08:35 -0800259 setImeWindowStatus(binders.get(0), switches[3], switches[4]);
Jeff Brown2992ea72011-01-28 22:04:14 -0800260 setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
Joe Onorato93056472010-09-10 10:30:46 -0400261
Joe Onorato0cbda992010-05-02 16:28:15 -0700262 // Set up the initial icon state
Joe Onorato75199e32010-05-29 17:22:51 -0400263 int N = iconList.size();
Joe Onorato0cbda992010-05-02 16:28:15 -0700264 int viewIndex = 0;
265 for (int i=0; i<N; i++) {
266 StatusBarIcon icon = iconList.getIcon(i);
267 if (icon != null) {
268 addIcon(iconList.getSlot(i), i, viewIndex, icon);
269 viewIndex++;
270 }
271 }
272
Joe Onorato75199e32010-05-29 17:22:51 -0400273 // Set up the initial notification state
274 N = notificationKeys.size();
Joe Onorato75199e32010-05-29 17:22:51 -0400275 if (N == notifications.size()) {
276 for (int i=0; i<N; i++) {
277 addNotification(notificationKeys.get(i), notifications.get(i));
278 }
279 } else {
280 Log.wtf(TAG, "Notification list length mismatch: keys=" + N
281 + " notifications=" + notifications.size());
282 }
283
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500284 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400285 Log.d(TAG, String.format(
Daniel Sandler6a858c32012-03-12 14:38:58 -0400286 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500287 iconList.size(),
288 switches[0],
289 switches[1],
290 switches[2],
291 switches[3]
292 ));
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400293 }
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400294
Christopher Tate5e08af02012-09-21 17:17:22 -0700295 mCurrentUserId = ActivityManager.getCurrentUser();
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400296
297 IntentFilter filter = new IntentFilter();
298 filter.addAction(Intent.ACTION_USER_SWITCHED);
John Spurlock5c454122013-06-17 07:35:46 -0400299 mContext.registerReceiver(mBroadcastReceiver, filter);
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400300 }
301
302 public void userSwitched(int newUserId) {
303 // should be overridden
304 }
305
306 public boolean notificationIsForCurrentUser(StatusBarNotification n) {
307 final int thisUserId = mCurrentUserId;
308 final int notificationUserId = n.getUserId();
309 if (DEBUG && MULTIUSER_DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400310 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400311 n, thisUserId, notificationUserId));
312 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -0700313 return notificationUserId == UserHandle.USER_ALL
314 || thisUserId == notificationUserId;
Joe Onorato2314aab2010-04-08 16:41:23 -0500315 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400316
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700317 @Override
318 protected void onConfigurationChanged(Configuration newConfig) {
Daniel Sandler777dcde2013-09-30 10:21:45 -0400319 final Locale locale = mContext.getResources().getConfiguration().locale;
320 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
321 if (! locale.equals(mLocale) || ld != mLayoutDirection) {
322 if (DEBUG) {
323 Log.v(TAG, String.format(
324 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
325 locale, ld));
326 }
327 mLocale = locale;
328 mLayoutDirection = ld;
329 refreshLayout(ld);
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700330 }
331 }
332
Winson Chungc57ccf02011-10-13 15:04:59 -0700333 protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
334 View vetoButton = row.findViewById(R.id.veto);
Chris Wren6d15a362013-08-20 18:46:29 -0400335 if (n.isClearable() || (mInterruptingNotificationEntry != null
336 && mInterruptingNotificationEntry.row == row)) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400337 final String _pkg = n.getPackageName();
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400338 final String _tag = n.getTag();
339 final int _id = n.getId();
Winson Chungc57ccf02011-10-13 15:04:59 -0700340 vetoButton.setOnClickListener(new View.OnClickListener() {
341 public void onClick(View v) {
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700342 // Accessibility feedback
343 v.announceForAccessibility(
344 mContext.getString(R.string.accessibility_notification_dismissed));
Winson Chungc57ccf02011-10-13 15:04:59 -0700345 try {
346 mBarService.onNotificationClear(_pkg, _tag, _id);
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700347
Winson Chungc57ccf02011-10-13 15:04:59 -0700348 } catch (RemoteException ex) {
349 // system process is dead if we're here.
350 }
351 }
352 });
353 vetoButton.setVisibility(View.VISIBLE);
354 } else {
355 vetoButton.setVisibility(View.GONE);
356 }
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700357 vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
Winson Chungc57ccf02011-10-13 15:04:59 -0700358 return vetoButton;
359 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400360
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400361
362 protected void applyLegacyRowBackground(StatusBarNotification sbn, View content) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400363 if (sbn.getNotification().contentView.getLayoutId() !=
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400364 com.android.internal.R.layout.notification_template_base) {
365 int version = 0;
366 try {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400367 ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.getPackageName(), 0);
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400368 version = info.targetSdkVersion;
369 } catch (NameNotFoundException ex) {
John Spurlockcd686b52013-06-05 10:13:46 -0400370 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400371 }
372 if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) {
373 content.setBackgroundResource(R.drawable.notification_row_legacy_bg);
374 } else {
Chris Wrend84e5932012-04-19 17:58:03 -0400375 content.setBackgroundResource(com.android.internal.R.drawable.notification_bg);
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400376 }
377 }
378 }
379
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400380 private void startApplicationDetailsActivity(String packageName) {
381 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
382 Uri.fromParts("package", packageName, null));
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700383 intent.setComponent(intent.resolveActivity(mContext.getPackageManager()));
Amith Yamasaniea7e9152012-09-24 16:11:18 -0700384 TaskStackBuilder.create(mContext).addNextIntentWithParentStack(intent).startActivities(
385 null, UserHandle.CURRENT);
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400386 }
387
Jim Miller9a720f52012-05-30 03:19:43 -0700388 protected View.OnLongClickListener getNotificationLongClicker() {
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400389 return new View.OnLongClickListener() {
390 @Override
391 public boolean onLongClick(View v) {
392 final String packageNameF = (String) v.getTag();
393 if (packageNameF == null) return false;
Jeff Sharkeyaf232ed2012-04-30 15:19:39 -0700394 if (v.getWindowToken() == null) return false;
Daniel Sandler469e96e2012-05-04 15:56:19 -0400395 mNotificationBlamePopup = new PopupMenu(mContext, v);
396 mNotificationBlamePopup.getMenuInflater().inflate(
397 R.menu.notification_popup_menu,
398 mNotificationBlamePopup.getMenu());
399 mNotificationBlamePopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400400 public boolean onMenuItemClick(MenuItem item) {
401 if (item.getItemId() == R.id.notification_inspect_item) {
402 startApplicationDetailsActivity(packageNameF);
Daniel Sandler11cf1782012-09-27 14:03:08 -0400403 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400404 } else {
405 return false;
406 }
407 return true;
408 }
409 });
Daniel Sandler469e96e2012-05-04 15:56:19 -0400410 mNotificationBlamePopup.show();
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400411
412 return true;
413 }
414 };
415 }
416
Daniel Sandler469e96e2012-05-04 15:56:19 -0400417 public void dismissPopups() {
418 if (mNotificationBlamePopup != null) {
419 mNotificationBlamePopup.dismiss();
420 mNotificationBlamePopup = null;
421 }
422 }
423
Chris Wren51c75102013-07-16 20:49:17 -0400424 public void onHeadsUpDismissed() {
Daniel Sandlerfa7887b2012-03-26 09:43:31 -0400425 }
Michael Jurka7f2668c2012-03-27 07:49:52 -0700426
427 @Override
428 public void toggleRecentApps() {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700429 int msg = MSG_TOGGLE_RECENTS_PANEL;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700430 mHandler.removeMessages(msg);
431 mHandler.sendEmptyMessage(msg);
432 }
433
434 @Override
435 public void preloadRecentApps() {
436 int msg = MSG_PRELOAD_RECENT_APPS;
437 mHandler.removeMessages(msg);
438 mHandler.sendEmptyMessage(msg);
439 }
440
441 @Override
442 public void cancelPreloadRecentApps() {
443 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
444 mHandler.removeMessages(msg);
445 mHandler.sendEmptyMessage(msg);
446 }
447
448 @Override
Jim Millere898ac52012-04-06 17:10:57 -0700449 public void showSearchPanel() {
450 int msg = MSG_OPEN_SEARCH_PANEL;
451 mHandler.removeMessages(msg);
452 mHandler.sendEmptyMessage(msg);
453 }
454
455 @Override
456 public void hideSearchPanel() {
457 int msg = MSG_CLOSE_SEARCH_PANEL;
458 mHandler.removeMessages(msg);
459 mHandler.sendEmptyMessage(msg);
460 }
461
Jim Millere898ac52012-04-06 17:10:57 -0700462 protected abstract WindowManager.LayoutParams getSearchLayoutParams(
463 LayoutParams layoutParams);
464
Jim Millere898ac52012-04-06 17:10:57 -0700465 protected void updateSearchPanel() {
466 // Search Panel
467 boolean visible = false;
468 if (mSearchPanelView != null) {
469 visible = mSearchPanelView.isShowing();
Jeff Brown98365d72012-08-19 20:30:52 -0700470 mWindowManager.removeView(mSearchPanelView);
Jim Millere898ac52012-04-06 17:10:57 -0700471 }
472
473 // Provide SearchPanel with a temporary parent to allow layout params to work.
474 LinearLayout tmpRoot = new LinearLayout(mContext);
475 mSearchPanelView = (SearchPanelView) LayoutInflater.from(mContext).inflate(
476 R.layout.status_bar_search_panel, tmpRoot, false);
477 mSearchPanelView.setOnTouchListener(
478 new TouchOutsideListener(MSG_CLOSE_SEARCH_PANEL, mSearchPanelView));
479 mSearchPanelView.setVisibility(View.GONE);
480
481 WindowManager.LayoutParams lp = getSearchLayoutParams(mSearchPanelView.getLayoutParams());
482
Jeff Brown98365d72012-08-19 20:30:52 -0700483 mWindowManager.addView(mSearchPanelView, lp);
Jim Millere898ac52012-04-06 17:10:57 -0700484 mSearchPanelView.setBar(this);
485 if (visible) {
486 mSearchPanelView.show(true, false);
487 }
488 }
489
Michael Jurkaecc395a2012-03-30 05:31:46 -0700490 protected H createHandler() {
Michael Jurka7f2668c2012-03-27 07:49:52 -0700491 return new H();
492 }
493
Michael Jurka56a57832012-05-14 13:24:43 -0700494 static void sendCloseSystemWindows(Context context, String reason) {
495 if (ActivityManagerNative.isSystemReady()) {
496 try {
497 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
498 } catch (RemoteException e) {
499 }
500 }
501 }
502
Michael Jurkacb2522c2012-04-13 09:32:47 -0700503 protected abstract View getStatusBarView();
504
Michael Jurka80343f62012-10-18 13:13:46 +0200505 protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
506 // additional optimization when we have software system buttons - start loading the recent
507 // tasks on touch down
508 @Override
509 public boolean onTouch(View v, MotionEvent event) {
510 int action = event.getAction() & MotionEvent.ACTION_MASK;
511 if (action == MotionEvent.ACTION_DOWN) {
512 preloadRecentTasksList();
513 } else if (action == MotionEvent.ACTION_CANCEL) {
514 cancelPreloadingRecentTasksList();
515 } else if (action == MotionEvent.ACTION_UP) {
516 if (!v.isPressed()) {
517 cancelPreloadingRecentTasksList();
518 }
519
520 }
521 return false;
522 }
523 };
524
John Spurlockd08de372013-06-24 13:06:08 -0400525 protected void toggleRecentsActivity() {
526 if (mRecents != null) {
527 mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
528 }
529 }
Michael Jurka80343f62012-10-18 13:13:46 +0200530
John Spurlockd08de372013-06-24 13:06:08 -0400531 protected void preloadRecentTasksList() {
532 if (mRecents != null) {
533 mRecents.preloadRecentTasksList();
534 }
Michael Jurka80343f62012-10-18 13:13:46 +0200535 }
536
537 protected void cancelPreloadingRecentTasksList() {
John Spurlockd08de372013-06-24 13:06:08 -0400538 if (mRecents != null) {
539 mRecents.cancelPreloadingRecentTasksList();
540 }
541 }
Michael Jurka80343f62012-10-18 13:13:46 +0200542
John Spurlockd08de372013-06-24 13:06:08 -0400543 protected void closeRecents() {
544 if (mRecents != null) {
545 mRecents.closeRecents();
546 }
Michael Jurka80343f62012-10-18 13:13:46 +0200547 }
548
Chris Wren51c75102013-07-16 20:49:17 -0400549 public abstract void resetHeadsUpDecayTimer();
550
Michael Jurka7f2668c2012-03-27 07:49:52 -0700551 protected class H extends Handler {
552 public void handleMessage(Message m) {
Michael Jurka80343f62012-10-18 13:13:46 +0200553 Intent intent;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700554 switch (m.what) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700555 case MSG_TOGGLE_RECENTS_PANEL:
Michael Jurkacb2522c2012-04-13 09:32:47 -0700556 toggleRecentsActivity();
557 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700558 case MSG_CLOSE_RECENTS_PANEL:
John Spurlockd08de372013-06-24 13:06:08 -0400559 closeRecents();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700560 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700561 case MSG_PRELOAD_RECENT_APPS:
Michael Jurka80343f62012-10-18 13:13:46 +0200562 preloadRecentTasksList();
Michael Jurka7f2668c2012-03-27 07:49:52 -0700563 break;
564 case MSG_CANCEL_PRELOAD_RECENT_APPS:
Michael Jurka80343f62012-10-18 13:13:46 +0200565 cancelPreloadingRecentTasksList();
Michael Jurka7f2668c2012-03-27 07:49:52 -0700566 break;
Jim Millere898ac52012-04-06 17:10:57 -0700567 case MSG_OPEN_SEARCH_PANEL:
John Spurlockcd686b52013-06-05 10:13:46 -0400568 if (DEBUG) Log.d(TAG, "opening search panel");
Jim Miller07994402012-05-02 14:22:27 -0700569 if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) {
Jim Millere898ac52012-04-06 17:10:57 -0700570 mSearchPanelView.show(true, true);
Jim Millerad178a42013-10-08 20:03:38 -0700571 onShowSearchPanel();
Jim Millere898ac52012-04-06 17:10:57 -0700572 }
573 break;
574 case MSG_CLOSE_SEARCH_PANEL:
John Spurlockcd686b52013-06-05 10:13:46 -0400575 if (DEBUG) Log.d(TAG, "closing search panel");
Jim Millere898ac52012-04-06 17:10:57 -0700576 if (mSearchPanelView != null && mSearchPanelView.isShowing()) {
577 mSearchPanelView.show(false, true);
Jim Millerad178a42013-10-08 20:03:38 -0700578 onHideSearchPanel();
Jim Millere898ac52012-04-06 17:10:57 -0700579 }
580 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700581 }
582 }
583 }
584
585 public class TouchOutsideListener implements View.OnTouchListener {
586 private int mMsg;
587 private StatusBarPanel mPanel;
588
589 public TouchOutsideListener(int msg, StatusBarPanel panel) {
590 mMsg = msg;
591 mPanel = panel;
592 }
593
594 public boolean onTouch(View v, MotionEvent ev) {
595 final int action = ev.getAction();
596 if (action == MotionEvent.ACTION_OUTSIDE
597 || (action == MotionEvent.ACTION_DOWN
598 && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
599 mHandler.removeMessages(mMsg);
600 mHandler.sendEmptyMessage(mMsg);
601 return true;
602 }
603 return false;
604 }
605 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400606
607 protected void workAroundBadLayerDrawableOpacity(View v) {
608 }
609
Jim Millerad178a42013-10-08 20:03:38 -0700610 protected void onHideSearchPanel() {
611 }
612
613 protected void onShowSearchPanel() {
614 }
615
Chris Wren51c75102013-07-16 20:49:17 -0400616 public boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
Daniel Sandler6a858c32012-03-12 14:38:58 -0400617 int minHeight =
618 mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height);
619 int maxHeight =
620 mContext.getResources().getDimensionPixelSize(R.dimen.notification_max_height);
621 StatusBarNotification sbn = entry.notification;
Chris Wren574a55e2013-07-15 18:48:37 -0400622 RemoteViews contentView = sbn.getNotification().contentView;
623 RemoteViews bigContentView = sbn.getNotification().bigContentView;
624 if (contentView == null) {
Daniel Sandler6a858c32012-03-12 14:38:58 -0400625 return false;
626 }
627
628 // create the row view
629 LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
630 Context.LAYOUT_INFLATER_SERVICE);
Chris Wren51c75102013-07-16 20:49:17 -0400631 ExpandableNotificationRow row = (ExpandableNotificationRow) inflater.inflate(
632 R.layout.status_bar_notification_row, parent, false);
Daniel Sandlerbc5559f2012-04-19 01:08:15 -0400633
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400634 // for blaming (see SwipeHelper.setLongPressListener)
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400635 row.setTag(sbn.getPackageName());
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400636
Daniel Sandler6a858c32012-03-12 14:38:58 -0400637 workAroundBadLayerDrawableOpacity(row);
638 View vetoButton = updateNotificationVetoButton(row, sbn);
639 vetoButton.setContentDescription(mContext.getString(
640 R.string.accessibility_remove_notification));
641
642 // NB: the large icon is now handled entirely by the template
643
644 // bind the click event to the content area
645 ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
646 ViewGroup adaptive = (ViewGroup)row.findViewById(R.id.adaptive);
Daniel Sandlerbc5559f2012-04-19 01:08:15 -0400647
Daniel Sandler6a858c32012-03-12 14:38:58 -0400648 content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
Daniel Sandlerbc5559f2012-04-19 01:08:15 -0400649
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400650 PendingIntent contentIntent = sbn.getNotification().contentIntent;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400651 if (contentIntent != null) {
652 final View.OnClickListener listener = new NotificationClicker(contentIntent,
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400653 sbn.getPackageName(), sbn.getTag(), sbn.getId());
Daniel Sandler6a858c32012-03-12 14:38:58 -0400654 content.setOnClickListener(listener);
655 } else {
656 content.setOnClickListener(null);
657 }
658
Chris Wren574a55e2013-07-15 18:48:37 -0400659 View contentViewLocal = null;
660 View bigContentViewLocal = null;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400661 try {
Chris Wren574a55e2013-07-15 18:48:37 -0400662 contentViewLocal = contentView.apply(mContext, adaptive, mOnClickHandler);
663 if (bigContentView != null) {
664 bigContentViewLocal = bigContentView.apply(mContext, adaptive, mOnClickHandler);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400665 }
666 }
667 catch (RuntimeException e) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400668 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
John Spurlockcd686b52013-06-05 10:13:46 -0400669 Log.e(TAG, "couldn't inflate view for notification " + ident, e);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400670 return false;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400671 }
672
Chris Wren574a55e2013-07-15 18:48:37 -0400673 if (contentViewLocal != null) {
Daniel Sandler8680bf82012-05-15 16:52:52 -0400674 SizeAdaptiveLayout.LayoutParams params =
Chris Wren574a55e2013-07-15 18:48:37 -0400675 new SizeAdaptiveLayout.LayoutParams(contentViewLocal.getLayoutParams());
Chris Wrened5cc0e2012-06-21 11:25:58 -0400676 params.minHeight = minHeight;
677 params.maxHeight = minHeight;
Chris Wren574a55e2013-07-15 18:48:37 -0400678 adaptive.addView(contentViewLocal, params);
Daniel Sandler8680bf82012-05-15 16:52:52 -0400679 }
Chris Wren574a55e2013-07-15 18:48:37 -0400680 if (bigContentViewLocal != null) {
Daniel Sandler8680bf82012-05-15 16:52:52 -0400681 SizeAdaptiveLayout.LayoutParams params =
Chris Wren574a55e2013-07-15 18:48:37 -0400682 new SizeAdaptiveLayout.LayoutParams(bigContentViewLocal.getLayoutParams());
Chris Wrened5cc0e2012-06-21 11:25:58 -0400683 params.minHeight = minHeight+1;
Daniel Sandler8680bf82012-05-15 16:52:52 -0400684 params.maxHeight = maxHeight;
Chris Wren574a55e2013-07-15 18:48:37 -0400685 adaptive.addView(bigContentViewLocal, params);
Daniel Sandler8680bf82012-05-15 16:52:52 -0400686 }
687 row.setDrawingCacheEnabled(true);
688
Daniel Sandler6a858c32012-03-12 14:38:58 -0400689 applyLegacyRowBackground(sbn, content);
690
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400691 if (MULTIUSER_DEBUG) {
692 TextView debug = (TextView) row.findViewById(R.id.debug_info);
693 if (debug != null) {
694 debug.setVisibility(View.VISIBLE);
695 debug.setText("U " + entry.notification.getUserId());
696 }
697 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400698 entry.row = row;
Chris Wren51c75102013-07-16 20:49:17 -0400699 entry.row.setRowHeight(mRowHeight);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400700 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
Jim Millera999d462013-10-30 13:58:11 -0700750 KeyguardTouchDelegate.getInstance(mContext).dismiss();
Daniel Sandler6a858c32012-03-12 14:38:58 -0400751 }
752
753 try {
754 mBarService.onNotificationClick(mPkg, mTag, mId);
755 } catch (RemoteException ex) {
756 // system process is dead if we're here.
757 }
758
759 // close the shade if it was open
Daniel Sandler11cf1782012-09-27 14:03:08 -0400760 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400761 visibilityChanged(false);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400762 }
763 }
764 /**
765 * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
766 * This was added last-minute and is inconsistent with the way the rest of the notifications
767 * are handled, because the notification isn't really cancelled. The lights are just
768 * turned off. If any other notifications happen, the lights will turn back on. Steve says
769 * this is what he wants. (see bug 1131461)
770 */
771 protected void visibilityChanged(boolean visible) {
772 if (mPanelSlightlyVisible != visible) {
773 mPanelSlightlyVisible = visible;
774 try {
775 mBarService.onPanelRevealed();
776 } catch (RemoteException ex) {
777 // Won't fail unless the world has ended.
778 }
779 }
780 }
781
Chris Wren0c8275b2012-05-08 13:36:48 -0400782 /**
783 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
784 * about the failure.
785 *
786 * WARNING: this will call back into us. Don't hold any locks.
787 */
788 void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
789 removeNotification(key);
790 try {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400791 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(), n.getInitialPid(), message);
Chris Wren0c8275b2012-05-08 13:36:48 -0400792 } catch (RemoteException ex) {
793 // The end is nigh.
794 }
795 }
796
797 protected StatusBarNotification removeNotificationViews(IBinder key) {
798 NotificationData.Entry entry = mNotificationData.remove(key);
799 if (entry == null) {
John Spurlockcd686b52013-06-05 10:13:46 -0400800 Log.w(TAG, "removeNotification for unknown key: " + key);
Chris Wren0c8275b2012-05-08 13:36:48 -0400801 return null;
802 }
803 // Remove the expanded view.
804 ViewGroup rowParent = (ViewGroup)entry.row.getParent();
805 if (rowParent != null) rowParent.removeView(entry.row);
Chris Wren8fd12652012-05-09 21:25:57 -0400806 updateExpansionStates();
Chris Wren0c8275b2012-05-08 13:36:48 -0400807 updateNotificationIcons();
808
809 return entry.notification;
810 }
811
Chris Wrenf0048ce2013-08-07 16:43:43 -0400812 protected NotificationData.Entry createNotificationViews(IBinder key,
Chris Wren0c8275b2012-05-08 13:36:48 -0400813 StatusBarNotification notification) {
814 if (DEBUG) {
Chris Wrenf0048ce2013-08-07 16:43:43 -0400815 Log.d(TAG, "createNotificationViews(key=" + key + ", notification=" + notification);
Chris Wren0c8275b2012-05-08 13:36:48 -0400816 }
817 // Construct the icon.
818 final StatusBarIconView iconView = new StatusBarIconView(mContext,
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400819 notification.getPackageName() + "/0x" + Integer.toHexString(notification.getId()),
John Spurlocke6f0a712013-09-03 16:23:49 -0400820 notification.getNotification());
Chris Wren0c8275b2012-05-08 13:36:48 -0400821 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
822
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400823 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400824 notification.getUser(),
825 notification.getNotification().icon,
826 notification.getNotification().iconLevel,
827 notification.getNotification().number,
828 notification.getNotification().tickerText);
Chris Wren0c8275b2012-05-08 13:36:48 -0400829 if (!iconView.set(ic)) {
830 handleNotificationError(key, notification, "Couldn't create icon: " + ic);
831 return null;
832 }
833 // Construct the expanded view.
834 NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
835 if (!inflateViews(entry, mPile)) {
836 handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
837 + notification);
838 return null;
839 }
Chris Wrenf0048ce2013-08-07 16:43:43 -0400840 return entry;
841 }
Chris Wren0c8275b2012-05-08 13:36:48 -0400842
Chris Wrenf0048ce2013-08-07 16:43:43 -0400843 protected void addNotificationViews(NotificationData.Entry entry) {
Chris Wren0c8275b2012-05-08 13:36:48 -0400844 // Add the expanded view and icon.
845 int pos = mNotificationData.add(entry);
846 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400847 Log.d(TAG, "addNotificationViews: added at " + pos);
Chris Wren0c8275b2012-05-08 13:36:48 -0400848 }
Chris Wren8fd12652012-05-09 21:25:57 -0400849 updateExpansionStates();
Chris Wren0c8275b2012-05-08 13:36:48 -0400850 updateNotificationIcons();
Chris Wrenf0048ce2013-08-07 16:43:43 -0400851 }
Chris Wren0c8275b2012-05-08 13:36:48 -0400852
Chris Wrenf0048ce2013-08-07 16:43:43 -0400853 private void addNotificationViews(IBinder key, StatusBarNotification notification) {
854 addNotificationViews(createNotificationViews(key, notification));
Chris Wren0c8275b2012-05-08 13:36:48 -0400855 }
856
Chris Wren8fd12652012-05-09 21:25:57 -0400857 protected void updateExpansionStates() {
858 int N = mNotificationData.size();
859 for (int i = 0; i < N; i++) {
860 NotificationData.Entry entry = mNotificationData.get(i);
Chris Wren51c75102013-07-16 20:49:17 -0400861 if (!entry.row.isUserLocked()) {
Chris Wren3ddab0d2012-08-02 16:52:21 -0400862 if (i == (N-1)) {
John Spurlockcd686b52013-06-05 10:13:46 -0400863 if (DEBUG) Log.d(TAG, "expanding top notification at " + i);
Chris Wren51c75102013-07-16 20:49:17 -0400864 entry.row.setExpanded(true);
Chris Wren8fd12652012-05-09 21:25:57 -0400865 } else {
Chris Wren51c75102013-07-16 20:49:17 -0400866 if (!entry.row.isUserExpanded()) {
John Spurlockcd686b52013-06-05 10:13:46 -0400867 if (DEBUG) Log.d(TAG, "collapsing notification at " + i);
Chris Wren51c75102013-07-16 20:49:17 -0400868 entry.row.setExpanded(false);
Chris Wren3ddab0d2012-08-02 16:52:21 -0400869 } else {
John Spurlockcd686b52013-06-05 10:13:46 -0400870 if (DEBUG) Log.d(TAG, "ignoring user-modified notification at " + i);
Chris Wren3ddab0d2012-08-02 16:52:21 -0400871 }
Chris Wren8fd12652012-05-09 21:25:57 -0400872 }
Chris Wren3ddab0d2012-08-02 16:52:21 -0400873 } else {
John Spurlockcd686b52013-06-05 10:13:46 -0400874 if (DEBUG) Log.d(TAG, "ignoring notification being held by user at " + i);
Chris Wren8fd12652012-05-09 21:25:57 -0400875 }
876 }
877 }
878
Chris Wren0c8275b2012-05-08 13:36:48 -0400879 protected abstract void haltTicker();
880 protected abstract void setAreThereNotifications();
881 protected abstract void updateNotificationIcons();
882 protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime);
883 protected abstract void updateExpandedViewPos(int expandedPosition);
Chris Wren8fd12652012-05-09 21:25:57 -0400884 protected abstract int getExpandedViewMaxHeight();
Jim Millerb4238e02012-05-14 15:26:20 -0700885 protected abstract boolean shouldDisableNavbarGestures();
Chris Wren0c8275b2012-05-08 13:36:48 -0400886
887 protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
Daniel Sandler26cda272012-05-22 15:44:08 -0400888 return parent != null && parent.indexOfChild(entry.row) == 0;
Chris Wren0c8275b2012-05-08 13:36:48 -0400889 }
890
891 public void updateNotification(IBinder key, StatusBarNotification notification) {
John Spurlockcd686b52013-06-05 10:13:46 -0400892 if (DEBUG) Log.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
Chris Wren0c8275b2012-05-08 13:36:48 -0400893
894 final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
895 if (oldEntry == null) {
John Spurlockcd686b52013-06-05 10:13:46 -0400896 Log.w(TAG, "updateNotification for unknown key: " + key);
Chris Wren0c8275b2012-05-08 13:36:48 -0400897 return;
898 }
899
900 final StatusBarNotification oldNotification = oldEntry.notification;
901
902 // XXX: modify when we do something more intelligent with the two content views
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400903 final RemoteViews oldContentView = oldNotification.getNotification().contentView;
904 final RemoteViews contentView = notification.getNotification().contentView;
905 final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
906 final RemoteViews bigContentView = notification.getNotification().bigContentView;
Chris Wren0c8275b2012-05-08 13:36:48 -0400907
908 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400909 Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
Chris Wren0c8275b2012-05-08 13:36:48 -0400910 + " ongoing=" + oldNotification.isOngoing()
911 + " expanded=" + oldEntry.expanded
912 + " contentView=" + oldContentView
Chris Wrene9e37722012-05-11 17:40:14 -0400913 + " bigContentView=" + oldBigContentView
Chris Wren0c8275b2012-05-08 13:36:48 -0400914 + " rowParent=" + oldEntry.row.getParent());
John Spurlockcd686b52013-06-05 10:13:46 -0400915 Log.d(TAG, "new notification: when=" + notification.getNotification().when
Chris Wren0c8275b2012-05-08 13:36:48 -0400916 + " ongoing=" + oldNotification.isOngoing()
Chris Wrene9e37722012-05-11 17:40:14 -0400917 + " contentView=" + contentView
918 + " bigContentView=" + bigContentView);
Chris Wren0c8275b2012-05-08 13:36:48 -0400919 }
920
921 // Can we just reapply the RemoteViews in place? If when didn't change, the order
922 // didn't change.
Chris Wrene9e37722012-05-11 17:40:14 -0400923
924 // 1U is never null
Chris Wren0c8275b2012-05-08 13:36:48 -0400925 boolean contentsUnchanged = oldEntry.expanded != null
Chris Wren0c8275b2012-05-08 13:36:48 -0400926 && contentView.getPackage() != null
927 && oldContentView.getPackage() != null
928 && oldContentView.getPackage().equals(contentView.getPackage())
929 && oldContentView.getLayoutId() == contentView.getLayoutId();
Chris Wrene9e37722012-05-11 17:40:14 -0400930 // large view may be null
931 boolean bigContentsUnchanged =
Chris Wren574a55e2013-07-15 18:48:37 -0400932 (oldEntry.getBigContentView() == null && bigContentView == null)
933 || ((oldEntry.getBigContentView() != null && bigContentView != null)
Chris Wrene9e37722012-05-11 17:40:14 -0400934 && bigContentView.getPackage() != null
935 && oldBigContentView.getPackage() != null
936 && oldBigContentView.getPackage().equals(bigContentView.getPackage())
937 && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
Chris Wren0c8275b2012-05-08 13:36:48 -0400938 ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400939 boolean orderUnchanged = notification.getNotification().when== oldNotification.getNotification().when
940 && notification.getScore() == oldNotification.getScore();
Chris Wren0c8275b2012-05-08 13:36:48 -0400941 // score now encompasses/supersedes isOngoing()
942
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400943 boolean updateTicker = notification.getNotification().tickerText != null
944 && !TextUtils.equals(notification.getNotification().tickerText,
945 oldEntry.notification.getNotification().tickerText);
Chris Wren0c8275b2012-05-08 13:36:48 -0400946 boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
Chris Wrene9e37722012-05-11 17:40:14 -0400947 if (contentsUnchanged && bigContentsUnchanged && (orderUnchanged || isTopAnyway)) {
John Spurlockcd686b52013-06-05 10:13:46 -0400948 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
Chris Wren0c8275b2012-05-08 13:36:48 -0400949 oldEntry.notification = notification;
950 try {
Chris Wrene03f4e12013-08-08 16:48:48 -0400951 updateNotificationViews(oldEntry, notification);
952
953 if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null
954 && oldNotification == mInterruptingNotificationEntry.notification) {
955 if (!shouldInterrupt(notification)) {
956 if (DEBUG) Log.d(TAG, "no longer interrupts!");
957 mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
958 } else {
959 if (DEBUG) Log.d(TAG, "updating the current heads up:" + notification);
960 mInterruptingNotificationEntry.notification = notification;
961 updateNotificationViews(mInterruptingNotificationEntry, notification);
962 }
Chris Wrene9e37722012-05-11 17:40:14 -0400963 }
Chris Wrene03f4e12013-08-08 16:48:48 -0400964
Chris Wren0c8275b2012-05-08 13:36:48 -0400965 // Update the icon.
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400966 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400967 notification.getUser(),
968 notification.getNotification().icon, notification.getNotification().iconLevel,
969 notification.getNotification().number,
970 notification.getNotification().tickerText);
Chris Wren0c8275b2012-05-08 13:36:48 -0400971 if (!oldEntry.icon.set(ic)) {
972 handleNotificationError(key, notification, "Couldn't update icon: " + ic);
973 return;
974 }
Chris Wren8fd12652012-05-09 21:25:57 -0400975 updateExpansionStates();
Chris Wren0c8275b2012-05-08 13:36:48 -0400976 }
977 catch (RuntimeException e) {
978 // It failed to add cleanly. Log, and remove the view from the panel.
John Spurlockcd686b52013-06-05 10:13:46 -0400979 Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
Chris Wren0c8275b2012-05-08 13:36:48 -0400980 removeNotificationViews(key);
981 addNotificationViews(key, notification);
982 }
983 } else {
John Spurlockcd686b52013-06-05 10:13:46 -0400984 if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
985 if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
986 if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
987 if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
Chris Wren51c75102013-07-16 20:49:17 -0400988 final boolean wasExpanded = oldEntry.row.isUserExpanded();
Chris Wren0c8275b2012-05-08 13:36:48 -0400989 removeNotificationViews(key);
Chris Wrene03f4e12013-08-08 16:48:48 -0400990 addNotificationViews(key, notification); // will also replace the heads up
Chris Wrenc9433ecb2012-06-27 17:37:03 -0400991 if (wasExpanded) {
992 final NotificationData.Entry newEntry = mNotificationData.findByKey(key);
Chris Wren51c75102013-07-16 20:49:17 -0400993 newEntry.row.setExpanded(true);
994 newEntry.row.setUserExpanded(true);
Chris Wrenc9433ecb2012-06-27 17:37:03 -0400995 }
Chris Wren0c8275b2012-05-08 13:36:48 -0400996 }
997
998 // Update the veto button accordingly (and as a result, whether this row is
999 // swipe-dismissable)
1000 updateNotificationVetoButton(oldEntry.row, notification);
1001
John Spurlock61e36832012-09-10 09:43:27 -04001002 // Is this for you?
1003 boolean isForCurrentUser = notificationIsForCurrentUser(notification);
John Spurlockcd686b52013-06-05 10:13:46 -04001004 if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
John Spurlock61e36832012-09-10 09:43:27 -04001005
Chris Wren0c8275b2012-05-08 13:36:48 -04001006 // Restart the ticker if it's still running
John Spurlock61e36832012-09-10 09:43:27 -04001007 if (updateTicker && isForCurrentUser) {
Chris Wren0c8275b2012-05-08 13:36:48 -04001008 haltTicker();
1009 tick(key, notification, false);
1010 }
1011
1012 // Recalculate the position of the sliding windows and the titles.
1013 setAreThereNotifications();
1014 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
Chris Wrene03f4e12013-08-08 16:48:48 -04001015 }
Chris Wren0c8275b2012-05-08 13:36:48 -04001016
Chris Wrene03f4e12013-08-08 16:48:48 -04001017 private void updateNotificationViews(NotificationData.Entry entry,
1018 StatusBarNotification notification) {
1019 final RemoteViews contentView = notification.getNotification().contentView;
1020 final RemoteViews bigContentView = notification.getNotification().bigContentView;
1021 // Reapply the RemoteViews
1022 contentView.reapply(mContext, entry.expanded, mOnClickHandler);
1023 if (bigContentView != null && entry.getBigContentView() != null) {
1024 bigContentView.reapply(mContext, entry.getBigContentView(), mOnClickHandler);
1025 }
1026 // update the contentIntent
1027 final PendingIntent contentIntent = notification.getNotification().contentIntent;
1028 if (contentIntent != null) {
1029 final View.OnClickListener listener = makeClicker(contentIntent,
1030 notification.getPackageName(), notification.getTag(), notification.getId());
1031 entry.content.setOnClickListener(listener);
1032 } else {
1033 entry.content.setOnClickListener(null);
Chris Wren0c8275b2012-05-08 13:36:48 -04001034 }
1035 }
John Spurlock36231282012-06-23 17:11:27 -04001036
Chris Wrene97f90b2013-08-07 17:39:35 -04001037 protected void notifyHeadsUpScreenOn(boolean screenOn) {
1038 if (!screenOn && mInterruptingNotificationEntry != null) {
1039 mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
1040 }
1041 }
1042
Chris Wren51c75102013-07-16 20:49:17 -04001043 protected boolean shouldInterrupt(StatusBarNotification sbn) {
1044 Notification notification = sbn.getNotification();
1045 // some predicates to make the boolean logic legible
1046 boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
1047 || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
1048 || notification.sound != null
1049 || notification.vibrate != null;
1050 boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
1051 boolean isFullscreen = notification.fullScreenIntent != null;
1052 boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
1053 Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
1054
Jim Millera999d462013-10-30 13:58:11 -07001055 final KeyguardTouchDelegate keyguard = KeyguardTouchDelegate.getInstance(mContext);
Chris Wren51c75102013-07-16 20:49:17 -04001056 boolean interrupt = (isFullscreen || (isHighPriority && isNoisy))
1057 && isAllowed
1058 && mPowerManager.isScreenOn()
Jim Millera999d462013-10-30 13:58:11 -07001059 && !keyguard.isShowingAndNotHidden()
1060 && !keyguard.isInputRestricted();
Chris Wren157026f2013-06-28 16:54:01 -04001061 try {
1062 interrupt = interrupt && !mDreamManager.isDreaming();
1063 } catch (RemoteException e) {
1064 Log.d(TAG, "failed to query dream manager", e);
1065 }
Chris Wren51c75102013-07-16 20:49:17 -04001066 if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
Chris Wren157026f2013-06-28 16:54:01 -04001067 return interrupt;
1068 }
1069
John Spurlock36231282012-06-23 17:11:27 -04001070 // Q: What kinds of notifications should show during setup?
1071 // A: Almost none! Only things coming from the system (package is "android") that also
1072 // have special "kind" tags marking them as relevant for setup (see below).
1073 protected boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001074 if ("android".equals(sbn.getPackageName())) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001075 if (sbn.getNotification().kind != null) {
1076 for (String aKind : sbn.getNotification().kind) {
John Spurlock36231282012-06-23 17:11:27 -04001077 // IME switcher, created by InputMethodManagerService
1078 if ("android.system.imeswitcher".equals(aKind)) return true;
1079 // OTA availability & errors, created by SystemUpdateService
1080 if ("android.system.update".equals(aKind)) return true;
1081 }
1082 }
1083 }
1084 return false;
1085 }
John Spurlock67ad3682012-06-26 17:42:00 -04001086
1087 public boolean inKeyguardRestrictedInputMode() {
Jim Millera999d462013-10-30 13:58:11 -07001088 return KeyguardTouchDelegate.getInstance(mContext).isInputRestricted();
John Spurlock67ad3682012-06-26 17:42:00 -04001089 }
John Spurlockad3e6cb2013-04-30 08:47:43 -04001090
John Spurlockcfc359a2013-09-05 10:42:03 -04001091 public void setInteracting(int barWindow, boolean interacting) {
John Spurlockad3e6cb2013-04-30 08:47:43 -04001092 // hook for subclasses
1093 }
John Spurlock5c454122013-06-17 07:35:46 -04001094
1095 public void destroy() {
1096 if (mSearchPanelView != null) {
1097 mWindowManager.removeViewImmediate(mSearchPanelView);
1098 }
1099 mContext.unregisterReceiver(mBroadcastReceiver);
1100 }
Joe Onorato2314aab2010-04-08 16:41:23 -05001101}