blob: 13d4b8edb8d45e79214e48bae58f2669813e1096 [file] [log] [blame]
Jason Monk297c04e2018-08-23 17:16:59 -04001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.systemui.statusbar.phone;
16
17import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;
18
19import static com.android.systemui.SysUiServiceProvider.getComponent;
Gus Prevas21437b32018-12-05 10:36:13 -050020import static com.android.systemui.statusbar.NotificationLockscreenUserManager.NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
Jason Monk297c04e2018-08-23 17:16:59 -040021
22import android.app.ActivityManager;
23import android.app.KeyguardManager;
24import android.app.PendingIntent;
25import android.app.StatusBarManager;
26import android.content.BroadcastReceiver;
27import android.content.Context;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.IntentSender;
Selim Cinekf7d88932019-05-01 17:31:25 -070031import android.os.Handler;
Jason Monk297c04e2018-08-23 17:16:59 -040032import android.os.RemoteException;
33import android.os.UserHandle;
34import android.view.View;
35import android.view.ViewParent;
Jason Monk297c04e2018-08-23 17:16:59 -040036
Mark Renouf6b2331c2019-03-21 13:40:08 -040037import com.android.systemui.ActivityIntentHelper;
Jason Monk297c04e2018-08-23 17:16:59 -040038import com.android.systemui.Dependency;
39import com.android.systemui.plugins.ActivityStarter;
Beverly8fdb5332019-02-04 14:29:49 -050040import com.android.systemui.plugins.statusbar.StatusBarStateController;
Jason Monk297c04e2018-08-23 17:16:59 -040041import com.android.systemui.statusbar.CommandQueue;
42import com.android.systemui.statusbar.CommandQueue.Callbacks;
43import com.android.systemui.statusbar.NotificationLockscreenUserManager;
44import com.android.systemui.statusbar.NotificationRemoteInputManager;
45import com.android.systemui.statusbar.NotificationRemoteInputManager.Callback;
46import com.android.systemui.statusbar.StatusBarState;
Beverly8fdb5332019-02-04 14:29:49 -050047import com.android.systemui.statusbar.SysuiStatusBarStateController;
Jason Monk297c04e2018-08-23 17:16:59 -040048import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
49import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
50import com.android.systemui.statusbar.policy.KeyguardMonitor;
Selim Cinekd17b3502019-07-02 20:38:32 -070051import com.android.systemui.statusbar.policy.RemoteInputView;
Jason Monk297c04e2018-08-23 17:16:59 -040052
Jason Monk196d6392018-12-20 13:25:34 -050053import javax.inject.Inject;
54import javax.inject.Singleton;
55
56/**
57 */
58@Singleton
Lucas Dupin7fc9dc12019-01-03 09:19:43 -080059public class StatusBarRemoteInputCallback implements Callback, Callbacks,
60 StatusBarStateController.StateListener {
Jason Monk297c04e2018-08-23 17:16:59 -040061
62 private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
Beverly8fdb5332019-02-04 14:29:49 -050063 private final SysuiStatusBarStateController mStatusBarStateController =
64 (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
65 private final NotificationLockscreenUserManager mLockscreenUserManager =
66 Dependency.get(NotificationLockscreenUserManager.class);
Jason Monk297c04e2018-08-23 17:16:59 -040067 private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
68 private final Context mContext;
Mark Renouf6b2331c2019-03-21 13:40:08 -040069 private final ActivityIntentHelper mActivityIntentHelper;
Selim Cinekf7d88932019-05-01 17:31:25 -070070 private final NotificationGroupManager mGroupManager;
Jason Monk297c04e2018-08-23 17:16:59 -040071 private View mPendingWorkRemoteInputView;
Jason Monk297c04e2018-08-23 17:16:59 -040072 private View mPendingRemoteInputView;
73 private final ShadeController mShadeController = Dependency.get(ShadeController.class);
74 private KeyguardManager mKeyguardManager;
75 private final CommandQueue mCommandQueue;
76 private int mDisabled2;
77 protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver();
Selim Cinekf7d88932019-05-01 17:31:25 -070078 private Handler mMainHandler = new Handler();
Jason Monk297c04e2018-08-23 17:16:59 -040079
Jason Monk196d6392018-12-20 13:25:34 -050080 /**
81 */
82 @Inject
Selim Cinekf7d88932019-05-01 17:31:25 -070083 public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager) {
Jason Monk297c04e2018-08-23 17:16:59 -040084 mContext = context;
85 mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
86 new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
Lucas Dupin7fc9dc12019-01-03 09:19:43 -080087 mStatusBarStateController.addCallback(this);
Jason Monk297c04e2018-08-23 17:16:59 -040088 mKeyguardManager = context.getSystemService(KeyguardManager.class);
89 mCommandQueue = getComponent(context, CommandQueue.class);
Jason Monkd7c98552018-12-04 11:14:50 -050090 mCommandQueue.addCallback(this);
Mark Renouf6b2331c2019-03-21 13:40:08 -040091 mActivityIntentHelper = new ActivityIntentHelper(mContext);
Selim Cinekf7d88932019-05-01 17:31:25 -070092 mGroupManager = groupManager;
Jason Monk297c04e2018-08-23 17:16:59 -040093 }
94
Lucas Dupin7fc9dc12019-01-03 09:19:43 -080095 @Override
96 public void onStateChanged(int state) {
Selim Cinekd17b3502019-07-02 20:38:32 -070097 boolean hasPendingRemoteInput = mPendingRemoteInputView != null;
98 if (state == StatusBarState.SHADE
99 && (mStatusBarStateController.leaveOpenOnKeyguardHide() || hasPendingRemoteInput)) {
Jason Monk297c04e2018-08-23 17:16:59 -0400100 if (!mStatusBarStateController.isKeyguardRequested()) {
Selim Cinekd17b3502019-07-02 20:38:32 -0700101 if (hasPendingRemoteInput) {
Selim Cinekf7d88932019-05-01 17:31:25 -0700102 mMainHandler.post(mPendingRemoteInputView::callOnClick);
Jason Monk297c04e2018-08-23 17:16:59 -0400103 }
104 mPendingRemoteInputView = null;
105 }
106 }
107 }
108
109 @Override
110 public void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
Selim Cinekd17b3502019-07-02 20:38:32 -0700111 if (!row.isPinned()) {
112 mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
113 }
Jason Monk297c04e2018-08-23 17:16:59 -0400114 mShadeController.showBouncer(true /* scrimmed */);
115 mPendingRemoteInputView = clicked;
116 }
117
118 protected void onWorkChallengeChanged() {
Pavel Grafove2cf85a2019-05-17 13:11:01 +0100119 mLockscreenUserManager.updatePublicMode();
Jason Monk297c04e2018-08-23 17:16:59 -0400120 if (mPendingWorkRemoteInputView != null
121 && !mLockscreenUserManager.isAnyProfilePublicMode()) {
122 // Expand notification panel and the notification row, then click on remote input view
123 final Runnable clickPendingViewRunnable = () -> {
124 final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
125 if (pendingWorkRemoteInputView == null) {
126 return;
127 }
128
129 // Climb up the hierarchy until we get to the container for this row.
130 ViewParent p = pendingWorkRemoteInputView.getParent();
131 while (!(p instanceof ExpandableNotificationRow)) {
132 if (p == null) {
133 return;
134 }
135 p = p.getParent();
136 }
137
138 final ExpandableNotificationRow row = (ExpandableNotificationRow) p;
139 ViewParent viewParent = row.getParent();
140 if (viewParent instanceof NotificationStackScrollLayout) {
141 final NotificationStackScrollLayout scrollLayout =
142 (NotificationStackScrollLayout) viewParent;
143 row.makeActionsVisibile();
144 row.post(() -> {
145 final Runnable finishScrollingCallback = () -> {
146 mPendingWorkRemoteInputView.callOnClick();
147 mPendingWorkRemoteInputView = null;
148 scrollLayout.setFinishScrollingCallback(null);
149 };
150 if (scrollLayout.scrollTo(row)) {
151 // It scrolls! So call it when it's finished.
152 scrollLayout.setFinishScrollingCallback(finishScrollingCallback);
153 } else {
154 // It does not scroll, so call it now!
155 finishScrollingCallback.run();
156 }
157 });
158 }
159 };
160 mShadeController.postOnShadeExpanded(clickPendingViewRunnable);
161 mShadeController.instantExpandNotificationsPanel();
162 }
163 }
164
165 @Override
166 public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row,
167 View clickedView) {
168 if (mKeyguardMonitor.isShowing()) {
169 onLockedRemoteInput(row, clickedView);
170 } else {
Selim Cinekf7d88932019-05-01 17:31:25 -0700171 if (row.isChildInGroup() && !row.areChildrenExpanded()) {
172 // The group isn't expanded, let's make sure it's visible!
173 mGroupManager.toggleGroupExpansion(row.getStatusBarNotification());
174 }
Jason Monk297c04e2018-08-23 17:16:59 -0400175 row.setUserExpanded(true);
176 row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick);
177 }
178 }
179
180 @Override
181 public void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
182 View clicked) {
183 // Collapse notification and show work challenge
184 mCommandQueue.animateCollapsePanels();
185 startWorkChallengeIfNecessary(userId, null, null);
186 // Add pending remote input view after starting work challenge, as starting work challenge
187 // will clear all previous pending review view
188 mPendingWorkRemoteInputView = clicked;
189 }
190
Gus Prevas21437b32018-12-05 10:36:13 -0500191 boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
Jason Monk297c04e2018-08-23 17:16:59 -0400192 String notificationKey) {
193 // Clear pending remote view, as we do not want to trigger pending remote input view when
194 // it's called by other code
195 mPendingWorkRemoteInputView = null;
196 // Begin old BaseStatusBar.startWorkChallengeIfNecessary.
197 final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
198 null, userId);
199 if (newIntent == null) {
200 return false;
201 }
202 final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
203 callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
204 callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
205 callBackIntent.setPackage(mContext.getPackageName());
206
207 PendingIntent callBackPendingIntent = PendingIntent.getBroadcast(
208 mContext,
209 0,
210 callBackIntent,
211 PendingIntent.FLAG_CANCEL_CURRENT |
212 PendingIntent.FLAG_ONE_SHOT |
213 PendingIntent.FLAG_IMMUTABLE);
214 newIntent.putExtra(
215 Intent.EXTRA_INTENT,
216 callBackPendingIntent.getIntentSender());
217 try {
218 ActivityManager.getService().startConfirmDeviceCredentialIntent(newIntent,
219 null /*options*/);
220 } catch (RemoteException ex) {
221 // ignore
222 }
223 return true;
224 // End old BaseStatusBar.startWorkChallengeIfNecessary.
225 }
226
227 @Override
228 public boolean shouldHandleRemoteInput(View view, PendingIntent pendingIntent) {
229 // Skip remote input as doing so will expand the notification shade.
230 return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
231 }
232
233 @Override
yoshiki iguchi67c166d2018-11-26 13:11:36 +0900234 public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
Sunny Goyal43c97042018-08-23 15:21:26 -0700235 NotificationRemoteInputManager.ClickHandler defaultHandler) {
Jason Monk297c04e2018-08-23 17:16:59 -0400236 final boolean isActivity = pendingIntent.isActivity();
237 if (isActivity) {
Mark Renouf6b2331c2019-03-21 13:40:08 -0400238 final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity(
239 pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId());
Jason Monk297c04e2018-08-23 17:16:59 -0400240 mActivityStarter.dismissKeyguardThenExecute(() -> {
241 try {
242 ActivityManager.getService().resumeAppSwitches();
243 } catch (RemoteException e) {
244 }
245
246 boolean handled = defaultHandler.handleClick();
247
248 // close the shade if it was open and maybe wait for activity start.
249 return handled && mShadeController.closeShadeIfOpen();
250 }, null, afterKeyguardGone);
251 return true;
252 } else {
253 return defaultHandler.handleClick();
254 }
255 }
256
257 @Override
Charles Chenf3d295c2018-11-30 18:15:21 +0800258 public void disable(int displayId, int state1, int state2, boolean animate) {
259 if (displayId == mContext.getDisplayId()) {
260 mDisabled2 = state2;
261 }
Jason Monk297c04e2018-08-23 17:16:59 -0400262 }
263
264 protected class ChallengeReceiver extends BroadcastReceiver {
265 @Override
266 public void onReceive(Context context, Intent intent) {
267 final String action = intent.getAction();
268 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
269 if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
270 if (userId != mLockscreenUserManager.getCurrentUserId()
271 && mLockscreenUserManager.isCurrentProfile(userId)) {
272 onWorkChallengeChanged();
273 }
274 }
275 }
276 };
277}