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