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