NavigationBarFragment.java revision a83859ffaa4d339729cbc1a8c114b73c6afedb00
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 15package com.android.systemui.statusbar.phone; 16 17import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; 18import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; 19import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; 20import static android.app.StatusBarManager.windowStateToString; 21 22import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; 23import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE; 24import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions; 25 26import android.accessibilityservice.AccessibilityServiceInfo; 27import android.annotation.Nullable; 28import android.app.ActivityManager; 29import android.app.ActivityManagerNative; 30import android.app.Fragment; 31import android.app.IActivityManager; 32import android.app.StatusBarManager; 33import android.content.BroadcastReceiver; 34import android.content.ContentResolver; 35import android.content.Context; 36import android.content.Intent; 37import android.content.IntentFilter; 38import android.content.res.Configuration; 39import android.database.ContentObserver; 40import android.graphics.PixelFormat; 41import android.graphics.Rect; 42import android.inputmethodservice.InputMethodService; 43import android.os.Binder; 44import android.os.Bundle; 45import android.os.Handler; 46import android.os.IBinder; 47import android.os.Message; 48import android.os.RemoteException; 49import android.os.UserHandle; 50import android.provider.Settings; 51import android.support.annotation.VisibleForTesting; 52import android.telecom.TelecomManager; 53import android.text.TextUtils; 54import android.util.Log; 55import android.view.IRotationWatcher.Stub; 56import android.view.KeyEvent; 57import android.view.LayoutInflater; 58import android.view.MotionEvent; 59import android.view.View; 60import android.view.ViewGroup; 61import android.view.WindowManager; 62import android.view.WindowManager.LayoutParams; 63import android.view.WindowManagerGlobal; 64import android.view.accessibility.AccessibilityEvent; 65import android.view.accessibility.AccessibilityManager; 66import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; 67 68import com.android.internal.logging.MetricsLogger; 69import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 70import com.android.keyguard.LatencyTracker; 71import com.android.systemui.Dependency; 72import com.android.systemui.R; 73import com.android.systemui.SysUiServiceProvider; 74import com.android.systemui.assist.AssistManager; 75import com.android.systemui.fragments.FragmentHostManager; 76import com.android.systemui.fragments.FragmentHostManager.FragmentListener; 77import com.android.systemui.recents.Recents; 78import com.android.systemui.stackdivider.Divider; 79import com.android.systemui.statusbar.CommandQueue; 80import com.android.systemui.statusbar.CommandQueue.Callbacks; 81import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; 82import com.android.systemui.statusbar.policy.KeyButtonView; 83import com.android.systemui.statusbar.stack.StackStateAnimator; 84 85import java.io.FileDescriptor; 86import java.io.PrintWriter; 87import java.util.List; 88import java.util.Locale; 89 90/** 91 * Fragment containing the NavigationBarFragment. Contains logic for what happens 92 * on clicks and view states of the nav bar. 93 */ 94public class NavigationBarFragment extends Fragment implements Callbacks { 95 96 public static final String TAG = "NavigationBar"; 97 private static final boolean DEBUG = false; 98 private static final String EXTRA_DISABLE_STATE = "disabled_state"; 99 100 /** Allow some time inbetween the long press for back and recents. */ 101 private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; 102 103 protected NavigationBarView mNavigationBarView = null; 104 protected AssistManager mAssistManager; 105 106 private int mNavigationBarWindowState = WINDOW_STATE_SHOWING; 107 108 private int mNavigationIconHints = 0; 109 private int mNavigationBarMode; 110 private AccessibilityManager mAccessibilityManager; 111 private MagnificationContentObserver mMagnificationObserver; 112 private ContentResolver mContentResolver; 113 114 private int mDisabledFlags1; 115 private StatusBar mStatusBar; 116 private Recents mRecents; 117 private Divider mDivider; 118 private WindowManager mWindowManager; 119 private CommandQueue mCommandQueue; 120 private long mLastLockToAppLongPress; 121 122 private Locale mLocale; 123 private int mLayoutDirection; 124 125 private int mSystemUiVisibility; 126 private LightBarController mLightBarController; 127 128 public boolean mHomeBlockedThisTouch; 129 130 // ----- Fragment Lifecycle Callbacks ----- 131 132 @Override 133 public void onCreate(@Nullable Bundle savedInstanceState) { 134 super.onCreate(savedInstanceState); 135 mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class); 136 mCommandQueue.addCallbacks(this); 137 mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class); 138 mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class); 139 mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class); 140 mWindowManager = getContext().getSystemService(WindowManager.class); 141 mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class); 142 Dependency.get(AccessibilityManagerWrapper.class).addCallback( 143 mAccessibilityListener); 144 mContentResolver = getContext().getContentResolver(); 145 mMagnificationObserver = new MagnificationContentObserver( 146 getContext().getMainThreadHandler()); 147 mContentResolver.registerContentObserver(Settings.Secure.getUriFor( 148 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false, 149 mMagnificationObserver, UserHandle.USER_ALL); 150 151 if (savedInstanceState != null) { 152 mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0); 153 } 154 mAssistManager = Dependency.get(AssistManager.class); 155 156 try { 157 WindowManagerGlobal.getWindowManagerService() 158 .watchRotation(mRotationWatcher, getContext().getDisplay().getDisplayId()); 159 } catch (RemoteException e) { 160 throw e.rethrowFromSystemServer(); 161 } 162 } 163 164 @Override 165 public void onDestroy() { 166 super.onDestroy(); 167 mCommandQueue.removeCallbacks(this); 168 Dependency.get(AccessibilityManagerWrapper.class).removeCallback( 169 mAccessibilityListener); 170 mContentResolver.unregisterContentObserver(mMagnificationObserver); 171 try { 172 WindowManagerGlobal.getWindowManagerService() 173 .removeRotationWatcher(mRotationWatcher); 174 } catch (RemoteException e) { 175 throw e.rethrowFromSystemServer(); 176 } 177 } 178 179 @Override 180 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 181 Bundle savedInstanceState) { 182 return inflater.inflate(R.layout.navigation_bar, container, false); 183 } 184 185 @Override 186 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 187 super.onViewCreated(view, savedInstanceState); 188 mNavigationBarView = (NavigationBarView) view; 189 190 mNavigationBarView.setDisabledFlags(mDisabledFlags1); 191 mNavigationBarView.setComponents(mRecents, mDivider); 192 mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged); 193 mNavigationBarView.setOnTouchListener(this::onNavigationTouch); 194 if (savedInstanceState != null) { 195 mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState); 196 } 197 198 prepareNavigationBarView(); 199 checkNavBarModes(); 200 201 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 202 filter.addAction(Intent.ACTION_SCREEN_ON); 203 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); 204 notifyNavigationBarScreenOn(); 205 } 206 207 @Override 208 public void onDestroyView() { 209 super.onDestroyView(); 210 mNavigationBarView.getLightTransitionsController().destroy(getContext()); 211 getContext().unregisterReceiver(mBroadcastReceiver); 212 } 213 214 @Override 215 public void onSaveInstanceState(Bundle outState) { 216 super.onSaveInstanceState(outState); 217 outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1); 218 if (mNavigationBarView != null) { 219 mNavigationBarView.getLightTransitionsController().saveState(outState); 220 } 221 } 222 223 @Override 224 public void onConfigurationChanged(Configuration newConfig) { 225 super.onConfigurationChanged(newConfig); 226 final Locale locale = getContext().getResources().getConfiguration().locale; 227 final int ld = TextUtils.getLayoutDirectionFromLocale(locale); 228 if (!locale.equals(mLocale) || ld != mLayoutDirection) { 229 if (DEBUG) { 230 Log.v(TAG, String.format( 231 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection, 232 locale, ld)); 233 } 234 mLocale = locale; 235 mLayoutDirection = ld; 236 refreshLayout(ld); 237 } 238 repositionNavigationBar(); 239 } 240 241 @Override 242 public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) { 243 if (mNavigationBarView != null) { 244 pw.print(" mNavigationBarWindowState="); 245 pw.println(windowStateToString(mNavigationBarWindowState)); 246 pw.print(" mNavigationBarMode="); 247 pw.println(BarTransitions.modeToString(mNavigationBarMode)); 248 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions()); 249 } 250 251 pw.print(" mNavigationBarView="); 252 if (mNavigationBarView == null) { 253 pw.println("null"); 254 } else { 255 mNavigationBarView.dump(fd, pw, args); 256 } 257 } 258 259 // ----- CommandQueue Callbacks ----- 260 261 @Override 262 public void setImeWindowStatus(IBinder token, int vis, int backDisposition, 263 boolean showImeSwitcher) { 264 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; 265 int hints = mNavigationIconHints; 266 if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) { 267 hints |= NAVIGATION_HINT_BACK_ALT; 268 } else { 269 hints &= ~NAVIGATION_HINT_BACK_ALT; 270 } 271 if (showImeSwitcher) { 272 hints |= NAVIGATION_HINT_IME_SHOWN; 273 } else { 274 hints &= ~NAVIGATION_HINT_IME_SHOWN; 275 } 276 if (hints == mNavigationIconHints) return; 277 278 mNavigationIconHints = hints; 279 280 if (mNavigationBarView != null) { 281 mNavigationBarView.setNavigationIconHints(hints); 282 } 283 mStatusBar.checkBarModes(); 284 } 285 286 @Override 287 public void topAppWindowChanged(boolean showMenu) { 288 if (mNavigationBarView != null) { 289 mNavigationBarView.setMenuVisibility(showMenu); 290 } 291 } 292 293 @Override 294 public void setWindowState(int window, int state) { 295 if (mNavigationBarView != null 296 && window == StatusBarManager.WINDOW_NAVIGATION_BAR 297 && mNavigationBarWindowState != state) { 298 mNavigationBarWindowState = state; 299 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); 300 } 301 } 302 303 // Injected from StatusBar at creation. 304 public void setCurrentSysuiVisibility(int systemUiVisibility) { 305 mSystemUiVisibility = systemUiVisibility; 306 mNavigationBarMode = mStatusBar.computeBarMode(0, mSystemUiVisibility, 307 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT, 308 View.NAVIGATION_BAR_TRANSPARENT); 309 checkNavBarModes(); 310 mStatusBar.touchAutoHide(); 311 mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, 312 true /* nbModeChanged */, mNavigationBarMode); 313 } 314 315 @Override 316 public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, 317 int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { 318 final int oldVal = mSystemUiVisibility; 319 final int newVal = (oldVal & ~mask) | (vis & mask); 320 final int diff = newVal ^ oldVal; 321 boolean nbModeChanged = false; 322 if (diff != 0) { 323 mSystemUiVisibility = newVal; 324 325 // update navigation bar mode 326 final int nbMode = getView() == null 327 ? -1 : mStatusBar.computeBarMode(oldVal, newVal, 328 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT, 329 View.NAVIGATION_BAR_TRANSPARENT); 330 nbModeChanged = nbMode != -1; 331 if (nbModeChanged) { 332 if (mNavigationBarMode != nbMode) { 333 mNavigationBarMode = nbMode; 334 checkNavBarModes(); 335 } 336 mStatusBar.touchAutoHide(); 337 } 338 } 339 340 mLightBarController.onNavigationVisibilityChanged(vis, mask, nbModeChanged, 341 mNavigationBarMode); 342 } 343 344 @Override 345 public void disable(int state1, int state2, boolean animate) { 346 // All navigation bar flags are in state1. 347 int masked = state1 & (StatusBarManager.DISABLE_HOME 348 | StatusBarManager.DISABLE_RECENT 349 | StatusBarManager.DISABLE_BACK 350 | StatusBarManager.DISABLE_SEARCH); 351 if (masked != mDisabledFlags1) { 352 mDisabledFlags1 = masked; 353 if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1); 354 } 355 } 356 357 // ----- Internal stuffz ----- 358 359 private void refreshLayout(int layoutDirection) { 360 if (mNavigationBarView != null) { 361 mNavigationBarView.setLayoutDirection(layoutDirection); 362 } 363 } 364 365 private boolean shouldDisableNavbarGestures() { 366 return !mStatusBar.isDeviceProvisioned() 367 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0; 368 } 369 370 private void repositionNavigationBar() { 371 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return; 372 373 prepareNavigationBarView(); 374 375 mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(), 376 ((View) mNavigationBarView.getParent()).getLayoutParams()); 377 } 378 379 private void notifyNavigationBarScreenOn() { 380 mNavigationBarView.notifyScreenOn(); 381 } 382 383 private void prepareNavigationBarView() { 384 mNavigationBarView.reorient(); 385 386 ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton(); 387 recentsButton.setOnClickListener(this::onRecentsClick); 388 recentsButton.setOnTouchListener(this::onRecentsTouch); 389 recentsButton.setLongClickable(true); 390 recentsButton.setOnLongClickListener(this::onLongPressBackRecents); 391 392 ButtonDispatcher backButton = mNavigationBarView.getBackButton(); 393 backButton.setLongClickable(true); 394 backButton.setOnLongClickListener(this::onLongPressBackRecents); 395 396 ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); 397 homeButton.setOnTouchListener(this::onHomeTouch); 398 homeButton.setOnLongClickListener(this::onHomeLongClick); 399 400 ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); 401 accessibilityButton.setOnClickListener(this::onAccessibilityClick); 402 accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick); 403 updateAccessibilityServicesState(mAccessibilityManager); 404 } 405 406 private boolean onHomeTouch(View v, MotionEvent event) { 407 if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) { 408 return true; 409 } 410 // If an incoming call is ringing, HOME is totally disabled. 411 // (The user is already on the InCallUI at this point, 412 // and his ONLY options are to answer or reject the call.) 413 switch (event.getAction()) { 414 case MotionEvent.ACTION_DOWN: 415 mHomeBlockedThisTouch = false; 416 TelecomManager telecomManager = 417 getContext().getSystemService(TelecomManager.class); 418 if (telecomManager != null && telecomManager.isRinging()) { 419 if (mStatusBar.isKeyguardShowing()) { 420 Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " + 421 "No heads up"); 422 mHomeBlockedThisTouch = true; 423 return true; 424 } 425 } 426 break; 427 case MotionEvent.ACTION_UP: 428 case MotionEvent.ACTION_CANCEL: 429 mStatusBar.awakenDreams(); 430 break; 431 } 432 return false; 433 } 434 435 private void onVerticalChanged(boolean isVertical) { 436 mStatusBar.setQsScrimEnabled(!isVertical); 437 } 438 439 private boolean onNavigationTouch(View v, MotionEvent event) { 440 mStatusBar.checkUserAutohide(v, event); 441 return false; 442 } 443 444 @VisibleForTesting 445 boolean onHomeLongClick(View v) { 446 if (shouldDisableNavbarGestures()) { 447 return false; 448 } 449 MetricsLogger.action(getContext(), MetricsEvent.ACTION_ASSIST_LONG_PRESS); 450 mAssistManager.startAssist(new Bundle() /* args */); 451 mStatusBar.awakenDreams(); 452 if (mNavigationBarView != null) { 453 mNavigationBarView.abortCurrentGesture(); 454 } 455 return true; 456 } 457 458 // additional optimization when we have software system buttons - start loading the recent 459 // tasks on touch down 460 private boolean onRecentsTouch(View v, MotionEvent event) { 461 int action = event.getAction() & MotionEvent.ACTION_MASK; 462 if (action == MotionEvent.ACTION_DOWN) { 463 mCommandQueue.preloadRecentApps(); 464 } else if (action == MotionEvent.ACTION_CANCEL) { 465 mCommandQueue.cancelPreloadRecentApps(); 466 } else if (action == MotionEvent.ACTION_UP) { 467 if (!v.isPressed()) { 468 mCommandQueue.cancelPreloadRecentApps(); 469 } 470 } 471 return false; 472 } 473 474 private void onRecentsClick(View v) { 475 if (LatencyTracker.isEnabled(getContext())) { 476 LatencyTracker.getInstance(getContext()).onActionStart( 477 LatencyTracker.ACTION_TOGGLE_RECENTS); 478 } 479 mStatusBar.awakenDreams(); 480 mCommandQueue.toggleRecentApps(); 481 } 482 483 /** 484 * This handles long-press of both back and recents. They are 485 * handled together to capture them both being long-pressed 486 * at the same time to exit screen pinning (lock task). 487 * 488 * When accessibility mode is on, only a long-press from recents 489 * is required to exit. 490 * 491 * In all other circumstances we try to pass through long-press events 492 * for Back, so that apps can still use it. Which can be from two things. 493 * 1) Not currently in screen pinning (lock task). 494 * 2) Back is long-pressed without recents. 495 */ 496 private boolean onLongPressBackRecents(View v) { 497 try { 498 boolean sendBackLongPress = false; 499 IActivityManager activityManager = ActivityManagerNative.getDefault(); 500 boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); 501 boolean inLockTaskMode = activityManager.isInLockTaskMode(); 502 if (inLockTaskMode && !touchExplorationEnabled) { 503 long time = System.currentTimeMillis(); 504 // If we recently long-pressed the other button then they were 505 // long-pressed 'together' 506 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) { 507 activityManager.stopSystemLockTaskMode(); 508 // When exiting refresh disabled flags. 509 mNavigationBarView.setDisabledFlags(mDisabledFlags1, true); 510 return true; 511 } else if ((v.getId() == R.id.back) 512 && !mNavigationBarView.getRecentsButton().getCurrentView().isPressed()) { 513 // If we aren't pressing recents right now then they presses 514 // won't be together, so send the standard long-press action. 515 sendBackLongPress = true; 516 } 517 mLastLockToAppLongPress = time; 518 } else { 519 // If this is back still need to handle sending the long-press event. 520 if (v.getId() == R.id.back) { 521 sendBackLongPress = true; 522 } else if (touchExplorationEnabled && inLockTaskMode) { 523 // When in accessibility mode a long press that is recents (not back) 524 // should stop lock task. 525 activityManager.stopSystemLockTaskMode(); 526 // When exiting refresh disabled flags. 527 mNavigationBarView.setDisabledFlags(mDisabledFlags1, true); 528 return true; 529 } else if (v.getId() == R.id.recent_apps) { 530 return onLongPressRecents(); 531 } 532 } 533 if (sendBackLongPress) { 534 KeyButtonView keyButtonView = (KeyButtonView) v; 535 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); 536 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 537 return true; 538 } 539 } catch (RemoteException e) { 540 Log.d(TAG, "Unable to reach activity manager", e); 541 } 542 return false; 543 } 544 545 private boolean onLongPressRecents() { 546 if (mRecents == null || !ActivityManager.supportsMultiWindow(getContext()) 547 || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible() 548 || Recents.getConfiguration().isLowRamDevice) { 549 return false; 550 } 551 552 return mStatusBar.toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS, 553 MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS); 554 } 555 556 private void onAccessibilityClick(View v) { 557 mAccessibilityManager.notifyAccessibilityButtonClicked(); 558 } 559 560 private boolean onAccessibilityLongClick(View v) { 561 Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); 562 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 563 v.getContext().startActivityAsUser(intent, UserHandle.CURRENT); 564 return true; 565 } 566 567 private void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) { 568 int requestingServices = 0; 569 try { 570 if (Settings.Secure.getIntForUser(mContentResolver, 571 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 572 UserHandle.USER_CURRENT) == 1) { 573 requestingServices++; 574 } 575 } catch (Settings.SettingNotFoundException e) { 576 } 577 578 // AccessibilityManagerService resolves services for the current user since the local 579 // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission 580 final List<AccessibilityServiceInfo> services = 581 accessibilityManager.getEnabledAccessibilityServiceList( 582 AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 583 for (int i = services.size() - 1; i >= 0; --i) { 584 AccessibilityServiceInfo info = services.get(i); 585 if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { 586 requestingServices++; 587 } 588 } 589 590 final boolean showAccessibilityButton = requestingServices >= 1; 591 final boolean targetSelection = requestingServices >= 2; 592 mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection); 593 } 594 595 // ----- Methods that StatusBar talks to (should be minimized) ----- 596 597 public void setLightBarController(LightBarController lightBarController) { 598 mLightBarController = lightBarController; 599 mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController()); 600 } 601 602 public boolean isSemiTransparent() { 603 return mNavigationBarMode == MODE_SEMI_TRANSPARENT; 604 } 605 606 public void disableAnimationsDuringHide(long delay) { 607 mNavigationBarView.setLayoutTransitionsEnabled(false); 608 mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true), 609 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE); 610 } 611 612 public BarTransitions getBarTransitions() { 613 return mNavigationBarView.getBarTransitions(); 614 } 615 616 public void checkNavBarModes() { 617 mStatusBar.checkBarMode(mNavigationBarMode, 618 mNavigationBarWindowState, mNavigationBarView.getBarTransitions()); 619 } 620 621 public void finishBarAnimations() { 622 mNavigationBarView.getBarTransitions().finishAnimations(); 623 } 624 625 private final AccessibilityServicesStateChangeListener mAccessibilityListener = 626 this::updateAccessibilityServicesState; 627 628 private class MagnificationContentObserver extends ContentObserver { 629 630 public MagnificationContentObserver(Handler handler) { 631 super(handler); 632 } 633 634 @Override 635 public void onChange(boolean selfChange) { 636 NavigationBarFragment.this.updateAccessibilityServicesState(mAccessibilityManager); 637 } 638 } 639 640 private final Stub mRotationWatcher = new Stub() { 641 @Override 642 public void onRotationChanged(int rotation) throws RemoteException { 643 // We need this to be scheduled as early as possible to beat the redrawing of 644 // window in response to the orientation change. 645 Handler h = getView().getHandler(); 646 Message msg = Message.obtain(h, () -> { 647 if (mNavigationBarView != null 648 && mNavigationBarView.needsReorient(rotation)) { 649 repositionNavigationBar(); 650 } 651 }); 652 msg.setAsynchronous(true); 653 h.sendMessageAtFrontOfQueue(msg); 654 } 655 }; 656 657 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 658 @Override 659 public void onReceive(Context context, Intent intent) { 660 String action = intent.getAction(); 661 if (Intent.ACTION_SCREEN_OFF.equals(action) 662 || Intent.ACTION_SCREEN_ON.equals(action)) { 663 notifyNavigationBarScreenOn(); 664 } 665 } 666 }; 667 668 public static View create(Context context, FragmentListener listener) { 669 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 670 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 671 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 672 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING 673 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 674 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 675 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 676 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 677 | WindowManager.LayoutParams.FLAG_SLIPPERY, 678 PixelFormat.TRANSLUCENT); 679 lp.token = new Binder(); 680 lp.setTitle("NavigationBar"); 681 lp.windowAnimations = 0; 682 683 View navigationBarView = LayoutInflater.from(context).inflate( 684 R.layout.navigation_bar_window, null); 685 686 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView); 687 if (navigationBarView == null) return null; 688 689 context.getSystemService(WindowManager.class).addView(navigationBarView, lp); 690 FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView); 691 NavigationBarFragment fragment = new NavigationBarFragment(); 692 fragmentHost.getFragmentManager().beginTransaction() 693 .replace(R.id.navigation_bar_frame, fragment, TAG) 694 .commit(); 695 fragmentHost.addTagListener(TAG, listener); 696 return navigationBarView; 697 } 698} 699