blob: 9149e2d82ad6fd31533a78bce3ecb9e7a5af35c9 [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;
Dan Sandlera5e0f412014-01-23 15:11:54 -050029import android.content.pm.PackageManager;
Daniel Sandler96fd7c12012-03-30 16:37:36 -040030import android.content.pm.PackageManager.NameNotFoundException;
Kenny Guy3a7c4a52014-03-03 18:24:03 +000031import android.content.pm.UserInfo;
John Spurlockde84f0e2013-06-12 12:41:00 -040032import android.content.res.Configuration;
Daniel Sandler26cda272012-05-22 15:44:08 -040033import android.database.ContentObserver;
Daniel Sandler6a858c32012-03-12 14:38:58 -040034import android.graphics.Rect;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +010035import android.graphics.drawable.Drawable;
Daniel Sandlerf7a19562012-04-04 14:04:21 -040036import android.net.Uri;
Daniel Sandler96fd7c12012-03-30 16:37:36 -040037import android.os.Build;
Michael Jurka7f2668c2012-03-27 07:49:52 -070038import android.os.Handler;
Joe Onorato808182d2010-07-09 18:52:06 -040039import android.os.IBinder;
Michael Jurka7f2668c2012-03-27 07:49:52 -070040import android.os.Message;
Chris Wren157026f2013-06-28 16:54:01 -040041import android.os.PowerManager;
Joe Onorato808182d2010-07-09 18:52:06 -040042import android.os.RemoteException;
43import android.os.ServiceManager;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070044import android.os.UserHandle;
Kenny Guy3a7c4a52014-03-03 18:24:03 +000045import android.os.UserManager;
Daniel Sandlerf7a19562012-04-04 14:04:21 -040046import android.provider.Settings;
Chris Wren157026f2013-06-28 16:54:01 -040047import android.service.dreams.DreamService;
48import android.service.dreams.IDreamManager;
John Spurlockde84f0e2013-06-12 12:41:00 -040049import android.service.notification.StatusBarNotification;
Chris Wren0c8275b2012-05-08 13:36:48 -040050import android.text.TextUtils;
Joe Onorato808182d2010-07-09 18:52:06 -040051import android.util.Log;
Kenny Guy3a7c4a52014-03-03 18:24:03 +000052import android.util.SparseArray;
Dan Sandlerfd16d562014-02-13 18:43:31 -080053import android.util.SparseBooleanArray;
Daniel Sandlerc638c1e2011-08-24 16:19:23 -070054import android.view.Display;
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -050055import android.view.IWindowManager;
Michael Jurka7f2668c2012-03-27 07:49:52 -070056import android.view.LayoutInflater;
Daniel Sandlerf7a19562012-04-04 14:04:21 -040057import android.view.MenuItem;
Michael Jurka7f2668c2012-03-27 07:49:52 -070058import android.view.MotionEvent;
Joe Onorato808182d2010-07-09 18:52:06 -040059import android.view.View;
Daniel Sandler6a858c32012-03-12 14:38:58 -040060import android.view.ViewGroup;
Michael Jurka7f2668c2012-03-27 07:49:52 -070061import android.view.ViewGroup.LayoutParams;
Joe Onorato808182d2010-07-09 18:52:06 -040062import android.view.WindowManager;
Michael Jurka80343f62012-10-18 13:13:46 +020063import android.view.WindowManagerGlobal;
Chris Wren0c8275b2012-05-08 13:36:48 -040064import android.widget.ImageView;
Michael Jurka7f2668c2012-03-27 07:49:52 -070065import android.widget.LinearLayout;
Daniel Sandlerf7a19562012-04-04 14:04:21 -040066import android.widget.PopupMenu;
Michael Jurkaa600fd92012-06-25 15:57:05 -070067import android.widget.RemoteViews;
Daniel Sandlerb9301c32012-08-14 15:08:24 -040068import android.widget.TextView;
Joe Onorato808182d2010-07-09 18:52:06 -040069
John Spurlockde84f0e2013-06-12 12:41:00 -040070import com.android.internal.statusbar.IStatusBarService;
71import com.android.internal.statusbar.StatusBarIcon;
72import com.android.internal.statusbar.StatusBarIconList;
Jorim Jaggi5c2d8462014-03-21 17:37:00 +010073import com.android.internal.util.LegacyNotificationUtil;
John Spurlockde84f0e2013-06-12 12:41:00 -040074import com.android.systemui.R;
John Spurlockd08de372013-06-24 13:06:08 -040075import com.android.systemui.RecentsComponent;
John Spurlockde84f0e2013-06-12 12:41:00 -040076import com.android.systemui.SearchPanelView;
77import com.android.systemui.SystemUI;
Jim Millera999d462013-10-30 13:58:11 -070078import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
Selim Cinekb6d85eb2014-03-28 20:21:01 +010079import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
John Spurlockde84f0e2013-06-12 12:41:00 -040080
Michael Jurkaa600fd92012-06-25 15:57:05 -070081import java.util.ArrayList;
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -070082import java.util.Locale;
Joe Onorato1c95ecb2010-06-28 17:19:12 -040083
Michael Jurka7f2668c2012-03-27 07:49:52 -070084public abstract class BaseStatusBar extends SystemUI implements
Jorim Jaggife40f7d2014-04-28 15:20:04 +020085 CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener {
Daniel Sandler198a0302012-08-17 16:04:31 -040086 public static final String TAG = "StatusBar";
87 public static final boolean DEBUG = false;
Daniel Sandlerb9301c32012-08-14 15:08:24 -040088 public static final boolean MULTIUSER_DEBUG = false;
Joe Onorato1c95ecb2010-06-28 17:19:12 -040089
Michael Jurkacb2522c2012-04-13 09:32:47 -070090 protected static final int MSG_TOGGLE_RECENTS_PANEL = 1020;
Michael Jurka7f2668c2012-03-27 07:49:52 -070091 protected static final int MSG_CLOSE_RECENTS_PANEL = 1021;
92 protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
93 protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
Jim Millere898ac52012-04-06 17:10:57 -070094 protected static final int MSG_OPEN_SEARCH_PANEL = 1024;
95 protected static final int MSG_CLOSE_SEARCH_PANEL = 1025;
Chris Wren157026f2013-06-28 16:54:01 -040096 protected static final int MSG_SHOW_HEADS_UP = 1026;
97 protected static final int MSG_HIDE_HEADS_UP = 1027;
Chris Wrene97f90b2013-08-07 17:39:35 -040098 protected static final int MSG_ESCALATE_HEADS_UP = 1028;
Chris Wren0c8275b2012-05-08 13:36:48 -040099
Chris Wren157026f2013-06-28 16:54:01 -0400100 protected static final boolean ENABLE_HEADS_UP = true;
101 // scores above this threshold should be displayed in heads up mode.
Chris Wren5d9b1532014-03-21 14:31:05 -0400102 protected static final int INTERRUPTION_THRESHOLD = 10;
Chris Wren22ae46e2014-02-26 18:08:09 -0500103 protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
Chris Wren0c8275b2012-05-08 13:36:48 -0400104
Michael Jurka56a57832012-05-14 13:24:43 -0700105 // Should match the value in PhoneWindowManager
106 public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
107
Chris Wren0c8275b2012-05-08 13:36:48 -0400108 public static final int EXPANDED_LEAVE_ALONE = -10000;
109 public static final int EXPANDED_FULL_OPEN = -10001;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700110
Joe Onorato808182d2010-07-09 18:52:06 -0400111 protected CommandQueue mCommandQueue;
112 protected IStatusBarService mBarService;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700113 protected H mHandler = createHandler();
114
Chris Wren0c8275b2012-05-08 13:36:48 -0400115 // all notifications
116 protected NotificationData mNotificationData = new NotificationData();
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100117 protected NotificationStackScrollLayout mStackScroller;
Chris Wren0c8275b2012-05-08 13:36:48 -0400118
Chris Wren51c75102013-07-16 20:49:17 -0400119 protected NotificationData.Entry mInterruptingNotificationEntry;
120 protected long mInterruptingNotificationTime;
Chris Wren0c8275b2012-05-08 13:36:48 -0400121
Daniel Sandler6a858c32012-03-12 14:38:58 -0400122 // used to notify status bar for suppressing notification LED
123 protected boolean mPanelSlightlyVisible;
124
Jim Millere898ac52012-04-06 17:10:57 -0700125 // Search panel
126 protected SearchPanelView mSearchPanelView;
127
Daniel Sandler469e96e2012-05-04 15:56:19 -0400128 protected PopupMenu mNotificationBlamePopup;
129
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400130 protected int mCurrentUserId = 0;
Kenny Guy2a764942014-04-02 13:29:20 +0100131 final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400132
Daniel Sandler777dcde2013-09-30 10:21:45 -0400133 protected int mLayoutDirection = -1; // invalid
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700134 private Locale mLocale;
Chris Wrenf6e83f42013-09-11 14:02:59 -0400135 protected boolean mUseHeadsUp = false;
Chris Wren22ae46e2014-02-26 18:08:09 -0500136 protected boolean mHeadsUpTicker = false;
Chris Wren157026f2013-06-28 16:54:01 -0400137
138 protected IDreamManager mDreamManager;
Chris Wren157026f2013-06-28 16:54:01 -0400139 PowerManager mPowerManager;
Selim Cinek1685e632014-04-08 02:27:49 +0200140 protected int mRowMinHeight;
141 protected int mRowMaxHeight;
Dan Sandlerfd16d562014-02-13 18:43:31 -0800142
143 // public mode, private notifications, etc
144 private boolean mLockscreenPublicMode = false;
145 private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
Jorim Jaggi5c2d8462014-03-21 17:37:00 +0100146 private LegacyNotificationUtil mLegacyNotificationUtil = LegacyNotificationUtil.getInstance();
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700147
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000148 private UserManager mUserManager;
149
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500150 // UI-specific methods
Daniel Sandler6a858c32012-03-12 14:38:58 -0400151
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500152 /**
153 * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
154 * and add them to the window manager.
155 */
156 protected abstract void createAndAddWindows();
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400157
Jeff Brown98365d72012-08-19 20:30:52 -0700158 protected WindowManager mWindowManager;
159 protected IWindowManager mWindowManagerService;
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700160 protected abstract void refreshLayout(int layoutDirection);
161
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500162 protected Display mDisplay;
Jeff Brown98365d72012-08-19 20:30:52 -0700163
Daniel Sandler26cda272012-05-22 15:44:08 -0400164 private boolean mDeviceProvisioned = false;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400165
John Spurlockd08de372013-06-24 13:06:08 -0400166 private RecentsComponent mRecents;
167
John Spurlocke677d712014-02-13 12:52:19 -0500168 protected int mZenMode;
169
Jorim Jaggi649b7082014-04-03 13:15:18 +0200170 protected boolean mOnKeyguard;
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200171 protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
Jorim Jaggi649b7082014-04-03 13:15:18 +0200172
Daniel Sandler3ffdcc72012-09-23 14:31:48 -0400173 public boolean isDeviceProvisioned() {
Daniel Sandler26cda272012-05-22 15:44:08 -0400174 return mDeviceProvisioned;
175 }
176
John Spurlocke677d712014-02-13 12:52:19 -0500177 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
Daniel Sandler26cda272012-05-22 15:44:08 -0400178 @Override
179 public void onChange(boolean selfChange) {
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700180 final boolean provisioned = 0 != Settings.Global.getInt(
181 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
Daniel Sandler26cda272012-05-22 15:44:08 -0400182 if (provisioned != mDeviceProvisioned) {
183 mDeviceProvisioned = provisioned;
184 updateNotificationIcons();
185 }
John Spurlocke677d712014-02-13 12:52:19 -0500186 final int mode = Settings.Global.getInt(mContext.getContentResolver(),
187 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
188 setZenMode(mode);
Daniel Sandler26cda272012-05-22 15:44:08 -0400189 }
190 };
191
Dan Sandlerfd16d562014-02-13 18:43:31 -0800192 private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
193 @Override
194 public void onChange(boolean selfChange) {
195 // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
196 // so we just dump our cache ...
197 mUsersAllowingPrivateNotifications.clear();
198 // ... and refresh all the notifications
199 updateNotificationIcons();
200 }
201 };
202
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700203 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
204 @Override
205 public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) {
Daniel Sandler198a0302012-08-17 16:04:31 -0400206 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400207 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
Daniel Sandler198a0302012-08-17 16:04:31 -0400208 }
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700209 final boolean isActivity = pendingIntent.isActivity();
210 if (isActivity) {
211 try {
212 // The intent we are sending is for the application, which
213 // won't have permission to immediately start an activity after
214 // the user switches to home. We know it is safe to do at this
215 // point, so make sure new activity switches are now allowed.
216 ActivityManagerNative.getDefault().resumeAppSwitches();
217 // Also, notifications can be launched from the lock screen,
218 // so dismiss the lock screen when the activity starts.
219 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
220 } catch (RemoteException e) {
221 }
222 }
223
224 boolean handled = super.onClickHandler(view, pendingIntent, fillInIntent);
225
226 if (isActivity && handled) {
227 // close the shade if it was open
Daniel Sandler11cf1782012-09-27 14:03:08 -0400228 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700229 visibilityChanged(false);
230 }
231 return handled;
232 }
233 };
234
John Spurlock5c454122013-06-17 07:35:46 -0400235 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
236 @Override
237 public void onReceive(Context context, Intent intent) {
238 String action = intent.getAction();
239 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
240 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Kenny Guy2a764942014-04-02 13:29:20 +0100241 updateCurrentProfilesCache();
John Spurlock5c454122013-06-17 07:35:46 -0400242 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
243 userSwitched(mCurrentUserId);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000244 } else if (Intent.ACTION_USER_ADDED.equals(action)) {
Kenny Guy2a764942014-04-02 13:29:20 +0100245 updateCurrentProfilesCache();
John Spurlock5c454122013-06-17 07:35:46 -0400246 }
247 }
248 };
249
Kenny Guy2a764942014-04-02 13:29:20 +0100250 private void updateCurrentProfilesCache() {
251 synchronized (mCurrentProfiles) {
252 mCurrentProfiles.clear();
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000253 if (mUserManager != null) {
Kenny Guy2a764942014-04-02 13:29:20 +0100254 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
255 mCurrentProfiles.put(user.id, user);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000256 }
257 }
258 }
259 }
260
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400261 public void start() {
Jeff Brown98365d72012-08-19 20:30:52 -0700262 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
263 mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
264 mDisplay = mWindowManager.getDefaultDisplay();
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500265
Chris Wren157026f2013-06-28 16:54:01 -0400266 mDreamManager = IDreamManager.Stub.asInterface(
267 ServiceManager.checkService(DreamService.DREAM_SERVICE));
Chris Wren157026f2013-06-28 16:54:01 -0400268 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
269
John Spurlocke677d712014-02-13 12:52:19 -0500270 mSettingsObserver.onChange(false); // set up
Daniel Sandler26cda272012-05-22 15:44:08 -0400271 mContext.getContentResolver().registerContentObserver(
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700272 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
John Spurlocke677d712014-02-13 12:52:19 -0500273 mSettingsObserver);
274 mContext.getContentResolver().registerContentObserver(
275 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
276 mSettingsObserver);
Daniel Sandler26cda272012-05-22 15:44:08 -0400277
Dan Sandlerfd16d562014-02-13 18:43:31 -0800278 mContext.getContentResolver().registerContentObserver(
279 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
280 true,
281 mLockscreenSettingsObserver,
282 UserHandle.USER_ALL);
283
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500284 mBarService = IStatusBarService.Stub.asInterface(
285 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400286
John Spurlockd08de372013-06-24 13:06:08 -0400287 mRecents = getComponent(RecentsComponent.class);
288
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700289 mLocale = mContext.getResources().getConfiguration().locale;
290 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
291
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000292 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
293
Joe Onorato2314aab2010-04-08 16:41:23 -0500294 // Connect in to the status bar manager service
Joe Onorato0cbda992010-05-02 16:28:15 -0700295 StatusBarIconList iconList = new StatusBarIconList();
Joe Onorato75199e32010-05-29 17:22:51 -0400296 ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
297 ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
Joe Onorato66d7d012010-05-14 10:05:10 -0700298 mCommandQueue = new CommandQueue(this, iconList);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400299
Jeff Brown2992ea72011-01-28 22:04:14 -0800300 int[] switches = new int[7];
satokcd7cd292010-11-20 15:46:23 +0900301 ArrayList<IBinder> binders = new ArrayList<IBinder>();
Joe Onorato2314aab2010-04-08 16:41:23 -0500302 try {
Joe Onorato93056472010-09-10 10:30:46 -0400303 mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
satokcd7cd292010-11-20 15:46:23 +0900304 switches, binders);
Joe Onorato2314aab2010-04-08 16:41:23 -0500305 } catch (RemoteException ex) {
306 // If the system process isn't there we're doomed anyway.
307 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400308
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500309 createAndAddWindows();
Joe Onorato0cbda992010-05-02 16:28:15 -0700310
Joe Onoratoe4c7b3f2010-10-30 12:15:03 -0700311 disable(switches[0]);
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700312 setSystemUiVisibility(switches[1], 0xffffffff);
Dianne Hackborn7d049322011-06-14 15:00:32 -0700313 topAppWindowChanged(switches[2] != 0);
satokcd7cd292010-11-20 15:46:23 +0900314 // StatusBarManagerService has a back up of IME token and it's restored here.
Joe Onorato857fd9b2011-01-27 15:08:35 -0800315 setImeWindowStatus(binders.get(0), switches[3], switches[4]);
Jeff Brown2992ea72011-01-28 22:04:14 -0800316 setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
Joe Onorato93056472010-09-10 10:30:46 -0400317
Joe Onorato0cbda992010-05-02 16:28:15 -0700318 // Set up the initial icon state
Joe Onorato75199e32010-05-29 17:22:51 -0400319 int N = iconList.size();
Joe Onorato0cbda992010-05-02 16:28:15 -0700320 int viewIndex = 0;
321 for (int i=0; i<N; i++) {
322 StatusBarIcon icon = iconList.getIcon(i);
323 if (icon != null) {
324 addIcon(iconList.getSlot(i), i, viewIndex, icon);
325 viewIndex++;
326 }
327 }
328
Joe Onorato75199e32010-05-29 17:22:51 -0400329 // Set up the initial notification state
330 N = notificationKeys.size();
Joe Onorato75199e32010-05-29 17:22:51 -0400331 if (N == notifications.size()) {
332 for (int i=0; i<N; i++) {
333 addNotification(notificationKeys.get(i), notifications.get(i));
334 }
335 } else {
336 Log.wtf(TAG, "Notification list length mismatch: keys=" + N
337 + " notifications=" + notifications.size());
338 }
339
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500340 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400341 Log.d(TAG, String.format(
Daniel Sandler6a858c32012-03-12 14:38:58 -0400342 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500343 iconList.size(),
344 switches[0],
345 switches[1],
346 switches[2],
347 switches[3]
348 ));
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400349 }
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400350
Christopher Tate5e08af02012-09-21 17:17:22 -0700351 mCurrentUserId = ActivityManager.getCurrentUser();
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400352
353 IntentFilter filter = new IntentFilter();
354 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000355 filter.addAction(Intent.ACTION_USER_ADDED);
John Spurlock5c454122013-06-17 07:35:46 -0400356 mContext.registerReceiver(mBroadcastReceiver, filter);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000357
Kenny Guy2a764942014-04-02 13:29:20 +0100358 updateCurrentProfilesCache();
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400359 }
360
361 public void userSwitched(int newUserId) {
362 // should be overridden
363 }
364
Kenny Guy2a764942014-04-02 13:29:20 +0100365 public boolean notificationIsForCurrentProfiles(StatusBarNotification n) {
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400366 final int thisUserId = mCurrentUserId;
367 final int notificationUserId = n.getUserId();
368 if (DEBUG && MULTIUSER_DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400369 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400370 n, thisUserId, notificationUserId));
371 }
Kenny Guy2a764942014-04-02 13:29:20 +0100372 synchronized (mCurrentProfiles) {
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000373 return notificationUserId == UserHandle.USER_ALL
Kenny Guy2a764942014-04-02 13:29:20 +0100374 || mCurrentProfiles.get(notificationUserId) != null;
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000375 }
Joe Onorato2314aab2010-04-08 16:41:23 -0500376 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400377
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700378 @Override
379 protected void onConfigurationChanged(Configuration newConfig) {
Daniel Sandler777dcde2013-09-30 10:21:45 -0400380 final Locale locale = mContext.getResources().getConfiguration().locale;
381 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
382 if (! locale.equals(mLocale) || ld != mLayoutDirection) {
383 if (DEBUG) {
384 Log.v(TAG, String.format(
385 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
386 locale, ld));
387 }
388 mLocale = locale;
389 mLayoutDirection = ld;
390 refreshLayout(ld);
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700391 }
392 }
393
Winson Chungc57ccf02011-10-13 15:04:59 -0700394 protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
395 View vetoButton = row.findViewById(R.id.veto);
Chris Wren6d15a362013-08-20 18:46:29 -0400396 if (n.isClearable() || (mInterruptingNotificationEntry != null
397 && mInterruptingNotificationEntry.row == row)) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400398 final String _pkg = n.getPackageName();
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400399 final String _tag = n.getTag();
400 final int _id = n.getId();
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000401 final int _userId = n.getUserId();
Winson Chungc57ccf02011-10-13 15:04:59 -0700402 vetoButton.setOnClickListener(new View.OnClickListener() {
403 public void onClick(View v) {
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700404 // Accessibility feedback
405 v.announceForAccessibility(
406 mContext.getString(R.string.accessibility_notification_dismissed));
Winson Chungc57ccf02011-10-13 15:04:59 -0700407 try {
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000408 mBarService.onNotificationClear(_pkg, _tag, _id, _userId);
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700409
Winson Chungc57ccf02011-10-13 15:04:59 -0700410 } catch (RemoteException ex) {
411 // system process is dead if we're here.
412 }
413 }
414 });
415 vetoButton.setVisibility(View.VISIBLE);
416 } else {
417 vetoButton.setVisibility(View.GONE);
418 }
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700419 vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
Winson Chungc57ccf02011-10-13 15:04:59 -0700420 return vetoButton;
421 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400422
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400423
Jorim Jaggi46739852014-04-15 09:58:24 +0200424 protected void applyLegacyRowBackground(StatusBarNotification sbn,
425 NotificationData.Entry entry) {
426 if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) {
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400427 int version = 0;
428 try {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400429 ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.getPackageName(), 0);
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400430 version = info.targetSdkVersion;
431 } catch (NameNotFoundException ex) {
John Spurlockcd686b52013-06-05 10:13:46 -0400432 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400433 }
434 if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) {
Jorim Jaggi46739852014-04-15 09:58:24 +0200435 entry.row.setBackgroundResource(R.drawable.notification_row_legacy_bg);
436 } else if (version < Build.VERSION_CODES.L) {
437 entry.row.setBackgroundResourceIds(
438 com.android.internal.R.drawable.notification_bg,
439 com.android.internal.R.drawable.notification_bg_dim);
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400440 }
441 }
442 }
443
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400444 private void startApplicationDetailsActivity(String packageName) {
445 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
446 Uri.fromParts("package", packageName, null));
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700447 intent.setComponent(intent.resolveActivity(mContext.getPackageManager()));
Amith Yamasaniea7e9152012-09-24 16:11:18 -0700448 TaskStackBuilder.create(mContext).addNextIntentWithParentStack(intent).startActivities(
449 null, UserHandle.CURRENT);
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400450 }
451
Jim Miller9a720f52012-05-30 03:19:43 -0700452 protected View.OnLongClickListener getNotificationLongClicker() {
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400453 return new View.OnLongClickListener() {
454 @Override
455 public boolean onLongClick(View v) {
456 final String packageNameF = (String) v.getTag();
457 if (packageNameF == null) return false;
Jeff Sharkeyaf232ed2012-04-30 15:19:39 -0700458 if (v.getWindowToken() == null) return false;
Daniel Sandler469e96e2012-05-04 15:56:19 -0400459 mNotificationBlamePopup = new PopupMenu(mContext, v);
460 mNotificationBlamePopup.getMenuInflater().inflate(
461 R.menu.notification_popup_menu,
462 mNotificationBlamePopup.getMenu());
463 mNotificationBlamePopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400464 public boolean onMenuItemClick(MenuItem item) {
465 if (item.getItemId() == R.id.notification_inspect_item) {
466 startApplicationDetailsActivity(packageNameF);
Daniel Sandler11cf1782012-09-27 14:03:08 -0400467 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400468 } else {
469 return false;
470 }
471 return true;
472 }
473 });
Daniel Sandler469e96e2012-05-04 15:56:19 -0400474 mNotificationBlamePopup.show();
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400475
476 return true;
477 }
478 };
479 }
480
Daniel Sandler469e96e2012-05-04 15:56:19 -0400481 public void dismissPopups() {
482 if (mNotificationBlamePopup != null) {
483 mNotificationBlamePopup.dismiss();
484 mNotificationBlamePopup = null;
485 }
486 }
487
Chris Wren51c75102013-07-16 20:49:17 -0400488 public void onHeadsUpDismissed() {
Daniel Sandlerfa7887b2012-03-26 09:43:31 -0400489 }
Michael Jurka7f2668c2012-03-27 07:49:52 -0700490
491 @Override
492 public void toggleRecentApps() {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700493 int msg = MSG_TOGGLE_RECENTS_PANEL;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700494 mHandler.removeMessages(msg);
495 mHandler.sendEmptyMessage(msg);
496 }
497
498 @Override
499 public void preloadRecentApps() {
500 int msg = MSG_PRELOAD_RECENT_APPS;
501 mHandler.removeMessages(msg);
502 mHandler.sendEmptyMessage(msg);
503 }
504
505 @Override
506 public void cancelPreloadRecentApps() {
507 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
508 mHandler.removeMessages(msg);
509 mHandler.sendEmptyMessage(msg);
510 }
511
512 @Override
Jim Millere898ac52012-04-06 17:10:57 -0700513 public void showSearchPanel() {
514 int msg = MSG_OPEN_SEARCH_PANEL;
515 mHandler.removeMessages(msg);
516 mHandler.sendEmptyMessage(msg);
517 }
518
519 @Override
520 public void hideSearchPanel() {
521 int msg = MSG_CLOSE_SEARCH_PANEL;
522 mHandler.removeMessages(msg);
523 mHandler.sendEmptyMessage(msg);
524 }
525
Jim Millere898ac52012-04-06 17:10:57 -0700526 protected abstract WindowManager.LayoutParams getSearchLayoutParams(
527 LayoutParams layoutParams);
528
Jim Millere898ac52012-04-06 17:10:57 -0700529 protected void updateSearchPanel() {
530 // Search Panel
531 boolean visible = false;
532 if (mSearchPanelView != null) {
533 visible = mSearchPanelView.isShowing();
Jeff Brown98365d72012-08-19 20:30:52 -0700534 mWindowManager.removeView(mSearchPanelView);
Jim Millere898ac52012-04-06 17:10:57 -0700535 }
536
537 // Provide SearchPanel with a temporary parent to allow layout params to work.
538 LinearLayout tmpRoot = new LinearLayout(mContext);
539 mSearchPanelView = (SearchPanelView) LayoutInflater.from(mContext).inflate(
540 R.layout.status_bar_search_panel, tmpRoot, false);
541 mSearchPanelView.setOnTouchListener(
542 new TouchOutsideListener(MSG_CLOSE_SEARCH_PANEL, mSearchPanelView));
543 mSearchPanelView.setVisibility(View.GONE);
544
545 WindowManager.LayoutParams lp = getSearchLayoutParams(mSearchPanelView.getLayoutParams());
546
Jeff Brown98365d72012-08-19 20:30:52 -0700547 mWindowManager.addView(mSearchPanelView, lp);
Jim Millere898ac52012-04-06 17:10:57 -0700548 mSearchPanelView.setBar(this);
549 if (visible) {
550 mSearchPanelView.show(true, false);
551 }
552 }
553
Michael Jurkaecc395a2012-03-30 05:31:46 -0700554 protected H createHandler() {
Michael Jurka7f2668c2012-03-27 07:49:52 -0700555 return new H();
556 }
557
Michael Jurka56a57832012-05-14 13:24:43 -0700558 static void sendCloseSystemWindows(Context context, String reason) {
559 if (ActivityManagerNative.isSystemReady()) {
560 try {
561 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
562 } catch (RemoteException e) {
563 }
564 }
565 }
566
Michael Jurkacb2522c2012-04-13 09:32:47 -0700567 protected abstract View getStatusBarView();
568
Michael Jurka80343f62012-10-18 13:13:46 +0200569 protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
570 // additional optimization when we have software system buttons - start loading the recent
571 // tasks on touch down
572 @Override
573 public boolean onTouch(View v, MotionEvent event) {
574 int action = event.getAction() & MotionEvent.ACTION_MASK;
575 if (action == MotionEvent.ACTION_DOWN) {
576 preloadRecentTasksList();
577 } else if (action == MotionEvent.ACTION_CANCEL) {
578 cancelPreloadingRecentTasksList();
579 } else if (action == MotionEvent.ACTION_UP) {
580 if (!v.isPressed()) {
581 cancelPreloadingRecentTasksList();
582 }
583
584 }
585 return false;
586 }
587 };
588
John Spurlockd08de372013-06-24 13:06:08 -0400589 protected void toggleRecentsActivity() {
590 if (mRecents != null) {
Winson Chung4c71aef2014-03-21 15:15:11 -0700591 sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
John Spurlockd08de372013-06-24 13:06:08 -0400592 mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
593 }
594 }
Michael Jurka80343f62012-10-18 13:13:46 +0200595
John Spurlockd08de372013-06-24 13:06:08 -0400596 protected void preloadRecentTasksList() {
597 if (mRecents != null) {
598 mRecents.preloadRecentTasksList();
599 }
Michael Jurka80343f62012-10-18 13:13:46 +0200600 }
601
602 protected void cancelPreloadingRecentTasksList() {
John Spurlockd08de372013-06-24 13:06:08 -0400603 if (mRecents != null) {
604 mRecents.cancelPreloadingRecentTasksList();
605 }
606 }
Michael Jurka80343f62012-10-18 13:13:46 +0200607
John Spurlockd08de372013-06-24 13:06:08 -0400608 protected void closeRecents() {
609 if (mRecents != null) {
610 mRecents.closeRecents();
611 }
Michael Jurka80343f62012-10-18 13:13:46 +0200612 }
613
Chris Wren51c75102013-07-16 20:49:17 -0400614 public abstract void resetHeadsUpDecayTimer();
615
Dan Sandlerfd16d562014-02-13 18:43:31 -0800616 /**
617 * Save the current "public" (locked and secure) state of the lockscreen.
618 */
619 public void setLockscreenPublicMode(boolean publicMode) {
620 mLockscreenPublicMode = publicMode;
Dan Sandlera5e0f412014-01-23 15:11:54 -0500621 }
622
Dan Sandlerfd16d562014-02-13 18:43:31 -0800623 public boolean isLockscreenPublicMode() {
624 return mLockscreenPublicMode;
625 }
626
627 /**
628 * Has the given user chosen to allow their private (full) notifications to be shown even
629 * when the lockscreen is in "public" (secure & locked) mode?
630 */
631 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
632 if (userHandle == UserHandle.USER_ALL) {
633 return true;
634 }
635
636 if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
637 final boolean allowed = 0 != Settings.Secure.getIntForUser(
638 mContext.getContentResolver(),
639 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
640 mUsersAllowingPrivateNotifications.append(userHandle, allowed);
641 return allowed;
642 }
643
644 return mUsersAllowingPrivateNotifications.get(userHandle);
Dan Sandlera5e0f412014-01-23 15:11:54 -0500645 }
646
Michael Jurka7f2668c2012-03-27 07:49:52 -0700647 protected class H extends Handler {
648 public void handleMessage(Message m) {
Michael Jurka80343f62012-10-18 13:13:46 +0200649 Intent intent;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700650 switch (m.what) {
Michael Jurkacb2522c2012-04-13 09:32:47 -0700651 case MSG_TOGGLE_RECENTS_PANEL:
Michael Jurkacb2522c2012-04-13 09:32:47 -0700652 toggleRecentsActivity();
653 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700654 case MSG_CLOSE_RECENTS_PANEL:
John Spurlockd08de372013-06-24 13:06:08 -0400655 closeRecents();
Michael Jurkacb2522c2012-04-13 09:32:47 -0700656 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700657 case MSG_PRELOAD_RECENT_APPS:
Michael Jurka80343f62012-10-18 13:13:46 +0200658 preloadRecentTasksList();
Michael Jurka7f2668c2012-03-27 07:49:52 -0700659 break;
660 case MSG_CANCEL_PRELOAD_RECENT_APPS:
Michael Jurka80343f62012-10-18 13:13:46 +0200661 cancelPreloadingRecentTasksList();
Michael Jurka7f2668c2012-03-27 07:49:52 -0700662 break;
Jim Millere898ac52012-04-06 17:10:57 -0700663 case MSG_OPEN_SEARCH_PANEL:
John Spurlockcd686b52013-06-05 10:13:46 -0400664 if (DEBUG) Log.d(TAG, "opening search panel");
Jim Miller07994402012-05-02 14:22:27 -0700665 if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) {
Jim Millere898ac52012-04-06 17:10:57 -0700666 mSearchPanelView.show(true, true);
Jim Millerad178a42013-10-08 20:03:38 -0700667 onShowSearchPanel();
Jim Millere898ac52012-04-06 17:10:57 -0700668 }
669 break;
670 case MSG_CLOSE_SEARCH_PANEL:
John Spurlockcd686b52013-06-05 10:13:46 -0400671 if (DEBUG) Log.d(TAG, "closing search panel");
Jim Millere898ac52012-04-06 17:10:57 -0700672 if (mSearchPanelView != null && mSearchPanelView.isShowing()) {
673 mSearchPanelView.show(false, true);
Jim Millerad178a42013-10-08 20:03:38 -0700674 onHideSearchPanel();
Jim Millere898ac52012-04-06 17:10:57 -0700675 }
676 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700677 }
678 }
679 }
680
681 public class TouchOutsideListener implements View.OnTouchListener {
682 private int mMsg;
683 private StatusBarPanel mPanel;
684
685 public TouchOutsideListener(int msg, StatusBarPanel panel) {
686 mMsg = msg;
687 mPanel = panel;
688 }
689
690 public boolean onTouch(View v, MotionEvent ev) {
691 final int action = ev.getAction();
692 if (action == MotionEvent.ACTION_OUTSIDE
693 || (action == MotionEvent.ACTION_DOWN
694 && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
695 mHandler.removeMessages(mMsg);
696 mHandler.sendEmptyMessage(mMsg);
697 return true;
698 }
699 return false;
700 }
701 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400702
703 protected void workAroundBadLayerDrawableOpacity(View v) {
704 }
705
Jim Millerad178a42013-10-08 20:03:38 -0700706 protected void onHideSearchPanel() {
707 }
708
709 protected void onShowSearchPanel() {
710 }
711
Chris Wren51c75102013-07-16 20:49:17 -0400712 public boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
Chris Wrenf29c3b42013-11-27 16:30:30 -0500713 return inflateViews(entry, parent, false);
714 }
715
716 public boolean inflateViewsForHeadsUp(NotificationData.Entry entry, ViewGroup parent) {
717 return inflateViews(entry, parent, true);
718 }
719
720 public boolean inflateViews(NotificationData.Entry entry, ViewGroup parent, boolean isHeadsUp) {
Daniel Sandler6a858c32012-03-12 14:38:58 -0400721 int minHeight =
722 mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height);
723 int maxHeight =
724 mContext.getResources().getDimensionPixelSize(R.dimen.notification_max_height);
725 StatusBarNotification sbn = entry.notification;
Chris Wren574a55e2013-07-15 18:48:37 -0400726 RemoteViews contentView = sbn.getNotification().contentView;
727 RemoteViews bigContentView = sbn.getNotification().bigContentView;
Chris Wren8fd39ec2014-02-27 17:43:26 -0500728
729 if (isHeadsUp) {
730 maxHeight =
731 mContext.getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
732 bigContentView = sbn.getNotification().headsUpContentView;
733 }
734
Chris Wren574a55e2013-07-15 18:48:37 -0400735 if (contentView == null) {
Daniel Sandler6a858c32012-03-12 14:38:58 -0400736 return false;
737 }
738
Dan Sandlerf68448e2014-04-24 15:54:16 -0400739 if (DEBUG) {
740 Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion);
741 }
Dan Sandlera5e0f412014-01-23 15:11:54 -0500742
743 Notification publicNotification = sbn.getNotification().publicVersion;
744
Daniel Sandler6a858c32012-03-12 14:38:58 -0400745 // create the row view
746 LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
747 Context.LAYOUT_INFLATER_SERVICE);
Chris Wren51c75102013-07-16 20:49:17 -0400748 ExpandableNotificationRow row = (ExpandableNotificationRow) inflater.inflate(
749 R.layout.status_bar_notification_row, parent, false);
Daniel Sandlerbc5559f2012-04-19 01:08:15 -0400750
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400751 // for blaming (see SwipeHelper.setLongPressListener)
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400752 row.setTag(sbn.getPackageName());
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400753
Daniel Sandler6a858c32012-03-12 14:38:58 -0400754 workAroundBadLayerDrawableOpacity(row);
755 View vetoButton = updateNotificationVetoButton(row, sbn);
756 vetoButton.setContentDescription(mContext.getString(
757 R.string.accessibility_remove_notification));
758
759 // NB: the large icon is now handled entirely by the template
760
761 // bind the click event to the content area
Jorim Jaggibe565df2014-04-28 17:51:23 +0200762 NotificationContentView expanded =
763 (NotificationContentView) row.findViewById(R.id.expanded);
764 NotificationContentView expandedPublic =
765 (NotificationContentView) row.findViewById(R.id.expandedPublic);
Daniel Sandlerbc5559f2012-04-19 01:08:15 -0400766
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200767 row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
Daniel Sandlerbc5559f2012-04-19 01:08:15 -0400768
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400769 PendingIntent contentIntent = sbn.getNotification().contentIntent;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400770 if (contentIntent != null) {
Chris Wrenf29c3b42013-11-27 16:30:30 -0500771 final View.OnClickListener listener = makeClicker(contentIntent,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000772 sbn.getPackageName(), sbn.getTag(), sbn.getId(), isHeadsUp, sbn.getUserId());
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200773 row.setOnClickListener(listener);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400774 } else {
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200775 row.setOnClickListener(null);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400776 }
777
Dan Sandlera5e0f412014-01-23 15:11:54 -0500778 // set up the adaptive layout
Chris Wren574a55e2013-07-15 18:48:37 -0400779 View contentViewLocal = null;
780 View bigContentViewLocal = null;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400781 try {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +0100782 contentViewLocal = contentView.apply(mContext, expanded,
Jorim Jaggi39fa59f2014-02-25 15:38:45 +0100783 mOnClickHandler);
Chris Wren574a55e2013-07-15 18:48:37 -0400784 if (bigContentView != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +0100785 bigContentViewLocal = bigContentView.apply(mContext, expanded,
Jorim Jaggi39fa59f2014-02-25 15:38:45 +0100786 mOnClickHandler);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400787 }
788 }
789 catch (RuntimeException e) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400790 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
John Spurlockcd686b52013-06-05 10:13:46 -0400791 Log.e(TAG, "couldn't inflate view for notification " + ident, e);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400792 return false;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400793 }
794
Chris Wren574a55e2013-07-15 18:48:37 -0400795 if (contentViewLocal != null) {
Dan Sandler6d94aa72014-03-12 00:53:21 -0400796 contentViewLocal.setIsRootNamespace(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200797 expanded.setContractedChild(contentViewLocal);
Daniel Sandler8680bf82012-05-15 16:52:52 -0400798 }
Chris Wren574a55e2013-07-15 18:48:37 -0400799 if (bigContentViewLocal != null) {
Dan Sandler6d94aa72014-03-12 00:53:21 -0400800 bigContentViewLocal.setIsRootNamespace(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200801 expanded.setExpandedChild(bigContentViewLocal);
Daniel Sandler8680bf82012-05-15 16:52:52 -0400802 }
Dan Sandlera5e0f412014-01-23 15:11:54 -0500803
804 PackageManager pm = mContext.getPackageManager();
805
806 // now the public version
807 View publicViewLocal = null;
808 if (publicNotification != null) {
809 try {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +0100810 publicViewLocal = publicNotification.contentView.apply(mContext, expandedPublic,
811 mOnClickHandler);
Dan Sandlera5e0f412014-01-23 15:11:54 -0500812
813 if (publicViewLocal != null) {
Dan Sandler6d94aa72014-03-12 00:53:21 -0400814 publicViewLocal.setIsRootNamespace(true);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200815 expandedPublic.setContractedChild(publicViewLocal);
Dan Sandlera5e0f412014-01-23 15:11:54 -0500816 }
817 }
818 catch (RuntimeException e) {
819 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
820 Log.e(TAG, "couldn't inflate public view for notification " + ident, e);
821 publicViewLocal = null;
822 }
823 }
824
825 if (publicViewLocal == null) {
826 // Add a basic notification template
827 publicViewLocal = LayoutInflater.from(mContext).inflate(
Jorim Jaggi5c2d8462014-03-21 17:37:00 +0100828 com.android.internal.R.layout.notification_template_quantum_base,
829 expandedPublic, true);
Dan Sandlera5e0f412014-01-23 15:11:54 -0500830
831 final TextView title = (TextView) publicViewLocal.findViewById(com.android.internal.R.id.title);
832 try {
833 title.setText(pm.getApplicationLabel(
834 pm.getApplicationInfo(entry.notification.getPackageName(), 0)));
835 } catch (NameNotFoundException e) {
836 title.setText(entry.notification.getPackageName());
837 }
838
839 final ImageView icon = (ImageView) publicViewLocal.findViewById(com.android.internal.R.id.icon);
840
841 final StatusBarIcon ic = new StatusBarIcon(entry.notification.getPackageName(),
842 entry.notification.getUser(),
843 entry.notification.getNotification().icon,
844 entry.notification.getNotification().iconLevel,
845 entry.notification.getNotification().number,
846 entry.notification.getNotification().tickerText);
847
Jorim Jaggi5c2d8462014-03-21 17:37:00 +0100848 Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
849 icon.setImageDrawable(iconDrawable);
850 if (mLegacyNotificationUtil.isGrayscale(iconDrawable)) {
851 icon.setBackgroundResource(
852 com.android.internal.R.drawable.notification_icon_legacy_bg_inset);
853 }
Dan Sandlera5e0f412014-01-23 15:11:54 -0500854
855 final TextView text = (TextView) publicViewLocal.findViewById(com.android.internal.R.id.text);
856 text.setText("Unlock your device to see this notification.");
857
858 // TODO: fill out "time" as well
859 }
860
Daniel Sandler8680bf82012-05-15 16:52:52 -0400861 row.setDrawingCacheEnabled(true);
862
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400863 if (MULTIUSER_DEBUG) {
864 TextView debug = (TextView) row.findViewById(R.id.debug_info);
865 if (debug != null) {
866 debug.setVisibility(View.VISIBLE);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000867 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400868 }
869 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400870 entry.row = row;
Selim Cinek1685e632014-04-08 02:27:49 +0200871 entry.row.setHeightRange(mRowMinHeight, mRowMaxHeight);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200872 entry.row.setOnActivatedListener(this);
Chris Wren574a55e2013-07-15 18:48:37 -0400873 entry.expanded = contentViewLocal;
Dan Sandlera5e0f412014-01-23 15:11:54 -0500874 entry.expandedPublic = publicViewLocal;
Chris Wren574a55e2013-07-15 18:48:37 -0400875 entry.setBigContentView(bigContentViewLocal);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400876
Jorim Jaggi46739852014-04-15 09:58:24 +0200877 applyLegacyRowBackground(sbn, entry);
878
Daniel Sandler6a858c32012-03-12 14:38:58 -0400879 return true;
880 }
881
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000882 public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag,
883 int id, boolean forHun, int userId) {
884 return new NotificationClicker(intent, pkg, tag, id, forHun, userId);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400885 }
886
Chris Wren157026f2013-06-28 16:54:01 -0400887 protected class NotificationClicker implements View.OnClickListener {
Daniel Sandler6a858c32012-03-12 14:38:58 -0400888 private PendingIntent mIntent;
889 private String mPkg;
890 private String mTag;
891 private int mId;
Chris Wrenf29c3b42013-11-27 16:30:30 -0500892 private boolean mIsHeadsUp;
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000893 private int mUserId;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400894
Chris Wrenf29c3b42013-11-27 16:30:30 -0500895 public NotificationClicker(PendingIntent intent, String pkg, String tag, int id,
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000896 boolean forHun, int userId) {
Daniel Sandler6a858c32012-03-12 14:38:58 -0400897 mIntent = intent;
898 mPkg = pkg;
899 mTag = tag;
900 mId = id;
Chris Wrenf29c3b42013-11-27 16:30:30 -0500901 mIsHeadsUp = forHun;
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000902 mUserId = userId;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400903 }
904
905 public void onClick(View v) {
906 try {
907 // The intent we are sending is for the application, which
908 // won't have permission to immediately start an activity after
909 // the user switches to home. We know it is safe to do at this
910 // point, so make sure new activity switches are now allowed.
911 ActivityManagerNative.getDefault().resumeAppSwitches();
912 // Also, notifications can be launched from the lock screen,
913 // so dismiss the lock screen when the activity starts.
914 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
915 } catch (RemoteException e) {
916 }
917
918 if (mIntent != null) {
919 int[] pos = new int[2];
920 v.getLocationOnScreen(pos);
921 Intent overlay = new Intent();
922 overlay.setSourceBounds(
923 new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
924 try {
925 mIntent.send(mContext, 0, overlay);
926 } catch (PendingIntent.CanceledException e) {
927 // the stack trace isn't very helpful here. Just log the exception message.
John Spurlockcd686b52013-06-05 10:13:46 -0400928 Log.w(TAG, "Sending contentIntent failed: " + e);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400929 }
930
Jim Millera999d462013-10-30 13:58:11 -0700931 KeyguardTouchDelegate.getInstance(mContext).dismiss();
Daniel Sandler6a858c32012-03-12 14:38:58 -0400932 }
933
934 try {
Chris Wrenf29c3b42013-11-27 16:30:30 -0500935 if (mIsHeadsUp) {
936 mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
937 }
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000938 mBarService.onNotificationClick(mPkg, mTag, mId, mUserId);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400939 } catch (RemoteException ex) {
940 // system process is dead if we're here.
941 }
942
943 // close the shade if it was open
Daniel Sandler11cf1782012-09-27 14:03:08 -0400944 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400945 visibilityChanged(false);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400946 }
947 }
Chris Wrenf29c3b42013-11-27 16:30:30 -0500948
Daniel Sandler6a858c32012-03-12 14:38:58 -0400949 /**
950 * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
951 * This was added last-minute and is inconsistent with the way the rest of the notifications
952 * are handled, because the notification isn't really cancelled. The lights are just
953 * turned off. If any other notifications happen, the lights will turn back on. Steve says
954 * this is what he wants. (see bug 1131461)
955 */
956 protected void visibilityChanged(boolean visible) {
957 if (mPanelSlightlyVisible != visible) {
958 mPanelSlightlyVisible = visible;
959 try {
Christoph Studer760ea552014-03-21 13:10:21 +0100960 if (visible) {
961 mBarService.onPanelRevealed();
962 } else {
963 mBarService.onPanelHidden();
964 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400965 } catch (RemoteException ex) {
966 // Won't fail unless the world has ended.
967 }
968 }
969 }
970
Chris Wren0c8275b2012-05-08 13:36:48 -0400971 /**
972 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
973 * about the failure.
974 *
975 * WARNING: this will call back into us. Don't hold any locks.
976 */
977 void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
978 removeNotification(key);
979 try {
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000980 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
981 n.getInitialPid(), message, n.getUserId());
Chris Wren0c8275b2012-05-08 13:36:48 -0400982 } catch (RemoteException ex) {
983 // The end is nigh.
984 }
985 }
986
987 protected StatusBarNotification removeNotificationViews(IBinder key) {
988 NotificationData.Entry entry = mNotificationData.remove(key);
989 if (entry == null) {
John Spurlockcd686b52013-06-05 10:13:46 -0400990 Log.w(TAG, "removeNotification for unknown key: " + key);
Chris Wren0c8275b2012-05-08 13:36:48 -0400991 return null;
992 }
993 // Remove the expanded view.
994 ViewGroup rowParent = (ViewGroup)entry.row.getParent();
995 if (rowParent != null) rowParent.removeView(entry.row);
Jorim Jaggi251957d2014-04-09 04:24:09 +0200996 updateRowStates();
Chris Wren0c8275b2012-05-08 13:36:48 -0400997 updateNotificationIcons();
998
999 return entry.notification;
1000 }
1001
Chris Wrenf0048ce2013-08-07 16:43:43 -04001002 protected NotificationData.Entry createNotificationViews(IBinder key,
Chris Wren0c8275b2012-05-08 13:36:48 -04001003 StatusBarNotification notification) {
1004 if (DEBUG) {
Chris Wrenf0048ce2013-08-07 16:43:43 -04001005 Log.d(TAG, "createNotificationViews(key=" + key + ", notification=" + notification);
Chris Wren0c8275b2012-05-08 13:36:48 -04001006 }
1007 // Construct the icon.
1008 final StatusBarIconView iconView = new StatusBarIconView(mContext,
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001009 notification.getPackageName() + "/0x" + Integer.toHexString(notification.getId()),
John Spurlocke6f0a712013-09-03 16:23:49 -04001010 notification.getNotification());
Chris Wren0c8275b2012-05-08 13:36:48 -04001011 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
1012
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001013 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001014 notification.getUser(),
1015 notification.getNotification().icon,
1016 notification.getNotification().iconLevel,
1017 notification.getNotification().number,
1018 notification.getNotification().tickerText);
Chris Wren0c8275b2012-05-08 13:36:48 -04001019 if (!iconView.set(ic)) {
1020 handleNotificationError(key, notification, "Couldn't create icon: " + ic);
1021 return null;
1022 }
1023 // Construct the expanded view.
1024 NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
Selim Cinekb6d85eb2014-03-28 20:21:01 +01001025 if (!inflateViews(entry, mStackScroller)) {
Chris Wren0c8275b2012-05-08 13:36:48 -04001026 handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
1027 + notification);
1028 return null;
1029 }
Chris Wrenf0048ce2013-08-07 16:43:43 -04001030 return entry;
1031 }
Chris Wren0c8275b2012-05-08 13:36:48 -04001032
Chris Wrenf0048ce2013-08-07 16:43:43 -04001033 protected void addNotificationViews(NotificationData.Entry entry) {
Chris Wren0c8275b2012-05-08 13:36:48 -04001034 // Add the expanded view and icon.
1035 int pos = mNotificationData.add(entry);
1036 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -04001037 Log.d(TAG, "addNotificationViews: added at " + pos);
Chris Wren0c8275b2012-05-08 13:36:48 -04001038 }
Jorim Jaggi251957d2014-04-09 04:24:09 +02001039 updateRowStates();
Chris Wren0c8275b2012-05-08 13:36:48 -04001040 updateNotificationIcons();
Chris Wrenf0048ce2013-08-07 16:43:43 -04001041 }
Chris Wren0c8275b2012-05-08 13:36:48 -04001042
Chris Wrenf0048ce2013-08-07 16:43:43 -04001043 private void addNotificationViews(IBinder key, StatusBarNotification notification) {
1044 addNotificationViews(createNotificationViews(key, notification));
Chris Wren0c8275b2012-05-08 13:36:48 -04001045 }
1046
Jorim Jaggi251957d2014-04-09 04:24:09 +02001047 /**
Jorim Jaggid4a57442014-04-10 02:45:55 +02001048 * @return The number of notifications we show on Keyguard.
1049 */
1050 protected abstract int getMaxKeyguardNotifications();
1051
1052 /**
Jorim Jaggi251957d2014-04-09 04:24:09 +02001053 * Updates expanded, dimmed and locked states of notification rows.
1054 */
1055 protected void updateRowStates() {
Jorim Jaggid4a57442014-04-10 02:45:55 +02001056 int maxKeyguardNotifications = getMaxKeyguardNotifications();
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001057 mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
Jorim Jaggi649b7082014-04-03 13:15:18 +02001058 int n = mNotificationData.size();
Jorim Jaggid4a57442014-04-10 02:45:55 +02001059 int visibleNotifications = 0;
1060 for (int i = n-1; i >= 0; i--) {
Chris Wren8fd12652012-05-09 21:25:57 -04001061 NotificationData.Entry entry = mNotificationData.get(i);
Jorim Jaggi649b7082014-04-03 13:15:18 +02001062 if (mOnKeyguard) {
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001063 entry.row.setExpansionDisabled(true);
Chris Wren3ddab0d2012-08-02 16:52:21 -04001064 } else {
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001065 entry.row.setExpansionDisabled(false);
Jorim Jaggi649b7082014-04-03 13:15:18 +02001066 if (!entry.row.isUserLocked()) {
1067 boolean top = (i == n-1);
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001068 entry.row.setSystemExpanded(top);
Jorim Jaggi649b7082014-04-03 13:15:18 +02001069 }
Chris Wren8fd12652012-05-09 21:25:57 -04001070 }
Jorim Jaggi251957d2014-04-09 04:24:09 +02001071 entry.row.setDimmed(mOnKeyguard);
1072 entry.row.setLocked(mOnKeyguard);
Jorim Jaggid4a57442014-04-10 02:45:55 +02001073 boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
1074 if (mOnKeyguard && (visibleNotifications >= maxKeyguardNotifications
1075 || !showOnKeyguard)) {
1076 entry.row.setVisibility(View.GONE);
1077 if (showOnKeyguard) {
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001078 mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
Jorim Jaggid4a57442014-04-10 02:45:55 +02001079 }
1080 } else {
Selim Cinek572bbd42014-04-25 16:43:27 +02001081 if (entry.row.getVisibility() == View.GONE) {
1082 // notify the scroller of a child addition
1083 mStackScroller.generateAddAnimation(entry.row);
1084 }
Jorim Jaggid4a57442014-04-10 02:45:55 +02001085 entry.row.setVisibility(View.VISIBLE);
1086 visibleNotifications++;
1087 }
Chris Wren8fd12652012-05-09 21:25:57 -04001088 }
Jorim Jaggid4a57442014-04-10 02:45:55 +02001089
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001090 if (mOnKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
Jorim Jaggid4a57442014-04-10 02:45:55 +02001091 mKeyguardIconOverflowContainer.setVisibility(View.VISIBLE);
1092 } else {
1093 mKeyguardIconOverflowContainer.setVisibility(View.GONE);
1094 }
1095 }
1096
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001097 @Override
1098 public void onActivated(View view) {
1099 int n = mNotificationData.size();
1100 for (int i = 0; i < n; i++) {
1101 NotificationData.Entry entry = mNotificationData.get(i);
1102 if (entry.row.getVisibility() != View.GONE) {
1103 if (view == entry.row) {
1104 entry.row.getActivator().activate();
1105 } else {
1106 entry.row.getActivator().activateInverse();
1107 }
1108 }
1109 }
1110 if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) {
1111 if (view == mKeyguardIconOverflowContainer) {
1112 mKeyguardIconOverflowContainer.getActivator().activate();
1113 } else {
1114 mKeyguardIconOverflowContainer.getActivator().activateInverse();
1115 }
1116 }
1117 }
1118
1119 @Override
1120 public void onReset(View view) {
1121 int n = mNotificationData.size();
1122 for (int i = 0; i < n; i++) {
1123 NotificationData.Entry entry = mNotificationData.get(i);
1124 if (entry.row.getVisibility() != View.GONE) {
1125 entry.row.getActivator().reset();
1126 }
1127 }
1128 if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) {
1129 mKeyguardIconOverflowContainer.getActivator().reset();
1130 }
1131 }
1132
Jorim Jaggid4a57442014-04-10 02:45:55 +02001133 private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
1134 return sbn.getNotification().priority >= Notification.PRIORITY_LOW;
Chris Wren8fd12652012-05-09 21:25:57 -04001135 }
1136
John Spurlocke677d712014-02-13 12:52:19 -05001137 protected void setZenMode(int mode) {
John Spurlock110e5b12014-02-27 13:09:35 -05001138 if (!isDeviceProvisioned()) return;
John Spurlocke677d712014-02-13 12:52:19 -05001139 mZenMode = mode;
John Spurlocke677d712014-02-13 12:52:19 -05001140 updateNotificationIcons();
1141 }
1142
Chris Wren0c8275b2012-05-08 13:36:48 -04001143 protected abstract void haltTicker();
1144 protected abstract void setAreThereNotifications();
1145 protected abstract void updateNotificationIcons();
1146 protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime);
1147 protected abstract void updateExpandedViewPos(int expandedPosition);
Chris Wren8fd12652012-05-09 21:25:57 -04001148 protected abstract int getExpandedViewMaxHeight();
Jim Millerb4238e02012-05-14 15:26:20 -07001149 protected abstract boolean shouldDisableNavbarGestures();
Chris Wren0c8275b2012-05-08 13:36:48 -04001150
1151 protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
Daniel Sandler26cda272012-05-22 15:44:08 -04001152 return parent != null && parent.indexOfChild(entry.row) == 0;
Chris Wren0c8275b2012-05-08 13:36:48 -04001153 }
1154
1155 public void updateNotification(IBinder key, StatusBarNotification notification) {
John Spurlockcd686b52013-06-05 10:13:46 -04001156 if (DEBUG) Log.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
Chris Wren0c8275b2012-05-08 13:36:48 -04001157
1158 final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
1159 if (oldEntry == null) {
John Spurlockcd686b52013-06-05 10:13:46 -04001160 Log.w(TAG, "updateNotification for unknown key: " + key);
Chris Wren0c8275b2012-05-08 13:36:48 -04001161 return;
1162 }
1163
1164 final StatusBarNotification oldNotification = oldEntry.notification;
1165
1166 // XXX: modify when we do something more intelligent with the two content views
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001167 final RemoteViews oldContentView = oldNotification.getNotification().contentView;
1168 final RemoteViews contentView = notification.getNotification().contentView;
1169 final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
1170 final RemoteViews bigContentView = notification.getNotification().bigContentView;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001171 final RemoteViews oldHeadsUpContentView = oldNotification.getNotification().headsUpContentView;
1172 final RemoteViews headsUpContentView = notification.getNotification().headsUpContentView;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001173 final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
1174 final RemoteViews oldPublicContentView = oldPublicNotification != null
1175 ? oldPublicNotification.contentView : null;
1176 final Notification publicNotification = notification.getNotification().publicVersion;
1177 final RemoteViews publicContentView = publicNotification != null
1178 ? publicNotification.contentView : null;
Chris Wren0c8275b2012-05-08 13:36:48 -04001179
1180 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -04001181 Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
Chris Wren0c8275b2012-05-08 13:36:48 -04001182 + " ongoing=" + oldNotification.isOngoing()
1183 + " expanded=" + oldEntry.expanded
1184 + " contentView=" + oldContentView
Chris Wrene9e37722012-05-11 17:40:14 -04001185 + " bigContentView=" + oldBigContentView
Dan Sandlera5e0f412014-01-23 15:11:54 -05001186 + " publicView=" + oldPublicContentView
Chris Wren0c8275b2012-05-08 13:36:48 -04001187 + " rowParent=" + oldEntry.row.getParent());
John Spurlockcd686b52013-06-05 10:13:46 -04001188 Log.d(TAG, "new notification: when=" + notification.getNotification().when
Chris Wren0c8275b2012-05-08 13:36:48 -04001189 + " ongoing=" + oldNotification.isOngoing()
Chris Wrene9e37722012-05-11 17:40:14 -04001190 + " contentView=" + contentView
Dan Sandlera5e0f412014-01-23 15:11:54 -05001191 + " bigContentView=" + bigContentView
1192 + " publicView=" + publicContentView);
Chris Wren0c8275b2012-05-08 13:36:48 -04001193 }
1194
1195 // Can we just reapply the RemoteViews in place? If when didn't change, the order
1196 // didn't change.
Chris Wrene9e37722012-05-11 17:40:14 -04001197
1198 // 1U is never null
Chris Wren0c8275b2012-05-08 13:36:48 -04001199 boolean contentsUnchanged = oldEntry.expanded != null
Chris Wren0c8275b2012-05-08 13:36:48 -04001200 && contentView.getPackage() != null
1201 && oldContentView.getPackage() != null
1202 && oldContentView.getPackage().equals(contentView.getPackage())
1203 && oldContentView.getLayoutId() == contentView.getLayoutId();
Chris Wrene9e37722012-05-11 17:40:14 -04001204 // large view may be null
1205 boolean bigContentsUnchanged =
Chris Wren574a55e2013-07-15 18:48:37 -04001206 (oldEntry.getBigContentView() == null && bigContentView == null)
1207 || ((oldEntry.getBigContentView() != null && bigContentView != null)
Chris Wrene9e37722012-05-11 17:40:14 -04001208 && bigContentView.getPackage() != null
1209 && oldBigContentView.getPackage() != null
1210 && oldBigContentView.getPackage().equals(bigContentView.getPackage())
1211 && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
Chris Wren8fd39ec2014-02-27 17:43:26 -05001212 boolean headsUpContentsUnchanged =
1213 (oldHeadsUpContentView == null && headsUpContentView == null)
1214 || ((oldHeadsUpContentView != null && headsUpContentView != null)
1215 && headsUpContentView.getPackage() != null
1216 && oldHeadsUpContentView.getPackage() != null
1217 && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
1218 && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
Dan Sandlera5e0f412014-01-23 15:11:54 -05001219 boolean publicUnchanged =
1220 (oldPublicContentView == null && publicContentView == null)
1221 || ((oldPublicContentView != null && publicContentView != null)
1222 && publicContentView.getPackage() != null
1223 && oldPublicContentView.getPackage() != null
1224 && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
1225 && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
1226
Chris Wren0c8275b2012-05-08 13:36:48 -04001227 ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
Dan Sandlera5e0f412014-01-23 15:11:54 -05001228 boolean orderUnchanged =
1229 notification.getNotification().when == oldNotification.getNotification().when
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001230 && notification.getScore() == oldNotification.getScore();
Chris Wren0c8275b2012-05-08 13:36:48 -04001231 // score now encompasses/supersedes isOngoing()
1232
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001233 boolean updateTicker = notification.getNotification().tickerText != null
1234 && !TextUtils.equals(notification.getNotification().tickerText,
1235 oldEntry.notification.getNotification().tickerText);
Chris Wren0c8275b2012-05-08 13:36:48 -04001236 boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
Chris Wren8fd39ec2014-02-27 17:43:26 -05001237 if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged
Dan Sandlera5e0f412014-01-23 15:11:54 -05001238 && (orderUnchanged || isTopAnyway)) {
John Spurlockcd686b52013-06-05 10:13:46 -04001239 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
Chris Wren0c8275b2012-05-08 13:36:48 -04001240 oldEntry.notification = notification;
1241 try {
Chris Wrene03f4e12013-08-08 16:48:48 -04001242 updateNotificationViews(oldEntry, notification);
1243
1244 if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null
1245 && oldNotification == mInterruptingNotificationEntry.notification) {
1246 if (!shouldInterrupt(notification)) {
1247 if (DEBUG) Log.d(TAG, "no longer interrupts!");
1248 mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
1249 } else {
1250 if (DEBUG) Log.d(TAG, "updating the current heads up:" + notification);
1251 mInterruptingNotificationEntry.notification = notification;
Chris Wrenf29c3b42013-11-27 16:30:30 -05001252 updateHeadsUpViews(mInterruptingNotificationEntry, notification);
Chris Wrene03f4e12013-08-08 16:48:48 -04001253 }
Chris Wrene9e37722012-05-11 17:40:14 -04001254 }
Chris Wrene03f4e12013-08-08 16:48:48 -04001255
Chris Wren0c8275b2012-05-08 13:36:48 -04001256 // Update the icon.
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001257 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001258 notification.getUser(),
1259 notification.getNotification().icon, notification.getNotification().iconLevel,
1260 notification.getNotification().number,
1261 notification.getNotification().tickerText);
Chris Wren0c8275b2012-05-08 13:36:48 -04001262 if (!oldEntry.icon.set(ic)) {
1263 handleNotificationError(key, notification, "Couldn't update icon: " + ic);
1264 return;
1265 }
Jorim Jaggi251957d2014-04-09 04:24:09 +02001266 updateRowStates();
Chris Wren0c8275b2012-05-08 13:36:48 -04001267 }
1268 catch (RuntimeException e) {
1269 // It failed to add cleanly. Log, and remove the view from the panel.
John Spurlockcd686b52013-06-05 10:13:46 -04001270 Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
Chris Wren0c8275b2012-05-08 13:36:48 -04001271 removeNotificationViews(key);
1272 addNotificationViews(key, notification);
1273 }
1274 } else {
John Spurlockcd686b52013-06-05 10:13:46 -04001275 if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
1276 if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
1277 if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
1278 if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
Chris Wren0c8275b2012-05-08 13:36:48 -04001279 removeNotificationViews(key);
Chris Wrene03f4e12013-08-08 16:48:48 -04001280 addNotificationViews(key, notification); // will also replace the heads up
Selim Cinek1685e632014-04-08 02:27:49 +02001281 final NotificationData.Entry newEntry = mNotificationData.findByKey(key);
1282 final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion();
1283 if (userChangedExpansion) {
1284 boolean userExpanded = oldEntry.row.isUserExpanded();
Selim Cinek1685e632014-04-08 02:27:49 +02001285 newEntry.row.setUserExpanded(userExpanded);
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001286 newEntry.row.applyExpansionToLayout();
Chris Wrenc9433ecb2012-06-27 17:37:03 -04001287 }
Chris Wren0c8275b2012-05-08 13:36:48 -04001288 }
1289
1290 // Update the veto button accordingly (and as a result, whether this row is
1291 // swipe-dismissable)
1292 updateNotificationVetoButton(oldEntry.row, notification);
1293
John Spurlock61e36832012-09-10 09:43:27 -04001294 // Is this for you?
Kenny Guy2a764942014-04-02 13:29:20 +01001295 boolean isForCurrentUser = notificationIsForCurrentProfiles(notification);
John Spurlockcd686b52013-06-05 10:13:46 -04001296 if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
John Spurlock61e36832012-09-10 09:43:27 -04001297
Chris Wren0c8275b2012-05-08 13:36:48 -04001298 // Restart the ticker if it's still running
John Spurlock61e36832012-09-10 09:43:27 -04001299 if (updateTicker && isForCurrentUser) {
Chris Wren0c8275b2012-05-08 13:36:48 -04001300 haltTicker();
1301 tick(key, notification, false);
1302 }
1303
1304 // Recalculate the position of the sliding windows and the titles.
1305 setAreThereNotifications();
1306 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
Chris Wrene03f4e12013-08-08 16:48:48 -04001307 }
Chris Wren0c8275b2012-05-08 13:36:48 -04001308
Chris Wrene03f4e12013-08-08 16:48:48 -04001309 private void updateNotificationViews(NotificationData.Entry entry,
1310 StatusBarNotification notification) {
Chris Wrenf29c3b42013-11-27 16:30:30 -05001311 updateNotificationViews(entry, notification, false);
1312 }
1313
1314 private void updateHeadsUpViews(NotificationData.Entry entry,
1315 StatusBarNotification notification) {
1316 updateNotificationViews(entry, notification, true);
1317 }
1318
1319 private void updateNotificationViews(NotificationData.Entry entry,
1320 StatusBarNotification notification, boolean isHeadsUp) {
Chris Wrene03f4e12013-08-08 16:48:48 -04001321 final RemoteViews contentView = notification.getNotification().contentView;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001322 final RemoteViews bigContentView = isHeadsUp
1323 ? notification.getNotification().headsUpContentView
1324 : notification.getNotification().bigContentView;
John Spurlock70cf4c12014-02-13 09:39:14 -05001325 final Notification publicVersion = notification.getNotification().publicVersion;
1326 final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView
1327 : null;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001328
Chris Wrene03f4e12013-08-08 16:48:48 -04001329 // Reapply the RemoteViews
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001330 contentView.reapply(mContext, entry.expanded, mOnClickHandler);
Chris Wrene03f4e12013-08-08 16:48:48 -04001331 if (bigContentView != null && entry.getBigContentView() != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001332 bigContentView.reapply(mContext, entry.getBigContentView(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01001333 mOnClickHandler);
Chris Wrene03f4e12013-08-08 16:48:48 -04001334 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001335 if (publicContentView != null && entry.getPublicContentView() != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001336 publicContentView.reapply(mContext, entry.getPublicContentView(), mOnClickHandler);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001337 }
Chris Wrene03f4e12013-08-08 16:48:48 -04001338 // update the contentIntent
1339 final PendingIntent contentIntent = notification.getNotification().contentIntent;
1340 if (contentIntent != null) {
1341 final View.OnClickListener listener = makeClicker(contentIntent,
Chris Wrenf29c3b42013-11-27 16:30:30 -05001342 notification.getPackageName(), notification.getTag(), notification.getId(),
Kenny Guy3a7c4a52014-03-03 18:24:03 +00001343 isHeadsUp, notification.getUserId());
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001344 entry.row.setOnClickListener(listener);
Chris Wrene03f4e12013-08-08 16:48:48 -04001345 } else {
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001346 entry.row.setOnClickListener(null);
Chris Wren0c8275b2012-05-08 13:36:48 -04001347 }
Jorim Jaggibe565df2014-04-28 17:51:23 +02001348 entry.row.notifyContentUpdated();
Chris Wren0c8275b2012-05-08 13:36:48 -04001349 }
John Spurlock36231282012-06-23 17:11:27 -04001350
Chris Wrene97f90b2013-08-07 17:39:35 -04001351 protected void notifyHeadsUpScreenOn(boolean screenOn) {
1352 if (!screenOn && mInterruptingNotificationEntry != null) {
1353 mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
1354 }
1355 }
1356
Chris Wren51c75102013-07-16 20:49:17 -04001357 protected boolean shouldInterrupt(StatusBarNotification sbn) {
1358 Notification notification = sbn.getNotification();
1359 // some predicates to make the boolean logic legible
1360 boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
1361 || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
1362 || notification.sound != null
1363 || notification.vibrate != null;
1364 boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
1365 boolean isFullscreen = notification.fullScreenIntent != null;
Chris Wren22ae46e2014-02-26 18:08:09 -05001366 boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
Chris Wren51c75102013-07-16 20:49:17 -04001367 boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
1368 Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
1369
Jim Millera999d462013-10-30 13:58:11 -07001370 final KeyguardTouchDelegate keyguard = KeyguardTouchDelegate.getInstance(mContext);
Chris Wren22ae46e2014-02-26 18:08:09 -05001371 boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
Chris Wren51c75102013-07-16 20:49:17 -04001372 && isAllowed
1373 && mPowerManager.isScreenOn()
Jorim Jaggi5cf17872014-03-26 18:31:48 +01001374 && !keyguard.isShowingAndNotOccluded()
Jim Millera999d462013-10-30 13:58:11 -07001375 && !keyguard.isInputRestricted();
Chris Wren157026f2013-06-28 16:54:01 -04001376 try {
1377 interrupt = interrupt && !mDreamManager.isDreaming();
1378 } catch (RemoteException e) {
1379 Log.d(TAG, "failed to query dream manager", e);
1380 }
Chris Wren51c75102013-07-16 20:49:17 -04001381 if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
Chris Wren157026f2013-06-28 16:54:01 -04001382 return interrupt;
1383 }
1384
John Spurlock36231282012-06-23 17:11:27 -04001385 // Q: What kinds of notifications should show during setup?
1386 // A: Almost none! Only things coming from the system (package is "android") that also
1387 // have special "kind" tags marking them as relevant for setup (see below).
1388 protected boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
John Spurlockfd7f1e02014-03-18 16:41:57 -04001389 return "android".equals(sbn.getPackageName())
1390 && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
John Spurlock36231282012-06-23 17:11:27 -04001391 }
John Spurlock67ad3682012-06-26 17:42:00 -04001392
1393 public boolean inKeyguardRestrictedInputMode() {
Jim Millera999d462013-10-30 13:58:11 -07001394 return KeyguardTouchDelegate.getInstance(mContext).isInputRestricted();
John Spurlock67ad3682012-06-26 17:42:00 -04001395 }
John Spurlockad3e6cb2013-04-30 08:47:43 -04001396
John Spurlockcfc359a2013-09-05 10:42:03 -04001397 public void setInteracting(int barWindow, boolean interacting) {
John Spurlockad3e6cb2013-04-30 08:47:43 -04001398 // hook for subclasses
1399 }
John Spurlock5c454122013-06-17 07:35:46 -04001400
1401 public void destroy() {
1402 if (mSearchPanelView != null) {
1403 mWindowManager.removeViewImmediate(mSearchPanelView);
1404 }
1405 mContext.unregisterReceiver(mBroadcastReceiver);
1406 }
Joe Onorato2314aab2010-04-08 16:41:23 -05001407}