1 /* 2 * Copyright (C) 2017 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 15 package com.android.systemui.statusbar.phone; 16 17 import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; 18 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; 19 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; 20 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; 21 import static android.app.StatusBarManager.WindowType; 22 import static android.app.StatusBarManager.WindowVisibleState; 23 import static android.app.StatusBarManager.windowStateToString; 24 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; 25 26 import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; 27 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; 28 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; 29 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; 30 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; 31 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; 32 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; 33 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; 34 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT; 35 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; 36 import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; 37 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE; 38 import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions; 39 40 import android.accessibilityservice.AccessibilityServiceInfo; 41 import android.annotation.IdRes; 42 import android.annotation.Nullable; 43 import android.app.ActivityManager; 44 import android.app.ActivityTaskManager; 45 import android.app.IActivityTaskManager; 46 import android.app.StatusBarManager; 47 import android.content.BroadcastReceiver; 48 import android.content.ContentResolver; 49 import android.content.Context; 50 import android.content.Intent; 51 import android.content.IntentFilter; 52 import android.content.res.Configuration; 53 import android.database.ContentObserver; 54 import android.graphics.PixelFormat; 55 import android.graphics.Rect; 56 import android.inputmethodservice.InputMethodService; 57 import android.net.Uri; 58 import android.os.Binder; 59 import android.os.Bundle; 60 import android.os.Handler; 61 import android.os.IBinder; 62 import android.os.Looper; 63 import android.os.RemoteException; 64 import android.os.UserHandle; 65 import android.provider.Settings; 66 import android.telecom.TelecomManager; 67 import android.text.TextUtils; 68 import android.util.Log; 69 import android.view.Display; 70 import android.view.KeyEvent; 71 import android.view.LayoutInflater; 72 import android.view.MotionEvent; 73 import android.view.Surface; 74 import android.view.View; 75 import android.view.ViewGroup; 76 import android.view.WindowManager; 77 import android.view.WindowManager.LayoutParams; 78 import android.view.accessibility.AccessibilityEvent; 79 import android.view.accessibility.AccessibilityManager; 80 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; 81 82 import androidx.annotation.VisibleForTesting; 83 84 import com.android.internal.logging.MetricsLogger; 85 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 86 import com.android.internal.util.LatencyTracker; 87 import com.android.systemui.Dependency; 88 import com.android.systemui.R; 89 import com.android.systemui.ScreenDecorations; 90 import com.android.systemui.SysUiServiceProvider; 91 import com.android.systemui.assist.AssistManager; 92 import com.android.systemui.fragments.FragmentHostManager; 93 import com.android.systemui.fragments.FragmentHostManager.FragmentListener; 94 import com.android.systemui.plugins.statusbar.StatusBarStateController; 95 import com.android.systemui.recents.OverviewProxyService; 96 import com.android.systemui.recents.Recents; 97 import com.android.systemui.shared.system.ActivityManagerWrapper; 98 import com.android.systemui.shared.system.QuickStepContract; 99 import com.android.systemui.stackdivider.Divider; 100 import com.android.systemui.statusbar.CommandQueue; 101 import com.android.systemui.statusbar.CommandQueue.Callbacks; 102 import com.android.systemui.statusbar.StatusBarState; 103 import com.android.systemui.statusbar.notification.stack.StackStateAnimator; 104 import com.android.systemui.statusbar.phone.ContextualButton.ContextButtonListener; 105 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; 106 import com.android.systemui.statusbar.policy.DeviceProvisionedController; 107 import com.android.systemui.statusbar.policy.KeyButtonView; 108 import com.android.systemui.util.LifecycleFragment; 109 110 import java.io.FileDescriptor; 111 import java.io.PrintWriter; 112 import java.util.List; 113 import java.util.Locale; 114 import java.util.function.Consumer; 115 116 import javax.inject.Inject; 117 118 /** 119 * Fragment containing the NavigationBarFragment. Contains logic for what happens 120 * on clicks and view states of the nav bar. 121 */ 122 public class NavigationBarFragment extends LifecycleFragment implements Callbacks, 123 NavigationModeController.ModeChangedListener, AutoHideElement { 124 125 public static final String TAG = "NavigationBar"; 126 private static final boolean DEBUG = false; 127 private static final String EXTRA_DISABLE_STATE = "disabled_state"; 128 private static final String EXTRA_DISABLE2_STATE = "disabled2_state"; 129 private static final String EXTRA_SYSTEM_UI_VISIBILITY = "system_ui_visibility"; 130 131 /** Allow some time inbetween the long press for back and recents. */ 132 private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; 133 private static final long AUTODIM_TIMEOUT_MS = 2250; 134 135 private final AccessibilityManagerWrapper mAccessibilityManagerWrapper; 136 protected final AssistManager mAssistManager; 137 private final MetricsLogger mMetricsLogger; 138 private final DeviceProvisionedController mDeviceProvisionedController; 139 private final StatusBarStateController mStatusBarStateController; 140 private final NavigationModeController mNavigationModeController; 141 142 protected NavigationBarView mNavigationBarView = null; 143 144 private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING; 145 146 private int mNavigationIconHints = 0; 147 private @TransitionMode int mNavigationBarMode; 148 private AccessibilityManager mAccessibilityManager; 149 private MagnificationContentObserver mMagnificationObserver; 150 private ContentResolver mContentResolver; 151 private boolean mAssistantAvailable; 152 153 private int mDisabledFlags1; 154 private int mDisabledFlags2; 155 private StatusBar mStatusBar; 156 private Recents mRecents; 157 private Divider mDivider; 158 private WindowManager mWindowManager; 159 private CommandQueue mCommandQueue; 160 private long mLastLockToAppLongPress; 161 162 private Locale mLocale; 163 private int mLayoutDirection; 164 165 private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; 166 private int mNavBarMode = NAV_BAR_MODE_3BUTTON; 167 private LightBarController mLightBarController; 168 private AutoHideController mAutoHideController; 169 170 private OverviewProxyService mOverviewProxyService; 171 172 @VisibleForTesting 173 public int mDisplayId; 174 private boolean mIsOnDefaultDisplay; 175 public boolean mHomeBlockedThisTouch; 176 private ScreenDecorations mScreenDecorations; 177 178 private Handler mHandler = Dependency.get(Dependency.MAIN_HANDLER); 179 180 private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() { 181 @Override 182 public void onConnectionChanged(boolean isConnected) { 183 mNavigationBarView.updateStates(); 184 updateScreenPinningGestures(); 185 186 // Send the assistant availability upon connection 187 if (isConnected) { 188 sendAssistantAvailability(mAssistantAvailable); 189 } 190 } 191 192 @Override 193 public void onQuickStepStarted() { 194 // Use navbar dragging as a signal to hide the rotate button 195 mNavigationBarView.getRotationButtonController().setRotateSuggestionButtonState(false); 196 197 // Hide the notifications panel when quick step starts 198 mStatusBar.collapsePanel(true /* animate */); 199 } 200 201 @Override 202 public void startAssistant(Bundle bundle) { 203 mAssistManager.startAssist(bundle); 204 } 205 206 @Override 207 public void onNavBarButtonAlphaChanged(float alpha, boolean animate) { 208 ButtonDispatcher buttonDispatcher = null; 209 if (QuickStepContract.isSwipeUpMode(mNavBarMode)) { 210 buttonDispatcher = mNavigationBarView.getBackButton(); 211 } else if (QuickStepContract.isGesturalMode(mNavBarMode)) { 212 buttonDispatcher = mNavigationBarView.getHomeHandle(); 213 } 214 if (buttonDispatcher != null) { 215 buttonDispatcher.setVisibility(alpha > 0 ? View.VISIBLE : View.INVISIBLE); 216 buttonDispatcher.setAlpha(alpha, animate); 217 } 218 } 219 }; 220 221 private final ContextButtonListener mRotationButtonListener = (button, visible) -> { 222 if (visible) { 223 // If the button will actually become visible and the navbar is about to hide, 224 // tell the statusbar to keep it around for longer 225 mAutoHideController.touchAutoHide(); 226 } 227 }; 228 229 private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true); 230 231 private final ContentObserver mAssistContentObserver = new ContentObserver( 232 new Handler(Looper.getMainLooper())) { 233 @Override 234 public void onChange(boolean selfChange, Uri uri) { 235 boolean available = mAssistManager 236 .getAssistInfoForUser(UserHandle.USER_CURRENT) != null; 237 if (mAssistantAvailable != available) { 238 sendAssistantAvailability(available); 239 mAssistantAvailable = available; 240 } 241 } 242 }; 243 244 @Inject NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper, DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger, AssistManager assistManager, OverviewProxyService overviewProxyService, NavigationModeController navigationModeController, StatusBarStateController statusBarStateController)245 public NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper, 246 DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger, 247 AssistManager assistManager, OverviewProxyService overviewProxyService, 248 NavigationModeController navigationModeController, 249 StatusBarStateController statusBarStateController) { 250 mAccessibilityManagerWrapper = accessibilityManagerWrapper; 251 mDeviceProvisionedController = deviceProvisionedController; 252 mStatusBarStateController = statusBarStateController; 253 mMetricsLogger = metricsLogger; 254 mAssistManager = assistManager; 255 mAssistantAvailable = mAssistManager.getAssistInfoForUser(UserHandle.USER_CURRENT) != null; 256 mOverviewProxyService = overviewProxyService; 257 mNavigationModeController = navigationModeController; 258 mNavBarMode = navigationModeController.addListener(this); 259 } 260 261 // ----- Fragment Lifecycle Callbacks ----- 262 263 @Override onCreate(@ullable Bundle savedInstanceState)264 public void onCreate(@Nullable Bundle savedInstanceState) { 265 super.onCreate(savedInstanceState); 266 mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class); 267 mCommandQueue.observe(getLifecycle(), this); 268 mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class); 269 mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class); 270 mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class); 271 mWindowManager = getContext().getSystemService(WindowManager.class); 272 mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class); 273 mContentResolver = getContext().getContentResolver(); 274 mMagnificationObserver = new MagnificationContentObserver( 275 getContext().getMainThreadHandler()); 276 mContentResolver.registerContentObserver(Settings.Secure.getUriFor( 277 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false, 278 mMagnificationObserver, UserHandle.USER_ALL); 279 mContentResolver.registerContentObserver( 280 Settings.Secure.getUriFor(Settings.Secure.ASSISTANT), 281 false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL); 282 283 if (savedInstanceState != null) { 284 mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0); 285 mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0); 286 mSystemUiVisibility = savedInstanceState.getInt(EXTRA_SYSTEM_UI_VISIBILITY, 0); 287 } 288 mAccessibilityManagerWrapper.addCallback(mAccessibilityListener); 289 290 // Respect the latest disabled-flags. 291 mCommandQueue.recomputeDisableFlags(mDisplayId, false); 292 } 293 294 @Override onDestroy()295 public void onDestroy() { 296 super.onDestroy(); 297 mNavigationModeController.removeListener(this); 298 mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener); 299 mContentResolver.unregisterContentObserver(mMagnificationObserver); 300 mContentResolver.unregisterContentObserver(mAssistContentObserver); 301 } 302 303 @Override onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState)304 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 305 Bundle savedInstanceState) { 306 return inflater.inflate(R.layout.navigation_bar, container, false); 307 } 308 309 @Override onViewCreated(View view, @Nullable Bundle savedInstanceState)310 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 311 super.onViewCreated(view, savedInstanceState); 312 mNavigationBarView = (NavigationBarView) view; 313 final Display display = view.getDisplay(); 314 // It may not have display when running unit test. 315 if (display != null) { 316 mDisplayId = display.getDisplayId(); 317 mIsOnDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY; 318 } 319 320 mNavigationBarView.setComponents(mStatusBar.getPanel(), mAssistManager); 321 mNavigationBarView.setDisabledFlags(mDisabledFlags1); 322 mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged); 323 mNavigationBarView.setOnTouchListener(this::onNavigationTouch); 324 if (savedInstanceState != null) { 325 mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState); 326 } 327 mNavigationBarView.setNavigationIconHints(mNavigationIconHints); 328 mNavigationBarView.setWindowVisible(isNavBarWindowVisible()); 329 330 prepareNavigationBarView(); 331 checkNavBarModes(); 332 333 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 334 filter.addAction(Intent.ACTION_SCREEN_ON); 335 filter.addAction(Intent.ACTION_USER_SWITCHED); 336 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); 337 notifyNavigationBarScreenOn(); 338 339 mOverviewProxyService.addCallback(mOverviewProxyListener); 340 updateSystemUiStateFlags(-1); 341 342 // Currently there is no accelerometer sensor on non-default display. 343 if (mIsOnDefaultDisplay) { 344 mNavigationBarView.getRotateSuggestionButton().setListener(mRotationButtonListener); 345 346 final RotationButtonController rotationButtonController = 347 mNavigationBarView.getRotationButtonController(); 348 rotationButtonController.addRotationCallback(mRotationWatcher); 349 350 // Reset user rotation pref to match that of the WindowManager if starting in locked 351 // mode. This will automatically happen when switching from auto-rotate to locked mode. 352 if (display != null && rotationButtonController.isRotationLocked()) { 353 rotationButtonController.setRotationLockedAtAngle(display.getRotation()); 354 } 355 } else { 356 mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS; 357 } 358 setDisabled2Flags(mDisabledFlags2); 359 360 mScreenDecorations = SysUiServiceProvider.getComponent(getContext(), 361 ScreenDecorations.class); 362 getBarTransitions().addDarkIntensityListener(mScreenDecorations); 363 } 364 365 @Override onDestroyView()366 public void onDestroyView() { 367 super.onDestroyView(); 368 if (mNavigationBarView != null) { 369 mNavigationBarView.getBarTransitions().removeDarkIntensityListener(mScreenDecorations); 370 mNavigationBarView.getBarTransitions().destroy(); 371 mNavigationBarView.getLightTransitionsController().destroy(getContext()); 372 } 373 mOverviewProxyService.removeCallback(mOverviewProxyListener); 374 getContext().unregisterReceiver(mBroadcastReceiver); 375 } 376 377 @Override onSaveInstanceState(Bundle outState)378 public void onSaveInstanceState(Bundle outState) { 379 super.onSaveInstanceState(outState); 380 outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1); 381 outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2); 382 outState.putInt(EXTRA_SYSTEM_UI_VISIBILITY, mSystemUiVisibility); 383 if (mNavigationBarView != null) { 384 mNavigationBarView.getLightTransitionsController().saveState(outState); 385 } 386 } 387 388 @Override onConfigurationChanged(Configuration newConfig)389 public void onConfigurationChanged(Configuration newConfig) { 390 super.onConfigurationChanged(newConfig); 391 final Locale locale = getContext().getResources().getConfiguration().locale; 392 final int ld = TextUtils.getLayoutDirectionFromLocale(locale); 393 if (!locale.equals(mLocale) || ld != mLayoutDirection) { 394 if (DEBUG) { 395 Log.v(TAG, String.format( 396 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection, 397 locale, ld)); 398 } 399 mLocale = locale; 400 mLayoutDirection = ld; 401 refreshLayout(ld); 402 } 403 repositionNavigationBar(); 404 } 405 406 @Override dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args)407 public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) { 408 if (mNavigationBarView != null) { 409 pw.print(" mNavigationBarWindowState="); 410 pw.println(windowStateToString(mNavigationBarWindowState)); 411 pw.print(" mNavigationBarMode="); 412 pw.println(BarTransitions.modeToString(mNavigationBarMode)); 413 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions()); 414 } 415 416 pw.print(" mNavigationBarView="); 417 if (mNavigationBarView == null) { 418 pw.println("null"); 419 } else { 420 mNavigationBarView.dump(fd, pw, args); 421 } 422 } 423 424 // ----- CommandQueue Callbacks ----- 425 426 @Override setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher)427 public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, 428 boolean showImeSwitcher) { 429 if (displayId != mDisplayId) { 430 return; 431 } 432 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; 433 int hints = mNavigationIconHints; 434 switch (backDisposition) { 435 case InputMethodService.BACK_DISPOSITION_DEFAULT: 436 case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS: 437 case InputMethodService.BACK_DISPOSITION_WILL_DISMISS: 438 if (imeShown) { 439 hints |= NAVIGATION_HINT_BACK_ALT; 440 } else { 441 hints &= ~NAVIGATION_HINT_BACK_ALT; 442 } 443 break; 444 case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING: 445 hints &= ~NAVIGATION_HINT_BACK_ALT; 446 break; 447 } 448 if (showImeSwitcher) { 449 hints |= NAVIGATION_HINT_IME_SHOWN; 450 } else { 451 hints &= ~NAVIGATION_HINT_IME_SHOWN; 452 } 453 if (hints == mNavigationIconHints) return; 454 455 mNavigationIconHints = hints; 456 457 if (mNavigationBarView != null) { 458 mNavigationBarView.setNavigationIconHints(hints); 459 } 460 checkBarModes(); 461 } 462 463 @Override setWindowState( int displayId, @WindowType int window, @WindowVisibleState int state)464 public void setWindowState( 465 int displayId, @WindowType int window, @WindowVisibleState int state) { 466 if (displayId == mDisplayId 467 && mNavigationBarView != null 468 && window == StatusBarManager.WINDOW_NAVIGATION_BAR 469 && mNavigationBarWindowState != state) { 470 mNavigationBarWindowState = state; 471 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); 472 473 updateSystemUiStateFlags(-1); 474 mNavigationBarView.setWindowVisible(isNavBarWindowVisible()); 475 } 476 } 477 478 @Override onRotationProposal(final int rotation, boolean isValid)479 public void onRotationProposal(final int rotation, boolean isValid) { 480 final int winRotation = mNavigationBarView.getDisplay().getRotation(); 481 final boolean rotateSuggestionsDisabled = RotationButtonController 482 .hasDisable2RotateSuggestionFlag(mDisabledFlags2); 483 final RotationButtonController rotationButtonController = 484 mNavigationBarView.getRotationButtonController(); 485 final RotationButton rotationButton = rotationButtonController.getRotationButton(); 486 487 if (RotationContextButton.DEBUG_ROTATION) { 488 Log.v(TAG, "onRotationProposal proposedRotation=" + Surface.rotationToString(rotation) 489 + ", winRotation=" + Surface.rotationToString(winRotation) 490 + ", isValid=" + isValid + ", mNavBarWindowState=" 491 + StatusBarManager.windowStateToString(mNavigationBarWindowState) 492 + ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled 493 + ", isRotateButtonVisible=" + (mNavigationBarView == null ? "null" 494 : rotationButton.isVisible())); 495 } 496 497 // Respect the disabled flag, no need for action as flag change callback will handle hiding 498 if (rotateSuggestionsDisabled) return; 499 500 rotationButtonController.onRotationProposal(rotation, winRotation, isValid); 501 } 502 503 /** Restores the System UI flags saved state to {@link NavigationBarFragment}. */ restoreSystemUiVisibilityState()504 public void restoreSystemUiVisibilityState() { 505 final int barMode = computeBarMode(0, mSystemUiVisibility); 506 if (barMode != -1) { 507 mNavigationBarMode = barMode; 508 } 509 checkNavBarModes(); 510 mAutoHideController.touchAutoHide(); 511 512 mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, 513 true /* nbModeChanged */, mNavigationBarMode, false /* navbarColorManagedByIme */); 514 } 515 516 @Override setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean navbarColorManagedByIme)517 public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, 518 int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, 519 boolean navbarColorManagedByIme) { 520 if (displayId != mDisplayId) { 521 return; 522 } 523 final int oldVal = mSystemUiVisibility; 524 final int newVal = (oldVal & ~mask) | (vis & mask); 525 final int diff = newVal ^ oldVal; 526 boolean nbModeChanged = false; 527 if (diff != 0) { 528 mSystemUiVisibility = newVal; 529 530 // update navigation bar mode 531 final int nbMode = getView() == null 532 ? -1 : computeBarMode(oldVal, newVal); 533 nbModeChanged = nbMode != -1; 534 if (nbModeChanged) { 535 if (mNavigationBarMode != nbMode) { 536 if (mNavigationBarMode == MODE_TRANSPARENT 537 || mNavigationBarMode == MODE_LIGHTS_OUT_TRANSPARENT) { 538 mNavigationBarView.hideRecentsOnboarding(); 539 } 540 mNavigationBarMode = nbMode; 541 checkNavBarModes(); 542 } 543 mAutoHideController.touchAutoHide(); 544 } 545 if (mNavigationBarView != null) { 546 mNavigationBarView.onSystemUiVisibilityChanged(mSystemUiVisibility); 547 } 548 } 549 mLightBarController.onNavigationVisibilityChanged( 550 vis, mask, nbModeChanged, mNavigationBarMode, navbarColorManagedByIme); 551 } 552 computeBarMode(int oldVis, int newVis)553 private @TransitionMode int computeBarMode(int oldVis, int newVis) { 554 final int oldMode = barMode(oldVis); 555 final int newMode = barMode(newVis); 556 if (oldMode == newMode) { 557 return -1; // no mode change 558 } 559 return newMode; 560 } 561 barMode(int vis)562 private @TransitionMode int barMode(int vis) { 563 final int lightsOutTransparent = 564 View.SYSTEM_UI_FLAG_LOW_PROFILE | View.NAVIGATION_BAR_TRANSIENT; 565 if ((vis & View.NAVIGATION_BAR_TRANSIENT) != 0) { 566 return MODE_SEMI_TRANSPARENT; 567 } else if ((vis & View.NAVIGATION_BAR_TRANSLUCENT) != 0) { 568 return MODE_TRANSLUCENT; 569 } else if ((vis & lightsOutTransparent) == lightsOutTransparent) { 570 return MODE_LIGHTS_OUT_TRANSPARENT; 571 } else if ((vis & View.NAVIGATION_BAR_TRANSPARENT) != 0) { 572 return MODE_TRANSPARENT; 573 } else if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { 574 return MODE_LIGHTS_OUT; 575 } else { 576 return MODE_OPAQUE; 577 } 578 } 579 580 @Override disable(int displayId, int state1, int state2, boolean animate)581 public void disable(int displayId, int state1, int state2, boolean animate) { 582 if (displayId != mDisplayId) { 583 return; 584 } 585 // Navigation bar flags are in both state1 and state2. 586 final int masked = state1 & (StatusBarManager.DISABLE_HOME 587 | StatusBarManager.DISABLE_RECENT 588 | StatusBarManager.DISABLE_BACK 589 | StatusBarManager.DISABLE_SEARCH); 590 if (masked != mDisabledFlags1) { 591 mDisabledFlags1 = masked; 592 if (mNavigationBarView != null) { 593 mNavigationBarView.setDisabledFlags(state1); 594 } 595 updateScreenPinningGestures(); 596 } 597 598 // Only default display supports rotation suggestions. 599 if (mIsOnDefaultDisplay) { 600 final int masked2 = state2 & (StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS); 601 if (masked2 != mDisabledFlags2) { 602 mDisabledFlags2 = masked2; 603 setDisabled2Flags(masked2); 604 } 605 } 606 } 607 setDisabled2Flags(int state2)608 private void setDisabled2Flags(int state2) { 609 // Method only called on change of disable2 flags 610 if (mNavigationBarView != null) { 611 mNavigationBarView.getRotationButtonController().onDisable2FlagChanged(state2); 612 } 613 } 614 615 // ----- Internal stuff ----- 616 refreshLayout(int layoutDirection)617 private void refreshLayout(int layoutDirection) { 618 if (mNavigationBarView != null) { 619 mNavigationBarView.setLayoutDirection(layoutDirection); 620 } 621 } 622 shouldDisableNavbarGestures()623 private boolean shouldDisableNavbarGestures() { 624 return !mDeviceProvisionedController.isDeviceProvisioned() 625 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0; 626 } 627 repositionNavigationBar()628 private void repositionNavigationBar() { 629 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return; 630 631 prepareNavigationBarView(); 632 633 mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(), 634 ((View) mNavigationBarView.getParent()).getLayoutParams()); 635 } 636 updateScreenPinningGestures()637 private void updateScreenPinningGestures() { 638 if (mNavigationBarView == null) { 639 return; 640 } 641 642 // Change the cancel pin gesture to home and back if recents button is invisible 643 boolean recentsVisible = mNavigationBarView.isRecentsButtonVisible(); 644 ButtonDispatcher backButton = mNavigationBarView.getBackButton(); 645 if (recentsVisible) { 646 backButton.setOnLongClickListener(this::onLongPressBackRecents); 647 } else { 648 backButton.setOnLongClickListener(this::onLongPressBackHome); 649 } 650 } 651 notifyNavigationBarScreenOn()652 private void notifyNavigationBarScreenOn() { 653 mNavigationBarView.updateNavButtonIcons(); 654 } 655 prepareNavigationBarView()656 private void prepareNavigationBarView() { 657 mNavigationBarView.reorient(); 658 659 ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton(); 660 recentsButton.setOnClickListener(this::onRecentsClick); 661 recentsButton.setOnTouchListener(this::onRecentsTouch); 662 recentsButton.setLongClickable(true); 663 recentsButton.setOnLongClickListener(this::onLongPressBackRecents); 664 665 ButtonDispatcher backButton = mNavigationBarView.getBackButton(); 666 backButton.setLongClickable(true); 667 668 ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); 669 homeButton.setOnTouchListener(this::onHomeTouch); 670 homeButton.setOnLongClickListener(this::onHomeLongClick); 671 672 ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); 673 accessibilityButton.setOnClickListener(this::onAccessibilityClick); 674 accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick); 675 updateAccessibilityServicesState(mAccessibilityManager); 676 677 updateScreenPinningGestures(); 678 } 679 onHomeTouch(View v, MotionEvent event)680 private boolean onHomeTouch(View v, MotionEvent event) { 681 if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) { 682 return true; 683 } 684 // If an incoming call is ringing, HOME is totally disabled. 685 // (The user is already on the InCallUI at this point, 686 // and his ONLY options are to answer or reject the call.) 687 switch (event.getAction()) { 688 case MotionEvent.ACTION_DOWN: 689 mHomeBlockedThisTouch = false; 690 TelecomManager telecomManager = 691 getContext().getSystemService(TelecomManager.class); 692 if (telecomManager != null && telecomManager.isRinging()) { 693 if (mStatusBar.isKeyguardShowing()) { 694 Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " + 695 "No heads up"); 696 mHomeBlockedThisTouch = true; 697 return true; 698 } 699 } 700 break; 701 case MotionEvent.ACTION_UP: 702 case MotionEvent.ACTION_CANCEL: 703 mStatusBar.awakenDreams(); 704 break; 705 } 706 return false; 707 } 708 onVerticalChanged(boolean isVertical)709 private void onVerticalChanged(boolean isVertical) { 710 mStatusBar.setQsScrimEnabled(!isVertical); 711 } 712 onNavigationTouch(View v, MotionEvent event)713 private boolean onNavigationTouch(View v, MotionEvent event) { 714 mAutoHideController.checkUserAutoHide(event); 715 return false; 716 } 717 718 @VisibleForTesting onHomeLongClick(View v)719 boolean onHomeLongClick(View v) { 720 if (!mNavigationBarView.isRecentsButtonVisible() 721 && ActivityManagerWrapper.getInstance().isScreenPinningActive()) { 722 return onLongPressBackHome(v); 723 } 724 if (shouldDisableNavbarGestures()) { 725 return false; 726 } 727 mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS); 728 Bundle args = new Bundle(); 729 args.putInt( 730 AssistManager.INVOCATION_TYPE_KEY, AssistManager.INVOCATION_HOME_BUTTON_LONG_PRESS); 731 mAssistManager.startAssist(args); 732 mStatusBar.awakenDreams(); 733 734 if (mNavigationBarView != null) { 735 mNavigationBarView.abortCurrentGesture(); 736 } 737 return true; 738 } 739 740 // additional optimization when we have software system buttons - start loading the recent 741 // tasks on touch down onRecentsTouch(View v, MotionEvent event)742 private boolean onRecentsTouch(View v, MotionEvent event) { 743 int action = event.getAction() & MotionEvent.ACTION_MASK; 744 if (action == MotionEvent.ACTION_DOWN) { 745 mCommandQueue.preloadRecentApps(); 746 } else if (action == MotionEvent.ACTION_CANCEL) { 747 mCommandQueue.cancelPreloadRecentApps(); 748 } else if (action == MotionEvent.ACTION_UP) { 749 if (!v.isPressed()) { 750 mCommandQueue.cancelPreloadRecentApps(); 751 } 752 } 753 return false; 754 } 755 onRecentsClick(View v)756 private void onRecentsClick(View v) { 757 if (LatencyTracker.isEnabled(getContext())) { 758 LatencyTracker.getInstance(getContext()).onActionStart( 759 LatencyTracker.ACTION_TOGGLE_RECENTS); 760 } 761 mStatusBar.awakenDreams(); 762 mCommandQueue.toggleRecentApps(); 763 } 764 onLongPressBackHome(View v)765 private boolean onLongPressBackHome(View v) { 766 return onLongPressNavigationButtons(v, R.id.back, R.id.home); 767 } 768 onLongPressBackRecents(View v)769 private boolean onLongPressBackRecents(View v) { 770 return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps); 771 } 772 773 /** 774 * This handles long-press of both back and recents/home. Back is the common button with 775 * combination of recents if it is visible or home if recents is invisible. 776 * They are handled together to capture them both being long-pressed 777 * at the same time to exit screen pinning (lock task). 778 * 779 * When accessibility mode is on, only a long-press from recents/home 780 * is required to exit. 781 * 782 * In all other circumstances we try to pass through long-press events 783 * for Back, so that apps can still use it. Which can be from two things. 784 * 1) Not currently in screen pinning (lock task). 785 * 2) Back is long-pressed without recents/home. 786 */ onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2)787 private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) { 788 try { 789 boolean sendBackLongPress = false; 790 IActivityTaskManager activityManager = ActivityTaskManager.getService(); 791 boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); 792 boolean inLockTaskMode = activityManager.isInLockTaskMode(); 793 boolean stopLockTaskMode = false; 794 try { 795 if (inLockTaskMode && !touchExplorationEnabled) { 796 long time = System.currentTimeMillis(); 797 798 // If we recently long-pressed the other button then they were 799 // long-pressed 'together' 800 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) { 801 stopLockTaskMode = true; 802 return true; 803 } else if (v.getId() == btnId1) { 804 ButtonDispatcher button = btnId2 == R.id.recent_apps 805 ? mNavigationBarView.getRecentsButton() 806 : mNavigationBarView.getHomeButton(); 807 if (!button.getCurrentView().isPressed()) { 808 // If we aren't pressing recents/home right now then they presses 809 // won't be together, so send the standard long-press action. 810 sendBackLongPress = true; 811 } 812 } 813 mLastLockToAppLongPress = time; 814 } else { 815 // If this is back still need to handle sending the long-press event. 816 if (v.getId() == btnId1) { 817 sendBackLongPress = true; 818 } else if (touchExplorationEnabled && inLockTaskMode) { 819 // When in accessibility mode a long press that is recents/home (not back) 820 // should stop lock task. 821 stopLockTaskMode = true; 822 return true; 823 } else if (v.getId() == btnId2) { 824 return btnId2 == R.id.recent_apps 825 ? onLongPressRecents() 826 : onHomeLongClick( 827 mNavigationBarView.getHomeButton().getCurrentView()); 828 } 829 } 830 } finally { 831 if (stopLockTaskMode) { 832 activityManager.stopSystemLockTaskMode(); 833 // When exiting refresh disabled flags. 834 mNavigationBarView.updateNavButtonIcons(); 835 } 836 } 837 838 if (sendBackLongPress) { 839 KeyButtonView keyButtonView = (KeyButtonView) v; 840 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); 841 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 842 return true; 843 } 844 } catch (RemoteException e) { 845 Log.d(TAG, "Unable to reach activity manager", e); 846 } 847 return false; 848 } 849 onLongPressRecents()850 private boolean onLongPressRecents() { 851 if (mRecents == null || !ActivityTaskManager.supportsMultiWindow(getContext()) 852 || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible() 853 || ActivityManager.isLowRamDeviceStatic() 854 // If we are connected to the overview service, then disable the recents button 855 || mOverviewProxyService.getProxy() != null) { 856 return false; 857 } 858 859 return mStatusBar.toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS, 860 MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS); 861 } 862 onAccessibilityClick(View v)863 private void onAccessibilityClick(View v) { 864 final Display display = v.getDisplay(); 865 mAccessibilityManager.notifyAccessibilityButtonClicked( 866 display != null ? display.getDisplayId() : Display.DEFAULT_DISPLAY); 867 } 868 onAccessibilityLongClick(View v)869 private boolean onAccessibilityLongClick(View v) { 870 Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); 871 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 872 v.getContext().startActivityAsUser(intent, UserHandle.CURRENT); 873 return true; 874 } 875 updateAccessibilityServicesState(AccessibilityManager accessibilityManager)876 private void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) { 877 boolean[] feedbackEnabled = new boolean[1]; 878 int a11yFlags = getA11yButtonState(feedbackEnabled); 879 880 boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; 881 boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; 882 mNavigationBarView.setAccessibilityButtonState(clickable, longClickable); 883 884 updateSystemUiStateFlags(a11yFlags); 885 } 886 updateSystemUiStateFlags(int a11yFlags)887 public void updateSystemUiStateFlags(int a11yFlags) { 888 if (a11yFlags < 0) { 889 a11yFlags = getA11yButtonState(null); 890 } 891 boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; 892 boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; 893 mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, 894 clickable, mDisplayId); 895 mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, 896 longClickable, mDisplayId); 897 mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN, 898 !isNavBarWindowVisible(), mDisplayId); 899 } 900 901 /** 902 * Returns the system UI flags corresponding the the current accessibility button state 903 * @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled. 904 */ getA11yButtonState(@ullable boolean[] outFeedbackEnabled)905 public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) { 906 int requestingServices = 0; 907 try { 908 if (Settings.Secure.getIntForUser(mContentResolver, 909 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 910 UserHandle.USER_CURRENT) == 1) { 911 requestingServices++; 912 } 913 } catch (Settings.SettingNotFoundException e) { 914 } 915 916 boolean feedbackEnabled = false; 917 // AccessibilityManagerService resolves services for the current user since the local 918 // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission 919 final List<AccessibilityServiceInfo> services = 920 mAccessibilityManager.getEnabledAccessibilityServiceList( 921 AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 922 for (int i = services.size() - 1; i >= 0; --i) { 923 AccessibilityServiceInfo info = services.get(i); 924 if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { 925 requestingServices++; 926 } 927 928 if (info.feedbackType != 0 && info.feedbackType != 929 AccessibilityServiceInfo.FEEDBACK_GENERIC) { 930 feedbackEnabled = true; 931 } 932 } 933 934 if (outFeedbackEnabled != null) { 935 outFeedbackEnabled[0] = feedbackEnabled; 936 } 937 938 return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0) 939 | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0); 940 } 941 sendAssistantAvailability(boolean available)942 private void sendAssistantAvailability(boolean available) { 943 if (mOverviewProxyService.getProxy() != null) { 944 try { 945 mOverviewProxyService.getProxy().onAssistantAvailable(available 946 && QuickStepContract.isGesturalMode(mNavBarMode)); 947 } catch (RemoteException e) { 948 Log.w(TAG, "Unable to send assistant availability data to launcher"); 949 } 950 } 951 } 952 953 // ----- Methods that DisplayNavigationBarController talks to ----- 954 955 /** Applies auto dimming animation on navigation bar when touched. */ touchAutoDim()956 public void touchAutoDim() { 957 getBarTransitions().setAutoDim(false); 958 mHandler.removeCallbacks(mAutoDim); 959 int state = mStatusBarStateController.getState(); 960 if (state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED) { 961 mHandler.postDelayed(mAutoDim, AUTODIM_TIMEOUT_MS); 962 } 963 } 964 setLightBarController(LightBarController lightBarController)965 public void setLightBarController(LightBarController lightBarController) { 966 mLightBarController = lightBarController; 967 mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController()); 968 } 969 970 /** Sets {@link AutoHideController} to the navigation bar. */ setAutoHideController(AutoHideController autoHideController)971 public void setAutoHideController(AutoHideController autoHideController) { 972 mAutoHideController = autoHideController; 973 mAutoHideController.setNavigationBar(this); 974 } 975 976 // AutoHideElement 977 @Override isSemiTransparent()978 public boolean isSemiTransparent() { 979 return mNavigationBarMode == MODE_SEMI_TRANSPARENT; 980 } 981 982 // AutoHideElement 983 @Override synchronizeState()984 public void synchronizeState() { 985 checkNavBarModes(); 986 } 987 checkBarModes()988 private void checkBarModes() { 989 // We only have status bar on default display now. 990 if (mIsOnDefaultDisplay) { 991 mStatusBar.checkBarModes(); 992 } else { 993 checkNavBarModes(); 994 } 995 } 996 isNavBarWindowVisible()997 public boolean isNavBarWindowVisible() { 998 return mNavigationBarWindowState == WINDOW_STATE_SHOWING; 999 } 1000 1001 /** 1002 * Checks current navigation bar mode and make transitions. 1003 */ checkNavBarModes()1004 public void checkNavBarModes() { 1005 final boolean anim = mStatusBar.isDeviceInteractive() 1006 && mNavigationBarWindowState != WINDOW_STATE_HIDDEN; 1007 mNavigationBarView.getBarTransitions().transitionTo(mNavigationBarMode, anim); 1008 } 1009 1010 @Override onNavigationModeChanged(int mode)1011 public void onNavigationModeChanged(int mode) { 1012 mNavBarMode = mode; 1013 updateScreenPinningGestures(); 1014 1015 // Workaround for b/132825155, for secondary users, we currently don't receive configuration 1016 // changes on overlay package change since SystemUI runs for the system user. In this case, 1017 // trigger a new configuration change to ensure that the nav bar is updated in the same way. 1018 int userId = ActivityManagerWrapper.getInstance().getCurrentUserId(); 1019 if (userId != UserHandle.USER_SYSTEM) { 1020 mHandler.post(() -> { 1021 FragmentHostManager fragmentHost = FragmentHostManager.get(mNavigationBarView); 1022 fragmentHost.reloadFragments(); 1023 }); 1024 } 1025 } 1026 disableAnimationsDuringHide(long delay)1027 public void disableAnimationsDuringHide(long delay) { 1028 mNavigationBarView.setLayoutTransitionsEnabled(false); 1029 mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true), 1030 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE); 1031 } 1032 1033 /** 1034 * Performs transitions on navigation bar. 1035 * 1036 * @param barMode transition bar mode. 1037 * @param animate shows animations if {@code true}. 1038 */ transitionTo(@ransitionMode int barMode, boolean animate)1039 public void transitionTo(@TransitionMode int barMode, boolean animate) { 1040 getBarTransitions().transitionTo(barMode, animate); 1041 } 1042 getBarTransitions()1043 public NavigationBarTransitions getBarTransitions() { 1044 return mNavigationBarView.getBarTransitions(); 1045 } 1046 finishBarAnimations()1047 public void finishBarAnimations() { 1048 mNavigationBarView.getBarTransitions().finishAnimations(); 1049 } 1050 1051 private final AccessibilityServicesStateChangeListener mAccessibilityListener = 1052 this::updateAccessibilityServicesState; 1053 1054 private class MagnificationContentObserver extends ContentObserver { 1055 MagnificationContentObserver(Handler handler)1056 public MagnificationContentObserver(Handler handler) { 1057 super(handler); 1058 } 1059 1060 @Override onChange(boolean selfChange)1061 public void onChange(boolean selfChange) { 1062 NavigationBarFragment.this.updateAccessibilityServicesState(mAccessibilityManager); 1063 } 1064 } 1065 1066 private final Consumer<Integer> mRotationWatcher = rotation -> { 1067 if (mNavigationBarView != null 1068 && mNavigationBarView.needsReorient(rotation)) { 1069 repositionNavigationBar(); 1070 } 1071 }; 1072 1073 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 1074 @Override 1075 public void onReceive(Context context, Intent intent) { 1076 String action = intent.getAction(); 1077 if (Intent.ACTION_SCREEN_OFF.equals(action) 1078 || Intent.ACTION_SCREEN_ON.equals(action)) { 1079 notifyNavigationBarScreenOn(); 1080 1081 if (Intent.ACTION_SCREEN_ON.equals(action)) { 1082 // Enabled and screen is on, start it again if enabled 1083 if (NavBarTintController.isEnabled(getContext(), mNavBarMode)) { 1084 mNavigationBarView.getTintController().start(); 1085 } 1086 } else { 1087 // Screen off disable it 1088 mNavigationBarView.getTintController().stop(); 1089 } 1090 } 1091 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 1092 // The accessibility settings may be different for the new user 1093 updateAccessibilityServicesState(mAccessibilityManager); 1094 } 1095 } 1096 }; 1097 create(Context context, FragmentListener listener)1098 public static View create(Context context, FragmentListener listener) { 1099 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 1100 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1101 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 1102 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING 1103 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1104 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 1105 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 1106 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 1107 | WindowManager.LayoutParams.FLAG_SLIPPERY, 1108 PixelFormat.TRANSLUCENT); 1109 lp.token = new Binder(); 1110 lp.setTitle("NavigationBar" + context.getDisplayId()); 1111 lp.accessibilityTitle = context.getString(R.string.nav_bar); 1112 lp.windowAnimations = 0; 1113 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; 1114 1115 View navigationBarView = LayoutInflater.from(context).inflate( 1116 R.layout.navigation_bar_window, null); 1117 1118 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView); 1119 if (navigationBarView == null) return null; 1120 1121 final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView) 1122 .create(NavigationBarFragment.class); 1123 navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { 1124 @Override 1125 public void onViewAttachedToWindow(View v) { 1126 final FragmentHostManager fragmentHost = FragmentHostManager.get(v); 1127 fragmentHost.getFragmentManager().beginTransaction() 1128 .replace(R.id.navigation_bar_frame, fragment, TAG) 1129 .commit(); 1130 fragmentHost.addTagListener(TAG, listener); 1131 } 1132 1133 @Override 1134 public void onViewDetachedFromWindow(View v) { 1135 FragmentHostManager.removeAndDestroy(v); 1136 } 1137 }); 1138 context.getSystemService(WindowManager.class).addView(navigationBarView, lp); 1139 return navigationBarView; 1140 } 1141 1142 @VisibleForTesting getNavigationIconHints()1143 int getNavigationIconHints() { 1144 return mNavigationIconHints; 1145 } 1146 } 1147