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