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