blob: de4874f31285ad06e3c666b9ca11bcd72991c999 [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
Selim Cinek29fa89b2015-04-17 10:39:11 -0700690 protected abstract void setHeadsUpUser(int newUserId);
Chris Wrend04f6ce2014-06-11 17:37:28 -0400691
Christoph Studerc8db24b2014-07-25 17:50:30 +0200692 @Override // NotificationData.Environment
693 public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400694 final int thisUserId = mCurrentUserId;
695 final int notificationUserId = n.getUserId();
696 if (DEBUG && MULTIUSER_DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400697 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
Daniel Sandlerb9301c32012-08-14 15:08:24 -0400698 n, thisUserId, notificationUserId));
699 }
Dan Sandlere63ba4b2014-12-05 00:26:24 -0500700 return isCurrentProfile(notificationUserId);
Joe Onorato2314aab2010-04-08 16:41:23 -0500701 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400702
Kenny Guy44fc65f2014-11-28 22:18:14 +0000703 protected boolean isCurrentProfile(int userId) {
704 synchronized (mCurrentProfiles) {
Dan Sandlere63ba4b2014-12-05 00:26:24 -0500705 return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
Kenny Guy44fc65f2014-11-28 22:18:14 +0000706 }
707 }
708
Christoph Studer2e731b52014-08-22 16:01:51 +0200709 @Override
710 public String getCurrentMediaNotificationKey() {
711 return null;
712 }
713
Selim Cinek25fd4e2b2015-02-20 17:46:07 +0100714 @Override
715 public NotificationGroupManager getGroupManager() {
716 return mGroupManager;
717 }
718
Adrian Roos7d7090d2014-05-21 13:10:23 +0200719 /**
720 * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
721 * @param action A dismiss action that is called if it's safe to start the activity.
Jorim Jaggi746f7fa2014-08-27 17:52:46 +0200722 * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone.
Adrian Roos7d7090d2014-05-21 13:10:23 +0200723 */
Jorim Jaggi746f7fa2014-08-27 17:52:46 +0200724 protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
Adrian Roos7d7090d2014-05-21 13:10:23 +0200725 action.onDismiss();
726 }
727
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700728 @Override
729 protected void onConfigurationChanged(Configuration newConfig) {
Daniel Sandler777dcde2013-09-30 10:21:45 -0400730 final Locale locale = mContext.getResources().getConfiguration().locale;
731 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
Dan Sandler539aad42014-08-04 00:43:39 -0400732 final float fontScale = newConfig.fontScale;
733
734 if (! locale.equals(mLocale) || ld != mLayoutDirection || fontScale != mFontScale) {
Daniel Sandler777dcde2013-09-30 10:21:45 -0400735 if (DEBUG) {
736 Log.v(TAG, String.format(
737 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
738 locale, ld));
739 }
740 mLocale = locale;
741 mLayoutDirection = ld;
742 refreshLayout(ld);
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700743 }
744 }
745
Winson Chungc57ccf02011-10-13 15:04:59 -0700746 protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
747 View vetoButton = row.findViewById(R.id.veto);
Selim Cinek8d490d42015-04-10 00:05:50 -0700748 if (n.isClearable()) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400749 final String _pkg = n.getPackageName();
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400750 final String _tag = n.getTag();
751 final int _id = n.getId();
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000752 final int _userId = n.getUserId();
Winson Chungc57ccf02011-10-13 15:04:59 -0700753 vetoButton.setOnClickListener(new View.OnClickListener() {
754 public void onClick(View v) {
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700755 // Accessibility feedback
756 v.announceForAccessibility(
757 mContext.getString(R.string.accessibility_notification_dismissed));
Winson Chungc57ccf02011-10-13 15:04:59 -0700758 try {
Kenny Guy3a7c4a52014-03-03 18:24:03 +0000759 mBarService.onNotificationClear(_pkg, _tag, _id, _userId);
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700760
Winson Chungc57ccf02011-10-13 15:04:59 -0700761 } catch (RemoteException ex) {
762 // system process is dead if we're here.
763 }
764 }
765 });
766 vetoButton.setVisibility(View.VISIBLE);
767 } else {
768 vetoButton.setVisibility(View.GONE);
769 }
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700770 vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
Winson Chungc57ccf02011-10-13 15:04:59 -0700771 return vetoButton;
772 }
Daniel Sandler6a858c32012-03-12 14:38:58 -0400773
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400774
Christoph Studer13b895122014-08-26 15:42:12 +0200775 protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
Jorim Jaggi46739852014-04-15 09:58:24 +0200776 NotificationData.Entry entry) {
Dan Sandlerfe266a32014-05-15 22:28:06 -0400777
Jorim Jaggi46739852014-04-15 09:58:24 +0200778 if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) {
Dan Sandlerfe266a32014-05-15 22:28:06 -0400779 // Using custom RemoteViews
Jorim Jaggia1eeade2014-09-08 22:34:39 +0200780 if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700781 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
Selim Cinek697178b2014-07-02 19:40:30 +0200782 entry.row.setShowingLegacyBackground(true);
Jorim Jaggi36b15232014-06-10 17:24:20 +0200783 entry.legacy = true;
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400784 }
Dan Sandler842dd772014-05-15 09:36:47 -0400785 } else {
786 // Using platform templates
787 final int color = sbn.getNotification().color;
788 if (isMediaNotification(entry)) {
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +0200789 entry.row.setTintColor(color == Notification.COLOR_DEFAULT
Alan Viverette4a357cd2015-03-18 18:37:18 -0700790 ? mContext.getColor(
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +0200791 R.color.notification_material_background_media_default_color)
792 : color);
Dan Sandler842dd772014-05-15 09:36:47 -0400793 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400794 }
Christoph Studer13b895122014-08-26 15:42:12 +0200795
796 if (entry.icon != null) {
Jorim Jaggidd0107a2015-01-21 22:23:48 +0100797 entry.icon.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
Christoph Studer13b895122014-08-26 15:42:12 +0200798 }
Daniel Sandler96fd7c12012-03-30 16:37:36 -0400799 }
800
Dan Sandler16128f42014-05-21 12:48:22 -0400801 public boolean isMediaNotification(NotificationData.Entry entry) {
Dan Sandler842dd772014-05-15 09:36:47 -0400802 // TODO: confirm that there's a valid media key
803 return entry.expandedBig != null &&
Jorim Jaggi17ee3ec2014-08-29 03:47:31 +0200804 entry.expandedBig.findViewById(com.android.internal.R.id.media_actions) != null;
Dan Sandler842dd772014-05-15 09:36:47 -0400805 }
806
Dan Sandlerf4db75c2014-09-03 18:02:31 +0200807 // The gear button in the guts that links to the app's own notification settings
808 private void startAppOwnNotificationSettingsActivity(Intent intent,
809 final int notificationId, final String notificationTag, final int appUid) {
810 intent.putExtra("notification_id", notificationId);
811 intent.putExtra("notification_tag", notificationTag);
812 startNotificationGutsIntent(intent, appUid);
813 }
814
815 // The (i) button in the guts that links to the system notification settings for that app
Jorim Jaggifb28c0e2014-08-22 16:28:42 +0200816 private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
817 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
Dan Sandlere9a4e902014-07-31 01:06:43 -0400818 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
819 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
Dan Sandlerf4db75c2014-09-03 18:02:31 +0200820 startNotificationGutsIntent(intent, appUid);
821 }
Jorim Jaggifb28c0e2014-08-22 16:28:42 +0200822
Dan Sandlerf4db75c2014-09-03 18:02:31 +0200823 private void startNotificationGutsIntent(final Intent intent, final int appUid) {
Jorim Jaggifb28c0e2014-08-22 16:28:42 +0200824 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
825 dismissKeyguardThenExecute(new OnDismissAction() {
826 @Override
827 public boolean onDismiss() {
828 AsyncTask.execute(new Runnable() {
829 public void run() {
830 try {
831 if (keyguardShowing) {
832 ActivityManagerNative.getDefault()
833 .keyguardWaitingForActivityDrawn();
834 }
835 TaskStackBuilder.create(mContext)
836 .addNextIntentWithParentStack(intent)
837 .startActivities(null,
838 new UserHandle(UserHandle.getUserId(appUid)));
839 overrideActivityPendingAppTransition(keyguardShowing);
840 } catch (RemoteException e) {
841 }
842 }
843 });
Jorim Jaggi4eedc1d2014-10-27 13:45:56 +0100844 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
Jorim Jaggifb28c0e2014-08-22 16:28:42 +0200845 return true;
846 }
Jorim Jaggi746f7fa2014-08-27 17:52:46 +0200847 }, false /* afterKeyguardGone */);
Dan Sandler4247a5c2014-07-23 15:58:08 -0400848 }
849
Selim Cinekab29aeb2015-02-20 18:18:32 +0100850 private void bindGuts(ExpandableNotificationRow row) {
851 row.inflateGuts();
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200852 final StatusBarNotification sbn = row.getStatusBarNotification();
853 PackageManager pmUser = getPackageManagerForUser(
854 sbn.getUser().getIdentifier());
855 row.setTag(sbn.getPackageName());
Selim Cinekab29aeb2015-02-20 18:18:32 +0100856 final View guts = row.getGuts();
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200857 final String pkg = sbn.getPackageName();
858 String appname = pkg;
859 Drawable pkgicon = null;
860 int appUid = -1;
861 try {
862 final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
863 PackageManager.GET_UNINSTALLED_PACKAGES
864 | PackageManager.GET_DISABLED_COMPONENTS);
865 if (info != null) {
866 appname = String.valueOf(pmUser.getApplicationLabel(info));
867 pkgicon = pmUser.getApplicationIcon(info);
868 appUid = info.uid;
869 }
870 } catch (NameNotFoundException e) {
871 // app is gone, just show package name and generic icon
872 pkgicon = pmUser.getDefaultActivityIcon();
873 }
874 ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon);
875 ((DateTimeView) row.findViewById(R.id.timestamp)).setTime(sbn.getPostTime());
876 ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
877 final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
878 final View appSettingsButton
879 = guts.findViewById(R.id.notification_inspect_app_provided_settings);
880 if (appUid >= 0) {
881 final int appUidF = appUid;
882 settingsButton.setOnClickListener(new View.OnClickListener() {
883 public void onClick(View v) {
884 startAppNotificationSettingsActivity(pkg, appUidF);
885 }
886 });
887
888 final Intent appSettingsQueryIntent
889 = new Intent(Intent.ACTION_MAIN)
890 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
891 .setPackage(pkg);
892 List<ResolveInfo> infos = pmUser.queryIntentActivities(appSettingsQueryIntent, 0);
893 if (infos.size() > 0) {
894 appSettingsButton.setVisibility(View.VISIBLE);
895 appSettingsButton.setContentDescription(
896 mContext.getResources().getString(
897 R.string.status_bar_notification_app_settings_title,
898 appname
899 ));
900 final Intent appSettingsLaunchIntent = new Intent(appSettingsQueryIntent)
901 .setClassName(pkg, infos.get(0).activityInfo.name);
902 appSettingsButton.setOnClickListener(new View.OnClickListener() {
903 public void onClick(View v) {
904 startAppOwnNotificationSettingsActivity(appSettingsLaunchIntent,
905 sbn.getId(),
906 sbn.getTag(),
907 appUidF);
908 }
909 });
910 } else {
911 appSettingsButton.setVisibility(View.GONE);
912 }
913 } else {
914 settingsButton.setVisibility(View.GONE);
915 appSettingsButton.setVisibility(View.GONE);
916 }
917
918 }
919
Dan Sandler4247a5c2014-07-23 15:58:08 -0400920 protected SwipeHelper.LongPressListener getNotificationLongClicker() {
921 return new SwipeHelper.LongPressListener() {
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400922 @Override
Dan Sandler4247a5c2014-07-23 15:58:08 -0400923 public boolean onLongPress(View v, int x, int y) {
924 dismissPopups();
925
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200926 if (!(v instanceof ExpandableNotificationRow)) {
927 return false;
928 }
Selim Cinekabdc5a02014-09-02 13:46:00 +0200929 if (v.getWindowToken() == null) {
930 Log.e(TAG, "Trying to show notification guts, but not attached to window");
931 return false;
932 }
Dan Sandler4247a5c2014-07-23 15:58:08 -0400933
Selim Cinekab29aeb2015-02-20 18:18:32 +0100934 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
935 bindGuts(row);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +0200936
Dan Sandler4247a5c2014-07-23 15:58:08 -0400937 // Assume we are a status_bar_notification_row
Selim Cinekab29aeb2015-02-20 18:18:32 +0100938 final NotificationGuts guts = row.getGuts();
Selim Cinekbdefcdf2014-09-11 12:41:47 +0200939 if (guts == null) {
940 // This view has no guts. Examples are the more card or the dismiss all view
941 return false;
942 }
Dan Sandler4247a5c2014-07-23 15:58:08 -0400943
944 // Already showing?
Selim Cinekabdc5a02014-09-02 13:46:00 +0200945 if (guts.getVisibility() == View.VISIBLE) {
946 Log.e(TAG, "Trying to show notification guts, but already visible");
947 return false;
948 }
Dan Sandler4247a5c2014-07-23 15:58:08 -0400949
Dan Sandler4247a5c2014-07-23 15:58:08 -0400950 guts.setVisibility(View.VISIBLE);
Selim Cinek024ca592014-09-01 15:11:28 +0200951 final double horz = Math.max(guts.getWidth() - x, x);
952 final double vert = Math.max(guts.getActualHeight() - y, y);
Dan Sandler4247a5c2014-07-23 15:58:08 -0400953 final float r = (float) Math.hypot(horz, vert);
954 final Animator a
955 = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
956 a.setDuration(400);
957 a.setInterpolator(mLinearOutSlowIn);
958 a.start();
959
960 mNotificationGutsExposed = guts;
Daniel Sandlerf7a19562012-04-04 14:04:21 -0400961
962 return true;
963 }
964 };
965 }
966
Daniel Sandler469e96e2012-05-04 15:56:19 -0400967 public void dismissPopups() {
Dan Sandler4247a5c2014-07-23 15:58:08 -0400968 if (mNotificationGutsExposed != null) {
Selim Cinek024ca592014-09-01 15:11:28 +0200969 final NotificationGuts v = mNotificationGutsExposed;
Dan Sandler4247a5c2014-07-23 15:58:08 -0400970 mNotificationGutsExposed = null;
971
Selim Cinekedb893b2014-09-02 12:51:31 +0200972 if (v.getWindowToken() == null) return;
973
Dan Sandler4247a5c2014-07-23 15:58:08 -0400974 final int x = (v.getLeft() + v.getRight()) / 2;
Selim Cinek024ca592014-09-01 15:11:28 +0200975 final int y = (v.getTop() + v.getActualHeight() / 2);
Dan Sandler4247a5c2014-07-23 15:58:08 -0400976 final Animator a = ViewAnimationUtils.createCircularReveal(v,
977 x, y, x, 0);
978 a.setDuration(200);
979 a.setInterpolator(mFastOutLinearIn);
980 a.addListener(new AnimatorListenerAdapter() {
981 @Override
982 public void onAnimationEnd(Animator animation) {
983 super.onAnimationEnd(animation);
984 v.setVisibility(View.GONE);
985 }
986 });
987 a.start();
Daniel Sandler469e96e2012-05-04 15:56:19 -0400988 }
989 }
990
Michael Jurka7f2668c2012-03-27 07:49:52 -0700991 @Override
Winson Chung1e8d71b2014-05-16 17:05:22 -0700992 public void showRecentApps(boolean triggeredFromAltTab) {
993 int msg = MSG_SHOW_RECENT_APPS;
994 mHandler.removeMessages(msg);
995 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 0).sendToTarget();
996 }
997
998 @Override
Winson Chungcdcd4872014-08-05 18:00:13 -0700999 public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001000 int msg = MSG_HIDE_RECENT_APPS;
1001 mHandler.removeMessages(msg);
Winson Chungcdcd4872014-08-05 18:00:13 -07001002 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
1003 triggeredFromHomeKey ? 1 : 0).sendToTarget();
Winson Chung1e8d71b2014-05-16 17:05:22 -07001004 }
1005
1006 @Override
Michael Jurka7f2668c2012-03-27 07:49:52 -07001007 public void toggleRecentApps() {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001008 int msg = MSG_TOGGLE_RECENTS_APPS;
Michael Jurka7f2668c2012-03-27 07:49:52 -07001009 mHandler.removeMessages(msg);
1010 mHandler.sendEmptyMessage(msg);
1011 }
1012
1013 @Override
1014 public void preloadRecentApps() {
1015 int msg = MSG_PRELOAD_RECENT_APPS;
1016 mHandler.removeMessages(msg);
1017 mHandler.sendEmptyMessage(msg);
1018 }
1019
1020 @Override
1021 public void cancelPreloadRecentApps() {
1022 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
1023 mHandler.removeMessages(msg);
1024 mHandler.sendEmptyMessage(msg);
1025 }
1026
Winson Chungb1f74992014-08-08 12:53:09 -07001027 /** Jumps to the next affiliated task in the group. */
1028 public void showNextAffiliatedTask() {
1029 int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
1030 mHandler.removeMessages(msg);
1031 mHandler.sendEmptyMessage(msg);
1032 }
1033
1034 /** Jumps to the previous affiliated task in the group. */
1035 public void showPreviousAffiliatedTask() {
1036 int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
1037 mHandler.removeMessages(msg);
1038 mHandler.sendEmptyMessage(msg);
1039 }
1040
Michael Jurkaecc395a2012-03-30 05:31:46 -07001041 protected H createHandler() {
Michael Jurka7f2668c2012-03-27 07:49:52 -07001042 return new H();
1043 }
1044
Michael Jurka56a57832012-05-14 13:24:43 -07001045 static void sendCloseSystemWindows(Context context, String reason) {
1046 if (ActivityManagerNative.isSystemReady()) {
1047 try {
1048 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
1049 } catch (RemoteException e) {
1050 }
1051 }
1052 }
1053
Michael Jurkacb2522c2012-04-13 09:32:47 -07001054 protected abstract View getStatusBarView();
1055
Michael Jurka80343f62012-10-18 13:13:46 +02001056 protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
1057 // additional optimization when we have software system buttons - start loading the recent
1058 // tasks on touch down
1059 @Override
1060 public boolean onTouch(View v, MotionEvent event) {
1061 int action = event.getAction() & MotionEvent.ACTION_MASK;
1062 if (action == MotionEvent.ACTION_DOWN) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001063 preloadRecents();
Michael Jurka80343f62012-10-18 13:13:46 +02001064 } else if (action == MotionEvent.ACTION_CANCEL) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001065 cancelPreloadingRecents();
Michael Jurka80343f62012-10-18 13:13:46 +02001066 } else if (action == MotionEvent.ACTION_UP) {
1067 if (!v.isPressed()) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001068 cancelPreloadingRecents();
Michael Jurka80343f62012-10-18 13:13:46 +02001069 }
1070
1071 }
1072 return false;
1073 }
1074 };
1075
Winson Chung1e8d71b2014-05-16 17:05:22 -07001076 /** Proxy for RecentsComponent */
1077
1078 protected void showRecents(boolean triggeredFromAltTab) {
1079 if (mRecents != null) {
1080 sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
1081 mRecents.showRecents(triggeredFromAltTab, getStatusBarView());
1082 }
1083 }
1084
Winson Chungcdcd4872014-08-05 18:00:13 -07001085 protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001086 if (mRecents != null) {
Winson Chungcdcd4872014-08-05 18:00:13 -07001087 mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
Winson Chung1e8d71b2014-05-16 17:05:22 -07001088 }
1089 }
1090
1091 protected void toggleRecents() {
John Spurlockd08de372013-06-24 13:06:08 -04001092 if (mRecents != null) {
Winson Chung4c71aef2014-03-21 15:15:11 -07001093 sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
John Spurlockd08de372013-06-24 13:06:08 -04001094 mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
1095 }
1096 }
Michael Jurka80343f62012-10-18 13:13:46 +02001097
Winson Chung1e8d71b2014-05-16 17:05:22 -07001098 protected void preloadRecents() {
John Spurlockd08de372013-06-24 13:06:08 -04001099 if (mRecents != null) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001100 mRecents.preloadRecents();
John Spurlockd08de372013-06-24 13:06:08 -04001101 }
Michael Jurka80343f62012-10-18 13:13:46 +02001102 }
1103
Winson Chung1e8d71b2014-05-16 17:05:22 -07001104 protected void cancelPreloadingRecents() {
John Spurlockd08de372013-06-24 13:06:08 -04001105 if (mRecents != null) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001106 mRecents.cancelPreloadingRecents();
John Spurlockd08de372013-06-24 13:06:08 -04001107 }
Michael Jurka80343f62012-10-18 13:13:46 +02001108 }
1109
Winson Chungb1f74992014-08-08 12:53:09 -07001110 protected void showRecentsNextAffiliatedTask() {
1111 if (mRecents != null) {
1112 mRecents.showNextAffiliatedTask();
1113 }
1114 }
1115
1116 protected void showRecentsPreviousAffiliatedTask() {
1117 if (mRecents != null) {
1118 mRecents.showPrevAffiliatedTask();
1119 }
1120 }
1121
Winson Chung9214eff2014-06-12 13:59:25 -07001122 @Override
1123 public void onVisibilityChanged(boolean visible) {
1124 // Do nothing
1125 }
1126
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001127 /**
1128 * if the interrupting notification had a fullscreen intent, fire it now.
1129 */
1130 public abstract void escalateHeadsUp();
Chris Wrena4ef6202014-06-09 18:07:30 -04001131
Dan Sandlerfd16d562014-02-13 18:43:31 -08001132 /**
1133 * Save the current "public" (locked and secure) state of the lockscreen.
1134 */
1135 public void setLockscreenPublicMode(boolean publicMode) {
1136 mLockscreenPublicMode = publicMode;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001137 }
1138
Dan Sandlerfd16d562014-02-13 18:43:31 -08001139 public boolean isLockscreenPublicMode() {
1140 return mLockscreenPublicMode;
1141 }
1142
1143 /**
1144 * Has the given user chosen to allow their private (full) notifications to be shown even
1145 * when the lockscreen is in "public" (secure & locked) mode?
1146 */
1147 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
1148 if (userHandle == UserHandle.USER_ALL) {
1149 return true;
1150 }
1151
1152 if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
Kenny Guyb0d3ab22015-02-10 16:13:11 +00001153 final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
Dan Sandlerfd16d562014-02-13 18:43:31 -08001154 mContext.getContentResolver(),
1155 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
Adrian Roosbd3409c2014-08-12 18:21:09 +02001156 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
1157 userHandle);
1158 final boolean allowedByDpm = (dpmFlags
1159 & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
Kenny Guyb0d3ab22015-02-10 16:13:11 +00001160 final boolean allowed = allowedByUser && allowedByDpm;
1161 mUsersAllowingPrivateNotifications.append(userHandle, allowed);
Dan Sandlerfd16d562014-02-13 18:43:31 -08001162 return allowed;
1163 }
1164
1165 return mUsersAllowingPrivateNotifications.get(userHandle);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001166 }
1167
Christoph Studerc8db24b2014-07-25 17:50:30 +02001168 /**
1169 * Returns true if we're on a secure lockscreen and the user wants to hide "sensitive"
1170 * notification data. If so, private notifications should show their (possibly
1171 * auto-generated) publicVersion, and secret notifications should be totally invisible.
1172 */
1173 @Override // NotificationData.Environment
1174 public boolean shouldHideSensitiveContents(int userid) {
1175 return isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(userid);
1176 }
1177
Chris Wrena4ef6202014-06-09 18:07:30 -04001178 public void onNotificationClear(StatusBarNotification notification) {
1179 try {
1180 mBarService.onNotificationClear(
1181 notification.getPackageName(),
1182 notification.getTag(),
1183 notification.getId(),
1184 notification.getUserId());
1185 } catch (android.os.RemoteException ex) {
1186 // oh well
1187 }
1188 }
1189
Michael Jurka7f2668c2012-03-27 07:49:52 -07001190 protected class H extends Handler {
1191 public void handleMessage(Message m) {
1192 switch (m.what) {
Winson Chung1e8d71b2014-05-16 17:05:22 -07001193 case MSG_SHOW_RECENT_APPS:
1194 showRecents(m.arg1 > 0);
Michael Jurkacb2522c2012-04-13 09:32:47 -07001195 break;
Winson Chung1e8d71b2014-05-16 17:05:22 -07001196 case MSG_HIDE_RECENT_APPS:
Winson Chungcdcd4872014-08-05 18:00:13 -07001197 hideRecents(m.arg1 > 0, m.arg2 > 0);
Winson Chung1e8d71b2014-05-16 17:05:22 -07001198 break;
1199 case MSG_TOGGLE_RECENTS_APPS:
1200 toggleRecents();
Michael Jurkacb2522c2012-04-13 09:32:47 -07001201 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -07001202 case MSG_PRELOAD_RECENT_APPS:
Winson Chung1e8d71b2014-05-16 17:05:22 -07001203 preloadRecents();
Michael Jurka7f2668c2012-03-27 07:49:52 -07001204 break;
1205 case MSG_CANCEL_PRELOAD_RECENT_APPS:
Winson Chung1e8d71b2014-05-16 17:05:22 -07001206 cancelPreloadingRecents();
Michael Jurka7f2668c2012-03-27 07:49:52 -07001207 break;
Winson Chungb1f74992014-08-08 12:53:09 -07001208 case MSG_SHOW_NEXT_AFFILIATED_TASK:
1209 showRecentsNextAffiliatedTask();
1210 break;
1211 case MSG_SHOW_PREV_AFFILIATED_TASK:
1212 showRecentsPreviousAffiliatedTask();
1213 break;
Michael Jurka7f2668c2012-03-27 07:49:52 -07001214 }
1215 }
1216 }
1217
Daniel Sandler6a858c32012-03-12 14:38:58 -04001218 protected void workAroundBadLayerDrawableOpacity(View v) {
1219 }
1220
Selim Cinek8d490d42015-04-10 00:05:50 -07001221 protected boolean inflateViews(Entry entry, ViewGroup parent) {
Dan Sandlere9a4e902014-07-31 01:06:43 -04001222 PackageManager pmUser = getPackageManagerForUser(
1223 entry.notification.getUser().getIdentifier());
1224
Chris Wrend04f6ce2014-06-11 17:37:28 -04001225 int maxHeight = mRowMaxHeight;
Dan Sandlerf4db75c2014-09-03 18:02:31 +02001226 final StatusBarNotification sbn = entry.notification;
Chris Wren574a55e2013-07-15 18:48:37 -04001227 RemoteViews contentView = sbn.getNotification().contentView;
1228 RemoteViews bigContentView = sbn.getNotification().bigContentView;
Selim Cinek8d490d42015-04-10 00:05:50 -07001229 RemoteViews headsUpContentView = sbn.getNotification().headsUpContentView;
Chris Wren8fd39ec2014-02-27 17:43:26 -05001230
Chris Wren574a55e2013-07-15 18:48:37 -04001231 if (contentView == null) {
Daniel Sandler6a858c32012-03-12 14:38:58 -04001232 return false;
1233 }
1234
Dan Sandlerf68448e2014-04-24 15:54:16 -04001235 if (DEBUG) {
1236 Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion);
1237 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001238
1239 Notification publicNotification = sbn.getNotification().publicVersion;
1240
Christoph Studera7fe6312014-06-27 19:32:44 +02001241 ExpandableNotificationRow row;
1242
1243 // Stash away previous user expansion state so we can restore it at
1244 // the end.
1245 boolean hasUserChangedExpansion = false;
1246 boolean userExpanded = false;
1247 boolean userLocked = false;
1248
1249 if (entry.row != null) {
1250 row = entry.row;
1251 hasUserChangedExpansion = row.hasUserChangedExpansion();
1252 userExpanded = row.isUserExpanded();
1253 userLocked = row.isUserLocked();
Christoph Studer22f2ee52014-07-29 22:57:21 +02001254 entry.reset();
Christoph Studera7fe6312014-06-27 19:32:44 +02001255 if (hasUserChangedExpansion) {
1256 row.setUserExpanded(userExpanded);
1257 }
1258 } else {
1259 // create the row view
1260 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
1261 Context.LAYOUT_INFLATER_SERVICE);
1262 row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
1263 parent, false);
Chris Wren78403d72014-07-28 10:23:24 +01001264 row.setExpansionLogger(this, entry.notification.getKey());
Selim Cinekb5605e52015-02-20 18:21:41 +01001265 row.setGroupManager(mGroupManager);
Christoph Studera7fe6312014-06-27 19:32:44 +02001266 }
Daniel Sandlerbc5559f2012-04-19 01:08:15 -04001267
Daniel Sandler6a858c32012-03-12 14:38:58 -04001268 workAroundBadLayerDrawableOpacity(row);
1269 View vetoButton = updateNotificationVetoButton(row, sbn);
1270 vetoButton.setContentDescription(mContext.getString(
1271 R.string.accessibility_remove_notification));
1272
1273 // NB: the large icon is now handled entirely by the template
1274
1275 // bind the click event to the content area
Selim Cinek8d490d42015-04-10 00:05:50 -07001276 NotificationContentView contentContainer = row.getPrivateLayout();
1277 NotificationContentView contentContainerPublic = row.getPublicLayout();
Daniel Sandlerbc5559f2012-04-19 01:08:15 -04001278
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001279 row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
Adrian Roos497ab022015-02-10 20:49:33 +01001280 if (ENABLE_REMOTE_INPUT) {
1281 row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
1282 }
Daniel Sandlerbc5559f2012-04-19 01:08:15 -04001283
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001284 PendingIntent contentIntent = sbn.getNotification().contentIntent;
Daniel Sandler6a858c32012-03-12 14:38:58 -04001285 if (contentIntent != null) {
Chris Wren930ecca2014-11-12 17:43:41 -05001286 final View.OnClickListener listener = makeClicker(contentIntent, sbn.getKey());
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001287 row.setOnClickListener(listener);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001288 } else {
Jorim Jaggife40f7d2014-04-28 15:20:04 +02001289 row.setOnClickListener(null);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001290 }
1291
Dan Sandlera5e0f412014-01-23 15:11:54 -05001292 // set up the adaptive layout
Chris Wren574a55e2013-07-15 18:48:37 -04001293 View contentViewLocal = null;
1294 View bigContentViewLocal = null;
Selim Cinek8d490d42015-04-10 00:05:50 -07001295 View headsUpContentViewLocal = null;
Daniel Sandler6a858c32012-03-12 14:38:58 -04001296 try {
Selim Cinek8d490d42015-04-10 00:05:50 -07001297 contentViewLocal = contentView.apply(mContext, contentContainer,
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01001298 mOnClickHandler);
Chris Wren574a55e2013-07-15 18:48:37 -04001299 if (bigContentView != null) {
Selim Cinek8d490d42015-04-10 00:05:50 -07001300 bigContentViewLocal = bigContentView.apply(mContext, contentContainer,
1301 mOnClickHandler);
1302 }
1303 if (headsUpContentView != null) {
1304 headsUpContentViewLocal = headsUpContentView.apply(mContext, contentContainer,
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01001305 mOnClickHandler);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001306 }
1307 }
1308 catch (RuntimeException e) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -04001309 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
John Spurlockcd686b52013-06-05 10:13:46 -04001310 Log.e(TAG, "couldn't inflate view for notification " + ident, e);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001311 return false;
Daniel Sandler6a858c32012-03-12 14:38:58 -04001312 }
1313
Chris Wren574a55e2013-07-15 18:48:37 -04001314 if (contentViewLocal != null) {
Dan Sandler6d94aa72014-03-12 00:53:21 -04001315 contentViewLocal.setIsRootNamespace(true);
Selim Cinek8d490d42015-04-10 00:05:50 -07001316 contentContainer.setContractedChild(contentViewLocal);
Daniel Sandler8680bf82012-05-15 16:52:52 -04001317 }
Chris Wren574a55e2013-07-15 18:48:37 -04001318 if (bigContentViewLocal != null) {
Dan Sandler6d94aa72014-03-12 00:53:21 -04001319 bigContentViewLocal.setIsRootNamespace(true);
Selim Cinek8d490d42015-04-10 00:05:50 -07001320 contentContainer.setExpandedChild(bigContentViewLocal);
1321 }
1322 if (headsUpContentViewLocal != null) {
1323 headsUpContentViewLocal.setIsRootNamespace(true);
1324 contentContainer.setHeadsUpChild(headsUpContentViewLocal);
Daniel Sandler8680bf82012-05-15 16:52:52 -04001325 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001326
Dan Sandlera5e0f412014-01-23 15:11:54 -05001327 // now the public version
1328 View publicViewLocal = null;
1329 if (publicNotification != null) {
1330 try {
Selim Cinek8d490d42015-04-10 00:05:50 -07001331 publicViewLocal = publicNotification.contentView.apply(mContext, contentContainerPublic,
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001332 mOnClickHandler);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001333
1334 if (publicViewLocal != null) {
Dan Sandler6d94aa72014-03-12 00:53:21 -04001335 publicViewLocal.setIsRootNamespace(true);
Selim Cinek8d490d42015-04-10 00:05:50 -07001336 contentContainerPublic.setContractedChild(publicViewLocal);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001337 }
1338 }
1339 catch (RuntimeException e) {
1340 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
1341 Log.e(TAG, "couldn't inflate public view for notification " + ident, e);
1342 publicViewLocal = null;
1343 }
1344 }
1345
Jorim Jaggia1eeade2014-09-08 22:34:39 +02001346 // Extract target SDK version.
1347 try {
1348 ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
1349 entry.targetSdk = info.targetSdkVersion;
1350 } catch (NameNotFoundException ex) {
1351 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
1352 }
1353
Dan Sandlera5e0f412014-01-23 15:11:54 -05001354 if (publicViewLocal == null) {
1355 // Add a basic notification template
1356 publicViewLocal = LayoutInflater.from(mContext).inflate(
Jorim Jaggied713ee2014-09-09 23:56:09 +02001357 R.layout.notification_public_default,
Selim Cinek8d490d42015-04-10 00:05:50 -07001358 contentContainerPublic, false);
Selim Cinekcab4a602014-09-03 14:47:57 +02001359 publicViewLocal.setIsRootNamespace(true);
Selim Cinek8d490d42015-04-10 00:05:50 -07001360 contentContainerPublic.setContractedChild(publicViewLocal);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001361
Jorim Jaggied713ee2014-09-09 23:56:09 +02001362 final TextView title = (TextView) publicViewLocal.findViewById(R.id.title);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001363 try {
Dan Sandlere9a4e902014-07-31 01:06:43 -04001364 title.setText(pmUser.getApplicationLabel(
1365 pmUser.getApplicationInfo(entry.notification.getPackageName(), 0)));
Dan Sandlera5e0f412014-01-23 15:11:54 -05001366 } catch (NameNotFoundException e) {
1367 title.setText(entry.notification.getPackageName());
1368 }
1369
Jorim Jaggied713ee2014-09-09 23:56:09 +02001370 final ImageView icon = (ImageView) publicViewLocal.findViewById(R.id.icon);
Kenny Guy98193ea2014-07-24 19:54:37 +01001371 final ImageView profileBadge = (ImageView) publicViewLocal.findViewById(
Jorim Jaggied713ee2014-09-09 23:56:09 +02001372 R.id.profile_badge_line3);
Dan Sandlera5e0f412014-01-23 15:11:54 -05001373
1374 final StatusBarIcon ic = new StatusBarIcon(entry.notification.getPackageName(),
1375 entry.notification.getUser(),
1376 entry.notification.getNotification().icon,
1377 entry.notification.getNotification().iconLevel,
1378 entry.notification.getNotification().number,
1379 entry.notification.getNotification().tickerText);
1380
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001381 Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
1382 icon.setImageDrawable(iconDrawable);
Dianne Hackborn955d8d62014-10-07 20:17:19 -07001383 if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP
Jorim Jaggia1eeade2014-09-08 22:34:39 +02001384 || mNotificationColorUtil.isGrayscaleIcon(iconDrawable)) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001385 icon.setBackgroundResource(
Jorim Jaggi445d3c02014-08-19 22:33:42 +02001386 com.android.internal.R.drawable.notification_icon_legacy_bg);
1387 int padding = mContext.getResources().getDimensionPixelSize(
1388 com.android.internal.R.dimen.notification_large_icon_circle_padding);
1389 icon.setPadding(padding, padding, padding, padding);
Jorim Jaggia1eeade2014-09-08 22:34:39 +02001390 if (sbn.getNotification().color != Notification.COLOR_DEFAULT) {
1391 icon.getBackground().setColorFilter(
1392 sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
1393 }
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01001394 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001395
Kenny Guy98193ea2014-07-24 19:54:37 +01001396 if (profileBadge != null) {
Svetoslavc7d62f02014-09-04 15:39:54 -07001397 Drawable profileDrawable = mContext.getPackageManager().getUserBadgeForDensity(
1398 entry.notification.getUser(), 0);
Kenny Guy8a0101b2014-05-08 23:34:12 +01001399 if (profileDrawable != null) {
Kenny Guy98193ea2014-07-24 19:54:37 +01001400 profileBadge.setImageDrawable(profileDrawable);
1401 profileBadge.setVisibility(View.VISIBLE);
Kenny Guy8a0101b2014-05-08 23:34:12 +01001402 } else {
Kenny Guy98193ea2014-07-24 19:54:37 +01001403 profileBadge.setVisibility(View.GONE);
Kenny Guy8a0101b2014-05-08 23:34:12 +01001404 }
1405 }
1406
Dan Sandler18bb4f92014-07-14 16:48:27 -04001407 final View privateTime = contentViewLocal.findViewById(com.android.internal.R.id.time);
Jorim Jaggied713ee2014-09-09 23:56:09 +02001408 final DateTimeView time = (DateTimeView) publicViewLocal.findViewById(R.id.time);
Dan Sandler18bb4f92014-07-14 16:48:27 -04001409 if (privateTime != null && privateTime.getVisibility() == View.VISIBLE) {
Jorim Jaggied713ee2014-09-09 23:56:09 +02001410 time.setVisibility(View.VISIBLE);
1411 time.setTime(entry.notification.getNotification().when);
Dan Sandler18bb4f92014-07-14 16:48:27 -04001412 }
1413
Jorim Jaggied713ee2014-09-09 23:56:09 +02001414 final TextView text = (TextView) publicViewLocal.findViewById(R.id.text);
Dan Sandler18bb4f92014-07-14 16:48:27 -04001415 if (text != null) {
1416 text.setText(R.string.notification_hidden_text);
1417 text.setTextAppearance(mContext,
Alan Viverette41ad3942014-08-27 15:06:51 -07001418 R.style.TextAppearance_Material_Notification_Parenthetical);
Dan Sandler18bb4f92014-07-14 16:48:27 -04001419 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05001420
Jorim Jaggi445d3c02014-08-19 22:33:42 +02001421 int topPadding = Notification.Builder.calculateTopPadding(mContext,
1422 false /* hasThreeLines */,
1423 mContext.getResources().getConfiguration().fontScale);
1424 title.setPadding(0, topPadding, 0, 0);
1425
Jorim Jaggi36b15232014-06-10 17:24:20 +02001426 entry.autoRedacted = true;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001427 }
1428
Daniel Sandlerb9301c32012-08-14 15:08:24 -04001429 if (MULTIUSER_DEBUG) {
1430 TextView debug = (TextView) row.findViewById(R.id.debug_info);
1431 if (debug != null) {
1432 debug.setVisibility(View.VISIBLE);
Kenny Guy3a7c4a52014-03-03 18:24:03 +00001433 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
Daniel Sandlerb9301c32012-08-14 15:08:24 -04001434 }
1435 }
Daniel Sandler6a858c32012-03-12 14:38:58 -04001436 entry.row = row;
Chris Wrend04f6ce2014-06-11 17:37:28 -04001437 entry.row.setHeightRange(mRowMinHeight, maxHeight);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001438 entry.row.setOnActivatedListener(this);
Chris Wren574a55e2013-07-15 18:48:37 -04001439 entry.expanded = contentViewLocal;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001440 entry.expandedPublic = publicViewLocal;
Chris Wren574a55e2013-07-15 18:48:37 -04001441 entry.setBigContentView(bigContentViewLocal);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001442
Christoph Studer13b895122014-08-26 15:42:12 +02001443 applyColorsAndBackgrounds(sbn, entry);
Jorim Jaggi46739852014-04-15 09:58:24 +02001444
Christoph Studera7fe6312014-06-27 19:32:44 +02001445 // Restore previous flags.
1446 if (hasUserChangedExpansion) {
1447 // Note: setUserExpanded() conveniently ignores calls with
1448 // userExpanded=true if !isExpandable().
1449 row.setUserExpanded(userExpanded);
1450 }
1451 row.setUserLocked(userLocked);
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02001452 row.setStatusBarNotification(entry.notification);
Adrian Roos497ab022015-02-10 20:49:33 +01001453 applyRemoteInput(entry);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001454 return true;
1455 }
1456
Adrian Roos497ab022015-02-10 20:49:33 +01001457 /**
1458 * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
1459 * via first-class API.
1460 *
1461 * TODO: Remove once enough apps specify remote inputs on their own.
1462 */
1463 private void processForRemoteInput(Notification n) {
1464 if (!ENABLE_REMOTE_INPUT) return;
1465
1466 if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") &&
1467 (n.actions == null || n.actions.length == 0)) {
1468 Notification.Action viableAction = null;
1469 Notification.WearableExtender we = new Notification.WearableExtender(n);
1470
1471 List<Notification.Action> actions = we.getActions();
1472 final int numActions = actions.size();
1473
1474 for (int i = 0; i < numActions; i++) {
1475 Notification.Action action = actions.get(i);
1476 RemoteInput[] remoteInputs = action.getRemoteInputs();
1477 for (RemoteInput ri : action.getRemoteInputs()) {
1478 if (ri.getAllowFreeFormInput()) {
1479 viableAction = action;
1480 break;
1481 }
1482 }
1483 if (viableAction != null) {
1484 break;
1485 }
1486 }
1487
1488 if (viableAction != null) {
1489 Notification stripped = n.clone();
1490 Notification.Builder.stripForDelivery(stripped);
1491 stripped.actions = new Notification.Action[] { viableAction };
1492 stripped.extras.putBoolean("android.rebuild.contentView", true);
1493 stripped.contentView = null;
1494 stripped.extras.putBoolean("android.rebuild.bigView", true);
1495 stripped.bigContentView = null;
Selim Cinek8d490d42015-04-10 00:05:50 -07001496 stripped.extras.putBoolean("android.rebuild.hudView", true);
1497 stripped.headsUpContentView = null;
Adrian Roos497ab022015-02-10 20:49:33 +01001498
1499 Notification rebuilt = Notification.Builder.rebuild(mContext, stripped);
1500
1501 n.actions = rebuilt.actions;
1502 n.bigContentView = rebuilt.bigContentView;
1503 n.headsUpContentView = rebuilt.headsUpContentView;
1504 n.publicVersion = rebuilt.publicVersion;
1505 }
1506 }
1507 }
1508
1509 private void applyRemoteInput(final Entry entry) {
1510 if (!ENABLE_REMOTE_INPUT) return;
1511
1512 RemoteInput remoteInput = null;
1513
1514 // See if the notification has exactly one action and this action allows free-form input
1515 // TODO: relax restrictions once we support more than one remote input action.
1516 Notification.Action[] actions = entry.notification.getNotification().actions;
1517 if (actions != null && actions.length == 1) {
1518 if (actions[0].getRemoteInputs() != null) {
1519 for (RemoteInput ri : actions[0].getRemoteInputs()) {
1520 if (ri.getAllowFreeFormInput()) {
1521 remoteInput = ri;
1522 break;
1523 }
1524 }
1525 }
1526 }
1527
1528 // See if we have somewhere to put that remote input
Selim Cinek8d490d42015-04-10 00:05:50 -07001529 if (remoteInput != null) {
1530 if (entry.expandedBig != null) {
1531 inflateRemoteInput(entry.expandedBig, remoteInput, actions);
1532 }
1533 View headsUpChild = entry.row.getPrivateLayout().getHeadsUpChild();
1534 if (headsUpChild != null) {
1535 inflateRemoteInput(headsUpChild, remoteInput, actions);
Adrian Roos497ab022015-02-10 20:49:33 +01001536 }
1537 }
1538
Selim Cinek8d490d42015-04-10 00:05:50 -07001539 }
1540
1541 private void inflateRemoteInput(View view, RemoteInput remoteInput,
1542 Notification.Action[] actions) {
1543 View actionContainerCandidate = view.findViewById(com.android.internal.R.id.actions);
1544 if (actionContainerCandidate instanceof ViewGroup) {
1545 ViewGroup actionContainer = (ViewGroup) actionContainerCandidate;
Adrian Roos497ab022015-02-10 20:49:33 +01001546 actionContainer.removeAllViews();
1547 actionContainer.addView(
1548 RemoteInputView.inflate(mContext, actionContainer, actions[0], remoteInput));
1549 }
1550 }
1551
Chris Wren930ecca2014-11-12 17:43:41 -05001552 public NotificationClicker makeClicker(PendingIntent intent, String notificationKey) {
1553 return new NotificationClicker(intent, notificationKey);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001554 }
1555
Chris Wren157026f2013-06-28 16:54:01 -04001556 protected class NotificationClicker implements View.OnClickListener {
Daniel Sandler6a858c32012-03-12 14:38:58 -04001557 private PendingIntent mIntent;
Christoph Studer03b87a22014-04-30 17:33:27 +02001558 private final String mNotificationKey;
Daniel Sandler6a858c32012-03-12 14:38:58 -04001559
Chris Wren930ecca2014-11-12 17:43:41 -05001560 public NotificationClicker(PendingIntent intent, String notificationKey) {
Daniel Sandler6a858c32012-03-12 14:38:58 -04001561 mIntent = intent;
Christoph Studer03b87a22014-04-30 17:33:27 +02001562 mNotificationKey = notificationKey;
Daniel Sandler6a858c32012-03-12 14:38:58 -04001563 }
1564
Adrian Roos7d7090d2014-05-21 13:10:23 +02001565 public void onClick(final View v) {
Chris Wren79ac09e2014-10-29 14:20:19 -04001566 if (NOTIFICATION_CLICK_DEBUG) {
1567 Log.d(TAG, "Clicked on content of " + mNotificationKey);
1568 }
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001569 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
Jorim Jaggi85dc23c2014-09-08 14:42:29 +02001570 final boolean afterKeyguardGone = mIntent.isActivity()
1571 && PreviewInflater.wouldLaunchResolverActivity(mContext, mIntent.getIntent(),
1572 mCurrentUserId);
Adrian Roos4314f6d2014-05-28 14:10:27 +02001573 dismissKeyguardThenExecute(new OnDismissAction() {
Adrian Roos7d7090d2014-05-21 13:10:23 +02001574 public boolean onDismiss() {
Selim Cinek29fa89b2015-04-17 10:39:11 -07001575 if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(mNotificationKey)) {
Christoph Studerc6a656c2015-01-22 15:02:49 +01001576 // Release the HUN notification to the shade.
1577 //
1578 // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
1579 // become canceled shortly by NoMan, but we can't assume that.
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001580 mHeadsUpManager.releaseImmediately(mNotificationKey);
Adrian Roos7d7090d2014-05-21 13:10:23 +02001581 }
Jorim Jaggi89f7f282014-10-17 19:54:15 +02001582 new Thread() {
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001583 @Override
1584 public void run() {
Jorim Jaggi89f7f282014-10-17 19:54:15 +02001585 try {
1586 if (keyguardShowing && !afterKeyguardGone) {
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001587 ActivityManagerNative.getDefault()
1588 .keyguardWaitingForActivityDrawn();
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001589 }
Jorim Jaggi89f7f282014-10-17 19:54:15 +02001590
1591 // The intent we are sending is for the application, which
1592 // won't have permission to immediately start an activity after
1593 // the user switches to home. We know it is safe to do at this
1594 // point, so make sure new activity switches are now allowed.
1595 ActivityManagerNative.getDefault().resumeAppSwitches();
1596 } catch (RemoteException e) {
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001597 }
Daniel Sandler6a858c32012-03-12 14:38:58 -04001598
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001599 if (mIntent != null) {
1600 try {
1601 mIntent.send();
1602 } catch (PendingIntent.CanceledException e) {
1603 // the stack trace isn't very helpful here.
1604 // Just log the exception message.
1605 Log.w(TAG, "Sending contentIntent failed: " + e);
Adrian Roos7d7090d2014-05-21 13:10:23 +02001606
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001607 // TODO: Dismiss Keyguard.
1608 }
1609 if (mIntent.isActivity()) {
Jorim Jaggi85dc23c2014-09-08 14:42:29 +02001610 overrideActivityPendingAppTransition(keyguardShowing
1611 && !afterKeyguardGone);
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001612 }
1613 }
1614
1615 try {
1616 mBarService.onNotificationClick(mNotificationKey);
1617 } catch (RemoteException ex) {
1618 // system process is dead if we're here.
1619 }
Adrian Roos7d7090d2014-05-21 13:10:23 +02001620 }
Jorim Jaggi89f7f282014-10-17 19:54:15 +02001621 }.start();
Adrian Roos7d7090d2014-05-21 13:10:23 +02001622
1623 // close the shade if it was open
Jorim Jaggi4eedc1d2014-10-27 13:45:56 +01001624 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
Jorim Jaggi27c9b742015-04-09 10:34:49 -07001625 true /* force */, true /* delayed */);
Adrian Roos7d7090d2014-05-21 13:10:23 +02001626 visibilityChanged(false);
1627
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001628 return mIntent != null && mIntent.isActivity();
Daniel Sandler6a858c32012-03-12 14:38:58 -04001629 }
Jorim Jaggi85dc23c2014-09-08 14:42:29 +02001630 }, afterKeyguardGone);
Daniel Sandler6a858c32012-03-12 14:38:58 -04001631 }
1632 }
Chris Wrenf29c3b42013-11-27 16:30:30 -05001633
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001634 public void animateCollapsePanels(int flags, boolean force) {
1635 }
1636
Jorim Jaggi27c9b742015-04-09 10:34:49 -07001637 public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
1638 }
1639
Jorim Jaggi8de4311c2014-08-11 22:36:20 +02001640 public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
1641 if (keyguardShowing) {
1642 try {
1643 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
1644 } catch (RemoteException e) {
1645 Log.w(TAG, "Error overriding app transition: " + e);
1646 }
1647 }
1648 }
1649
Christoph Studere8e28652014-10-29 17:27:53 +01001650 protected void visibilityChanged(boolean visible) {
1651 if (mVisible != visible) {
1652 mVisible = visible;
1653 if (!visible) {
1654 dismissPopups();
1655 }
1656 }
1657 updateVisibleToUser();
1658 }
1659
1660 protected void updateVisibleToUser() {
1661 boolean oldVisibleToUser = mVisibleToUser;
1662 mVisibleToUser = mVisible && mScreenOnFromKeyguard;
1663
1664 if (oldVisibleToUser != mVisibleToUser) {
1665 handleVisibleToUserChanged(mVisibleToUser);
1666 }
1667 }
1668
Daniel Sandler6a858c32012-03-12 14:38:58 -04001669 /**
Christoph Studere8e28652014-10-29 17:27:53 +01001670 * The LEDs are turned off when the notification panel is shown, even just a little bit.
Daniel Sandler6a858c32012-03-12 14:38:58 -04001671 * This was added last-minute and is inconsistent with the way the rest of the notifications
1672 * are handled, because the notification isn't really cancelled. The lights are just
1673 * turned off. If any other notifications happen, the lights will turn back on. Steve says
1674 * this is what he wants. (see bug 1131461)
1675 */
Christoph Studere8e28652014-10-29 17:27:53 +01001676 protected void handleVisibleToUserChanged(boolean visibleToUser) {
1677 try {
1678 if (visibleToUser) {
Christoph Studer1f32c652014-11-26 15:32:20 +01001679 // Only stop blinking, vibrating, ringing when the user went into the shade
1680 // manually (SHADE or SHADE_LOCKED).
1681 boolean clearNotificationEffects =
1682 (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
1683 mBarService.onPanelRevealed(clearNotificationEffects);
Christoph Studere8e28652014-10-29 17:27:53 +01001684 } else {
1685 mBarService.onPanelHidden();
Dan Sandler4247a5c2014-07-23 15:58:08 -04001686 }
Christoph Studere8e28652014-10-29 17:27:53 +01001687 } catch (RemoteException ex) {
1688 // Won't fail unless the world has ended.
Daniel Sandler6a858c32012-03-12 14:38:58 -04001689 }
1690 }
1691
Chris Wren0c8275b2012-05-08 13:36:48 -04001692 /**
1693 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
1694 * about the failure.
1695 *
1696 * WARNING: this will call back into us. Don't hold any locks.
1697 */
Christoph Studer71f18fd2014-05-20 17:02:04 +02001698 void handleNotificationError(StatusBarNotification n, String message) {
Christoph Studere71fefc2014-06-24 16:16:49 +02001699 removeNotification(n.getKey(), null);
Chris Wren0c8275b2012-05-08 13:36:48 -04001700 try {
Kenny Guy3a7c4a52014-03-03 18:24:03 +00001701 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
1702 n.getInitialPid(), message, n.getUserId());
Chris Wren0c8275b2012-05-08 13:36:48 -04001703 } catch (RemoteException ex) {
1704 // The end is nigh.
1705 }
1706 }
1707
Christoph Studerd0694b62014-06-04 16:36:01 +02001708 protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
Christoph Studer37fe6932014-05-26 13:10:30 +02001709 NotificationData.Entry entry = mNotificationData.remove(key, ranking);
Chris Wren0c8275b2012-05-08 13:36:48 -04001710 if (entry == null) {
John Spurlockcd686b52013-06-05 10:13:46 -04001711 Log.w(TAG, "removeNotification for unknown key: " + key);
Chris Wren0c8275b2012-05-08 13:36:48 -04001712 return null;
1713 }
Christoph Studer37fe6932014-05-26 13:10:30 +02001714 updateNotifications();
Chris Wren0c8275b2012-05-08 13:36:48 -04001715 return entry.notification;
1716 }
1717
Selim Cinek8d490d42015-04-10 00:05:50 -07001718 protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
Chris Wren0c8275b2012-05-08 13:36:48 -04001719 if (DEBUG) {
Christoph Studera0506e72014-07-31 20:27:39 +02001720 Log.d(TAG, "createNotificationViews(notification=" + sbn);
Chris Wren0c8275b2012-05-08 13:36:48 -04001721 }
Selim Cinek379ff8f2015-02-20 17:03:16 +01001722 final StatusBarIconView iconView = createIcon(sbn);
1723 if (iconView == null) {
1724 return null;
1725 }
1726
1727 // Construct the expanded view.
1728 NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
Selim Cinek8d490d42015-04-10 00:05:50 -07001729 if (!inflateViews(entry, mStackScroller)) {
Selim Cinek379ff8f2015-02-20 17:03:16 +01001730 handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
1731 return null;
1732 }
1733 return entry;
1734 }
1735
1736 protected StatusBarIconView createIcon(StatusBarNotification sbn) {
Chris Wren0c8275b2012-05-08 13:36:48 -04001737 // Construct the icon.
Christoph Studera0506e72014-07-31 20:27:39 +02001738 Notification n = sbn.getNotification();
Chris Wren0c8275b2012-05-08 13:36:48 -04001739 final StatusBarIconView iconView = new StatusBarIconView(mContext,
Christoph Studera0506e72014-07-31 20:27:39 +02001740 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);
Chris Wren0c8275b2012-05-08 13:36:48 -04001741 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
1742
Christoph Studera0506e72014-07-31 20:27:39 +02001743 final StatusBarIcon ic = new StatusBarIcon(sbn.getPackageName(),
1744 sbn.getUser(),
1745 n.icon,
1746 n.iconLevel,
1747 n.number,
1748 n.tickerText);
Chris Wren0c8275b2012-05-08 13:36:48 -04001749 if (!iconView.set(ic)) {
Christoph Studera0506e72014-07-31 20:27:39 +02001750 handleNotificationError(sbn, "Couldn't create icon: " + ic);
Chris Wren0c8275b2012-05-08 13:36:48 -04001751 return null;
1752 }
Selim Cinek379ff8f2015-02-20 17:03:16 +01001753 return iconView;
Chris Wrenf0048ce2013-08-07 16:43:43 -04001754 }
Chris Wren0c8275b2012-05-08 13:36:48 -04001755
Christoph Studerd0694b62014-06-04 16:36:01 +02001756 protected void addNotificationViews(Entry entry, RankingMap ranking) {
Raju Yadav5f330262014-03-24 10:44:00 +01001757 if (entry == null) {
1758 return;
1759 }
Chris Wren0c8275b2012-05-08 13:36:48 -04001760 // Add the expanded view and icon.
Christoph Studer37fe6932014-05-26 13:10:30 +02001761 mNotificationData.add(entry, ranking);
1762 updateNotifications();
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001763 }
1764
Jorim Jaggi251957d2014-04-09 04:24:09 +02001765 /**
Jorim Jaggid4a57442014-04-10 02:45:55 +02001766 * @return The number of notifications we show on Keyguard.
1767 */
1768 protected abstract int getMaxKeyguardNotifications();
1769
1770 /**
Jorim Jaggi251957d2014-04-09 04:24:09 +02001771 * Updates expanded, dimmed and locked states of notification rows.
1772 */
1773 protected void updateRowStates() {
Jorim Jaggif6411742014-08-05 17:10:43 +00001774 int maxKeyguardNotifications = getMaxKeyguardNotifications();
1775 mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
1776
Christoph Studerc8db24b2014-07-25 17:50:30 +02001777 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1778 final int N = activeNotifications.size();
1779
Jorim Jaggif6411742014-08-05 17:10:43 +00001780 int visibleNotifications = 0;
Jorim Jaggiecbab362014-04-23 16:13:15 +02001781 boolean onKeyguard = mState == StatusBarState.KEYGUARD;
Christoph Studer37fe6932014-05-26 13:10:30 +02001782 for (int i = 0; i < N; i++) {
Christoph Studerc8db24b2014-07-25 17:50:30 +02001783 NotificationData.Entry entry = activeNotifications.get(i);
Jorim Jaggiecbab362014-04-23 16:13:15 +02001784 if (onKeyguard) {
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001785 entry.row.setExpansionDisabled(true);
Chris Wren3ddab0d2012-08-02 16:52:21 -04001786 } else {
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001787 entry.row.setExpansionDisabled(false);
Jorim Jaggi649b7082014-04-03 13:15:18 +02001788 if (!entry.row.isUserLocked()) {
Christoph Studer37fe6932014-05-26 13:10:30 +02001789 boolean top = (i == 0);
Jorim Jaggidce3c4c2014-04-29 23:12:24 +02001790 entry.row.setSystemExpanded(top);
Jorim Jaggi649b7082014-04-03 13:15:18 +02001791 }
Chris Wren8fd12652012-05-09 21:25:57 -04001792 }
Selim Cinekb5605e52015-02-20 18:21:41 +01001793 boolean isInvisibleChild = !mGroupManager.isVisible(entry.notification);
Jorim Jaggif6411742014-08-05 17:10:43 +00001794 boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
Jorim Jaggi9261b3b2014-09-08 14:02:07 +02001795 if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
Jorim Jaggida30a032014-09-05 15:11:46 +02001796 (onKeyguard && (visibleNotifications >= maxKeyguardNotifications
Selim Cinekb5605e52015-02-20 18:21:41 +01001797 || !showOnKeyguard || isInvisibleChild))) {
Jorim Jaggif6411742014-08-05 17:10:43 +00001798 entry.row.setVisibility(View.GONE);
Selim Cinekb5605e52015-02-20 18:21:41 +01001799 if (onKeyguard && showOnKeyguard && !isInvisibleChild) {
Jorim Jaggif6411742014-08-05 17:10:43 +00001800 mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
1801 }
1802 } else {
1803 boolean wasGone = entry.row.getVisibility() == View.GONE;
1804 entry.row.setVisibility(View.VISIBLE);
Selim Cinekb5605e52015-02-20 18:21:41 +01001805 if (!isInvisibleChild) {
1806 if (wasGone) {
1807 // notify the scroller of a child addition
1808 mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */);
1809 }
1810 visibleNotifications++;
Jorim Jaggif6411742014-08-05 17:10:43 +00001811 }
Jorim Jaggif6411742014-08-05 17:10:43 +00001812 }
Chris Wren8fd12652012-05-09 21:25:57 -04001813 }
Jorim Jaggif6411742014-08-05 17:10:43 +00001814
1815 if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
1816 mKeyguardIconOverflowContainer.setVisibility(View.VISIBLE);
1817 } else {
1818 mKeyguardIconOverflowContainer.setVisibility(View.GONE);
1819 }
1820
1821 mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
1822 mStackScroller.getChildCount() - 3);
Selim Cinekbecf5e32014-08-12 14:29:18 +02001823 mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
1824 mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
Jorim Jaggif6411742014-08-05 17:10:43 +00001825 }
1826
1827 private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
1828 return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
Chris Wren8fd12652012-05-09 21:25:57 -04001829 }
1830
John Spurlocke677d712014-02-13 12:52:19 -05001831 protected void setZenMode(int mode) {
John Spurlock110e5b12014-02-27 13:09:35 -05001832 if (!isDeviceProvisioned()) return;
John Spurlocke677d712014-02-13 12:52:19 -05001833 mZenMode = mode;
Christoph Studer37fe6932014-05-26 13:10:30 +02001834 updateNotifications();
John Spurlocke677d712014-02-13 12:52:19 -05001835 }
1836
Dan Sandler52e5701e2014-07-22 23:14:54 -04001837 // extended in PhoneStatusBar
1838 protected void setShowLockscreenNotifications(boolean show) {
1839 mShowLockscreenNotifications = show;
1840 }
1841
1842 private void updateLockscreenNotificationSetting() {
1843 final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1844 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
1845 1,
1846 mCurrentUserId) != 0;
Adrian Roosbd3409c2014-08-12 18:21:09 +02001847 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
1848 null /* admin */, mCurrentUserId);
1849 final boolean allowedByDpm = (dpmFlags
1850 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
1851 setShowLockscreenNotifications(show && allowedByDpm);
Dan Sandler52e5701e2014-07-22 23:14:54 -04001852 }
1853
Chris Wren0c8275b2012-05-08 13:36:48 -04001854 protected abstract void setAreThereNotifications();
Christoph Studer37fe6932014-05-26 13:10:30 +02001855 protected abstract void updateNotifications();
Jorim Jaggi2fdeeab2015-04-01 15:13:03 -07001856 public abstract boolean shouldDisableNavbarGestures();
Chris Wren0c8275b2012-05-08 13:36:48 -04001857
Christoph Studere71fefc2014-06-24 16:16:49 +02001858 public abstract void addNotification(StatusBarNotification notification,
Selim Cinek379ff8f2015-02-20 17:03:16 +01001859 RankingMap ranking, Entry oldEntry);
Christoph Studere71fefc2014-06-24 16:16:49 +02001860 protected abstract void updateNotificationRanking(RankingMap ranking);
1861 public abstract void removeNotification(String key, RankingMap ranking);
Christoph Studera7fe6312014-06-27 19:32:44 +02001862
Christoph Studere71fefc2014-06-24 16:16:49 +02001863 public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
Christoph Studer71f18fd2014-05-20 17:02:04 +02001864 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
Chris Wren0c8275b2012-05-08 13:36:48 -04001865
Chris Wrend04f6ce2014-06-11 17:37:28 -04001866 final String key = notification.getKey();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001867 Entry entry = mNotificationData.get(key);
1868 if (entry == null) {
Chris Wren0c8275b2012-05-08 13:36:48 -04001869 return;
1870 }
1871
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001872 Notification n = notification.getNotification();
1873 if (DEBUG) {
1874 logUpdate(entry, n);
1875 }
1876 boolean applyInPlace = shouldApplyInPlace(entry, n);
1877 final boolean shouldInterrupt = shouldInterrupt(notification);
Selim Cineka59ecc32015-04-07 10:51:49 -07001878 final boolean alertAgain = alertAgain(entry, n);
Chris Wren0c8275b2012-05-08 13:36:48 -04001879
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001880 entry.notification = notification;
1881 mGroupManager.onEntryUpdated(entry, entry.notification);
1882
1883 boolean updateSuccessful = false;
1884 if (applyInPlace) {
1885 // We can just reapply the notifications in place
1886 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
1887 try {
1888 if (entry.icon != null) {
1889 // Update the icon
1890 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
1891 notification.getUser(),
1892 n.icon,
1893 n.iconLevel,
1894 n.number,
1895 n.tickerText);
1896 entry.icon.setNotification(n);
1897 if (!entry.icon.set(ic)) {
1898 handleNotificationError(notification, "Couldn't update icon: " + ic);
1899 return;
1900 }
1901 }
Selim Cinek8d490d42015-04-10 00:05:50 -07001902 updateNotificationViews(entry, notification);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001903 updateSuccessful = true;
1904 }
1905 catch (RuntimeException e) {
1906 // It failed to add cleanly. Log, and remove the view from the panel.
1907 Log.w(TAG, "Couldn't reapply views for package " + n.contentView.getPackage(), e);
1908 }
1909 }
1910 if (!updateSuccessful) {
1911 if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
1912 final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
1913 notification.getUser(),
1914 n.icon,
1915 n.iconLevel,
1916 n.number,
1917 n.tickerText);
1918 entry.icon.setNotification(n);
1919 entry.icon.set(ic);
Selim Cinek8d490d42015-04-10 00:05:50 -07001920 inflateViews(entry, mStackScroller);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001921 }
Selim Cinekfbe9a442015-04-13 16:09:49 -07001922 updateHeadsUp(key, entry, shouldInterrupt, alertAgain);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001923 mNotificationData.updateRanking(ranking);
1924 updateNotifications();
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001925
1926 // Update the veto button accordingly (and as a result, whether this row is
1927 // swipe-dismissable)
1928 updateNotificationVetoButton(entry.row, notification);
1929
1930 // Is this for you?
1931 boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
1932 if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
1933
1934 // Recalculate the position of the sliding windows and the titles.
1935 setAreThereNotifications();
1936 }
1937
Selim Cinek29fa89b2015-04-17 10:39:11 -07001938 protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
1939 boolean alertAgain);
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001940
1941 private void logUpdate(Entry oldEntry, Notification n) {
1942 StatusBarNotification oldNotification = oldEntry.notification;
1943 Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
1944 + " ongoing=" + oldNotification.isOngoing()
1945 + " expanded=" + oldEntry.expanded
1946 + " contentView=" + oldNotification.getNotification().contentView
1947 + " bigContentView=" + oldNotification.getNotification().bigContentView
1948 + " publicView=" + oldNotification.getNotification().publicVersion
1949 + " rowParent=" + oldEntry.row.getParent());
1950 Log.d(TAG, "new notification: when=" + n.when
1951 + " ongoing=" + oldNotification.isOngoing()
1952 + " contentView=" + n.contentView
1953 + " bigContentView=" + n.bigContentView
1954 + " publicView=" + n.publicVersion);
1955 }
1956
1957 /**
1958 * @return whether we can just reapply the RemoteViews in place when it is updated
1959 */
1960 private boolean shouldApplyInPlace(Entry entry, Notification n) {
1961 StatusBarNotification oldNotification = entry.notification;
Chris Wren0c8275b2012-05-08 13:36:48 -04001962 // XXX: modify when we do something more intelligent with the two content views
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001963 final RemoteViews oldContentView = oldNotification.getNotification().contentView;
Christoph Studera0506e72014-07-31 20:27:39 +02001964 final RemoteViews contentView = n.contentView;
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -04001965 final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
Christoph Studera0506e72014-07-31 20:27:39 +02001966 final RemoteViews bigContentView = n.bigContentView;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001967 final RemoteViews oldHeadsUpContentView
1968 = oldNotification.getNotification().headsUpContentView;
Christoph Studera0506e72014-07-31 20:27:39 +02001969 final RemoteViews headsUpContentView = n.headsUpContentView;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001970 final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
1971 final RemoteViews oldPublicContentView = oldPublicNotification != null
1972 ? oldPublicNotification.contentView : null;
Christoph Studera0506e72014-07-31 20:27:39 +02001973 final Notification publicNotification = n.publicVersion;
Dan Sandlera5e0f412014-01-23 15:11:54 -05001974 final RemoteViews publicContentView = publicNotification != null
1975 ? publicNotification.contentView : null;
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001976 boolean contentsUnchanged = entry.expanded != null
Chris Wren0c8275b2012-05-08 13:36:48 -04001977 && contentView.getPackage() != null
1978 && oldContentView.getPackage() != null
1979 && oldContentView.getPackage().equals(contentView.getPackage())
1980 && oldContentView.getLayoutId() == contentView.getLayoutId();
Chris Wrene9e37722012-05-11 17:40:14 -04001981 // large view may be null
1982 boolean bigContentsUnchanged =
Selim Cinekb8f09cf2015-03-16 17:09:28 -07001983 (entry.getBigContentView() == null && bigContentView == null)
1984 || ((entry.getBigContentView() != null && bigContentView != null)
Chris Wrene9e37722012-05-11 17:40:14 -04001985 && bigContentView.getPackage() != null
1986 && oldBigContentView.getPackage() != null
1987 && oldBigContentView.getPackage().equals(bigContentView.getPackage())
1988 && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
Chris Wren8fd39ec2014-02-27 17:43:26 -05001989 boolean headsUpContentsUnchanged =
1990 (oldHeadsUpContentView == null && headsUpContentView == null)
1991 || ((oldHeadsUpContentView != null && headsUpContentView != null)
1992 && headsUpContentView.getPackage() != null
1993 && oldHeadsUpContentView.getPackage() != null
1994 && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
1995 && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
Dan Sandlera5e0f412014-01-23 15:11:54 -05001996 boolean publicUnchanged =
1997 (oldPublicContentView == null && publicContentView == null)
1998 || ((oldPublicContentView != null && publicContentView != null)
1999 && publicContentView.getPackage() != null
2000 && oldPublicContentView.getPackage() != null
2001 && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
2002 && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
Selim Cinekb8f09cf2015-03-16 17:09:28 -07002003 return contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
2004 && publicUnchanged;
Chris Wrenf29c3b42013-11-27 16:30:30 -05002005 }
2006
Selim Cinek8d490d42015-04-10 00:05:50 -07002007 private void updateNotificationViews(Entry entry, StatusBarNotification notification) {
Chris Wrene03f4e12013-08-08 16:48:48 -04002008 final RemoteViews contentView = notification.getNotification().contentView;
Selim Cinek8d490d42015-04-10 00:05:50 -07002009 final RemoteViews bigContentView = notification.getNotification().bigContentView;
2010 final RemoteViews headsUpContentView = notification.getNotification().headsUpContentView;
John Spurlock70cf4c12014-02-13 09:39:14 -05002011 final Notification publicVersion = notification.getNotification().publicVersion;
2012 final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView
2013 : null;
Dan Sandlera5e0f412014-01-23 15:11:54 -05002014
Chris Wrene03f4e12013-08-08 16:48:48 -04002015 // Reapply the RemoteViews
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002016 contentView.reapply(mContext, entry.expanded, mOnClickHandler);
Chris Wrene03f4e12013-08-08 16:48:48 -04002017 if (bigContentView != null && entry.getBigContentView() != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002018 bigContentView.reapply(mContext, entry.getBigContentView(),
Jorim Jaggi39fa59f2014-02-25 15:38:45 +01002019 mOnClickHandler);
Chris Wrene03f4e12013-08-08 16:48:48 -04002020 }
Selim Cinek8d490d42015-04-10 00:05:50 -07002021 View headsUpChild = entry.row.getPrivateLayout().getHeadsUpChild();
2022 if (headsUpContentView != null && headsUpChild != null) {
2023 headsUpContentView.reapply(mContext, headsUpChild, mOnClickHandler);
2024 }
Dan Sandlera5e0f412014-01-23 15:11:54 -05002025 if (publicContentView != null && entry.getPublicContentView() != null) {
Jorim Jaggi5c2d8462014-03-21 17:37:00 +01002026 publicContentView.reapply(mContext, entry.getPublicContentView(), mOnClickHandler);
Dan Sandlera5e0f412014-01-23 15:11:54 -05002027 }
Chris Wrene03f4e12013-08-08 16:48:48 -04002028 // update the contentIntent
2029 final PendingIntent contentIntent = notification.getNotification().contentIntent;
2030 if (contentIntent != null) {
Chris Wren930ecca2014-11-12 17:43:41 -05002031 final View.OnClickListener listener = makeClicker(contentIntent, notification.getKey());
Jorim Jaggife40f7d2014-04-28 15:20:04 +02002032 entry.row.setOnClickListener(listener);
Chris Wrene03f4e12013-08-08 16:48:48 -04002033 } else {
Jorim Jaggife40f7d2014-04-28 15:20:04 +02002034 entry.row.setOnClickListener(null);
Chris Wren0c8275b2012-05-08 13:36:48 -04002035 }
Jorim Jaggib1cd3c12014-09-08 19:55:17 +02002036 entry.row.setStatusBarNotification(notification);
Jorim Jaggibe565df2014-04-28 17:51:23 +02002037 entry.row.notifyContentUpdated();
Selim Cinek51ae05d2014-09-09 15:51:38 +02002038 entry.row.resetHeight();
Adrian Roos497ab022015-02-10 20:49:33 +01002039
2040 applyRemoteInput(entry);
Chris Wren0c8275b2012-05-08 13:36:48 -04002041 }
John Spurlock36231282012-06-23 17:11:27 -04002042
Selim Cinekb8f09cf2015-03-16 17:09:28 -07002043 protected void notifyHeadsUpScreenOff() {
2044 escalateHeadsUp();
Chris Wrene97f90b2013-08-07 17:39:35 -04002045 }
2046
Chris Wrend93d5ad2014-09-08 16:34:03 -04002047 private boolean alertAgain(Entry oldEntry, Notification newNotification) {
2048 return oldEntry == null || !oldEntry.hasInterrupted()
2049 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
Chris Wrend04f6ce2014-06-11 17:37:28 -04002050 }
2051
Chris Wren51c75102013-07-16 20:49:17 -04002052 protected boolean shouldInterrupt(StatusBarNotification sbn) {
Christoph Studer2f9dbba2014-09-03 17:35:54 +02002053 if (mNotificationData.shouldFilterOut(sbn)) {
2054 if (DEBUG) {
2055 Log.d(TAG, "Skipping HUN check for " + sbn.getKey() + " since it's filtered out.");
2056 }
2057 return false;
2058 }
2059
Selim Cinek29fa89b2015-04-17 10:39:11 -07002060 if (isSnoozedPackage(sbn)) {
Chris Wrena6d4fb62014-11-20 14:46:23 -05002061 return false;
2062 }
2063
Chris Wren51c75102013-07-16 20:49:17 -04002064 Notification notification = sbn.getNotification();
2065 // some predicates to make the boolean logic legible
2066 boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
2067 || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
2068 || notification.sound != null
2069 || notification.vibrate != null;
2070 boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
2071 boolean isFullscreen = notification.fullScreenIntent != null;
Chris Wren22ae46e2014-02-26 18:08:09 -05002072 boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
Chris Wren51c75102013-07-16 20:49:17 -04002073 boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
2074 Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
Selim Cineka8fefa52014-09-08 16:10:50 +02002075 boolean accessibilityForcesLaunch = isFullscreen
2076 && mAccessibilityManager.isTouchExplorationEnabled();
Chris Wren51c75102013-07-16 20:49:17 -04002077
Chris Wren22ae46e2014-02-26 18:08:09 -05002078 boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
Chris Wren51c75102013-07-16 20:49:17 -04002079 && isAllowed
Selim Cineka8fefa52014-09-08 16:10:50 +02002080 && !accessibilityForcesLaunch
Chris Wren51c75102013-07-16 20:49:17 -04002081 && mPowerManager.isScreenOn()
Jim Millerab954542014-10-10 18:21:49 -07002082 && (!mStatusBarKeyguardViewManager.isShowing()
2083 || mStatusBarKeyguardViewManager.isOccluded())
2084 && !mStatusBarKeyguardViewManager.isInputRestricted();
Chris Wren157026f2013-06-28 16:54:01 -04002085 try {
2086 interrupt = interrupt && !mDreamManager.isDreaming();
2087 } catch (RemoteException e) {
2088 Log.d(TAG, "failed to query dream manager", e);
2089 }
Chris Wren51c75102013-07-16 20:49:17 -04002090 if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
Chris Wren157026f2013-06-28 16:54:01 -04002091 return interrupt;
2092 }
2093
Selim Cinek29fa89b2015-04-17 10:39:11 -07002094 protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
2095
John Spurlockcfc359a2013-09-05 10:42:03 -04002096 public void setInteracting(int barWindow, boolean interacting) {
John Spurlockad3e6cb2013-04-30 08:47:43 -04002097 // hook for subclasses
2098 }
John Spurlock5c454122013-06-17 07:35:46 -04002099
Jorim Jaggi4222d9a2014-04-23 16:13:15 +02002100 public void setBouncerShowing(boolean bouncerShowing) {
2101 mBouncerShowing = bouncerShowing;
2102 }
2103
2104 /**
2105 * @return Whether the security bouncer from Keyguard is showing.
2106 */
2107 public boolean isBouncerShowing() {
2108 return mBouncerShowing;
2109 }
2110
John Spurlock5c454122013-06-17 07:35:46 -04002111 public void destroy() {
John Spurlock5c454122013-06-17 07:35:46 -04002112 mContext.unregisterReceiver(mBroadcastReceiver);
Christoph Studere71fefc2014-06-24 16:16:49 +02002113 try {
2114 mNotificationListener.unregisterAsSystemService();
2115 } catch (RemoteException e) {
2116 // Ignore.
Christoph Studer37fe6932014-05-26 13:10:30 +02002117 }
John Spurlock5c454122013-06-17 07:35:46 -04002118 }
Kenny Guy4dcb0dc2014-07-15 12:41:24 +01002119
2120 /**
2121 * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
2122 * return PackageManager for mContext
2123 */
2124 protected PackageManager getPackageManagerForUser(int userId) {
2125 Context contextForUser = mContext;
2126 // UserHandle defines special userId as negative values, e.g. USER_ALL
2127 if (userId >= 0) {
2128 try {
2129 // Create a context for the correct user so if a package isn't installed
2130 // for user 0 we can still load information about the package.
2131 contextForUser =
2132 mContext.createPackageContextAsUser(mContext.getPackageName(),
2133 Context.CONTEXT_RESTRICTED,
2134 new UserHandle(userId));
2135 } catch (NameNotFoundException e) {
2136 // Shouldn't fail to find the package name for system ui.
2137 }
2138 }
2139 return contextForUser.getPackageManager();
2140 }
Chris Wren78403d72014-07-28 10:23:24 +01002141
2142 @Override
2143 public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
2144 try {
2145 mBarService.onNotificationExpansionChanged(key, userAction, expanded);
2146 } catch (RemoteException e) {
2147 // Ignore.
2148 }
2149 }
Jim Millerab954542014-10-10 18:21:49 -07002150
2151 public boolean isKeyguardSecure() {
Dan Sandlere163a642015-03-01 22:58:32 -05002152 if (mStatusBarKeyguardViewManager == null) {
2153 // startKeyguard() hasn't been called yet, so we don't know.
2154 // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
2155 // value onVisibilityChanged().
2156 Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
2157 new Throwable());
2158 return false;
2159 }
Jim Millerab954542014-10-10 18:21:49 -07002160 return mStatusBarKeyguardViewManager.isSecure();
2161 }
Joe Onorato2314aab2010-04-08 16:41:23 -05002162}