1 /* 2 * Copyright (C) 2022 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 17 package com.android.systemui.shade; 18 19 import android.content.ComponentCallbacks2; 20 import android.os.Looper; 21 import android.util.Log; 22 import android.view.MotionEvent; 23 import android.view.ViewTreeObserver; 24 import android.view.WindowManagerGlobal; 25 26 import com.android.systemui.DejankUtils; 27 import com.android.systemui.assist.AssistManager; 28 import com.android.systemui.dagger.SysUISingleton; 29 import com.android.systemui.dagger.qualifiers.DisplayId; 30 import com.android.systemui.dagger.qualifiers.Main; 31 import com.android.systemui.plugins.statusbar.StatusBarStateController; 32 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor; 33 import com.android.systemui.scene.shared.flag.SceneContainerFlag; 34 import com.android.systemui.statusbar.CommandQueue; 35 import com.android.systemui.statusbar.NotificationShadeWindowController; 36 import com.android.systemui.statusbar.StatusBarState; 37 import com.android.systemui.statusbar.notification.row.NotificationGutsManager; 38 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; 39 import com.android.systemui.statusbar.policy.DeviceProvisionedController; 40 import com.android.systemui.statusbar.policy.KeyguardStateController; 41 import com.android.systemui.statusbar.window.StatusBarWindowController; 42 43 import dagger.Lazy; 44 45 import java.util.concurrent.Executor; 46 47 import javax.inject.Inject; 48 49 /** An implementation of {@link ShadeController}. */ 50 @SysUISingleton 51 public final class ShadeControllerImpl extends BaseShadeControllerImpl { 52 53 private static final String TAG = "ShadeControllerImpl"; 54 private static final boolean SPEW = false; 55 56 private final int mDisplayId; 57 58 private final CommandQueue mCommandQueue; 59 private final Executor mMainExecutor; 60 private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor; 61 private final KeyguardStateController mKeyguardStateController; 62 private final NotificationShadeWindowController mNotificationShadeWindowController; 63 private final StatusBarStateController mStatusBarStateController; 64 private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 65 private final StatusBarWindowController mStatusBarWindowController; 66 private final DeviceProvisionedController mDeviceProvisionedController; 67 68 private final Lazy<NotificationPanelViewController> mNpvc; 69 private final Lazy<AssistManager> mAssistManagerLazy; 70 private final Lazy<NotificationGutsManager> mGutsManager; 71 72 private boolean mExpandedVisible; 73 private boolean mLockscreenOrShadeVisible; 74 75 private NotificationShadeWindowViewController mNotificationShadeWindowViewController; 76 private ShadeVisibilityListener mShadeVisibilityListener; 77 78 @Inject ShadeControllerImpl( CommandQueue commandQueue, @Main Executor mainExecutor, WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, StatusBarWindowController statusBarWindowController, DeviceProvisionedController deviceProvisionedController, NotificationShadeWindowController notificationShadeWindowController, @DisplayId int displayId, Lazy<NotificationPanelViewController> shadeViewControllerLazy, Lazy<AssistManager> assistManagerLazy, Lazy<NotificationGutsManager> gutsManager )79 public ShadeControllerImpl( 80 CommandQueue commandQueue, 81 @Main Executor mainExecutor, 82 WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor, 83 KeyguardStateController keyguardStateController, 84 StatusBarStateController statusBarStateController, 85 StatusBarKeyguardViewManager statusBarKeyguardViewManager, 86 StatusBarWindowController statusBarWindowController, 87 DeviceProvisionedController deviceProvisionedController, 88 NotificationShadeWindowController notificationShadeWindowController, 89 @DisplayId int displayId, 90 Lazy<NotificationPanelViewController> shadeViewControllerLazy, 91 Lazy<AssistManager> assistManagerLazy, 92 Lazy<NotificationGutsManager> gutsManager 93 ) { 94 super(commandQueue, 95 statusBarKeyguardViewManager, 96 notificationShadeWindowController, 97 assistManagerLazy); 98 SceneContainerFlag.assertInLegacyMode(); 99 mCommandQueue = commandQueue; 100 mMainExecutor = mainExecutor; 101 mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor; 102 mNpvc = shadeViewControllerLazy; 103 mStatusBarStateController = statusBarStateController; 104 mStatusBarWindowController = statusBarWindowController; 105 mDeviceProvisionedController = deviceProvisionedController; 106 mGutsManager = gutsManager; 107 mNotificationShadeWindowController = notificationShadeWindowController; 108 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 109 mDisplayId = displayId; 110 mKeyguardStateController = keyguardStateController; 111 mAssistManagerLazy = assistManagerLazy; 112 } 113 114 @Override isShadeEnabled()115 public boolean isShadeEnabled() { 116 return mCommandQueue.panelsEnabled() && mDeviceProvisionedController.isCurrentUserSetup(); 117 } 118 119 @Override instantExpandShade()120 public void instantExpandShade() { 121 // Make our window larger and the panel expanded. 122 makeExpandedVisible(true /* force */); 123 getNpvc().expand(false /* animate */); 124 getCommandQueue().recomputeDisableFlags(mDisplayId, false /* animate */); 125 } 126 127 @Override animateCollapseShade(int flags, boolean force, boolean delayed, float speedUpFactor)128 public void animateCollapseShade(int flags, boolean force, boolean delayed, 129 float speedUpFactor) { 130 int statusBarState = mStatusBarStateController.getState(); 131 if (!force && statusBarState != StatusBarState.SHADE 132 && statusBarState != StatusBarState.SHADE_LOCKED) { 133 runPostCollapseActions(); 134 return; 135 } 136 if (getNotificationShadeWindowView() != null 137 && getNpvc().canBeCollapsed() 138 && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) { 139 // release focus immediately to kick off focus change transition 140 mNotificationShadeWindowController.setNotificationShadeFocusable(false); 141 142 mNotificationShadeWindowViewController.cancelExpandHelper(); 143 getNpvc().collapse(true, delayed, speedUpFactor); 144 } 145 } 146 147 @Override collapseWithDuration(int animationDuration)148 public void collapseWithDuration(int animationDuration) { 149 mNpvc.get().collapseWithDuration(animationDuration); 150 } 151 152 @Override expandToNotifications()153 protected void expandToNotifications() { 154 getNpvc().expandToNotifications(); 155 } 156 157 @Override expandToQs()158 protected void expandToQs() { 159 getNpvc().expandToQs(); 160 } 161 162 @Override closeShadeIfOpen()163 public boolean closeShadeIfOpen() { 164 if (!getNpvc().isFullyCollapsed()) { 165 getCommandQueue().animateCollapsePanels( 166 CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); 167 notifyVisibilityChanged(false); 168 mAssistManagerLazy.get().hideAssist(); 169 } 170 return false; 171 } 172 173 @Override isShadeFullyOpen()174 public boolean isShadeFullyOpen() { 175 return getNpvc().isShadeFullyExpanded(); 176 } 177 178 @Override isExpandingOrCollapsing()179 public boolean isExpandingOrCollapsing() { 180 return getNpvc().isExpandingOrCollapsing(); 181 } 182 @Override postAnimateCollapseShade()183 public void postAnimateCollapseShade() { 184 mMainExecutor.execute(this::animateCollapseShade); 185 } 186 187 @Override postAnimateForceCollapseShade()188 public void postAnimateForceCollapseShade() { 189 mMainExecutor.execute(this::animateCollapseShadeForced); 190 } 191 192 @Override postAnimateExpandQs()193 public void postAnimateExpandQs() { 194 mMainExecutor.execute(this::animateExpandQs); 195 } 196 197 @Override postOnShadeExpanded(Runnable executable)198 public void postOnShadeExpanded(Runnable executable) { 199 getNpvc().addOnGlobalLayoutListener( 200 new ViewTreeObserver.OnGlobalLayoutListener() { 201 @Override 202 public void onGlobalLayout() { 203 if (getNotificationShadeWindowView().isVisibleToUser()) { 204 getNpvc().removeOnGlobalLayoutListener(this); 205 getNpvc().postToView(executable); 206 } 207 } 208 }); 209 } 210 211 @Override collapseShade()212 public void collapseShade() { 213 collapseShadeInternal(); 214 } 215 collapseShadeInternal()216 private boolean collapseShadeInternal() { 217 if (!getNpvc().isFullyCollapsed()) { 218 // close the shade if it was open 219 animateCollapseShadeForcedDelayed(); 220 notifyVisibilityChanged(false); 221 return true; 222 } else { 223 return false; 224 } 225 } 226 227 @Override collapseShade(boolean animate)228 public void collapseShade(boolean animate) { 229 if (animate) { 230 boolean willCollapse = collapseShadeInternal(); 231 if (!willCollapse) { 232 runPostCollapseActions(); 233 } 234 } else if (!getNotifPresenter().isPresenterFullyCollapsed()) { 235 instantCollapseShade(); 236 notifyVisibilityChanged(false); 237 } else { 238 runPostCollapseActions(); 239 } 240 } 241 242 @Override cancelExpansionAndCollapseShade()243 public void cancelExpansionAndCollapseShade() { 244 if (getNpvc().isTracking()) { 245 mNotificationShadeWindowViewController.cancelCurrentTouch(); 246 } 247 if (getNpvc().isPanelExpanded() 248 && mStatusBarStateController.getState() == StatusBarState.SHADE) { 249 animateCollapseShade(); 250 } 251 } 252 253 @Override collapseOnMainThread()254 public void collapseOnMainThread() { 255 if (Looper.getMainLooper().isCurrentThread()) { 256 collapseShade(); 257 } else { 258 mMainExecutor.execute(this::collapseShade); 259 } 260 } 261 262 @Override onStatusBarTouch(MotionEvent event)263 public void onStatusBarTouch(MotionEvent event) { 264 if (event.getAction() == MotionEvent.ACTION_UP) { 265 if (mExpandedVisible) { 266 animateCollapseShade(); 267 } 268 } 269 } 270 271 @Override performHapticFeedback(int constant)272 public void performHapticFeedback(int constant) { 273 getNpvc().performHapticFeedback(constant); 274 } 275 276 @Override instantCollapseShade()277 public void instantCollapseShade() { 278 getNpvc().instantCollapse(); 279 runPostCollapseActions(); 280 } 281 282 @Override makeExpandedVisible(boolean force)283 public void makeExpandedVisible(boolean force) { 284 if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); 285 if (!force && (mExpandedVisible || !getCommandQueue().panelsEnabled())) { 286 return; 287 } 288 289 mExpandedVisible = true; 290 291 // Expand the window to encompass the full screen in anticipation of the drag. 292 // It's only possible to do atomically because the status bar is at the top of the screen! 293 mNotificationShadeWindowController.setPanelVisible(true); 294 295 notifyVisibilityChanged(true); 296 getCommandQueue().recomputeDisableFlags(mDisplayId, !force /* animate */); 297 notifyExpandedVisibleChanged(true); 298 } 299 300 @Override makeExpandedInvisible()301 public void makeExpandedInvisible() { 302 if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible); 303 304 if (!mExpandedVisible || getNotificationShadeWindowView() == null) { 305 return; 306 } 307 308 // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) 309 getNpvc().collapse(false, false, 1.0f); 310 311 mExpandedVisible = false; 312 notifyVisibilityChanged(false); 313 314 // Update the visibility of notification shade and status bar window. 315 mNotificationShadeWindowController.setPanelVisible(false); 316 mStatusBarWindowController.setForceStatusBarVisible(false); 317 318 // Close any guts that might be visible 319 mGutsManager.get().closeAndSaveGuts( 320 true /* removeLeavebehind */, 321 true /* force */, 322 true /* removeControls */, 323 -1 /* x */, 324 -1 /* y */, 325 true /* resetMenu */); 326 327 runPostCollapseActions(); 328 notifyExpandedVisibleChanged(false); 329 getCommandQueue().recomputeDisableFlags( 330 mDisplayId, 331 getNpvc().shouldHideStatusBarIconsWhenExpanded()); 332 333 // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in 334 // the bouncer appear animation. 335 if (!mKeyguardStateController.isShowing()) { 336 WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); 337 } 338 } 339 340 @Override isExpandedVisible()341 public boolean isExpandedVisible() { 342 return mExpandedVisible; 343 } 344 345 @Override setVisibilityListener(ShadeVisibilityListener listener)346 public void setVisibilityListener(ShadeVisibilityListener listener) { 347 mShadeVisibilityListener = listener; 348 } 349 notifyVisibilityChanged(boolean visible)350 private void notifyVisibilityChanged(boolean visible) { 351 mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(visible); 352 if (mLockscreenOrShadeVisible != visible) { 353 mLockscreenOrShadeVisible = visible; 354 if (visible) { 355 // It would be best if this could be done as a side effect of listening to the 356 // [WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible] flow inside 357 // NotificationShadeWindowViewController. However, there's no guarantee that the 358 // flow will emit in the same frame as when the visibility changed, and we want the 359 // DejankUtils to be notified immediately, so we do it immediately here. 360 DejankUtils.notifyRendererOfExpensiveFrame( 361 getNotificationShadeWindowView(), "onShadeVisibilityChanged"); 362 } 363 } 364 } 365 notifyExpandedVisibleChanged(boolean expandedVisible)366 private void notifyExpandedVisibleChanged(boolean expandedVisible) { 367 mShadeVisibilityListener.expandedVisibleChanged(expandedVisible); 368 } 369 370 @Override setNotificationShadeWindowViewController( NotificationShadeWindowViewController controller)371 public void setNotificationShadeWindowViewController( 372 NotificationShadeWindowViewController controller) { 373 mNotificationShadeWindowViewController = controller; 374 } 375 getNotificationShadeWindowView()376 private NotificationShadeWindowView getNotificationShadeWindowView() { 377 return mNotificationShadeWindowViewController.getView(); 378 } 379 getNpvc()380 private NotificationPanelViewController getNpvc() { 381 return mNpvc.get(); 382 } 383 384 @Override start()385 public void start() { 386 getNpvc().setTrackingStartedListener(this::runPostCollapseActions); 387 getNpvc().setOpenCloseListener( 388 new OpenCloseListener() { 389 @Override 390 public void onClosingFinished() { 391 ShadeControllerImpl.this.onClosingFinished(); 392 } 393 394 @Override 395 public void onOpenStarted() { 396 makeExpandedVisible(false); 397 } 398 }); 399 } 400 401 @Override collapseShadeForActivityStart()402 public void collapseShadeForActivityStart() { 403 if (isExpandedVisible() && !mStatusBarKeyguardViewManager.isBouncerShowing()) { 404 animateCollapseShadeForcedDelayed(); 405 } else { 406 // Do it after DismissAction has been processed to conserve the 407 // needed ordering. 408 mMainExecutor.execute(this::runPostCollapseActions); 409 } 410 } 411 412 } 413