blob: 9f86475fd1265c7504c91c15e9858f5d86fe7968 [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
Dan Sandler4247a5c2014-07-23 15:58:08 -040019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.TimeInterpolator;
Christopher Tate5e08af02012-09-21 17:17:22 -070022import android.app.ActivityManager;
Daniel Sandler6a858c32012-03-12 14:38:58 -040023import android.app.ActivityManagerNative;
Chris Wren51c75102013-07-16 20:49:17 -040024import android.app.Notification;
Chris Wrencd8f4f72014-08-27 18:48:13 -040025import android.app.NotificationManager;
Daniel Sandler6a858c32012-03-12 14:38:58 -040026import android.app.PendingIntent;
Adrian Roos497ab022015-02-10 20:49:33 +010027import android.app.RemoteInput;
Adam Powell0fc5b2b2012-07-18 18:20:29 -070028import android.app.TaskStackBuilder;
Adrian Roosbd3409c2014-08-12 18:21:09 +020029import android.app.admin.DevicePolicyManager;
Daniel Sandlerb9301c32012-08-14 15:08:24 -040030import android.content.BroadcastReceiver;
Christoph Studer60748e72014-05-22 16:51:41 +020031import android.content.ComponentName;
Joe Onorato808182d2010-07-09 18:52:06 -040032import android.content.Context;
Daniel Sandler6a858c32012-03-12 14:38:58 -040033import android.content.Intent;
Daniel Sandlerb9301c32012-08-14 15:08:24 -040034import android.content.IntentFilter;
Daniel Sandler96fd7c12012-03-30 16:37:36 -040035import android.content.pm.ApplicationInfo;
Dan Sandlera5e0f412014-01-23 15:11:54 -050036import android.content.pm.PackageManager;
Daniel Sandler96fd7c12012-03-30 16:37:36 -040037import android.content.pm.PackageManager.NameNotFoundException;
Dan Sandlerf4db75c2014-09-03 18:02:31 +020038import android.content.pm.ResolveInfo;
Kenny Guy3a7c4a52014-03-03 18:24:03 +000039import android.content.pm.UserInfo;
John Spurlockde84f0e2013-06-12 12:41:00 -040040import android.content.res.Configuration;
Chris Wrencd8f4f72014-08-27 18:48:13 -040041import android.content.res.Resources;
Daniel Sandler26cda272012-05-22 15:44:08 -040042import android.database.ContentObserver;
Jorim Jaggia1eeade2014-09-08 22:34:39 +020043import android.graphics.PorterDuff;
Jorim Jaggi39fa59f2014-02-25 15:38:45 +010044import android.graphics.drawable.Drawable;
Jorim Jaggi8de4311c2014-08-11 22:36:20 +020045import android.os.AsyncTask;
Daniel Sandler96fd7c12012-03-30 16:37:36 -040046import android.os.Build;
Michael Jurka7f2668c2012-03-27 07:49:52 -070047import android.os.Handler;
Joe Onorato808182d2010-07-09 18:52:06 -040048import android.os.IBinder;
Michael Jurka7f2668c2012-03-27 07:49:52 -070049import android.os.Message;
Chris Wren157026f2013-06-28 16:54:01 -040050import android.os.PowerManager;
Joe Onorato808182d2010-07-09 18:52:06 -040051import android.os.RemoteException;
52import android.os.ServiceManager;
Adrian Roos497ab022015-02-10 20:49:33 +010053import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070054import android.os.UserHandle;
Kenny Guy3a7c4a52014-03-03 18:24:03 +000055import android.os.UserManager;
Daniel Sandlerf7a19562012-04-04 14:04:21 -040056import android.provider.Settings;
Chris Wren157026f2013-06-28 16:54:01 -040057import android.service.dreams.DreamService;
58import android.service.dreams.IDreamManager;
Christoph Studer60748e72014-05-22 16:51:41 +020059import android.service.notification.NotificationListenerService;
Christoph Studerd0694b62014-06-04 16:36:01 +020060import android.service.notification.NotificationListenerService.RankingMap;
John Spurlockde84f0e2013-06-12 12:41:00 -040061import android.service.notification.StatusBarNotification;
Chris Wren0c8275b2012-05-08 13:36:48 -040062import android.text.TextUtils;
Joe Onorato808182d2010-07-09 18:52:06 -040063import android.util.Log;
Dan Sandlere163a642015-03-01 22:58:32 -050064import android.util.Slog;
Kenny Guy3a7c4a52014-03-03 18:24:03 +000065import android.util.SparseArray;
Dan Sandlerfd16d562014-02-13 18:43:31 -080066import android.util.SparseBooleanArray;
Daniel Sandlerc638c1e2011-08-24 16:19:23 -070067import android.view.Display;
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -050068import android.view.IWindowManager;
Michael Jurka7f2668c2012-03-27 07:49:52 -070069import android.view.LayoutInflater;
70import android.view.MotionEvent;
Joe Onorato808182d2010-07-09 18:52:06 -040071import android.view.View;
Dan Sandler4247a5c2014-07-23 15:58:08 -040072import android.view.ViewAnimationUtils;
Daniel Sandler6a858c32012-03-12 14:38:58 -040073import android.view.ViewGroup;
Christoph Studer4da84cd2014-10-21 17:24:20 +020074import android.view.ViewParent;
Joe Onorato808182d2010-07-09 18:52:06 -040075import android.view.WindowManager;
Michael Jurka80343f62012-10-18 13:13:46 +020076import android.view.WindowManagerGlobal;
Selim Cineka8fefa52014-09-08 16:10:50 +020077import android.view.accessibility.AccessibilityManager;
Dan Sandler4247a5c2014-07-23 15:58:08 -040078import android.view.animation.AnimationUtils;
Dan Sandler18bb4f92014-07-14 16:48:27 -040079import android.widget.DateTimeView;
Chris Wren0c8275b2012-05-08 13:36:48 -040080import android.widget.ImageView;
Michael Jurkaa600fd92012-06-25 15:57:05 -070081import android.widget.RemoteViews;
Daniel Sandlerb9301c32012-08-14 15:08:24 -040082import android.widget.TextView;
Kenny Guydae30d52015-04-01 19:11:35 +010083import android.widget.Toast;
Joe Onorato808182d2010-07-09 18:52:06 -040084
John Spurlockde84f0e2013-06-12 12:41:00 -040085import com.android.internal.statusbar.IStatusBarService;
86import com.android.internal.statusbar.StatusBarIcon;
87import com.android.internal.statusbar.StatusBarIconList;
Dan Sandler26e81cf2014-05-06 10:01:27 -040088import com.android.internal.util.NotificationColorUtil;
Chris Wrencd8f4f72014-08-27 18:48:13 -040089import com.android.internal.widget.LockPatternUtils;
John Spurlockde84f0e2013-06-12 12:41:00 -040090import com.android.systemui.R;
John Spurlockd08de372013-06-24 13:06:08 -040091import com.android.systemui.RecentsComponent;
Dan Sandler4247a5c2014-07-23 15:58:08 -040092import com.android.systemui.SwipeHelper;
John Spurlockde84f0e2013-06-12 12:41:00 -040093import com.android.systemui.SystemUI;
Jorim Jaggid61f2272014-12-19 20:35:35 +010094import com.android.systemui.recents.Recents;
Christoph Studer37fe6932014-05-26 13:10:30 +020095import com.android.systemui.statusbar.NotificationData.Entry;
Selim Cinek92d892c2014-09-11 15:11:00 +020096import com.android.systemui.statusbar.phone.NavigationBarView;
Selim Cinek25fd4e2b2015-02-20 17:46:07 +010097import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jorim Jaggi8de4311c2014-08-11 22:36:20 +020098import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
Selim Cinekb8f09cf2015-03-16 17:09:28 -070099import com.android.systemui.statusbar.policy.HeadsUpManager;
Jorim Jaggi85dc23c2014-09-08 14:42:29 +0200100import com.android.systemui.statusbar.policy.PreviewInflater;
Adrian Roos497ab022015-02-10 20:49:33 +0100101import com.android.systemui.statusbar.policy.RemoteInputView;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100102import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
John Spurlockde84f0e2013-06-12 12:41:00 -0400103
Michael Jurkaa600fd92012-06-25 15:57:05 -0700104import java.util.ArrayList;
Dan Sandlerf4db75c2014-09-03 18:02:31 +0200105import java.util.List;
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700106import java.util.Locale;
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400107
Jorim Jaggi6b88cdf2014-12-22 20:56:50 +0100108import static com.android.keyguard.KeyguardHostView.OnDismissAction;
Adrian Roos7d7090d2014-05-21 13:10:23 +0200109
Michael Jurka7f2668c2012-03-27 07:49:52 -0700110public abstract class BaseStatusBar extends SystemUI implements
Winson Chung9214eff2014-06-12 13:59:25 -0700111 CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
Christoph Studerc8db24b2014-07-25 17:50:30 +0200112 RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger,
113 NotificationData.Environment {
Daniel Sandler198a0302012-08-17 16:04:31 -0400114 public static final String TAG = "StatusBar";
John Spurlock7e6809a2014-08-06 16:03:14 -0400115 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400116 public static final boolean MULTIUSER_DEBUG = false;
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400117
Chris Wren79ac09e2014-10-29 14:20:19 -0400118 // STOPSHIP disable once we resolve b/18102199
Chris Wren755f4022014-10-29 16:43:26 -0400119 private static final boolean NOTIFICATION_CLICK_DEBUG = true;
Chris Wren79ac09e2014-10-29 14:20:19 -0400120
Adrian Roos497ab022015-02-10 20:49:33 +0100121 public static final boolean ENABLE_REMOTE_INPUT =
122 Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.enable_remote_input", false);
Selim Cinekb5605e52015-02-20 18:21:41 +0100123 public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
124 && SystemProperties.getBoolean("debug.child_notifs", false);
Adrian Roos497ab022015-02-10 20:49:33 +0100125
Winson Chung1e8d71b2014-05-16 17:05:22 -0700126 protected static final int MSG_SHOW_RECENT_APPS = 1019;
127 protected static final int MSG_HIDE_RECENT_APPS = 1020;
128 protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700129 protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
130 protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
Winson Chungb1f74992014-08-08 12:53:09 -0700131 protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
132 protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
Chris Wren0c8275b2012-05-08 13:36:48 -0400133
Chris Wren157026f2013-06-28 16:54:01 -0400134 protected static final boolean ENABLE_HEADS_UP = true;
135 // scores above this threshold should be displayed in heads up mode.
Chris Wren5d9b1532014-03-21 14:31:05 -0400136 protected static final int INTERRUPTION_THRESHOLD = 10;
Chris Wren22ae46e2014-02-26 18:08:09 -0500137 protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
Chris Wren0c8275b2012-05-08 13:36:48 -0400138
Michael Jurka56a57832012-05-14 13:24:43 -0700139 // Should match the value in PhoneWindowManager
140 public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
141
Chris Wrencd8f4f72014-08-27 18:48:13 -0400142 private static final String BANNER_ACTION_CANCEL =
143 "com.android.systemui.statusbar.banner_action_cancel";
144 private static final String BANNER_ACTION_SETUP =
145 "com.android.systemui.statusbar.banner_action_setup";
146
Joe Onorato808182d2010-07-09 18:52:06 -0400147 protected CommandQueue mCommandQueue;
148 protected IStatusBarService mBarService;
Michael Jurka7f2668c2012-03-27 07:49:52 -0700149 protected H mHandler = createHandler();
150
Chris Wren0c8275b2012-05-08 13:36:48 -0400151 // all notifications
Christoph Studerc8db24b2014-07-25 17:50:30 +0200152 protected NotificationData mNotificationData;
Selim Cinekb6d85eb2014-03-28 20:21:01 +0100153 protected NotificationStackScrollLayout mStackScroller;
Chris Wren0c8275b2012-05-08 13:36:48 -0400154
Selim Cinek25fd4e2b2015-02-20 17:46:07 +0100155 protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
156
Chris Wrena4ef6202014-06-09 18:07:30 -0400157 // for heads up notifications
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700158 protected HeadsUpManager mHeadsUpManager;
Chris Wren0c8275b2012-05-08 13:36:48 -0400159
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400160 protected int mCurrentUserId = 0;
Kenny Guy2a764942014-04-02 13:29:20 +0100161 final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400162
Daniel Sandler777dcde2013-09-30 10:21:45 -0400163 protected int mLayoutDirection = -1; // invalid
Selim Cineka8fefa52014-09-08 16:10:50 +0200164 protected AccessibilityManager mAccessibilityManager;
Selim Cinek92d892c2014-09-11 15:11:00 +0200165
166 // on-screen navigation buttons
167 protected NavigationBarView mNavigationBarView = null;
Christoph Studere8e28652014-10-29 17:27:53 +0100168
169 protected Boolean mScreenOn;
170
171 // The second field is a bit different from the first one because it only listens to screen on/
172 // screen of events from Keyguard. We need this so we don't have a race condition with the
173 // broadcast. In the future, we should remove the first field altogether and rename the second
174 // field.
175 protected boolean mScreenOnFromKeyguard;
176
177 protected boolean mVisible;
178
179 // mScreenOnFromKeyguard && mVisible.
180 private boolean mVisibleToUser;
181
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700182 private Locale mLocale;
Dan Sandler539aad42014-08-04 00:43:39 -0400183 private float mFontScale;
184
Chris Wrenf6e83f42013-09-11 14:02:59 -0400185 protected boolean mUseHeadsUp = false;
Chris Wren22ae46e2014-02-26 18:08:09 -0500186 protected boolean mHeadsUpTicker = false;
Jason Monkf7019542014-07-31 12:42:25 -0400187 protected boolean mDisableNotificationAlerts = false;
Chris Wren157026f2013-06-28 16:54:01 -0400188
Adrian Roosbd3409c2014-08-12 18:21:09 +0200189 protected DevicePolicyManager mDevicePolicyManager;
Chris Wren157026f2013-06-28 16:54:01 -0400190 protected IDreamManager mDreamManager;
Chris Wren157026f2013-06-28 16:54:01 -0400191 PowerManager mPowerManager;
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200192 protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
Selim Cinek1685e632014-04-08 02:27:49 +0200193 protected int mRowMinHeight;
194 protected int mRowMaxHeight;
Dan Sandlerfd16d562014-02-13 18:43:31 -0800195
196 // public mode, private notifications, etc
197 private boolean mLockscreenPublicMode = false;
198 private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
Dan Sandler05c362d2014-09-03 00:16:27 +0200199 private NotificationColorUtil mNotificationColorUtil;
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700200
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000201 private UserManager mUserManager;
202
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500203 // UI-specific methods
Daniel Sandler6a858c32012-03-12 14:38:58 -0400204
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500205 /**
206 * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
207 * and add them to the window manager.
208 */
209 protected abstract void createAndAddWindows();
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400210
Jeff Brown98365d72012-08-19 20:30:52 -0700211 protected WindowManager mWindowManager;
212 protected IWindowManager mWindowManagerService;
Christoph Studer60748e72014-05-22 16:51:41 +0200213
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700214 protected abstract void refreshLayout(int layoutDirection);
215
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500216 protected Display mDisplay;
Jeff Brown98365d72012-08-19 20:30:52 -0700217
Daniel Sandler26cda272012-05-22 15:44:08 -0400218 private boolean mDeviceProvisioned = false;
Daniel Sandler6a858c32012-03-12 14:38:58 -0400219
John Spurlockd08de372013-06-24 13:06:08 -0400220 private RecentsComponent mRecents;
221
John Spurlocke677d712014-02-13 12:52:19 -0500222 protected int mZenMode;
223
Dan Sandler4247a5c2014-07-23 15:58:08 -0400224 // which notification is currently being longpress-examined by the user
Selim Cinek024ca592014-09-01 15:11:28 +0200225 private NotificationGuts mNotificationGutsExposed;
Dan Sandler4247a5c2014-07-23 15:58:08 -0400226
227 private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn;
228
Jorim Jaggiecbab362014-04-23 16:13:15 +0200229 /**
230 * The {@link StatusBarState} of the status bar.
231 */
232 protected int mState;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200233 protected boolean mBouncerShowing;
John Spurlock4b3bda22014-05-22 14:32:20 -0400234 protected boolean mShowLockscreenNotifications;
Jorim Jaggiecbab362014-04-23 16:13:15 +0200235
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200236 protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
Dan Sandlereceda3d2014-07-21 15:35:01 -0400237 protected DismissView mDismissView;
Jorim Jaggia2052ea2014-08-05 16:22:30 +0200238 protected EmptyShadeView mEmptyShadeView;
Jorim Jaggi649b7082014-04-03 13:15:18 +0200239
Christoph Studerc8db24b2014-07-25 17:50:30 +0200240 @Override // NotificationData.Environment
Daniel Sandler3ffdcc72012-09-23 14:31:48 -0400241 public boolean isDeviceProvisioned() {
Daniel Sandler26cda272012-05-22 15:44:08 -0400242 return mDeviceProvisioned;
243 }
244
John Spurlocke677d712014-02-13 12:52:19 -0500245 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
Daniel Sandler26cda272012-05-22 15:44:08 -0400246 @Override
247 public void onChange(boolean selfChange) {
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700248 final boolean provisioned = 0 != Settings.Global.getInt(
249 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
Daniel Sandler26cda272012-05-22 15:44:08 -0400250 if (provisioned != mDeviceProvisioned) {
251 mDeviceProvisioned = provisioned;
Christoph Studer37fe6932014-05-26 13:10:30 +0200252 updateNotifications();
Daniel Sandler26cda272012-05-22 15:44:08 -0400253 }
John Spurlocke677d712014-02-13 12:52:19 -0500254 final int mode = Settings.Global.getInt(mContext.getContentResolver(),
255 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
256 setZenMode(mode);
Dan Sandler52e5701e2014-07-22 23:14:54 -0400257
258 updateLockscreenNotificationSetting();
Daniel Sandler26cda272012-05-22 15:44:08 -0400259 }
260 };
261
Dan Sandlerfd16d562014-02-13 18:43:31 -0800262 private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
263 @Override
264 public void onChange(boolean selfChange) {
265 // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
266 // so we just dump our cache ...
267 mUsersAllowingPrivateNotifications.clear();
268 // ... and refresh all the notifications
Christoph Studer37fe6932014-05-26 13:10:30 +0200269 updateNotifications();
Dan Sandlerfd16d562014-02-13 18:43:31 -0800270 }
271 };
272
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700273 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
274 @Override
Adrian Roos7d7090d2014-05-21 13:10:23 +0200275 public boolean onClickHandler(
276 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
Daniel Sandler198a0302012-08-17 16:04:31 -0400277 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400278 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
Daniel Sandler198a0302012-08-17 16:04:31 -0400279 }
Christoph Studer4da84cd2014-10-21 17:24:20 +0200280 logActionClick(view);
Jorim Jaggi89f7f282014-10-17 19:54:15 +0200281 // The intent we are sending is for the application, which
282 // won't have permission to immediately start an activity after
283 // the user switches to home. We know it is safe to do at this
284 // point, so make sure new activity switches are now allowed.
285 try {
286 ActivityManagerNative.getDefault().resumeAppSwitches();
287 } catch (RemoteException e) {
288 }
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700289 final boolean isActivity = pendingIntent.isActivity();
290 if (isActivity) {
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200291 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
Jorim Jaggi85dc23c2014-09-08 14:42:29 +0200292 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
293 mContext, pendingIntent.getIntent(), mCurrentUserId);
Adrian Roos4314f6d2014-05-28 14:10:27 +0200294 dismissKeyguardThenExecute(new OnDismissAction() {
Adrian Roos7d7090d2014-05-21 13:10:23 +0200295 @Override
296 public boolean onDismiss() {
Jorim Jaggi85dc23c2014-09-08 14:42:29 +0200297 if (keyguardShowing && !afterKeyguardGone) {
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200298 try {
299 ActivityManagerNative.getDefault()
300 .keyguardWaitingForActivityDrawn();
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200301 } catch (RemoteException e) {
302 }
Adrian Roos7d7090d2014-05-21 13:10:23 +0200303 }
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700304
Adrian Roos7d7090d2014-05-21 13:10:23 +0200305 boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
Jorim Jaggi85dc23c2014-09-08 14:42:29 +0200306 overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone);
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700307
Adrian Roos7d7090d2014-05-21 13:10:23 +0200308 // close the shade if it was open
309 if (handled) {
Jorim Jaggi4eedc1d2014-10-27 13:45:56 +0100310 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
311 true /* force */);
Adrian Roos7d7090d2014-05-21 13:10:23 +0200312 visibilityChanged(false);
313 }
Adrian Roos4314f6d2014-05-28 14:10:27 +0200314 // Wait for activity start.
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200315 return handled;
Adrian Roos7d7090d2014-05-21 13:10:23 +0200316 }
Jorim Jaggi85dc23c2014-09-08 14:42:29 +0200317 }, afterKeyguardGone);
Adrian Roos7d7090d2014-05-21 13:10:23 +0200318 return true;
319 } else {
320 return super.onClickHandler(view, pendingIntent, fillInIntent);
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700321 }
Adrian Roos7d7090d2014-05-21 13:10:23 +0200322 }
323
Christoph Studer4da84cd2014-10-21 17:24:20 +0200324 private void logActionClick(View view) {
325 ViewParent parent = view.getParent();
326 String key = getNotificationKeyForParent(parent);
327 if (key == null) {
328 Log.w(TAG, "Couldn't determine notification for click.");
329 return;
330 }
331 int index = -1;
332 // If this is a default template, determine the index of the button.
333 if (view.getId() == com.android.internal.R.id.action0 &&
334 parent != null && parent instanceof ViewGroup) {
335 ViewGroup actionGroup = (ViewGroup) parent;
336 index = actionGroup.indexOfChild(view);
337 }
Chris Wren79ac09e2014-10-29 14:20:19 -0400338 if (NOTIFICATION_CLICK_DEBUG) {
339 Log.d(TAG, "Clicked on button " + index + " for " + key);
340 }
Christoph Studer4da84cd2014-10-21 17:24:20 +0200341 try {
342 mBarService.onNotificationActionClick(key, index);
343 } catch (RemoteException e) {
344 // Ignore
345 }
346 }
347
348 private String getNotificationKeyForParent(ViewParent parent) {
349 while (parent != null) {
350 if (parent instanceof ExpandableNotificationRow) {
351 return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
352 }
353 parent = parent.getParent();
354 }
355 return null;
356 }
357
Adrian Roos7d7090d2014-05-21 13:10:23 +0200358 private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
359 Intent fillInIntent) {
360 return super.onClickHandler(view, pendingIntent, fillInIntent);
Dianne Hackborn1927ae82012-06-22 15:21:36 -0700361 }
362 };
363
John Spurlock5c454122013-06-17 07:35:46 -0400364 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
365 @Override
366 public void onReceive(Context context, Intent intent) {
367 String action = intent.getAction();
368 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
369 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Kenny Guy2a764942014-04-02 13:29:20 +0100370 updateCurrentProfilesCache();
John Spurlock5c454122013-06-17 07:35:46 -0400371 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
Dan Sandler52e5701e2014-07-22 23:14:54 -0400372
373 updateLockscreenNotificationSetting();
374
John Spurlock5c454122013-06-17 07:35:46 -0400375 userSwitched(mCurrentUserId);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000376 } else if (Intent.ACTION_USER_ADDED.equals(action)) {
Kenny Guy2a764942014-04-02 13:29:20 +0100377 updateCurrentProfilesCache();
Kenny Guydae30d52015-04-01 19:11:35 +0100378 } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
379 List<ActivityManager.RecentTaskInfo> recentTask = null;
380 try {
381 recentTask = ActivityManagerNative.getDefault().getRecentTasks(1,
382 ActivityManager.RECENT_WITH_EXCLUDED
383 | ActivityManager.RECENT_INCLUDE_PROFILES,
384 mCurrentUserId);
385 } catch (RemoteException e) {
386 // Abandon hope activity manager not running.
387 }
388 if (recentTask != null && recentTask.size() > 0) {
389 UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId);
390 if (user != null && user.isManagedProfile()) {
391 Toast.makeText(mContext, R.string.managed_profile_foreground_toast,
392 Toast.LENGTH_SHORT).show();
393 }
394 }
Adrian Roosbd3409c2014-08-12 18:21:09 +0200395 } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
396 action)) {
397 mUsersAllowingPrivateNotifications.clear();
398 updateLockscreenNotificationSetting();
399 updateNotifications();
Chris Wrencd8f4f72014-08-27 18:48:13 -0400400 } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
401 NotificationManager noMan = (NotificationManager)
402 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
Chris Wrend8775102015-01-16 16:16:43 -0500403 noMan.cancel(R.id.notification_hidden);
Chris Wrencd8f4f72014-08-27 18:48:13 -0400404
405 Settings.Secure.putInt(mContext.getContentResolver(),
406 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
407 if (BANNER_ACTION_SETUP.equals(action)) {
Jorim Jaggi4eedc1d2014-10-27 13:45:56 +0100408 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
409 true /* force */);
Chris Wrencd8f4f72014-08-27 18:48:13 -0400410 mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
411 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
412
413 );
414 }
John Spurlock5c454122013-06-17 07:35:46 -0400415 }
416 }
417 };
418
Christoph Studer60748e72014-05-22 16:51:41 +0200419 private final NotificationListenerService mNotificationListener =
420 new NotificationListenerService() {
421 @Override
422 public void onListenerConnected() {
423 if (DEBUG) Log.d(TAG, "onListenerConnected");
424 final StatusBarNotification[] notifications = getActiveNotifications();
Christoph Studerd0694b62014-06-04 16:36:01 +0200425 final RankingMap currentRanking = getCurrentRanking();
Christoph Studer60748e72014-05-22 16:51:41 +0200426 mHandler.post(new Runnable() {
427 @Override
428 public void run() {
429 for (StatusBarNotification sbn : notifications) {
Selim Cinek379ff8f2015-02-20 17:03:16 +0100430 addNotification(sbn, currentRanking, null /* oldEntry */);
Christoph Studer60748e72014-05-22 16:51:41 +0200431 }
432 }
433 });
434 }
435
436 @Override
Christoph Studerd0694b62014-06-04 16:36:01 +0200437 public void onNotificationPosted(final StatusBarNotification sbn,
438 final RankingMap rankingMap) {
Christoph Studer60748e72014-05-22 16:51:41 +0200439 if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
Chris Wren56919552015-02-24 15:56:34 -0500440 if (sbn != null) {
441 mHandler.post(new Runnable() {
442 @Override
443 public void run() {
Adrian Roos497ab022015-02-10 20:49:33 +0100444 processForRemoteInput(sbn.getNotification());
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700445 String key = sbn.getKey();
446 boolean isUpdate = mNotificationData.get(key) != null;
Christoph Studerd722f272014-09-02 11:49:19 +0200447
Selim Cinekb5605e52015-02-20 18:21:41 +0100448 // In case we don't allow child notifications, we ignore children of
449 // notifications that have a summary, since we're not going to show them
450 // anyway. This is true also when the summary is canceled,
Chris Wren56919552015-02-24 15:56:34 -0500451 // because children are automatically canceled by NoMan in that case.
Selim Cinekb5605e52015-02-20 18:21:41 +0100452 if (!ENABLE_CHILD_NOTIFICATIONS
453 && mGroupManager.isChildInGroupWithSummary(sbn)) {
Chris Wren56919552015-02-24 15:56:34 -0500454 if (DEBUG) {
455 Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
456 }
457
458 // Remove existing notification to avoid stale data.
459 if (isUpdate) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700460 removeNotification(key, rankingMap);
Chris Wren56919552015-02-24 15:56:34 -0500461 } else {
462 mNotificationData.updateRanking(rankingMap);
463 }
464 return;
Christoph Studerd722f272014-09-02 11:49:19 +0200465 }
Christoph Studerd722f272014-09-02 11:49:19 +0200466 if (isUpdate) {
Chris Wren56919552015-02-24 15:56:34 -0500467 updateNotification(sbn, rankingMap);
Christoph Studerd722f272014-09-02 11:49:19 +0200468 } else {
Selim Cinek379ff8f2015-02-20 17:03:16 +0100469 addNotification(sbn, rankingMap, null /* oldEntry */);
Christoph Studerd722f272014-09-02 11:49:19 +0200470 }
Christoph Studerd722f272014-09-02 11:49:19 +0200471 }
Chris Wren56919552015-02-24 15:56:34 -0500472 });
473 }
Christoph Studer60748e72014-05-22 16:51:41 +0200474 }
475
476 @Override
Chris Wren56919552015-02-24 15:56:34 -0500477 public void onNotificationRemoved(StatusBarNotification sbn,
Christoph Studerd0694b62014-06-04 16:36:01 +0200478 final RankingMap rankingMap) {
Christoph Studer60748e72014-05-22 16:51:41 +0200479 if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
Chris Wren56919552015-02-24 15:56:34 -0500480 if (sbn != null) {
481 final String key = sbn.getKey();
482 mHandler.post(new Runnable() {
483 @Override
484 public void run() {
485 removeNotification(key, rankingMap);
486 }
487 });
488 }
Christoph Studer37fe6932014-05-26 13:10:30 +0200489 }
490
491 @Override
Christoph Studerd0694b62014-06-04 16:36:01 +0200492 public void onNotificationRankingUpdate(final RankingMap rankingMap) {
Christoph Studer37fe6932014-05-26 13:10:30 +0200493 if (DEBUG) Log.d(TAG, "onRankingUpdate");
Chris Wren56919552015-02-24 15:56:34 -0500494 if (rankingMap != null) {
Christoph Studer37fe6932014-05-26 13:10:30 +0200495 mHandler.post(new Runnable() {
496 @Override
497 public void run() {
Christoph Studere71fefc2014-06-24 16:16:49 +0200498 updateNotificationRanking(rankingMap);
Christoph Studer60748e72014-05-22 16:51:41 +0200499 }
500 });
Chris Wren56919552015-02-24 15:56:34 -0500501 } }
Christoph Studer5b638262014-07-07 11:21:49 +0200502
Christoph Studer60748e72014-05-22 16:51:41 +0200503 };
504
Kenny Guy2a764942014-04-02 13:29:20 +0100505 private void updateCurrentProfilesCache() {
506 synchronized (mCurrentProfiles) {
507 mCurrentProfiles.clear();
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000508 if (mUserManager != null) {
Kenny Guy2a764942014-04-02 13:29:20 +0100509 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
510 mCurrentProfiles.put(user.id, user);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000511 }
512 }
513 }
514 }
515
Joe Onoratof3c3c4f2010-10-21 11:09:02 -0400516 public void start() {
Jeff Brown98365d72012-08-19 20:30:52 -0700517 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
518 mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
519 mDisplay = mWindowManager.getDefaultDisplay();
Adrian Roosbd3409c2014-08-12 18:21:09 +0200520 mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
521 Context.DEVICE_POLICY_SERVICE);
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500522
Dan Sandler05c362d2014-09-03 00:16:27 +0200523 mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);
524
Christoph Studerc8db24b2014-07-25 17:50:30 +0200525 mNotificationData = new NotificationData(this);
526
Selim Cineka8fefa52014-09-08 16:10:50 +0200527 mAccessibilityManager = (AccessibilityManager)
528 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
529
Chris Wren157026f2013-06-28 16:54:01 -0400530 mDreamManager = IDreamManager.Stub.asInterface(
531 ServiceManager.checkService(DreamService.DREAM_SERVICE));
Chris Wren157026f2013-06-28 16:54:01 -0400532 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
533
Daniel Sandler26cda272012-05-22 15:44:08 -0400534 mContext.getContentResolver().registerContentObserver(
Jeff Brownbf6f6f92012-09-25 15:03:20 -0700535 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
John Spurlocke677d712014-02-13 12:52:19 -0500536 mSettingsObserver);
537 mContext.getContentResolver().registerContentObserver(
538 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
539 mSettingsObserver);
John Spurlock4b3bda22014-05-22 14:32:20 -0400540 mContext.getContentResolver().registerContentObserver(
Dan Sandler52e5701e2014-07-22 23:14:54 -0400541 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
542 mSettingsObserver,
543 UserHandle.USER_ALL);
Daniel Sandler26cda272012-05-22 15:44:08 -0400544
Dan Sandlerfd16d562014-02-13 18:43:31 -0800545 mContext.getContentResolver().registerContentObserver(
546 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
547 true,
548 mLockscreenSettingsObserver,
549 UserHandle.USER_ALL);
550
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500551 mBarService = IStatusBarService.Stub.asInterface(
552 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400553
Jorim Jaggid61f2272014-12-19 20:35:35 +0100554 mRecents = getComponent(Recents.class);
Winson Chung9214eff2014-06-12 13:59:25 -0700555 mRecents.setCallback(this);
John Spurlockd08de372013-06-24 13:06:08 -0400556
Dan Sandler539aad42014-08-04 00:43:39 -0400557 final Configuration currentConfig = mContext.getResources().getConfiguration();
558 mLocale = currentConfig.locale;
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700559 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
Dan Sandler539aad42014-08-04 00:43:39 -0400560 mFontScale = currentConfig.fontScale;
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700561
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000562 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
563
Dan Sandler4247a5c2014-07-23 15:58:08 -0400564 mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
565 android.R.interpolator.linear_out_slow_in);
566 mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext,
567 android.R.interpolator.fast_out_linear_in);
568
Joe Onorato2314aab2010-04-08 16:41:23 -0500569 // Connect in to the status bar manager service
Joe Onorato0cbda992010-05-02 16:28:15 -0700570 StatusBarIconList iconList = new StatusBarIconList();
Joe Onorato66d7d012010-05-14 10:05:10 -0700571 mCommandQueue = new CommandQueue(this, iconList);
Daniel Sandler6a858c32012-03-12 14:38:58 -0400572
Jason Monkb605fec2014-05-02 17:04:10 -0400573 int[] switches = new int[8];
satokcd7cd292010-11-20 15:46:23 +0900574 ArrayList<IBinder> binders = new ArrayList<IBinder>();
Joe Onorato2314aab2010-04-08 16:41:23 -0500575 try {
Christoph Studere71fefc2014-06-24 16:16:49 +0200576 mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
Joe Onorato2314aab2010-04-08 16:41:23 -0500577 } catch (RemoteException ex) {
578 // If the system process isn't there we're doomed anyway.
579 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400580
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500581 createAndAddWindows();
Joe Onorato0cbda992010-05-02 16:28:15 -0700582
Jorim Jaggi66ac1332015-01-21 19:22:26 +0100583 mSettingsObserver.onChange(false); // set up
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200584 disable(switches[0], false /* animate */);
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700585 setSystemUiVisibility(switches[1], 0xffffffff);
Dianne Hackborn7d049322011-06-14 15:00:32 -0700586 topAppWindowChanged(switches[2] != 0);
satokcd7cd292010-11-20 15:46:23 +0900587 // StatusBarManagerService has a back up of IME token and it's restored here.
Michael Wright665366a2014-08-07 15:44:40 -0700588 setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
Joe Onorato93056472010-09-10 10:30:46 -0400589
Joe Onorato0cbda992010-05-02 16:28:15 -0700590 // Set up the initial icon state
Joe Onorato75199e32010-05-29 17:22:51 -0400591 int N = iconList.size();
Joe Onorato0cbda992010-05-02 16:28:15 -0700592 int viewIndex = 0;
593 for (int i=0; i<N; i++) {
594 StatusBarIcon icon = iconList.getIcon(i);
595 if (icon != null) {
596 addIcon(iconList.getSlot(i), i, viewIndex, icon);
597 viewIndex++;
598 }
599 }
600
Christoph Studer71f18fd2014-05-20 17:02:04 +0200601 // Set up the initial notification state.
Christoph Studere71fefc2014-06-24 16:16:49 +0200602 try {
Christoph Studer4600f9b2014-07-22 22:44:43 +0200603 mNotificationListener.registerAsSystemService(mContext,
Christoph Studere71fefc2014-06-24 16:16:49 +0200604 new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
605 UserHandle.USER_ALL);
606 } catch (RemoteException e) {
607 Log.e(TAG, "Unable to register notification listener", e);
Joe Onorato75199e32010-05-29 17:22:51 -0400608 }
609
Christoph Studer60748e72014-05-22 16:51:41 +0200610
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500611 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400612 Log.d(TAG, String.format(
Daniel Sandler6a858c32012-03-12 14:38:58 -0400613 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
Daniel Sandlerc6d29fc2012-02-25 00:33:12 -0500614 iconList.size(),
615 switches[0],
616 switches[1],
617 switches[2],
618 switches[3]
619 ));
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400620 }
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400621
Christopher Tate5e08af02012-09-21 17:17:22 -0700622 mCurrentUserId = ActivityManager.getCurrentUser();
Chris Wrena6d4fb62014-11-20 14:46:23 -0500623 setHeadsUpUser(mCurrentUserId);
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400624
625 IntentFilter filter = new IntentFilter();
626 filter.addAction(Intent.ACTION_USER_SWITCHED);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000627 filter.addAction(Intent.ACTION_USER_ADDED);
Kenny Guydae30d52015-04-01 19:11:35 +0100628 filter.addAction(Intent.ACTION_USER_PRESENT);
Chris Wrencd8f4f72014-08-27 18:48:13 -0400629 filter.addAction(BANNER_ACTION_CANCEL);
630 filter.addAction(BANNER_ACTION_SETUP);
Adrian Roosbd3409c2014-08-12 18:21:09 +0200631 filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
John Spurlock5c454122013-06-17 07:35:46 -0400632 mContext.registerReceiver(mBroadcastReceiver, filter);
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000633
Kenny Guy2a764942014-04-02 13:29:20 +0100634 updateCurrentProfilesCache();
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400635 }
636
Chris Wrencd8f4f72014-08-27 18:48:13 -0400637 protected void notifyUserAboutHiddenNotifications() {
638 if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
639 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
640 Log.d(TAG, "user hasn't seen notification about hidden notifications");
641 final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
642 if (!lockPatternUtils.isSecure()) {
643 Log.d(TAG, "insecure lockscreen, skipping notification");
644 Settings.Secure.putInt(mContext.getContentResolver(),
645 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
646 return;
647 }
648 Log.d(TAG, "disabling lockecreen notifications and alerting the user");
649 // disable lockscreen notifications until user acts on the banner.
650 Settings.Secure.putInt(mContext.getContentResolver(),
651 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
Adrian Roosaee70462014-09-03 16:27:39 +0200652 Settings.Secure.putInt(mContext.getContentResolver(),
653 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
Chris Wrencd8f4f72014-08-27 18:48:13 -0400654
655 final String packageName = mContext.getPackageName();
656 PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
657 new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
658 PendingIntent.FLAG_CANCEL_CURRENT);
659 PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
660 new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
661 PendingIntent.FLAG_CANCEL_CURRENT);
662
663 final Resources res = mContext.getResources();
664 final int colorRes = com.android.internal.R.color.system_notification_accent_color;
665 Notification.Builder note = new Notification.Builder(mContext)
666 .setSmallIcon(R.drawable.ic_android)
667 .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
668 .setContentText(mContext.getString(R.string.hidden_notifications_text))
669 .setPriority(Notification.PRIORITY_HIGH)
670 .setOngoing(true)
Alan Viverette4a357cd2015-03-18 18:37:18 -0700671 .setColor(mContext.getColor(colorRes))
Chris Wrencd8f4f72014-08-27 18:48:13 -0400672 .setContentIntent(setupIntent)
673 .addAction(R.drawable.ic_close,
674 mContext.getString(R.string.hidden_notifications_cancel),
675 cancelIntent)
676 .addAction(R.drawable.ic_settings,
677 mContext.getString(R.string.hidden_notifications_setup),
678 setupIntent);
679
680 NotificationManager noMan =
681 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
Chris Wrend8775102015-01-16 16:16:43 -0500682 noMan.notify(R.id.notification_hidden, note.build());
Chris Wrencd8f4f72014-08-27 18:48:13 -0400683 }
684 }
685
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400686 public void userSwitched(int newUserId) {
Chris Wrena6d4fb62014-11-20 14:46:23 -0500687 setHeadsUpUser(newUserId);
688 }
689
690 private void setHeadsUpUser(int newUserId) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700691 mHeadsUpManager.setUser(newUserId);
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400692 }
693
Chris Wrend04f6ce2014-06-11 17:37:28 -0400694 public boolean isHeadsUp(String key) {
Selim Cinekb8f09cf2015-03-16 17:09:28 -0700695 return mHeadsUpManager.isHeadsUp(key);
Chris Wrend04f6ce2014-06-11 17:37:28 -0400696 }
697
Christoph Studerc8db24b2014-07-25 17:50:30 +0200698 @Override // NotificationData.Environment
699 public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400700 final int thisUserId = mCurrentUserId;
701 final int notificationUserId = n.getUserId();
702 if (DEBUG && MULTIUSER_DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400703 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400704 n, thisUserId, notificationUserId));
705 }
Dan Sandlere63ba4b2014-12-05 00:26:24 -0500706 return isCurrentProfile(notificationUserId);
Joe Onorato2314aab2010-04-08 16:41:23 -0500707 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400708
Kenny Guy44fc65f2014-11-28 22:18:14 +0000709 protected boolean isCurrentProfile(int userId) {
710 synchronized (mCurrentProfiles) {
Dan Sandlere63ba4b2014-12-05 00:26:24 -0500711 return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
Kenny Guy44fc65f2014-11-28 22:18:14 +0000712 }
713 }
714
Christoph Studer2e731b52014-08-22 16:01:51 +0200715 @Override
716 public String getCurrentMediaNotificationKey() {
717 return null;
718 }
719
Selim Cinek25fd4e2b2015-02-20 17:46:07 +0100720 @Override
721 public NotificationGroupManager getGroupManager() {
722 return mGroupManager;
723 }
724
Adrian Roos7d7090d2014-05-21 13:10:23 +0200725 /**
726 * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
727 * @param action A dismiss action that is called if it's safe to start the activity.
Jorim Jaggi746f7fa2014-08-27 17:52:46 +0200728 * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone.
Adrian Roos7d7090d2014-05-21 13:10:23 +0200729 */
Jorim Jaggi746f7fa2014-08-27 17:52:46 +0200730 protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
Adrian Roos7d7090d2014-05-21 13:10:23 +0200731 action.onDismiss();
732 }
733
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700734 @Override
735 protected void onConfigurationChanged(Configuration newConfig) {
Daniel Sandler777dcde2013-09-30 10:21:45 -0400736 final Locale locale = mContext.getResources().getConfiguration().locale;
737 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
Dan Sandler539aad42014-08-04 00:43:39 -0400738 final float fontScale = newConfig.fontScale;
739
740 if (! locale.equals(mLocale) || ld != mLayoutDirection || fontScale != mFontScale) {
Daniel Sandler777dcde2013-09-30 10:21:45 -0400741 if (DEBUG) {
742 Log.v(TAG, String.format(
743 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
744 locale, ld));
745 }
746 mLocale = locale;
747 mLayoutDirection = ld;
748 refreshLayout(ld);
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700749 }
750 }
751
Winson Chungc57ccf02011-10-13 15:04:59 -0700752 protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
753 View vetoButton = row.findViewById(R.id.veto);
Selim Cinek8d490d42015-04-10 00:05:50 -0700754 if (n.isClearable()) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400755 final String _pkg = n.getPackageName();
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400756 final String _tag = n.getTag();
757 final int _id = n.getId();
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000758 final int _userId = n.getUserId();
Winson Chungc57ccf02011-10-13 15:04:59 -0700759 vetoButton.setOnClickListener(new View.OnClickListener() {
760 public void onClick(View v) {
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700761 // Accessibility feedback
762 v.announceForAccessibility(
763 mContext.getString(R.string.accessibility_notification_dismissed));
Winson Chungc57ccf02011-10-13 15:04:59 -0700764 try {
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000765 mBarService.onNotificationClear(_pkg, _tag, _id, _userId);
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700766
Winson Chungc57ccf02011-10-13 15:04:59 -0700767 } catch (RemoteException ex) {
768 // system process is dead if we're here.
769 }
770 }
771 });
772 vetoButton.setVisibility(View.VISIBLE);
773 } else {
774 vetoButton.setVisibility(View.GONE);
775 }
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700776 vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
Winson Chungc57ccf02011-10-13 15:04:59 -0700777 return vetoButton;
778 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400779
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400780
Christoph Studer13b895122014-08-26 15:42:12 +0200781 protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
Jorim Jaggi46739852014-04-15 09:58:24 +0200782 NotificationData.Entry entry) {
Dan Sandlerfe266a32014-05-15 22:28:06 -0400783
Jorim Jaggi46739852014-04-15 09:58:24 +0200784 if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) {
Dan Sandlerfe266a32014-05-15 22:28:06 -0400785 // Using custom RemoteViews
Jorim Jaggia1eeade2014-09-08 22:34:39 +0200786 if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700787 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
Selim Cinek697178b2014-07-02 19:40:30 +0200788 entry.row.setShowingLegacyBackground(true);
Jorim Jaggi36b15232014-06-10 17:24:20 +0200789 entry.legacy = true;
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400790 }
Dan Sandler842dd772014-05-15 09:36:47 -0400791 } else {
792 // Using platform templates
793 final int color = sbn.getNotification().color;
794 if (isMediaNotification(entry)) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +0200795 entry.row.setTintColor(color == Notification.COLOR_DEFAULT
Alan Viverette4a357cd2015-03-18 18:37:18 -0700796 ? mContext.getColor(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +0200797 R.color.notification_material_background_media_default_color)
798 : color);
Dan Sandler842dd772014-05-15 09:36:47 -0400799 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400800 }
Christoph Studer13b895122014-08-26 15:42:12 +0200801
802 if (entry.icon != null) {
Jorim Jaggidd0107a2015-01-21 22:23:48 +0100803 entry.icon.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
Christoph Studer13b895122014-08-26 15:42:12 +0200804 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400805 }
806
Dan Sandler16128f42014-05-21 12:48:22 -0400807 public boolean isMediaNotification(NotificationData.Entry entry) {
Dan Sandler842dd772014-05-15 09:36:47 -0400808 // TODO: confirm that there's a valid media key
809 return entry.expandedBig != null &&
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +0200810 entry.expandedBig.findViewById(com.android.internal.R.id.media_actions) != null;
Dan Sandler842dd772014-05-15 09:36:47 -0400811 }
812
Dan Sandlerf4db75c2014-09-03 18:02:31 +0200813 // The gear button in the guts that links to the app's own notification settings
814 private void startAppOwnNotificationSettingsActivity(Intent intent,
815 final int notificationId, final String notificationTag, final int appUid) {
816 intent.putExtra("notification_id", notificationId);
817 intent.putExtra("notification_tag", notificationTag);
818 startNotificationGutsIntent(intent, appUid);
819 }
820
821 // The (i) button in the guts that links to the system notification settings for that app
Jorim Jaggifb28c0e2014-08-22 16:28:42 +0200822 private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
823 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
Dan Sandlere9a4e902014-07-31 01:06:43 -0400824 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
825 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
Dan Sandlerf4db75c2014-09-03 18:02:31 +0200826 startNotificationGutsIntent(intent, appUid);
827 }
Jorim Jaggifb28c0e2014-08-22 16:28:42 +0200828
Dan Sandlerf4db75c2014-09-03 18:02:31 +0200829 private void startNotificationGutsIntent(final Intent intent, final int appUid) {
Jorim Jaggifb28c0e2014-08-22 16:28:42 +0200830 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
831 dismissKeyguardThenExecute(new OnDismissAction() {
832 @Override
833 public boolean onDismiss() {
834 AsyncTask.execute(new Runnable() {
835 public void run() {
836 try {
837 if (keyguardShowing) {
838 ActivityManagerNative.getDefault()
839 .keyguardWaitingForActivityDrawn();
840 }
841 TaskStackBuilder.create(mContext)
842 .addNextIntentWithParentStack(intent)
843 .startActivities(null,
844 new UserHandle(UserHandle.getUserId(appUid)));
845 overrideActivityPendingAppTransition(keyguardShowing);
846 } catch (RemoteException e) {
847 }
848 }
849 });
Jorim Jaggi4eedc1d2014-10-27 13:45:56 +0100850 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
Jorim Jaggifb28c0e2014-08-22 16:28:42 +0200851 return true;
852 }
Jorim Jaggi746f7fa2014-08-27 17:52:46 +0200853 }, false /* afterKeyguardGone */);
Dan Sandler4247a5c2014-07-23 15:58:08 -0400854 }
855
Selim Cinekab29aeb2015-02-20 18:18:32 +0100856 private void bindGuts(ExpandableNotificationRow row) {
857 row.inflateGuts();
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200858 final StatusBarNotification sbn = row.getStatusBarNotification();
859 PackageManager pmUser = getPackageManagerForUser(
860 sbn.getUser().getIdentifier());
861 row.setTag(sbn.getPackageName());
Selim Cinekab29aeb2015-02-20 18:18:32 +0100862 final View guts = row.getGuts();
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200863 final String pkg = sbn.getPackageName();
864 String appname = pkg;
865 Drawable pkgicon = null;
866 int appUid = -1;
867 try {
868 final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
869 PackageManager.GET_UNINSTALLED_PACKAGES
870 | PackageManager.GET_DISABLED_COMPONENTS);
871 if (info != null) {
872 appname = String.valueOf(pmUser.getApplicationLabel(info));
873 pkgicon = pmUser.getApplicationIcon(info);
874 appUid = info.uid;
875 }
876 } catch (NameNotFoundException e) {
877 // app is gone, just show package name and generic icon
878 pkgicon = pmUser.getDefaultActivityIcon();
879 }
880 ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon);
881 ((DateTimeView) row.findViewById(R.id.timestamp)).setTime(sbn.getPostTime());
882 ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
883 final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
884 final View appSettingsButton
885 = guts.findViewById(R.id.notification_inspect_app_provided_settings);
886 if (appUid >= 0) {
887 final int appUidF = appUid;
888 settingsButton.setOnClickListener(new View.OnClickListener() {
889 public void onClick(View v) {
890 startAppNotificationSettingsActivity(pkg, appUidF);
891 }
892 });
893
894 final Intent appSettingsQueryIntent
895 = new Intent(Intent.ACTION_MAIN)
896 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
897 .setPackage(pkg);
898 List<ResolveInfo> infos = pmUser.queryIntentActivities(appSettingsQueryIntent, 0);
899 if (infos.size() > 0) {
900 appSettingsButton.setVisibility(View.VISIBLE);
901 appSettingsButton.setContentDescription(
902 mContext.getResources().getString(
903 R.string.status_bar_notification_app_settings_title,
904 appname
905 ));
906 final Intent appSettingsLaunchIntent = new Intent(appSettingsQueryIntent)
907 .setClassName(pkg, infos.get(0).activityInfo.name);
908 appSettingsButton.setOnClickListener(new View.OnClickListener() {
909 public void onClick(View v) {
910 startAppOwnNotificationSettingsActivity(appSettingsLaunchIntent,
911 sbn.getId(),
912 sbn.getTag(),
913 appUidF);
914 }
915 });
916 } else {
917 appSettingsButton.setVisibility(View.GONE);
918 }
919 } else {
920 settingsButton.setVisibility(View.GONE);
921 appSettingsButton.setVisibility(View.GONE);
922 }
923
924 }
925
Dan Sandler4247a5c2014-07-23 15:58:08 -0400926 protected SwipeHelper.LongPressListener getNotificationLongClicker() {
927 return new SwipeHelper.LongPressListener() {
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400928 @Override
Dan Sandler4247a5c2014-07-23 15:58:08 -0400929 public boolean onLongPress(View v, int x, int y) {
930 dismissPopups();
931
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200932 if (!(v instanceof ExpandableNotificationRow)) {
933 return false;
934 }
Selim Cinekabdc5a02014-09-02 13:46:00 +0200935 if (v.getWindowToken() == null) {
936 Log.e(TAG, "Trying to show notification guts, but not attached to window");
937 return false;
938 }
Dan Sandler4247a5c2014-07-23 15:58:08 -0400939
Selim Cinekab29aeb2015-02-20 18:18:32 +0100940 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
941 bindGuts(row);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200942
Dan Sandler4247a5c2014-07-23 15:58:08 -0400943 // Assume we are a status_bar_notification_row
Selim Cinekab29aeb2015-02-20 18:18:32 +0100944 final NotificationGuts guts = row.getGuts();
Selim Cinekbdefcdf2014-09-11 12:41:47 +0200945 if (guts == null) {
946 // This view has no guts. Examples are the more card or the dismiss all view
947 return false;
948 }
Dan Sandler4247a5c2014-07-23 15:58:08 -0400949
950 // Already showing?
Selim Cinekabdc5a02014-09-02 13:46:00 +0200951 if (guts.getVisibility() == View.VISIBLE) {
952 Log.e(TAG, "Trying to show notification guts, but already visible");
953 return false;
954 }
Dan Sandler4247a5c2014-07-23 15:58:08 -0400955
Dan Sandler4247a5c2014-07-23 15:58:08 -0400956 guts.setVisibility(View.VISIBLE);
Selim Cinek024ca592014-09-01 15:11:28 +0200957 final double horz = Math.max(guts.getWidth() - x, x);
958 final double vert = Math.max(guts.getActualHeight() - y, y);
Dan Sandler4247a5c2014-07-23 15:58:08 -0400959 final float r = (float) Math.hypot(horz, vert);
960 final Animator a
961 = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
962 a.setDuration(400);
963 a.setInterpolator(mLinearOutSlowIn);
964 a.start();
965
966 mNotificationGutsExposed = guts;
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400967
968 return true;
969 }
970 };
971 }
972
Daniel Sandler469e96e2012-05-04 15:56:19 -0400973 public void dismissPopups() {
Dan Sandler4247a5c2014-07-23 15:58:08 -0400974 if (mNotificationGutsExposed != null) {
Selim Cinek024ca592014-09-01 15:11:28 +0200975 final NotificationGuts v = mNotificationGutsExposed;
Dan Sandler4247a5c2014-07-23 15:58:08 -0400976 mNotificationGutsExposed = null;
977
Selim Cinekedb893b2014-09-02 12:51:31 +0200978 if (v.getWindowToken() == null) return;
979
Dan Sandler4247a5c2014-07-23 15:58:08 -0400980 final int x = (v.getLeft() + v.getRight()) / 2;
Selim Cinek024ca592014-09-01 15:11:28 +0200981 final int y = (v.getTop() + v.getActualHeight() / 2);
Dan Sandler4247a5c2014-07-23 15:58:08 -0400982 final Animator a = ViewAnimationUtils.createCircularReveal(v,
983 x, y, x, 0);
984 a.setDuration(200);
985 a.setInterpolator(mFastOutLinearIn);
986 a.addListener(new AnimatorListenerAdapter() {
987 @Override
988 public void onAnimationEnd(Animator animation) {
989 super.onAnimationEnd(animation);
990 v.setVisibility(View.GONE);
991 }
992 });
993 a.start();
Daniel Sandler469e96e2012-05-04 15:56:19 -0400994 }
995 }
996
Michael Jurka7f2668c2012-03-27 07:49:52 -0700997 @Override
Winson Chung1e8d71b2014-05-16 17:05:22 -0700998 public void showRecentApps(boolean triggeredFromAltTab) {
999 int msg = MSG_SHOW_RECENT_APPS;
1000 mHandler.removeMessages(msg);
1001 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 0).sendToTarget();
1002 }
1003
1004 @Override
Winson Chungcdcd4872014-08-05 18:00:13 -07001005 public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001006 int msg = MSG_HIDE_RECENT_APPS;
1007 mHandler.removeMessages(msg);
Winson Chungcdcd4872014-08-05 18:00:13 -07001008 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
1009 triggeredFromHomeKey ? 1 : 0).sendToTarget();
Winson Chung1e8d71b2014-05-16 17:05:22 -07001010 }
1011
1012 @Override
Michael Jurka7f2668c2012-03-27 07:49:52 -07001013 public void toggleRecentApps() {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001014 int msg = MSG_TOGGLE_RECENTS_APPS;
Michael Jurka7f2668c2012-03-27 07:49:52 -07001015 mHandler.removeMessages(msg);
1016 mHandler.sendEmptyMessage(msg);
1017 }
1018
1019 @Override
1020 public void preloadRecentApps() {
1021 int msg = MSG_PRELOAD_RECENT_APPS;
1022 mHandler.removeMessages(msg);
1023 mHandler.sendEmptyMessage(msg);
1024 }
1025
1026 @Override
1027 public void cancelPreloadRecentApps() {
1028 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
1029 mHandler.removeMessages(msg);
1030 mHandler.sendEmptyMessage(msg);
1031 }
1032
Winson Chungb1f74992014-08-08 12:53:09 -07001033 /** Jumps to the next affiliated task in the group. */
1034 public void showNextAffiliatedTask() {
1035 int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
1036 mHandler.removeMessages(msg);
1037 mHandler.sendEmptyMessage(msg);
1038 }
1039
1040 /** Jumps to the previous affiliated task in the group. */
1041 public void showPreviousAffiliatedTask() {
1042 int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
1043 mHandler.removeMessages(msg);
1044 mHandler.sendEmptyMessage(msg);
1045 }
1046
Michael Jurkaecc395a2012-03-30 05:31:46 -07001047 protected H createHandler() {
Michael Jurka7f2668c2012-03-27 07:49:52 -07001048 return new H();
1049 }
1050
Michael Jurka56a57832012-05-14 13:24:43 -07001051 static void sendCloseSystemWindows(Context context, String reason) {
1052 if (ActivityManagerNative.isSystemReady()) {
1053 try {
1054 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
1055 } catch (RemoteException e) {
1056 }
1057 }
1058 }
1059
Michael Jurkacb2522c2012-04-13 09:32:47 -07001060 protected abstract View getStatusBarView();
1061
Michael Jurka80343f62012-10-18 13:13:46 +02001062 protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
1063 // additional optimization when we have software system buttons - start loading the recent
1064 // tasks on touch down
1065 @Override
1066 public boolean onTouch(View v, MotionEvent event) {
1067 int action = event.getAction() & MotionEvent.ACTION_MASK;
1068 if (action == MotionEvent.ACTION_DOWN) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001069 preloadRecents();
Michael Jurka80343f62012-10-18 13:13:46 +02001070 } else if (action == MotionEvent.ACTION_CANCEL) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001071 cancelPreloadingRecents();
Michael Jurka80343f62012-10-18 13:13:46 +02001072 } else if (action == MotionEvent.ACTION_UP) {
1073 if (!v.isPressed()) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001074 cancelPreloadingRecents();
Michael Jurka80343f62012-10-18 13:13:46 +02001075 }
1076
1077 }
1078 return false;
1079 }
1080 };
1081
Winson Chung1e8d71b2014-05-16 17:05:22 -07001082 /** Proxy for RecentsComponent */
1083
1084 protected void showRecents(boolean triggeredFromAltTab) {
1085 if (mRecents != null) {
1086 sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
1087 mRecents.showRecents(triggeredFromAltTab, getStatusBarView());
1088 }
1089 }
1090
Winson Chungcdcd4872014-08-05 18:00:13 -07001091 protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001092 if (mRecents != null) {
Winson Chungcdcd4872014-08-05 18:00:13 -07001093 mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
Winson Chung1e8d71b2014-05-16 17:05:22 -07001094 }
1095 }
1096
1097 protected void toggleRecents() {
John Spurlockd08de372013-06-24 13:06:08 -04001098 if (mRecents != null) {
Winson Chung4c71aef2014-03-21 15:15:11 -07001099 sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
John Spurlockd08de372013-06-24 13:06:08 -04001100 mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
1101 }
1102 }
Michael Jurka80343f62012-10-18 13:13:46 +02001103
Winson Chung1e8d71b2014-05-16 17:05:22 -07001104 protected void preloadRecents() {
John Spurlockd08de372013-06-24 13:06:08 -04001105 if (mRecents != null) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001106 mRecents.preloadRecents();
John Spurlockd08de372013-06-24 13:06:08 -04001107 }
Michael Jurka80343f62012-10-18 13:13:46 +02001108 }
1109
Winson Chung1e8d71b2014-05-16 17:05:22 -07001110 protected void cancelPreloadingRecents() {
John Spurlockd08de372013-06-24 13:06:08 -04001111 if (mRecents != null) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001112 mRecents.cancelPreloadingRecents();
John Spurlockd08de372013-06-24 13:06:08 -04001113 }
Michael Jurka80343f62012-10-18 13:13:46 +02001114 }
1115
Winson Chungb1f74992014-08-08 12:53:09 -07001116 protected void showRecentsNextAffiliatedTask() {
1117 if (mRecents != null) {
1118 mRecents.showNextAffiliatedTask();
1119 }
1120 }
1121
1122 protected void showRecentsPreviousAffiliatedTask() {
1123 if (mRecents != null) {
1124 mRecents.showPrevAffiliatedTask();
1125 }
1126 }
1127
Winson Chung9214eff2014-06-12 13:59:25 -07001128 @Override
1129 public void onVisibilityChanged(boolean visible) {
1130 // Do nothing
1131 }
1132
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001133 /**
1134 * if the interrupting notification had a fullscreen intent, fire it now.
1135 */
1136 public abstract void escalateHeadsUp();
Chris Wrena4ef6202014-06-09 18:07:30 -04001137
Dan Sandlerfd16d562014-02-13 18:43:31 -08001138 /**
1139 * Save the current "public" (locked and secure) state of the lockscreen.
1140 */
1141 public void setLockscreenPublicMode(boolean publicMode) {
1142 mLockscreenPublicMode = publicMode;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001143 }
1144
Dan Sandlerfd16d562014-02-13 18:43:31 -08001145 public boolean isLockscreenPublicMode() {
1146 return mLockscreenPublicMode;
1147 }
1148
1149 /**
1150 * Has the given user chosen to allow their private (full) notifications to be shown even
1151 * when the lockscreen is in "public" (secure & locked) mode?
1152 */
1153 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
1154 if (userHandle == UserHandle.USER_ALL) {
1155 return true;
1156 }
1157
1158 if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
Kenny Guyb0d3ab22015-02-10 16:13:11 +00001159 final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
Dan Sandlerfd16d562014-02-13 18:43:31 -08001160 mContext.getContentResolver(),
1161 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
Adrian Roosbd3409c2014-08-12 18:21:09 +02001162 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
1163 userHandle);
1164 final boolean allowedByDpm = (dpmFlags
1165 & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
Kenny Guyb0d3ab22015-02-10 16:13:11 +00001166 final boolean allowed = allowedByUser && allowedByDpm;
1167 mUsersAllowingPrivateNotifications.append(userHandle, allowed);
Dan Sandlerfd16d562014-02-13 18:43:31 -08001168 return allowed;
1169 }
1170
1171 return mUsersAllowingPrivateNotifications.get(userHandle);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001172 }
1173
Christoph Studerc8db24b2014-07-25 17:50:30 +02001174 /**
1175 * Returns true if we're on a secure lockscreen and the user wants to hide "sensitive"
1176 * notification data. If so, private notifications should show their (possibly
1177 * auto-generated) publicVersion, and secret notifications should be totally invisible.
1178 */
1179 @Override // NotificationData.Environment
1180 public boolean shouldHideSensitiveContents(int userid) {
1181 return isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(userid);
1182 }
1183
Chris Wrena4ef6202014-06-09 18:07:30 -04001184 public void onNotificationClear(StatusBarNotification notification) {
1185 try {
1186 mBarService.onNotificationClear(
1187 notification.getPackageName(),
1188 notification.getTag(),
1189 notification.getId(),
1190 notification.getUserId());
1191 } catch (android.os.RemoteException ex) {
1192 // oh well
1193 }
1194 }
1195
Michael Jurka7f2668c2012-03-27 07:49:52 -07001196 protected class H extends Handler {
1197 public void handleMessage(Message m) {
1198 switch (m.what) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001199 case MSG_SHOW_RECENT_APPS:
1200 showRecents(m.arg1 > 0);
Michael Jurkacb2522c2012-04-13 09:32:47 -07001201 break;
Winson Chung1e8d71b2014-05-16 17:05:22 -07001202 case MSG_HIDE_RECENT_APPS:
Winson Chungcdcd4872014-08-05 18:00:13 -07001203 hideRecents(m.arg1 > 0, m.arg2 > 0);
Winson Chung1e8d71b2014-05-16 17:05:22 -07001204 break;
1205 case MSG_TOGGLE_RECENTS_APPS:
1206 toggleRecents();
Michael Jurkacb2522c2012-04-13 09:32:47 -07001207 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -07001208 case MSG_PRELOAD_RECENT_APPS:
Winson Chung1e8d71b2014-05-16 17:05:22 -07001209 preloadRecents();
Michael Jurka7f2668c2012-03-27 07:49:52 -07001210 break;
1211 case MSG_CANCEL_PRELOAD_RECENT_APPS:
Winson Chung1e8d71b2014-05-16 17:05:22 -07001212 cancelPreloadingRecents();
Michael Jurka7f2668c2012-03-27 07:49:52 -07001213 break;
Winson Chungb1f74992014-08-08 12:53:09 -07001214 case MSG_SHOW_NEXT_AFFILIATED_TASK:
1215 showRecentsNextAffiliatedTask();
1216 break;
1217 case MSG_SHOW_PREV_AFFILIATED_TASK:
1218 showRecentsPreviousAffiliatedTask();
1219 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -07001220 }
1221 }
1222 }
1223
Daniel Sandler6a858c32012-03-12 14:38:58 -04001224 protected void workAroundBadLayerDrawableOpacity(View v) {
1225 }
1226
Selim Cinek8d490d42015-04-10 00:05:50 -07001227 protected boolean inflateViews(Entry entry, ViewGroup parent) {
Dan Sandlere9a4e902014-07-31 01:06:43 -04001228 PackageManager pmUser = getPackageManagerForUser(
1229 entry.notification.getUser().getIdentifier());
1230
Chris Wrend04f6ce2014-06-11 17:37:28 -04001231 int maxHeight = mRowMaxHeight;
Dan Sandlerf4db75c2014-09-03 18:02:31 +02001232 final StatusBarNotification sbn = entry.notification;
Chris Wren574a55e2013-07-15 18:48:37 -04001233 RemoteViews contentView = sbn.getNotification().contentView;
1234 RemoteViews bigContentView = sbn.getNotification().bigContentView;
Selim Cinek8d490d42015-04-10 00:05:50 -07001235 RemoteViews headsUpContentView = sbn.getNotification().headsUpContentView;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001236
Chris Wren574a55e2013-07-15 18:48:37 -04001237 if (contentView == null) {
Daniel Sandler6a858c32012-03-12 14:38:58 -04001238 return false;
1239 }
1240
Dan Sandlerf68448e2014-04-24 15:54:16 -04001241 if (DEBUG) {
1242 Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion);
1243 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001244
1245 Notification publicNotification = sbn.getNotification().publicVersion;
1246
Christoph Studera7fe6312014-06-27 19:32:44 +02001247 ExpandableNotificationRow row;
1248
1249 // Stash away previous user expansion state so we can restore it at
1250 // the end.
1251 boolean hasUserChangedExpansion = false;
1252 boolean userExpanded = false;
1253 boolean userLocked = false;
1254
1255 if (entry.row != null) {
1256 row = entry.row;
1257 hasUserChangedExpansion = row.hasUserChangedExpansion();
1258 userExpanded = row.isUserExpanded();
1259 userLocked = row.isUserLocked();
Christoph Studer22f2ee52014-07-29 22:57:21 +02001260 entry.reset();
Christoph Studera7fe6312014-06-27 19:32:44 +02001261 if (hasUserChangedExpansion) {
1262 row.setUserExpanded(userExpanded);
1263 }
1264 } else {
1265 // create the row view
1266 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
1267 Context.LAYOUT_INFLATER_SERVICE);
1268 row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
1269 parent, false);
Chris Wren78403d72014-07-28 10:23:24 +01001270 row.setExpansionLogger(this, entry.notification.getKey());
Selim Cinekb5605e52015-02-20 18:21:41 +01001271 row.setGroupManager(mGroupManager);
Christoph Studera7fe6312014-06-27 19:32:44 +02001272 }
Daniel Sandlerbc5559f2012-04-19 01:08:15 -04001273
Daniel Sandler6a858c32012-03-12 14:38:58 -04001274 workAroundBadLayerDrawableOpacity(row);
1275 View vetoButton = updateNotificationVetoButton(row, sbn);
1276 vetoButton.setContentDescription(mContext.getString(
1277 R.string.accessibility_remove_notification));
1278
1279 // NB: the large icon is now handled entirely by the template
1280
1281 // bind the click event to the content area
Selim Cinek8d490d42015-04-10 00:05:50 -07001282 NotificationContentView contentContainer = row.getPrivateLayout();
1283 NotificationContentView contentContainerPublic = row.getPublicLayout();
Daniel Sandlerbc5559f2012-04-19 01:08:15 -04001284
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001285 row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
Adrian Roos497ab022015-02-10 20:49:33 +01001286 if (ENABLE_REMOTE_INPUT) {
1287 row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
1288 }
Daniel Sandlerbc5559f2012-04-19 01:08:15 -04001289
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001290 PendingIntent contentIntent = sbn.getNotification().contentIntent;
Daniel Sandler6a858c32012-03-12 14:38:58 -04001291 if (contentIntent != null) {
Chris Wren930ecca2014-11-12 17:43:41 -05001292 final View.OnClickListener listener = makeClicker(contentIntent, sbn.getKey());
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001293 row.setOnClickListener(listener);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001294 } else {
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001295 row.setOnClickListener(null);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001296 }
1297
Dan Sandlera5e0f412014-01-23 15:11:54 -05001298 // set up the adaptive layout
Chris Wren574a55e2013-07-15 18:48:37 -04001299 View contentViewLocal = null;
1300 View bigContentViewLocal = null;
Selim Cinek8d490d42015-04-10 00:05:50 -07001301 View headsUpContentViewLocal = null;
Daniel Sandler6a858c32012-03-12 14:38:58 -04001302 try {
Selim Cinek8d490d42015-04-10 00:05:50 -07001303 contentViewLocal = contentView.apply(mContext, contentContainer,
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01001304 mOnClickHandler);
Chris Wren574a55e2013-07-15 18:48:37 -04001305 if (bigContentView != null) {
Selim Cinek8d490d42015-04-10 00:05:50 -07001306 bigContentViewLocal = bigContentView.apply(mContext, contentContainer,
1307 mOnClickHandler);
1308 }
1309 if (headsUpContentView != null) {
1310 headsUpContentViewLocal = headsUpContentView.apply(mContext, contentContainer,
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01001311 mOnClickHandler);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001312 }
1313 }
1314 catch (RuntimeException e) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001315 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
John Spurlockcd686b52013-06-05 10:13:46 -04001316 Log.e(TAG, "couldn't inflate view for notification " + ident, e);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001317 return false;
Daniel Sandler6a858c32012-03-12 14:38:58 -04001318 }
1319
Chris Wren574a55e2013-07-15 18:48:37 -04001320 if (contentViewLocal != null) {
Dan Sandler6d94aa72014-03-12 00:53:21 -04001321 contentViewLocal.setIsRootNamespace(true);
Selim Cinek8d490d42015-04-10 00:05:50 -07001322 contentContainer.setContractedChild(contentViewLocal);
Daniel Sandler8680bf82012-05-15 16:52:52 -04001323 }
Chris Wren574a55e2013-07-15 18:48:37 -04001324 if (bigContentViewLocal != null) {
Dan Sandler6d94aa72014-03-12 00:53:21 -04001325 bigContentViewLocal.setIsRootNamespace(true);
Selim Cinek8d490d42015-04-10 00:05:50 -07001326 contentContainer.setExpandedChild(bigContentViewLocal);
1327 }
1328 if (headsUpContentViewLocal != null) {
1329 headsUpContentViewLocal.setIsRootNamespace(true);
1330 contentContainer.setHeadsUpChild(headsUpContentViewLocal);
Daniel Sandler8680bf82012-05-15 16:52:52 -04001331 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001332
Dan Sandlera5e0f412014-01-23 15:11:54 -05001333 // now the public version
1334 View publicViewLocal = null;
1335 if (publicNotification != null) {
1336 try {
Selim Cinek8d490d42015-04-10 00:05:50 -07001337 publicViewLocal = publicNotification.contentView.apply(mContext, contentContainerPublic,
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001338 mOnClickHandler);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001339
1340 if (publicViewLocal != null) {
Dan Sandler6d94aa72014-03-12 00:53:21 -04001341 publicViewLocal.setIsRootNamespace(true);
Selim Cinek8d490d42015-04-10 00:05:50 -07001342 contentContainerPublic.setContractedChild(publicViewLocal);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001343 }
1344 }
1345 catch (RuntimeException e) {
1346 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
1347 Log.e(TAG, "couldn't inflate public view for notification " + ident, e);
1348 publicViewLocal = null;
1349 }
1350 }
1351
Jorim Jaggia1eeade2014-09-08 22:34:39 +02001352 // Extract target SDK version.
1353 try {
1354 ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
1355 entry.targetSdk = info.targetSdkVersion;
1356 } catch (NameNotFoundException ex) {
1357 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
1358 }
1359
Dan Sandlera5e0f412014-01-23 15:11:54 -05001360 if (publicViewLocal == null) {
1361 // Add a basic notification template
1362 publicViewLocal = LayoutInflater.from(mContext).inflate(
Jorim Jaggied713ee2014-09-09 23:56:09 +02001363 R.layout.notification_public_default,
Selim Cinek8d490d42015-04-10 00:05:50 -07001364 contentContainerPublic, false);
Selim Cinekcab4a602014-09-03 14:47:57 +02001365 publicViewLocal.setIsRootNamespace(true);
Selim Cinek8d490d42015-04-10 00:05:50 -07001366 contentContainerPublic.setContractedChild(publicViewLocal);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001367
Jorim Jaggied713ee2014-09-09 23:56:09 +02001368 final TextView title = (TextView) publicViewLocal.findViewById(R.id.title);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001369 try {
Dan Sandlere9a4e902014-07-31 01:06:43 -04001370 title.setText(pmUser.getApplicationLabel(
1371 pmUser.getApplicationInfo(entry.notification.getPackageName(), 0)));
Dan Sandlera5e0f412014-01-23 15:11:54 -05001372 } catch (NameNotFoundException e) {
1373 title.setText(entry.notification.getPackageName());
1374 }
1375
Jorim Jaggied713ee2014-09-09 23:56:09 +02001376 final ImageView icon = (ImageView) publicViewLocal.findViewById(R.id.icon);
Kenny Guy98193ea2014-07-24 19:54:37 +01001377 final ImageView profileBadge = (ImageView) publicViewLocal.findViewById(
Jorim Jaggied713ee2014-09-09 23:56:09 +02001378 R.id.profile_badge_line3);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001379
1380 final StatusBarIcon ic = new StatusBarIcon(entry.notification.getPackageName(),
1381 entry.notification.getUser(),
1382 entry.notification.getNotification().icon,
1383 entry.notification.getNotification().iconLevel,
1384 entry.notification.getNotification().number,
1385 entry.notification.getNotification().tickerText);
1386
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001387 Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
1388 icon.setImageDrawable(iconDrawable);
Dianne Hackborn955d8d62014-10-07 20:17:19 -07001389 if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP
Jorim Jaggia1eeade2014-09-08 22:34:39 +02001390 || mNotificationColorUtil.isGrayscaleIcon(iconDrawable)) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001391 icon.setBackgroundResource(
Jorim Jaggi445d3c02014-08-19 22:33:42 +02001392 com.android.internal.R.drawable.notification_icon_legacy_bg);
1393 int padding = mContext.getResources().getDimensionPixelSize(
1394 com.android.internal.R.dimen.notification_large_icon_circle_padding);
1395 icon.setPadding(padding, padding, padding, padding);
Jorim Jaggia1eeade2014-09-08 22:34:39 +02001396 if (sbn.getNotification().color != Notification.COLOR_DEFAULT) {
1397 icon.getBackground().setColorFilter(
1398 sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
1399 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001400 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001401
Kenny Guy98193ea2014-07-24 19:54:37 +01001402 if (profileBadge != null) {
Svetoslavc7d62f02014-09-04 15:39:54 -07001403 Drawable profileDrawable = mContext.getPackageManager().getUserBadgeForDensity(
1404 entry.notification.getUser(), 0);
Kenny Guy8a0101b2014-05-08 23:34:12 +01001405 if (profileDrawable != null) {
Kenny Guy98193ea2014-07-24 19:54:37 +01001406 profileBadge.setImageDrawable(profileDrawable);
1407 profileBadge.setVisibility(View.VISIBLE);
Kenny Guy8a0101b2014-05-08 23:34:12 +01001408 } else {
Kenny Guy98193ea2014-07-24 19:54:37 +01001409 profileBadge.setVisibility(View.GONE);
Kenny Guy8a0101b2014-05-08 23:34:12 +01001410 }
1411 }
1412
Dan Sandler18bb4f92014-07-14 16:48:27 -04001413 final View privateTime = contentViewLocal.findViewById(com.android.internal.R.id.time);
Jorim Jaggied713ee2014-09-09 23:56:09 +02001414 final DateTimeView time = (DateTimeView) publicViewLocal.findViewById(R.id.time);
Dan Sandler18bb4f92014-07-14 16:48:27 -04001415 if (privateTime != null && privateTime.getVisibility() == View.VISIBLE) {
Jorim Jaggied713ee2014-09-09 23:56:09 +02001416 time.setVisibility(View.VISIBLE);
1417 time.setTime(entry.notification.getNotification().when);
Dan Sandler18bb4f92014-07-14 16:48:27 -04001418 }
1419
Jorim Jaggied713ee2014-09-09 23:56:09 +02001420 final TextView text = (TextView) publicViewLocal.findViewById(R.id.text);
Dan Sandler18bb4f92014-07-14 16:48:27 -04001421 if (text != null) {
1422 text.setText(R.string.notification_hidden_text);
1423 text.setTextAppearance(mContext,
Alan Viverette41ad3942014-08-27 15:06:51 -07001424 R.style.TextAppearance_Material_Notification_Parenthetical);
Dan Sandler18bb4f92014-07-14 16:48:27 -04001425 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001426
Jorim Jaggi445d3c02014-08-19 22:33:42 +02001427 int topPadding = Notification.Builder.calculateTopPadding(mContext,
1428 false /* hasThreeLines */,
1429 mContext.getResources().getConfiguration().fontScale);
1430 title.setPadding(0, topPadding, 0, 0);
1431
Jorim Jaggi36b15232014-06-10 17:24:20 +02001432 entry.autoRedacted = true;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001433 }
1434
Daniel Sandlerb9301c32012-08-14 15:08:24 -04001435 if (MULTIUSER_DEBUG) {
1436 TextView debug = (TextView) row.findViewById(R.id.debug_info);
1437 if (debug != null) {
1438 debug.setVisibility(View.VISIBLE);
Kenny Guy3a7c4a52014-03-03 18:24:03 +00001439 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
Daniel Sandlerb9301c32012-08-14 15:08:24 -04001440 }
1441 }
Daniel Sandler6a858c32012-03-12 14:38:58 -04001442 entry.row = row;
Chris Wrend04f6ce2014-06-11 17:37:28 -04001443 entry.row.setHeightRange(mRowMinHeight, maxHeight);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001444 entry.row.setOnActivatedListener(this);
Chris Wren574a55e2013-07-15 18:48:37 -04001445 entry.expanded = contentViewLocal;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001446 entry.expandedPublic = publicViewLocal;
Chris Wren574a55e2013-07-15 18:48:37 -04001447 entry.setBigContentView(bigContentViewLocal);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001448
Christoph Studer13b895122014-08-26 15:42:12 +02001449 applyColorsAndBackgrounds(sbn, entry);
Jorim Jaggi46739852014-04-15 09:58:24 +02001450
Christoph Studera7fe6312014-06-27 19:32:44 +02001451 // Restore previous flags.
1452 if (hasUserChangedExpansion) {
1453 // Note: setUserExpanded() conveniently ignores calls with
1454 // userExpanded=true if !isExpandable().
1455 row.setUserExpanded(userExpanded);
1456 }
1457 row.setUserLocked(userLocked);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001458 row.setStatusBarNotification(entry.notification);
Adrian Roos497ab022015-02-10 20:49:33 +01001459 applyRemoteInput(entry);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001460 return true;
1461 }
1462
Adrian Roos497ab022015-02-10 20:49:33 +01001463 /**
1464 * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
1465 * via first-class API.
1466 *
1467 * TODO: Remove once enough apps specify remote inputs on their own.
1468 */
1469 private void processForRemoteInput(Notification n) {
1470 if (!ENABLE_REMOTE_INPUT) return;
1471
1472 if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") &&
1473 (n.actions == null || n.actions.length == 0)) {
1474 Notification.Action viableAction = null;
1475 Notification.WearableExtender we = new Notification.WearableExtender(n);
1476
1477 List<Notification.Action> actions = we.getActions();
1478 final int numActions = actions.size();
1479
1480 for (int i = 0; i < numActions; i++) {
1481 Notification.Action action = actions.get(i);
1482 RemoteInput[] remoteInputs = action.getRemoteInputs();
1483 for (RemoteInput ri : action.getRemoteInputs()) {
1484 if (ri.getAllowFreeFormInput()) {
1485 viableAction = action;
1486 break;
1487 }
1488 }
1489 if (viableAction != null) {
1490 break;
1491 }
1492 }
1493
1494 if (viableAction != null) {
1495 Notification stripped = n.clone();
1496 Notification.Builder.stripForDelivery(stripped);
1497 stripped.actions = new Notification.Action[] { viableAction };
1498 stripped.extras.putBoolean("android.rebuild.contentView", true);
1499 stripped.contentView = null;
1500 stripped.extras.putBoolean("android.rebuild.bigView", true);
1501 stripped.bigContentView = null;
Selim Cinek8d490d42015-04-10 00:05:50 -07001502 stripped.extras.putBoolean("android.rebuild.hudView", true);
1503 stripped.headsUpContentView = null;
Adrian Roos497ab022015-02-10 20:49:33 +01001504
1505 Notification rebuilt = Notification.Builder.rebuild(mContext, stripped);
1506
1507 n.actions = rebuilt.actions;
1508 n.bigContentView = rebuilt.bigContentView;
1509 n.headsUpContentView = rebuilt.headsUpContentView;
1510 n.publicVersion = rebuilt.publicVersion;
1511 }
1512 }
1513 }
1514
1515 private void applyRemoteInput(final Entry entry) {
1516 if (!ENABLE_REMOTE_INPUT) return;
1517
1518 RemoteInput remoteInput = null;
1519
1520 // See if the notification has exactly one action and this action allows free-form input
1521 // TODO: relax restrictions once we support more than one remote input action.
1522 Notification.Action[] actions = entry.notification.getNotification().actions;
1523 if (actions != null && actions.length == 1) {
1524 if (actions[0].getRemoteInputs() != null) {
1525 for (RemoteInput ri : actions[0].getRemoteInputs()) {
1526 if (ri.getAllowFreeFormInput()) {
1527 remoteInput = ri;
1528 break;
1529 }
1530 }
1531 }
1532 }
1533
1534 // See if we have somewhere to put that remote input
Selim Cinek8d490d42015-04-10 00:05:50 -07001535 if (remoteInput != null) {
1536 if (entry.expandedBig != null) {
1537 inflateRemoteInput(entry.expandedBig, remoteInput, actions);
1538 }
1539 View headsUpChild = entry.row.getPrivateLayout().getHeadsUpChild();
1540 if (headsUpChild != null) {
1541 inflateRemoteInput(headsUpChild, remoteInput, actions);
Adrian Roos497ab022015-02-10 20:49:33 +01001542 }
1543 }
1544
Selim Cinek8d490d42015-04-10 00:05:50 -07001545 }
1546
1547 private void inflateRemoteInput(View view, RemoteInput remoteInput,
1548 Notification.Action[] actions) {
1549 View actionContainerCandidate = view.findViewById(com.android.internal.R.id.actions);
1550 if (actionContainerCandidate instanceof ViewGroup) {
1551 ViewGroup actionContainer = (ViewGroup) actionContainerCandidate;
Adrian Roos497ab022015-02-10 20:49:33 +01001552 actionContainer.removeAllViews();
1553 actionContainer.addView(
1554 RemoteInputView.inflate(mContext, actionContainer, actions[0], remoteInput));
1555 }
1556 }
1557
Chris Wren930ecca2014-11-12 17:43:41 -05001558 public NotificationClicker makeClicker(PendingIntent intent, String notificationKey) {
1559 return new NotificationClicker(intent, notificationKey);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001560 }
1561
Chris Wren157026f2013-06-28 16:54:01 -04001562 protected class NotificationClicker implements View.OnClickListener {
Daniel Sandler6a858c32012-03-12 14:38:58 -04001563 private PendingIntent mIntent;
Christoph Studer03b87a22014-04-30 17:33:27 +02001564 private final String mNotificationKey;
Daniel Sandler6a858c32012-03-12 14:38:58 -04001565
Chris Wren930ecca2014-11-12 17:43:41 -05001566 public NotificationClicker(PendingIntent intent, String notificationKey) {
Daniel Sandler6a858c32012-03-12 14:38:58 -04001567 mIntent = intent;
Christoph Studer03b87a22014-04-30 17:33:27 +02001568 mNotificationKey = notificationKey;
Daniel Sandler6a858c32012-03-12 14:38:58 -04001569 }
1570
Adrian Roos7d7090d2014-05-21 13:10:23 +02001571 public void onClick(final View v) {
Chris Wren79ac09e2014-10-29 14:20:19 -04001572 if (NOTIFICATION_CLICK_DEBUG) {
1573 Log.d(TAG, "Clicked on content of " + mNotificationKey);
1574 }
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001575 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
Jorim Jaggi85dc23c2014-09-08 14:42:29 +02001576 final boolean afterKeyguardGone = mIntent.isActivity()
1577 && PreviewInflater.wouldLaunchResolverActivity(mContext, mIntent.getIntent(),
1578 mCurrentUserId);
Adrian Roos4314f6d2014-05-28 14:10:27 +02001579 dismissKeyguardThenExecute(new OnDismissAction() {
Adrian Roos7d7090d2014-05-21 13:10:23 +02001580 public boolean onDismiss() {
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001581 if (mHeadsUpManager.isHeadsUp(mNotificationKey)) {
Christoph Studerc6a656c2015-01-22 15:02:49 +01001582 // Release the HUN notification to the shade.
1583 //
1584 // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
1585 // become canceled shortly by NoMan, but we can't assume that.
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001586 mHeadsUpManager.releaseImmediately(mNotificationKey);
Adrian Roos7d7090d2014-05-21 13:10:23 +02001587 }
Jorim Jaggi89f7f282014-10-17 19:54:15 +02001588 new Thread() {
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001589 @Override
1590 public void run() {
Jorim Jaggi89f7f282014-10-17 19:54:15 +02001591 try {
1592 if (keyguardShowing && !afterKeyguardGone) {
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001593 ActivityManagerNative.getDefault()
1594 .keyguardWaitingForActivityDrawn();
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001595 }
Jorim Jaggi89f7f282014-10-17 19:54:15 +02001596
1597 // The intent we are sending is for the application, which
1598 // won't have permission to immediately start an activity after
1599 // the user switches to home. We know it is safe to do at this
1600 // point, so make sure new activity switches are now allowed.
1601 ActivityManagerNative.getDefault().resumeAppSwitches();
1602 } catch (RemoteException e) {
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001603 }
Daniel Sandler6a858c32012-03-12 14:38:58 -04001604
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001605 if (mIntent != null) {
1606 try {
1607 mIntent.send();
1608 } catch (PendingIntent.CanceledException e) {
1609 // the stack trace isn't very helpful here.
1610 // Just log the exception message.
1611 Log.w(TAG, "Sending contentIntent failed: " + e);
Adrian Roos7d7090d2014-05-21 13:10:23 +02001612
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001613 // TODO: Dismiss Keyguard.
1614 }
1615 if (mIntent.isActivity()) {
Jorim Jaggi85dc23c2014-09-08 14:42:29 +02001616 overrideActivityPendingAppTransition(keyguardShowing
1617 && !afterKeyguardGone);
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001618 }
1619 }
1620
1621 try {
1622 mBarService.onNotificationClick(mNotificationKey);
1623 } catch (RemoteException ex) {
1624 // system process is dead if we're here.
1625 }
Adrian Roos7d7090d2014-05-21 13:10:23 +02001626 }
Jorim Jaggi89f7f282014-10-17 19:54:15 +02001627 }.start();
Adrian Roos7d7090d2014-05-21 13:10:23 +02001628
1629 // close the shade if it was open
Jorim Jaggi4eedc1d2014-10-27 13:45:56 +01001630 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
Jorim Jaggi27c9b742015-04-09 10:34:49 -07001631 true /* force */, true /* delayed */);
Adrian Roos7d7090d2014-05-21 13:10:23 +02001632 visibilityChanged(false);
1633
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001634 return mIntent != null && mIntent.isActivity();
Daniel Sandler6a858c32012-03-12 14:38:58 -04001635 }
Jorim Jaggi85dc23c2014-09-08 14:42:29 +02001636 }, afterKeyguardGone);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001637 }
1638 }
Chris Wrenf29c3b42013-11-27 16:30:30 -05001639
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001640 public void animateCollapsePanels(int flags, boolean force) {
1641 }
1642
Jorim Jaggi27c9b742015-04-09 10:34:49 -07001643 public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
1644 }
1645
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001646 public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
1647 if (keyguardShowing) {
1648 try {
1649 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
1650 } catch (RemoteException e) {
1651 Log.w(TAG, "Error overriding app transition: " + e);
1652 }
1653 }
1654 }
1655
Christoph Studere8e28652014-10-29 17:27:53 +01001656 protected void visibilityChanged(boolean visible) {
1657 if (mVisible != visible) {
1658 mVisible = visible;
1659 if (!visible) {
1660 dismissPopups();
1661 }
1662 }
1663 updateVisibleToUser();
1664 }
1665
1666 protected void updateVisibleToUser() {
1667 boolean oldVisibleToUser = mVisibleToUser;
1668 mVisibleToUser = mVisible && mScreenOnFromKeyguard;
1669
1670 if (oldVisibleToUser != mVisibleToUser) {
1671 handleVisibleToUserChanged(mVisibleToUser);
1672 }
1673 }
1674
Daniel Sandler6a858c32012-03-12 14:38:58 -04001675 /**
Christoph Studere8e28652014-10-29 17:27:53 +01001676 * The LEDs are turned off when the notification panel is shown, even just a little bit.
Daniel Sandler6a858c32012-03-12 14:38:58 -04001677 * This was added last-minute and is inconsistent with the way the rest of the notifications
1678 * are handled, because the notification isn't really cancelled. The lights are just
1679 * turned off. If any other notifications happen, the lights will turn back on. Steve says
1680 * this is what he wants. (see bug 1131461)
1681 */
Christoph Studere8e28652014-10-29 17:27:53 +01001682 protected void handleVisibleToUserChanged(boolean visibleToUser) {
1683 try {
1684 if (visibleToUser) {
Christoph Studer1f32c652014-11-26 15:32:20 +01001685 // Only stop blinking, vibrating, ringing when the user went into the shade
1686 // manually (SHADE or SHADE_LOCKED).
1687 boolean clearNotificationEffects =
1688 (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
1689 mBarService.onPanelRevealed(clearNotificationEffects);
Christoph Studere8e28652014-10-29 17:27:53 +01001690 } else {
1691 mBarService.onPanelHidden();
Dan Sandler4247a5c2014-07-23 15:58:08 -04001692 }
Christoph Studere8e28652014-10-29 17:27:53 +01001693 } catch (RemoteException ex) {
1694 // Won't fail unless the world has ended.
Daniel Sandler6a858c32012-03-12 14:38:58 -04001695 }
1696 }
1697
Chris Wren0c8275b2012-05-08 13:36:48 -04001698 /**
1699 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
1700 * about the failure.
1701 *
1702 * WARNING: this will call back into us. Don't hold any locks.
1703 */
Christoph Studer71f18fd2014-05-20 17:02:04 +02001704 void handleNotificationError(StatusBarNotification n, String message) {
Christoph Studere71fefc2014-06-24 16:16:49 +02001705 removeNotification(n.getKey(), null);
Chris Wren0c8275b2012-05-08 13:36:48 -04001706 try {
Kenny Guy3a7c4a52014-03-03 18:24:03 +00001707 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
1708 n.getInitialPid(), message, n.getUserId());
Chris Wren0c8275b2012-05-08 13:36:48 -04001709 } catch (RemoteException ex) {
1710 // The end is nigh.
1711 }
1712 }
1713
Christoph Studerd0694b62014-06-04 16:36:01 +02001714 protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
Christoph Studer37fe6932014-05-26 13:10:30 +02001715 NotificationData.Entry entry = mNotificationData.remove(key, ranking);
Chris Wren0c8275b2012-05-08 13:36:48 -04001716 if (entry == null) {
John Spurlockcd686b52013-06-05 10:13:46 -04001717 Log.w(TAG, "removeNotification for unknown key: " + key);
Chris Wren0c8275b2012-05-08 13:36:48 -04001718 return null;
1719 }
Christoph Studer37fe6932014-05-26 13:10:30 +02001720 updateNotifications();
Chris Wren0c8275b2012-05-08 13:36:48 -04001721 return entry.notification;
1722 }
1723
Selim Cinek8d490d42015-04-10 00:05:50 -07001724 protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
Chris Wren0c8275b2012-05-08 13:36:48 -04001725 if (DEBUG) {
Christoph Studera0506e72014-07-31 20:27:39 +02001726 Log.d(TAG, "createNotificationViews(notification=" + sbn);
Chris Wren0c8275b2012-05-08 13:36:48 -04001727 }
Selim Cinek379ff8f2015-02-20 17:03:16 +01001728 final StatusBarIconView iconView = createIcon(sbn);
1729 if (iconView == null) {
1730 return null;
1731 }
1732
1733 // Construct the expanded view.
1734 NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
Selim Cinek8d490d42015-04-10 00:05:50 -07001735 if (!inflateViews(entry, mStackScroller)) {
Selim Cinek379ff8f2015-02-20 17:03:16 +01001736 handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
1737 return null;
1738 }
1739 return entry;
1740 }
1741
1742 protected StatusBarIconView createIcon(StatusBarNotification sbn) {
Chris Wren0c8275b2012-05-08 13:36:48 -04001743 // Construct the icon.
Christoph Studera0506e72014-07-31 20:27:39 +02001744 Notification n = sbn.getNotification();
Chris Wren0c8275b2012-05-08 13:36:48 -04001745 final StatusBarIconView iconView = new StatusBarIconView(mContext,
Christoph Studera0506e72014-07-31 20:27:39 +02001746 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);
Chris Wren0c8275b2012-05-08 13:36:48 -04001747 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
1748
Christoph Studera0506e72014-07-31 20:27:39 +02001749 final StatusBarIcon ic = new StatusBarIcon(sbn.getPackageName(),
1750 sbn.getUser(),
1751 n.icon,
1752 n.iconLevel,
1753 n.number,
1754 n.tickerText);
Chris Wren0c8275b2012-05-08 13:36:48 -04001755 if (!iconView.set(ic)) {
Christoph Studera0506e72014-07-31 20:27:39 +02001756 handleNotificationError(sbn, "Couldn't create icon: " + ic);
Chris Wren0c8275b2012-05-08 13:36:48 -04001757 return null;
1758 }
Selim Cinek379ff8f2015-02-20 17:03:16 +01001759 return iconView;
Chris Wrenf0048ce2013-08-07 16:43:43 -04001760 }
Chris Wren0c8275b2012-05-08 13:36:48 -04001761
Christoph Studerd0694b62014-06-04 16:36:01 +02001762 protected void addNotificationViews(Entry entry, RankingMap ranking) {
Raju Yadav5f330262014-03-24 10:44:00 +01001763 if (entry == null) {
1764 return;
1765 }
Chris Wren0c8275b2012-05-08 13:36:48 -04001766 // Add the expanded view and icon.
Christoph Studer37fe6932014-05-26 13:10:30 +02001767 mNotificationData.add(entry, ranking);
1768 updateNotifications();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001769 }
1770
Jorim Jaggi251957d2014-04-09 04:24:09 +02001771 /**
Jorim Jaggid4a57442014-04-10 02:45:55 +02001772 * @return The number of notifications we show on Keyguard.
1773 */
1774 protected abstract int getMaxKeyguardNotifications();
1775
1776 /**
Jorim Jaggi251957d2014-04-09 04:24:09 +02001777 * Updates expanded, dimmed and locked states of notification rows.
1778 */
1779 protected void updateRowStates() {
Jorim Jaggif6411742014-08-05 17:10:43 +00001780 int maxKeyguardNotifications = getMaxKeyguardNotifications();
1781 mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
1782
Christoph Studerc8db24b2014-07-25 17:50:30 +02001783 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1784 final int N = activeNotifications.size();
1785
Jorim Jaggif6411742014-08-05 17:10:43 +00001786 int visibleNotifications = 0;
Jorim Jaggiecbab362014-04-23 16:13:15 +02001787 boolean onKeyguard = mState == StatusBarState.KEYGUARD;
Christoph Studer37fe6932014-05-26 13:10:30 +02001788 for (int i = 0; i < N; i++) {
Christoph Studerc8db24b2014-07-25 17:50:30 +02001789 NotificationData.Entry entry = activeNotifications.get(i);
Jorim Jaggiecbab362014-04-23 16:13:15 +02001790 if (onKeyguard) {
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001791 entry.row.setExpansionDisabled(true);
Chris Wren3ddab0d2012-08-02 16:52:21 -04001792 } else {
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001793 entry.row.setExpansionDisabled(false);
Jorim Jaggi649b7082014-04-03 13:15:18 +02001794 if (!entry.row.isUserLocked()) {
Christoph Studer37fe6932014-05-26 13:10:30 +02001795 boolean top = (i == 0);
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001796 entry.row.setSystemExpanded(top);
Jorim Jaggi649b7082014-04-03 13:15:18 +02001797 }
Chris Wren8fd12652012-05-09 21:25:57 -04001798 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001799 boolean isInvisibleChild = !mGroupManager.isVisible(entry.notification);
Jorim Jaggif6411742014-08-05 17:10:43 +00001800 boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
Jorim Jaggi9261b3b2014-09-08 14:02:07 +02001801 if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
Jorim Jaggida30a032014-09-05 15:11:46 +02001802 (onKeyguard && (visibleNotifications >= maxKeyguardNotifications
Selim Cinekb5605e52015-02-20 18:21:41 +01001803 || !showOnKeyguard || isInvisibleChild))) {
Jorim Jaggif6411742014-08-05 17:10:43 +00001804 entry.row.setVisibility(View.GONE);
Selim Cinekb5605e52015-02-20 18:21:41 +01001805 if (onKeyguard && showOnKeyguard && !isInvisibleChild) {
Jorim Jaggif6411742014-08-05 17:10:43 +00001806 mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
1807 }
1808 } else {
1809 boolean wasGone = entry.row.getVisibility() == View.GONE;
1810 entry.row.setVisibility(View.VISIBLE);
Selim Cinekb5605e52015-02-20 18:21:41 +01001811 if (!isInvisibleChild) {
1812 if (wasGone) {
1813 // notify the scroller of a child addition
1814 mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */);
1815 }
1816 visibleNotifications++;
Jorim Jaggif6411742014-08-05 17:10:43 +00001817 }
Jorim Jaggif6411742014-08-05 17:10:43 +00001818 }
Chris Wren8fd12652012-05-09 21:25:57 -04001819 }
Jorim Jaggif6411742014-08-05 17:10:43 +00001820
1821 if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
1822 mKeyguardIconOverflowContainer.setVisibility(View.VISIBLE);
1823 } else {
1824 mKeyguardIconOverflowContainer.setVisibility(View.GONE);
1825 }
1826
1827 mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
1828 mStackScroller.getChildCount() - 3);
Selim Cinekbecf5e32014-08-12 14:29:18 +02001829 mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
1830 mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
Jorim Jaggif6411742014-08-05 17:10:43 +00001831 }
1832
1833 private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
1834 return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
Chris Wren8fd12652012-05-09 21:25:57 -04001835 }
1836
John Spurlocke677d712014-02-13 12:52:19 -05001837 protected void setZenMode(int mode) {
John Spurlock110e5b12014-02-27 13:09:35 -05001838 if (!isDeviceProvisioned()) return;
John Spurlocke677d712014-02-13 12:52:19 -05001839 mZenMode = mode;
Christoph Studer37fe6932014-05-26 13:10:30 +02001840 updateNotifications();
John Spurlocke677d712014-02-13 12:52:19 -05001841 }
1842
Dan Sandler52e5701e2014-07-22 23:14:54 -04001843 // extended in PhoneStatusBar
1844 protected void setShowLockscreenNotifications(boolean show) {
1845 mShowLockscreenNotifications = show;
1846 }
1847
1848 private void updateLockscreenNotificationSetting() {
1849 final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1850 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
1851 1,
1852 mCurrentUserId) != 0;
Adrian Roosbd3409c2014-08-12 18:21:09 +02001853 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
1854 null /* admin */, mCurrentUserId);
1855 final boolean allowedByDpm = (dpmFlags
1856 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
1857 setShowLockscreenNotifications(show && allowedByDpm);
Dan Sandler52e5701e2014-07-22 23:14:54 -04001858 }
1859
Chris Wren0c8275b2012-05-08 13:36:48 -04001860 protected abstract void setAreThereNotifications();
Christoph Studer37fe6932014-05-26 13:10:30 +02001861 protected abstract void updateNotifications();
Jorim Jaggi2fdeeab2015-04-01 15:13:03 -07001862 public abstract boolean shouldDisableNavbarGestures();
Chris Wren0c8275b2012-05-08 13:36:48 -04001863
Christoph Studere71fefc2014-06-24 16:16:49 +02001864 public abstract void addNotification(StatusBarNotification notification,
Selim Cinek379ff8f2015-02-20 17:03:16 +01001865 RankingMap ranking, Entry oldEntry);
Christoph Studere71fefc2014-06-24 16:16:49 +02001866 protected abstract void updateNotificationRanking(RankingMap ranking);
1867 public abstract void removeNotification(String key, RankingMap ranking);
Christoph Studera7fe6312014-06-27 19:32:44 +02001868
Christoph Studere71fefc2014-06-24 16:16:49 +02001869 public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
Christoph Studer71f18fd2014-05-20 17:02:04 +02001870 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
Chris Wren0c8275b2012-05-08 13:36:48 -04001871
Chris Wrend04f6ce2014-06-11 17:37:28 -04001872 final String key = notification.getKey();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001873 Entry entry = mNotificationData.get(key);
1874 if (entry == null) {
Chris Wren0c8275b2012-05-08 13:36:48 -04001875 return;
1876 }
1877
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001878 Notification n = notification.getNotification();
1879 if (DEBUG) {
1880 logUpdate(entry, n);
1881 }
1882 boolean applyInPlace = shouldApplyInPlace(entry, n);
1883 final boolean shouldInterrupt = shouldInterrupt(notification);
Selim Cineka59ecc32015-04-07 10:51:49 -07001884 final boolean alertAgain = alertAgain(entry, n);
Chris Wren0c8275b2012-05-08 13:36:48 -04001885
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001886 entry.notification = notification;
1887 mGroupManager.onEntryUpdated(entry, entry.notification);
1888
1889 boolean updateSuccessful = false;
1890 if (applyInPlace) {
1891 // We can just reapply the notifications in place
1892 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
1893 try {
1894 if (entry.icon != null) {
1895 // Update the icon
1896 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
1897 notification.getUser(),
1898 n.icon,
1899 n.iconLevel,
1900 n.number,
1901 n.tickerText);
1902 entry.icon.setNotification(n);
1903 if (!entry.icon.set(ic)) {
1904 handleNotificationError(notification, "Couldn't update icon: " + ic);
1905 return;
1906 }
1907 }
Selim Cinek8d490d42015-04-10 00:05:50 -07001908 updateNotificationViews(entry, notification);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001909 updateSuccessful = true;
1910 }
1911 catch (RuntimeException e) {
1912 // It failed to add cleanly. Log, and remove the view from the panel.
1913 Log.w(TAG, "Couldn't reapply views for package " + n.contentView.getPackage(), e);
1914 }
1915 }
1916 if (!updateSuccessful) {
1917 if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
1918 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
1919 notification.getUser(),
1920 n.icon,
1921 n.iconLevel,
1922 n.number,
1923 n.tickerText);
1924 entry.icon.setNotification(n);
1925 entry.icon.set(ic);
Selim Cinek8d490d42015-04-10 00:05:50 -07001926 inflateViews(entry, mStackScroller);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001927 }
Selim Cinekfbe9a442015-04-13 16:09:49 -07001928 updateHeadsUp(key, entry, shouldInterrupt, alertAgain);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001929 mNotificationData.updateRanking(ranking);
1930 updateNotifications();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001931
1932 // Update the veto button accordingly (and as a result, whether this row is
1933 // swipe-dismissable)
1934 updateNotificationVetoButton(entry.row, notification);
1935
1936 // Is this for you?
1937 boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
1938 if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
1939
1940 // Recalculate the position of the sliding windows and the titles.
1941 setAreThereNotifications();
1942 }
1943
1944 private void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
1945 boolean alertAgain) {
1946 final boolean wasHeadsUp = isHeadsUp(key);
1947 if (wasHeadsUp) {
1948 mHeadsUpManager.updateNotification(entry, alertAgain);
1949 if (!shouldInterrupt) {
Selim Cineka59ecc32015-04-07 10:51:49 -07001950 // We don't want this to be interrupting anymore, lets remove it
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001951 mHeadsUpManager.removeNotification(key);
1952 }
Selim Cineka59ecc32015-04-07 10:51:49 -07001953 } else if (shouldInterrupt && alertAgain) {
1954 // This notification was updated to be a heads-up, show it!
1955 mHeadsUpManager.showNotification(entry);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001956 }
1957 }
1958
1959 private void logUpdate(Entry oldEntry, Notification n) {
1960 StatusBarNotification oldNotification = oldEntry.notification;
1961 Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
1962 + " ongoing=" + oldNotification.isOngoing()
1963 + " expanded=" + oldEntry.expanded
1964 + " contentView=" + oldNotification.getNotification().contentView
1965 + " bigContentView=" + oldNotification.getNotification().bigContentView
1966 + " publicView=" + oldNotification.getNotification().publicVersion
1967 + " rowParent=" + oldEntry.row.getParent());
1968 Log.d(TAG, "new notification: when=" + n.when
1969 + " ongoing=" + oldNotification.isOngoing()
1970 + " contentView=" + n.contentView
1971 + " bigContentView=" + n.bigContentView
1972 + " publicView=" + n.publicVersion);
1973 }
1974
1975 /**
1976 * @return whether we can just reapply the RemoteViews in place when it is updated
1977 */
1978 private boolean shouldApplyInPlace(Entry entry, Notification n) {
1979 StatusBarNotification oldNotification = entry.notification;
Chris Wren0c8275b2012-05-08 13:36:48 -04001980 // XXX: modify when we do something more intelligent with the two content views
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001981 final RemoteViews oldContentView = oldNotification.getNotification().contentView;
Christoph Studera0506e72014-07-31 20:27:39 +02001982 final RemoteViews contentView = n.contentView;
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001983 final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
Christoph Studera0506e72014-07-31 20:27:39 +02001984 final RemoteViews bigContentView = n.bigContentView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001985 final RemoteViews oldHeadsUpContentView
1986 = oldNotification.getNotification().headsUpContentView;
Christoph Studera0506e72014-07-31 20:27:39 +02001987 final RemoteViews headsUpContentView = n.headsUpContentView;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001988 final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
1989 final RemoteViews oldPublicContentView = oldPublicNotification != null
1990 ? oldPublicNotification.contentView : null;
Christoph Studera0506e72014-07-31 20:27:39 +02001991 final Notification publicNotification = n.publicVersion;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001992 final RemoteViews publicContentView = publicNotification != null
1993 ? publicNotification.contentView : null;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001994 boolean contentsUnchanged = entry.expanded != null
Chris Wren0c8275b2012-05-08 13:36:48 -04001995 && contentView.getPackage() != null
1996 && oldContentView.getPackage() != null
1997 && oldContentView.getPackage().equals(contentView.getPackage())
1998 && oldContentView.getLayoutId() == contentView.getLayoutId();
Chris Wrene9e37722012-05-11 17:40:14 -04001999 // large view may be null
2000 boolean bigContentsUnchanged =
Selim Cinekb8f09cf2015-03-16 17:09:28 -07002001 (entry.getBigContentView() == null && bigContentView == null)
2002 || ((entry.getBigContentView() != null && bigContentView != null)
Chris Wrene9e37722012-05-11 17:40:14 -04002003 && bigContentView.getPackage() != null
2004 && oldBigContentView.getPackage() != null
2005 && oldBigContentView.getPackage().equals(bigContentView.getPackage())
2006 && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
Chris Wren8fd39ec2014-02-27 17:43:26 -05002007 boolean headsUpContentsUnchanged =
2008 (oldHeadsUpContentView == null && headsUpContentView == null)
2009 || ((oldHeadsUpContentView != null && headsUpContentView != null)
2010 && headsUpContentView.getPackage() != null
2011 && oldHeadsUpContentView.getPackage() != null
2012 && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
2013 && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
Dan Sandlera5e0f412014-01-23 15:11:54 -05002014 boolean publicUnchanged =
2015 (oldPublicContentView == null && publicContentView == null)
2016 || ((oldPublicContentView != null && publicContentView != null)
2017 && publicContentView.getPackage() != null
2018 && oldPublicContentView.getPackage() != null
2019 && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
2020 && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
Selim Cinekb8f09cf2015-03-16 17:09:28 -07002021 return contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
2022 && publicUnchanged;
Chris Wrenf29c3b42013-11-27 16:30:30 -05002023 }
2024
Selim Cinek8d490d42015-04-10 00:05:50 -07002025 private void updateNotificationViews(Entry entry, StatusBarNotification notification) {
Chris Wrene03f4e12013-08-08 16:48:48 -04002026 final RemoteViews contentView = notification.getNotification().contentView;
Selim Cinek8d490d42015-04-10 00:05:50 -07002027 final RemoteViews bigContentView = notification.getNotification().bigContentView;
2028 final RemoteViews headsUpContentView = notification.getNotification().headsUpContentView;
John Spurlock70cf4c12014-02-13 09:39:14 -05002029 final Notification publicVersion = notification.getNotification().publicVersion;
2030 final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView
2031 : null;
Dan Sandlera5e0f412014-01-23 15:11:54 -05002032
Chris Wrene03f4e12013-08-08 16:48:48 -04002033 // Reapply the RemoteViews
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002034 contentView.reapply(mContext, entry.expanded, mOnClickHandler);
Chris Wrene03f4e12013-08-08 16:48:48 -04002035 if (bigContentView != null && entry.getBigContentView() != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002036 bigContentView.reapply(mContext, entry.getBigContentView(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01002037 mOnClickHandler);
Chris Wrene03f4e12013-08-08 16:48:48 -04002038 }
Selim Cinek8d490d42015-04-10 00:05:50 -07002039 View headsUpChild = entry.row.getPrivateLayout().getHeadsUpChild();
2040 if (headsUpContentView != null && headsUpChild != null) {
2041 headsUpContentView.reapply(mContext, headsUpChild, mOnClickHandler);
2042 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05002043 if (publicContentView != null && entry.getPublicContentView() != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002044 publicContentView.reapply(mContext, entry.getPublicContentView(), mOnClickHandler);
Dan Sandlera5e0f412014-01-23 15:11:54 -05002045 }
Chris Wrene03f4e12013-08-08 16:48:48 -04002046 // update the contentIntent
2047 final PendingIntent contentIntent = notification.getNotification().contentIntent;
2048 if (contentIntent != null) {
Chris Wren930ecca2014-11-12 17:43:41 -05002049 final View.OnClickListener listener = makeClicker(contentIntent, notification.getKey());
Jorim Jaggife40f7d2014-04-28 15:20:04 +02002050 entry.row.setOnClickListener(listener);
Chris Wrene03f4e12013-08-08 16:48:48 -04002051 } else {
Jorim Jaggife40f7d2014-04-28 15:20:04 +02002052 entry.row.setOnClickListener(null);
Chris Wren0c8275b2012-05-08 13:36:48 -04002053 }
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02002054 entry.row.setStatusBarNotification(notification);
Jorim Jaggibe565df2014-04-28 17:51:23 +02002055 entry.row.notifyContentUpdated();
Selim Cinek51ae05d2014-09-09 15:51:38 +02002056 entry.row.resetHeight();
Adrian Roos497ab022015-02-10 20:49:33 +01002057
2058 applyRemoteInput(entry);
Chris Wren0c8275b2012-05-08 13:36:48 -04002059 }
John Spurlock36231282012-06-23 17:11:27 -04002060
Selim Cinekb8f09cf2015-03-16 17:09:28 -07002061 protected void notifyHeadsUpScreenOff() {
2062 escalateHeadsUp();
Chris Wrene97f90b2013-08-07 17:39:35 -04002063 }
2064
Chris Wrend93d5ad2014-09-08 16:34:03 -04002065 private boolean alertAgain(Entry oldEntry, Notification newNotification) {
2066 return oldEntry == null || !oldEntry.hasInterrupted()
2067 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
Chris Wrend04f6ce2014-06-11 17:37:28 -04002068 }
2069
Chris Wren51c75102013-07-16 20:49:17 -04002070 protected boolean shouldInterrupt(StatusBarNotification sbn) {
Christoph Studer2f9dbba2014-09-03 17:35:54 +02002071 if (mNotificationData.shouldFilterOut(sbn)) {
2072 if (DEBUG) {
2073 Log.d(TAG, "Skipping HUN check for " + sbn.getKey() + " since it's filtered out.");
2074 }
2075 return false;
2076 }
2077
Selim Cinekb8f09cf2015-03-16 17:09:28 -07002078 if (mHeadsUpManager.isSnoozed(sbn.getPackageName())) {
Chris Wrena6d4fb62014-11-20 14:46:23 -05002079 return false;
2080 }
2081
Chris Wren51c75102013-07-16 20:49:17 -04002082 Notification notification = sbn.getNotification();
2083 // some predicates to make the boolean logic legible
2084 boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
2085 || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
2086 || notification.sound != null
2087 || notification.vibrate != null;
2088 boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
2089 boolean isFullscreen = notification.fullScreenIntent != null;
Chris Wren22ae46e2014-02-26 18:08:09 -05002090 boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
Chris Wren51c75102013-07-16 20:49:17 -04002091 boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
2092 Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
Selim Cineka8fefa52014-09-08 16:10:50 +02002093 boolean accessibilityForcesLaunch = isFullscreen
2094 && mAccessibilityManager.isTouchExplorationEnabled();
Chris Wren51c75102013-07-16 20:49:17 -04002095
Chris Wren22ae46e2014-02-26 18:08:09 -05002096 boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
Chris Wren51c75102013-07-16 20:49:17 -04002097 && isAllowed
Selim Cineka8fefa52014-09-08 16:10:50 +02002098 && !accessibilityForcesLaunch
Chris Wren51c75102013-07-16 20:49:17 -04002099 && mPowerManager.isScreenOn()
Jim Millerab954542014-10-10 18:21:49 -07002100 && (!mStatusBarKeyguardViewManager.isShowing()
2101 || mStatusBarKeyguardViewManager.isOccluded())
2102 && !mStatusBarKeyguardViewManager.isInputRestricted();
Chris Wren157026f2013-06-28 16:54:01 -04002103 try {
2104 interrupt = interrupt && !mDreamManager.isDreaming();
2105 } catch (RemoteException e) {
2106 Log.d(TAG, "failed to query dream manager", e);
2107 }
Chris Wren51c75102013-07-16 20:49:17 -04002108 if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
Chris Wren157026f2013-06-28 16:54:01 -04002109 return interrupt;
2110 }
2111
John Spurlockcfc359a2013-09-05 10:42:03 -04002112 public void setInteracting(int barWindow, boolean interacting) {
John Spurlockad3e6cb2013-04-30 08:47:43 -04002113 // hook for subclasses
2114 }
John Spurlock5c454122013-06-17 07:35:46 -04002115
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02002116 public void setBouncerShowing(boolean bouncerShowing) {
2117 mBouncerShowing = bouncerShowing;
2118 }
2119
2120 /**
2121 * @return Whether the security bouncer from Keyguard is showing.
2122 */
2123 public boolean isBouncerShowing() {
2124 return mBouncerShowing;
2125 }
2126
John Spurlock5c454122013-06-17 07:35:46 -04002127 public void destroy() {
John Spurlock5c454122013-06-17 07:35:46 -04002128 mContext.unregisterReceiver(mBroadcastReceiver);
Christoph Studere71fefc2014-06-24 16:16:49 +02002129 try {
2130 mNotificationListener.unregisterAsSystemService();
2131 } catch (RemoteException e) {
2132 // Ignore.
Christoph Studer37fe6932014-05-26 13:10:30 +02002133 }
John Spurlock5c454122013-06-17 07:35:46 -04002134 }
Kenny Guy4dcb0dc2014-07-15 12:41:24 +01002135
2136 /**
2137 * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
2138 * return PackageManager for mContext
2139 */
2140 protected PackageManager getPackageManagerForUser(int userId) {
2141 Context contextForUser = mContext;
2142 // UserHandle defines special userId as negative values, e.g. USER_ALL
2143 if (userId >= 0) {
2144 try {
2145 // Create a context for the correct user so if a package isn't installed
2146 // for user 0 we can still load information about the package.
2147 contextForUser =
2148 mContext.createPackageContextAsUser(mContext.getPackageName(),
2149 Context.CONTEXT_RESTRICTED,
2150 new UserHandle(userId));
2151 } catch (NameNotFoundException e) {
2152 // Shouldn't fail to find the package name for system ui.
2153 }
2154 }
2155 return contextForUser.getPackageManager();
2156 }
Chris Wren78403d72014-07-28 10:23:24 +01002157
2158 @Override
2159 public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
2160 try {
2161 mBarService.onNotificationExpansionChanged(key, userAction, expanded);
2162 } catch (RemoteException e) {
2163 // Ignore.
2164 }
2165 }
Jim Millerab954542014-10-10 18:21:49 -07002166
2167 public boolean isKeyguardSecure() {
Dan Sandlere163a642015-03-01 22:58:32 -05002168 if (mStatusBarKeyguardViewManager == null) {
2169 // startKeyguard() hasn't been called yet, so we don't know.
2170 // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
2171 // value onVisibilityChanged().
2172 Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
2173 new Throwable());
2174 return false;
2175 }
Jim Millerab954542014-10-10 18:21:49 -07002176 return mStatusBarKeyguardViewManager.isSecure();
2177 }
Joe Onorato2314aab2010-04-08 16:41:23 -05002178}