Home | History | Annotate | Download | only in phone
      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
    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
    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
    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
    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
    210     public void onDestroyView() {
    211         super.onDestroyView();
    212         mNavigationBarView.getLightTransitionsController().destroy(getContext());
    213         getContext().unregisterReceiver(mBroadcastReceiver);
    214     }
    215 
    216     @Override
    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
    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
    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
    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
    289     public void topAppWindowChanged(boolean showMenu) {
    290         if (mNavigationBarView != null) {
    291             mNavigationBarView.setMenuVisibility(showMenu);
    292         }
    293     }
    294 
    295     @Override
    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.
    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
    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
    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 
    361     private void refreshLayout(int layoutDirection) {
    362         if (mNavigationBarView != null) {
    363             mNavigationBarView.setLayoutDirection(layoutDirection);
    364         }
    365     }
    366 
    367     private boolean shouldDisableNavbarGestures() {
    368         return !mStatusBar.isDeviceProvisioned()
    369                 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
    370     }
    371 
    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 
    381     private void notifyNavigationBarScreenOn(boolean screenOn) {
    382         mNavigationBarView.notifyScreenOn(screenOn);
    383     }
    384 
    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 
    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 
    437     private void onVerticalChanged(boolean isVertical) {
    438         mStatusBar.setQsScrimEnabled(!isVertical);
    439     }
    440 
    441     private boolean onNavigationTouch(View v, MotionEvent event) {
    442         mStatusBar.checkUserAutohide(v, event);
    443         return false;
    444     }
    445 
    446     @VisibleForTesting
    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
    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 
    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      */
    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 
    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 
    557     private void onAccessibilityClick(View v) {
    558         mAccessibilityManager.notifyAccessibilityButtonClicked();
    559     }
    560 
    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 
    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 
    598     public void setLightBarController(LightBarController lightBarController) {
    599         mLightBarController = lightBarController;
    600         mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController());
    601     }
    602 
    603     public boolean isSemiTransparent() {
    604         return mNavigationBarMode == MODE_SEMI_TRANSPARENT;
    605     }
    606 
    607     public void onKeyguardOccludedChanged(boolean keyguardOccluded) {
    608         mNavigationBarView.onKeyguardOccludedChanged(keyguardOccluded);
    609     }
    610 
    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 
    617     public BarTransitions getBarTransitions() {
    618         return mNavigationBarView.getBarTransitions();
    619     }
    620 
    621     public void checkNavBarModes() {
    622         mStatusBar.checkBarMode(mNavigationBarMode,
    623                 mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
    624     }
    625 
    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 
    635         public MagnificationContentObserver(Handler handler) {
    636             super(handler);
    637         }
    638 
    639         @Override
    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 
    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