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