NavigationBarFragment.java revision 72c510f1c4d5cff42a3925a784027e258ac0bcdc
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.animation.Animator; 28import android.animation.AnimatorListenerAdapter; 29import android.animation.ObjectAnimator; 30import android.annotation.Nullable; 31import android.app.ActivityManager; 32import android.app.ActivityManagerNative; 33import android.app.Fragment; 34import android.app.IActivityManager; 35import android.app.StatusBarManager; 36import android.content.BroadcastReceiver; 37import android.content.ContentResolver; 38import android.content.Context; 39import android.content.Intent; 40import android.content.IntentFilter; 41import android.content.res.Configuration; 42import android.database.ContentObserver; 43import android.graphics.PixelFormat; 44import android.graphics.Rect; 45import android.graphics.drawable.AnimatedVectorDrawable; 46import android.inputmethodservice.InputMethodService; 47import android.os.Binder; 48import android.os.Bundle; 49import android.os.Handler; 50import android.os.IBinder; 51import android.os.Message; 52import android.os.RemoteException; 53import android.os.UserHandle; 54import android.provider.Settings; 55import android.support.annotation.VisibleForTesting; 56import android.telecom.TelecomManager; 57import android.text.TextUtils; 58import android.util.Log; 59import android.view.IRotationWatcher.Stub; 60import android.view.KeyEvent; 61import android.view.LayoutInflater; 62import android.view.MotionEvent; 63import android.view.Surface; 64import android.view.View; 65import android.view.ViewGroup; 66import android.view.WindowManager; 67import android.view.WindowManager.LayoutParams; 68import android.view.WindowManagerGlobal; 69import android.view.accessibility.AccessibilityEvent; 70import android.view.accessibility.AccessibilityManager; 71import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; 72 73import com.android.internal.logging.MetricsLogger; 74import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 75import com.android.internal.util.LatencyTracker; 76import com.android.systemui.Dependency; 77import com.android.systemui.OverviewProxyService; 78import com.android.systemui.Interpolators; 79import com.android.systemui.R; 80import com.android.systemui.SysUiServiceProvider; 81import com.android.systemui.assist.AssistManager; 82import com.android.systemui.fragments.FragmentHostManager; 83import com.android.systemui.fragments.FragmentHostManager.FragmentListener; 84import com.android.systemui.recents.Recents; 85import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; 86import com.android.systemui.shared.system.ActivityManagerWrapper; 87import com.android.systemui.stackdivider.Divider; 88import com.android.systemui.statusbar.CommandQueue; 89import com.android.systemui.statusbar.CommandQueue.Callbacks; 90import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; 91import com.android.systemui.statusbar.policy.KeyButtonDrawable; 92import com.android.systemui.statusbar.policy.KeyButtonView; 93import com.android.systemui.statusbar.policy.RotationLockController; 94import com.android.systemui.statusbar.stack.StackStateAnimator; 95 96import java.io.FileDescriptor; 97import java.io.PrintWriter; 98import java.util.List; 99import java.util.Locale; 100 101/** 102 * Fragment containing the NavigationBarFragment. Contains logic for what happens 103 * on clicks and view states of the nav bar. 104 */ 105public class NavigationBarFragment extends Fragment implements Callbacks { 106 107 public static final String TAG = "NavigationBar"; 108 private static final boolean DEBUG = false; 109 private static final String EXTRA_DISABLE_STATE = "disabled_state"; 110 111 /** Allow some time inbetween the long press for back and recents. */ 112 private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; 113 114 private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100; 115 116 protected NavigationBarView mNavigationBarView = null; 117 protected AssistManager mAssistManager; 118 119 private int mNavigationBarWindowState = WINDOW_STATE_SHOWING; 120 121 private int mNavigationIconHints = 0; 122 private int mNavigationBarMode; 123 private boolean mAccessibilityFeedbackEnabled; 124 private AccessibilityManager mAccessibilityManager; 125 private MagnificationContentObserver mMagnificationObserver; 126 private ContentResolver mContentResolver; 127 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); 128 129 private int mDisabledFlags1; 130 private StatusBar mStatusBar; 131 private Recents mRecents; 132 private Divider mDivider; 133 private WindowManager mWindowManager; 134 private CommandQueue mCommandQueue; 135 private long mLastLockToAppLongPress; 136 137 private Locale mLocale; 138 private int mLayoutDirection; 139 140 private int mSystemUiVisibility; 141 private LightBarController mLightBarController; 142 143 private OverviewProxyService mOverviewProxyService; 144 145 public boolean mHomeBlockedThisTouch; 146 147 private int mLastRotationSuggestion; 148 private boolean mHoveringRotationSuggestion; 149 private RotationLockController mRotationLockController; 150 private TaskStackListenerImpl mTaskStackListener; 151 152 private final Runnable mRemoveRotationProposal = () -> setRotateSuggestionButtonState(false); 153 private Animator mRotateShowAnimator; 154 private Animator mRotateHideAnimator; 155 156 157 // ----- Fragment Lifecycle Callbacks ----- 158 159 @Override 160 public void onCreate(@Nullable Bundle savedInstanceState) { 161 super.onCreate(savedInstanceState); 162 mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class); 163 mCommandQueue.addCallbacks(this); 164 mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class); 165 mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class); 166 mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class); 167 mWindowManager = getContext().getSystemService(WindowManager.class); 168 mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class); 169 Dependency.get(AccessibilityManagerWrapper.class).addCallback( 170 mAccessibilityListener); 171 mContentResolver = getContext().getContentResolver(); 172 mMagnificationObserver = new MagnificationContentObserver( 173 getContext().getMainThreadHandler()); 174 mContentResolver.registerContentObserver(Settings.Secure.getUriFor( 175 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false, 176 mMagnificationObserver, UserHandle.USER_ALL); 177 178 if (savedInstanceState != null) { 179 mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0); 180 } 181 mAssistManager = Dependency.get(AssistManager.class); 182 mOverviewProxyService = Dependency.get(OverviewProxyService.class); 183 184 try { 185 WindowManagerGlobal.getWindowManagerService() 186 .watchRotation(mRotationWatcher, getContext().getDisplay().getDisplayId()); 187 } catch (RemoteException e) { 188 throw e.rethrowFromSystemServer(); 189 } 190 191 mRotationLockController = Dependency.get(RotationLockController.class); 192 193 // Register the task stack listener 194 mTaskStackListener = new TaskStackListenerImpl(); 195 ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); 196 } 197 198 @Override 199 public void onDestroy() { 200 super.onDestroy(); 201 mCommandQueue.removeCallbacks(this); 202 Dependency.get(AccessibilityManagerWrapper.class).removeCallback( 203 mAccessibilityListener); 204 mContentResolver.unregisterContentObserver(mMagnificationObserver); 205 try { 206 WindowManagerGlobal.getWindowManagerService() 207 .removeRotationWatcher(mRotationWatcher); 208 } catch (RemoteException e) { 209 throw e.rethrowFromSystemServer(); 210 } 211 212 // Unregister the task stack listener 213 ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener); 214 } 215 216 @Override 217 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 218 Bundle savedInstanceState) { 219 return inflater.inflate(R.layout.navigation_bar, container, false); 220 } 221 222 @Override 223 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 224 super.onViewCreated(view, savedInstanceState); 225 mNavigationBarView = (NavigationBarView) view; 226 227 mNavigationBarView.setDisabledFlags(mDisabledFlags1); 228 mNavigationBarView.setComponents(mRecents, mDivider, mStatusBar.getPanel()); 229 mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged); 230 mNavigationBarView.setOnTouchListener(this::onNavigationTouch); 231 if (savedInstanceState != null) { 232 mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState); 233 } 234 235 prepareNavigationBarView(); 236 checkNavBarModes(); 237 238 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 239 filter.addAction(Intent.ACTION_SCREEN_ON); 240 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); 241 notifyNavigationBarScreenOn(); 242 } 243 244 @Override 245 public void onDestroyView() { 246 super.onDestroyView(); 247 mNavigationBarView.getLightTransitionsController().destroy(getContext()); 248 getContext().unregisterReceiver(mBroadcastReceiver); 249 } 250 251 @Override 252 public void onSaveInstanceState(Bundle outState) { 253 super.onSaveInstanceState(outState); 254 outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1); 255 if (mNavigationBarView != null) { 256 mNavigationBarView.getLightTransitionsController().saveState(outState); 257 } 258 } 259 260 @Override 261 public void onConfigurationChanged(Configuration newConfig) { 262 super.onConfigurationChanged(newConfig); 263 final Locale locale = getContext().getResources().getConfiguration().locale; 264 final int ld = TextUtils.getLayoutDirectionFromLocale(locale); 265 if (!locale.equals(mLocale) || ld != mLayoutDirection) { 266 if (DEBUG) { 267 Log.v(TAG, String.format( 268 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection, 269 locale, ld)); 270 } 271 mLocale = locale; 272 mLayoutDirection = ld; 273 refreshLayout(ld); 274 } 275 repositionNavigationBar(); 276 } 277 278 @Override 279 public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) { 280 if (mNavigationBarView != null) { 281 pw.print(" mNavigationBarWindowState="); 282 pw.println(windowStateToString(mNavigationBarWindowState)); 283 pw.print(" mNavigationBarMode="); 284 pw.println(BarTransitions.modeToString(mNavigationBarMode)); 285 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions()); 286 } 287 288 pw.print(" mNavigationBarView="); 289 if (mNavigationBarView == null) { 290 pw.println("null"); 291 } else { 292 mNavigationBarView.dump(fd, pw, args); 293 } 294 } 295 296 // ----- CommandQueue Callbacks ----- 297 298 @Override 299 public void setImeWindowStatus(IBinder token, int vis, int backDisposition, 300 boolean showImeSwitcher) { 301 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; 302 int hints = mNavigationIconHints; 303 if (imeShown && backDisposition != InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS) { 304 hints |= NAVIGATION_HINT_BACK_ALT; 305 } else { 306 hints &= ~NAVIGATION_HINT_BACK_ALT; 307 } 308 if (showImeSwitcher) { 309 hints |= NAVIGATION_HINT_IME_SHOWN; 310 } else { 311 hints &= ~NAVIGATION_HINT_IME_SHOWN; 312 } 313 if (hints == mNavigationIconHints) return; 314 315 mNavigationIconHints = hints; 316 317 if (mNavigationBarView != null) { 318 mNavigationBarView.setNavigationIconHints(hints); 319 } 320 mStatusBar.checkBarModes(); 321 } 322 323 @Override 324 public void topAppWindowChanged(boolean showMenu) { 325 if (mNavigationBarView != null) { 326 mNavigationBarView.setMenuVisibility(showMenu); 327 } 328 } 329 330 @Override 331 public void setWindowState(int window, int state) { 332 if (mNavigationBarView != null 333 && window == StatusBarManager.WINDOW_NAVIGATION_BAR 334 && mNavigationBarWindowState != state) { 335 mNavigationBarWindowState = state; 336 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); 337 } 338 } 339 340 @Override 341 public void onRotationProposal(final int rotation, boolean isValid) { 342 // This method will be called on rotation suggestion changes even if the proposed rotation 343 // is not valid for the top app. Use invalid rotation choices as a signal to remove the 344 // rotate button if shown. 345 346 if (!isValid) { 347 setRotateSuggestionButtonState(false); 348 return; 349 } 350 351 if (rotation == mWindowManager.getDefaultDisplay().getRotation()) { 352 // Use this as a signal to remove any current suggestions 353 getView().getHandler().removeCallbacks(mRemoveRotationProposal); 354 setRotateSuggestionButtonState(false); 355 } else { 356 mLastRotationSuggestion = rotation; // Remember rotation for click 357 setRotateSuggestionButtonState(true); 358 rescheduleRotationTimeout(false); 359 mMetricsLogger.visible(MetricsEvent.ROTATION_SUGGESTION_SHOWN); 360 } 361 } 362 363 private void rescheduleRotationTimeout(final boolean reasonHover) { 364 // May be called due to a new rotation proposal or a change in hover state 365 if (reasonHover) { 366 // Don't reschedule if a hide animator is running 367 if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) { 368 return; 369 } 370 // Don't reschedule if not visible 371 if (mNavigationBarView.getRotateSuggestionButton().getVisibility() != View.VISIBLE) { 372 return; 373 } 374 } 375 376 Handler h = getView().getHandler(); 377 h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal 378 h.postDelayed(mRemoveRotationProposal, 379 computeRotationProposalTimeout()); // Schedule timeout 380 } 381 382 private int computeRotationProposalTimeout() { 383 if (mAccessibilityFeedbackEnabled) return 20000; 384 if (mHoveringRotationSuggestion) return 16000; 385 return 6000; 386 } 387 388 public void setRotateSuggestionButtonState(final boolean visible) { 389 setRotateSuggestionButtonState(visible, false); 390 } 391 392 public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) { 393 ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton(); 394 final boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE; 395 396 // Rerun a show animation to indicate change but don't rerun a hide animation 397 if (!visible && !currentlyVisible) return; 398 399 View currentView = rotBtn.getCurrentView(); 400 if (currentView == null) return; 401 402 KeyButtonDrawable kbd = rotBtn.getImageDrawable(); 403 if (kbd == null) return; 404 405 AnimatedVectorDrawable animIcon = null; 406 if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) { 407 animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0); 408 } 409 410 if (visible) { // Appear and change 411 rotBtn.setVisibility(View.VISIBLE); 412 mNavigationBarView.notifySubtreeAccessibilityStateChangedIfNeeded(); 413 414 if (skipAnim) { 415 currentView.setAlpha(1f); 416 return; 417 } 418 419 // Start a new animation if running 420 if (mRotateShowAnimator != null) mRotateShowAnimator.pause(); 421 if (mRotateHideAnimator != null) mRotateHideAnimator.pause(); 422 423 ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha", 424 0f, 1f); 425 appearFade.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS); 426 appearFade.setInterpolator(Interpolators.LINEAR); 427 mRotateShowAnimator = appearFade; 428 appearFade.start(); 429 430 // Run the rotate icon's animation if it has one 431 if (animIcon != null) { 432 animIcon.reset(); 433 animIcon.start(); 434 } 435 436 } else { // Hide 437 438 if (skipAnim) { 439 rotBtn.setVisibility(View.INVISIBLE); 440 mNavigationBarView.notifySubtreeAccessibilityStateChangedIfNeeded(); 441 return; 442 } 443 444 // Don't start any new hide animations if one is running 445 if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return; 446 // Pause any active show animations but don't reset the AVD to avoid jumps 447 if (mRotateShowAnimator != null) mRotateShowAnimator.pause(); 448 449 ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha", 450 0f); 451 fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS); 452 fadeOut.setInterpolator(Interpolators.LINEAR); 453 fadeOut.addListener(new AnimatorListenerAdapter() { 454 @Override 455 public void onAnimationEnd(Animator animation) { 456 rotBtn.setVisibility(View.INVISIBLE); 457 mNavigationBarView.notifySubtreeAccessibilityStateChangedIfNeeded(); 458 } 459 }); 460 461 mRotateHideAnimator = fadeOut; 462 fadeOut.start(); 463 } 464 } 465 466 // Injected from StatusBar at creation. 467 public void setCurrentSysuiVisibility(int systemUiVisibility) { 468 mSystemUiVisibility = systemUiVisibility; 469 mNavigationBarMode = mStatusBar.computeBarMode(0, mSystemUiVisibility, 470 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT, 471 View.NAVIGATION_BAR_TRANSPARENT); 472 checkNavBarModes(); 473 mStatusBar.touchAutoHide(); 474 mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, 475 true /* nbModeChanged */, mNavigationBarMode); 476 } 477 478 @Override 479 public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, 480 int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { 481 final int oldVal = mSystemUiVisibility; 482 final int newVal = (oldVal & ~mask) | (vis & mask); 483 final int diff = newVal ^ oldVal; 484 boolean nbModeChanged = false; 485 if (diff != 0) { 486 mSystemUiVisibility = newVal; 487 488 // update navigation bar mode 489 final int nbMode = getView() == null 490 ? -1 : mStatusBar.computeBarMode(oldVal, newVal, 491 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT, 492 View.NAVIGATION_BAR_TRANSPARENT); 493 nbModeChanged = nbMode != -1; 494 if (nbModeChanged) { 495 if (mNavigationBarMode != nbMode) { 496 mNavigationBarMode = nbMode; 497 checkNavBarModes(); 498 } 499 mStatusBar.touchAutoHide(); 500 } 501 } 502 503 mLightBarController.onNavigationVisibilityChanged(vis, mask, nbModeChanged, 504 mNavigationBarMode); 505 } 506 507 @Override 508 public void disable(int state1, int state2, boolean animate) { 509 // All navigation bar flags are in state1. 510 int masked = state1 & (StatusBarManager.DISABLE_HOME 511 | StatusBarManager.DISABLE_RECENT 512 | StatusBarManager.DISABLE_BACK 513 | StatusBarManager.DISABLE_SEARCH); 514 if (masked != mDisabledFlags1) { 515 mDisabledFlags1 = masked; 516 if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1); 517 } 518 } 519 520 // ----- Internal stuffz ----- 521 522 private void refreshLayout(int layoutDirection) { 523 if (mNavigationBarView != null) { 524 mNavigationBarView.setLayoutDirection(layoutDirection); 525 } 526 } 527 528 private boolean shouldDisableNavbarGestures() { 529 return !mStatusBar.isDeviceProvisioned() 530 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0 531 || mOverviewProxyService.getProxy() != null; 532 } 533 534 private void repositionNavigationBar() { 535 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return; 536 537 prepareNavigationBarView(); 538 539 mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(), 540 ((View) mNavigationBarView.getParent()).getLayoutParams()); 541 } 542 543 private void notifyNavigationBarScreenOn() { 544 mNavigationBarView.notifyScreenOn(); 545 } 546 547 private void prepareNavigationBarView() { 548 mNavigationBarView.reorient(); 549 550 ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton(); 551 recentsButton.setOnClickListener(this::onRecentsClick); 552 recentsButton.setOnTouchListener(this::onRecentsTouch); 553 recentsButton.setLongClickable(true); 554 recentsButton.setOnLongClickListener(this::onLongPressBackRecents); 555 556 ButtonDispatcher backButton = mNavigationBarView.getBackButton(); 557 backButton.setLongClickable(true); 558 backButton.setOnLongClickListener(this::onLongPressBackRecents); 559 560 ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); 561 homeButton.setOnTouchListener(this::onHomeTouch); 562 homeButton.setOnLongClickListener(this::onHomeLongClick); 563 564 ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); 565 accessibilityButton.setOnClickListener(this::onAccessibilityClick); 566 accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick); 567 updateAccessibilityServicesState(mAccessibilityManager); 568 569 ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton(); 570 rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick); 571 rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover); 572 } 573 574 private boolean onHomeTouch(View v, MotionEvent event) { 575 if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) { 576 return true; 577 } 578 // If an incoming call is ringing, HOME is totally disabled. 579 // (The user is already on the InCallUI at this point, 580 // and his ONLY options are to answer or reject the call.) 581 switch (event.getAction()) { 582 case MotionEvent.ACTION_DOWN: 583 mHomeBlockedThisTouch = false; 584 TelecomManager telecomManager = 585 getContext().getSystemService(TelecomManager.class); 586 if (telecomManager != null && telecomManager.isRinging()) { 587 if (mStatusBar.isKeyguardShowing()) { 588 Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " + 589 "No heads up"); 590 mHomeBlockedThisTouch = true; 591 return true; 592 } 593 } 594 break; 595 case MotionEvent.ACTION_UP: 596 case MotionEvent.ACTION_CANCEL: 597 mStatusBar.awakenDreams(); 598 break; 599 } 600 return false; 601 } 602 603 private void onVerticalChanged(boolean isVertical) { 604 mStatusBar.setQsScrimEnabled(!isVertical); 605 } 606 607 private boolean onNavigationTouch(View v, MotionEvent event) { 608 mStatusBar.checkUserAutohide(event); 609 return false; 610 } 611 612 @VisibleForTesting 613 boolean onHomeLongClick(View v) { 614 if (shouldDisableNavbarGestures()) { 615 return false; 616 } 617 mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS); 618 mAssistManager.startAssist(new Bundle() /* args */); 619 mStatusBar.awakenDreams(); 620 621 if (mNavigationBarView != null) { 622 mNavigationBarView.abortCurrentGesture(); 623 } 624 return true; 625 } 626 627 // additional optimization when we have software system buttons - start loading the recent 628 // tasks on touch down 629 private boolean onRecentsTouch(View v, MotionEvent event) { 630 int action = event.getAction() & MotionEvent.ACTION_MASK; 631 if (action == MotionEvent.ACTION_DOWN) { 632 mCommandQueue.preloadRecentApps(); 633 } else if (action == MotionEvent.ACTION_CANCEL) { 634 mCommandQueue.cancelPreloadRecentApps(); 635 } else if (action == MotionEvent.ACTION_UP) { 636 if (!v.isPressed()) { 637 mCommandQueue.cancelPreloadRecentApps(); 638 } 639 } 640 return false; 641 } 642 643 private void onRecentsClick(View v) { 644 if (LatencyTracker.isEnabled(getContext())) { 645 LatencyTracker.getInstance(getContext()).onActionStart( 646 LatencyTracker.ACTION_TOGGLE_RECENTS); 647 } 648 mStatusBar.awakenDreams(); 649 mCommandQueue.toggleRecentApps(); 650 } 651 652 /** 653 * This handles long-press of both back and recents. They are 654 * handled together to capture them both being long-pressed 655 * at the same time to exit screen pinning (lock task). 656 * 657 * When accessibility mode is on, only a long-press from recents 658 * is required to exit. 659 * 660 * In all other circumstances we try to pass through long-press events 661 * for Back, so that apps can still use it. Which can be from two things. 662 * 1) Not currently in screen pinning (lock task). 663 * 2) Back is long-pressed without recents. 664 */ 665 private boolean onLongPressBackRecents(View v) { 666 try { 667 boolean sendBackLongPress = false; 668 IActivityManager activityManager = ActivityManagerNative.getDefault(); 669 boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); 670 boolean inLockTaskMode = activityManager.isInLockTaskMode(); 671 if (inLockTaskMode && !touchExplorationEnabled) { 672 long time = System.currentTimeMillis(); 673 // If we recently long-pressed the other button then they were 674 // long-pressed 'together' 675 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) { 676 activityManager.stopSystemLockTaskMode(); 677 // When exiting refresh disabled flags. 678 mNavigationBarView.setDisabledFlags(mDisabledFlags1, true); 679 return true; 680 } else if ((v.getId() == R.id.back) 681 && !mNavigationBarView.getRecentsButton().getCurrentView().isPressed()) { 682 // If we aren't pressing recents right now then they presses 683 // won't be together, so send the standard long-press action. 684 sendBackLongPress = true; 685 } 686 mLastLockToAppLongPress = time; 687 } else { 688 // If this is back still need to handle sending the long-press event. 689 if (v.getId() == R.id.back) { 690 sendBackLongPress = true; 691 } else if (touchExplorationEnabled && inLockTaskMode) { 692 // When in accessibility mode a long press that is recents (not back) 693 // should stop lock task. 694 activityManager.stopSystemLockTaskMode(); 695 // When exiting refresh disabled flags. 696 mNavigationBarView.setDisabledFlags(mDisabledFlags1, true); 697 return true; 698 } else if (v.getId() == R.id.recent_apps) { 699 return onLongPressRecents(); 700 } 701 } 702 if (sendBackLongPress) { 703 KeyButtonView keyButtonView = (KeyButtonView) v; 704 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); 705 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); 706 return true; 707 } 708 } catch (RemoteException e) { 709 Log.d(TAG, "Unable to reach activity manager", e); 710 } 711 return false; 712 } 713 714 private boolean onLongPressRecents() { 715 if (mRecents == null || !ActivityManager.supportsMultiWindow(getContext()) 716 || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible() 717 || Recents.getConfiguration().isLowRamDevice) { 718 return false; 719 } 720 721 return mStatusBar.toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS, 722 MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS); 723 } 724 725 private void onAccessibilityClick(View v) { 726 mAccessibilityManager.notifyAccessibilityButtonClicked(); 727 } 728 729 private boolean onAccessibilityLongClick(View v) { 730 Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); 731 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 732 v.getContext().startActivityAsUser(intent, UserHandle.CURRENT); 733 return true; 734 } 735 736 private void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) { 737 int requestingServices = 0; 738 try { 739 if (Settings.Secure.getIntForUser(mContentResolver, 740 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 741 UserHandle.USER_CURRENT) == 1) { 742 requestingServices++; 743 } 744 } catch (Settings.SettingNotFoundException e) { 745 } 746 747 boolean feedbackEnabled = false; 748 // AccessibilityManagerService resolves services for the current user since the local 749 // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission 750 final List<AccessibilityServiceInfo> services = 751 accessibilityManager.getEnabledAccessibilityServiceList( 752 AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 753 for (int i = services.size() - 1; i >= 0; --i) { 754 AccessibilityServiceInfo info = services.get(i); 755 if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { 756 requestingServices++; 757 } 758 759 if (info.feedbackType != 0 && info.feedbackType != 760 AccessibilityServiceInfo.FEEDBACK_GENERIC) { 761 feedbackEnabled = true; 762 } 763 } 764 765 mAccessibilityFeedbackEnabled = feedbackEnabled; 766 767 final boolean showAccessibilityButton = requestingServices >= 1; 768 final boolean targetSelection = requestingServices >= 2; 769 mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection); 770 } 771 772 private void onRotateSuggestionClick(View v) { 773 mMetricsLogger.action(MetricsEvent.ACTION_ROTATION_SUGGESTION_ACCEPTED); 774 mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion); 775 } 776 777 private boolean onRotateSuggestionHover(View v, MotionEvent event) { 778 final int action = event.getActionMasked(); 779 mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER) 780 || (action == MotionEvent.ACTION_HOVER_MOVE); 781 rescheduleRotationTimeout(true); 782 return false; // Must return false so a11y hover events are dispatched correctly. 783 } 784 785 // ----- Methods that StatusBar talks to (should be minimized) ----- 786 787 public void setLightBarController(LightBarController lightBarController) { 788 mLightBarController = lightBarController; 789 mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController()); 790 } 791 792 public boolean isSemiTransparent() { 793 return mNavigationBarMode == MODE_SEMI_TRANSPARENT; 794 } 795 796 public void disableAnimationsDuringHide(long delay) { 797 mNavigationBarView.setLayoutTransitionsEnabled(false); 798 mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true), 799 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE); 800 } 801 802 public BarTransitions getBarTransitions() { 803 return mNavigationBarView.getBarTransitions(); 804 } 805 806 public void checkNavBarModes() { 807 mStatusBar.checkBarMode(mNavigationBarMode, 808 mNavigationBarWindowState, mNavigationBarView.getBarTransitions()); 809 } 810 811 public void finishBarAnimations() { 812 mNavigationBarView.getBarTransitions().finishAnimations(); 813 } 814 815 private final AccessibilityServicesStateChangeListener mAccessibilityListener = 816 this::updateAccessibilityServicesState; 817 818 private class MagnificationContentObserver extends ContentObserver { 819 820 public MagnificationContentObserver(Handler handler) { 821 super(handler); 822 } 823 824 @Override 825 public void onChange(boolean selfChange) { 826 NavigationBarFragment.this.updateAccessibilityServicesState(mAccessibilityManager); 827 } 828 } 829 830 private final Stub mRotationWatcher = new Stub() { 831 @Override 832 public void onRotationChanged(final int rotation) throws RemoteException { 833 // We need this to be scheduled as early as possible to beat the redrawing of 834 // window in response to the orientation change. 835 Handler h = getView().getHandler(); 836 Message msg = Message.obtain(h, () -> { 837 838 // If the screen rotation changes while locked, potentially update lock to flow with 839 // new screen rotation and hide any showing suggestions. 840 if (mRotationLockController.isRotationLocked()) { 841 if (shouldOverrideUserLockPrefs(rotation)) { 842 mRotationLockController.setRotationLockedAtAngle(true, rotation); 843 } 844 setRotateSuggestionButtonState(false, true); 845 } 846 847 if (mNavigationBarView != null 848 && mNavigationBarView.needsReorient(rotation)) { 849 repositionNavigationBar(); 850 } 851 }); 852 msg.setAsynchronous(true); 853 h.sendMessageAtFrontOfQueue(msg); 854 } 855 856 private boolean shouldOverrideUserLockPrefs(final int rotation) { 857 // Only override user prefs when returning to portrait. 858 // Don't let apps that force landscape or 180 alter user lock. 859 return rotation == Surface.ROTATION_0; 860 } 861 }; 862 863 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 864 @Override 865 public void onReceive(Context context, Intent intent) { 866 String action = intent.getAction(); 867 if (Intent.ACTION_SCREEN_OFF.equals(action) 868 || Intent.ACTION_SCREEN_ON.equals(action)) { 869 notifyNavigationBarScreenOn(); 870 } 871 } 872 }; 873 874 class TaskStackListenerImpl extends SysUiTaskStackChangeListener { 875 // Invalidate any rotation suggestion on task change or activity orientation change 876 // Note: all callbacks happen on main thread 877 878 @Override 879 public void onTaskStackChanged() { 880 setRotateSuggestionButtonState(false); 881 } 882 883 @Override 884 public void onTaskRemoved(int taskId) { 885 setRotateSuggestionButtonState(false); 886 } 887 888 @Override 889 public void onTaskMovedToFront(int taskId) { 890 setRotateSuggestionButtonState(false); 891 } 892 893 @Override 894 public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { 895 setRotateSuggestionButtonState(false); 896 } 897 } 898 899 public static View create(Context context, FragmentListener listener) { 900 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 901 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 902 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 903 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING 904 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 905 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 906 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 907 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 908 | WindowManager.LayoutParams.FLAG_SLIPPERY, 909 PixelFormat.TRANSLUCENT); 910 lp.token = new Binder(); 911 lp.setTitle("NavigationBar"); 912 lp.windowAnimations = 0; 913 914 View navigationBarView = LayoutInflater.from(context).inflate( 915 R.layout.navigation_bar_window, null); 916 917 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView); 918 if (navigationBarView == null) return null; 919 920 context.getSystemService(WindowManager.class).addView(navigationBarView, lp); 921 FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView); 922 NavigationBarFragment fragment = new NavigationBarFragment(); 923 fragmentHost.getFragmentManager().beginTransaction() 924 .replace(R.id.navigation_bar_frame, fragment, TAG) 925 .commit(); 926 fragmentHost.addTagListener(TAG, listener); 927 return navigationBarView; 928 } 929} 930