• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.navigationbar;
18 
19 import static android.inputmethodservice.InputMethodService.canImeRenderGesturalNavButtons;
20 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
21 
22 import static com.android.systemui.Flags.enableViewCaptureTracing;
23 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
24 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
25 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SEARCH_DISABLED;
26 import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
27 
28 import android.animation.LayoutTransition;
29 import android.animation.LayoutTransition.TransitionListener;
30 import android.animation.ObjectAnimator;
31 import android.animation.PropertyValuesHolder;
32 import android.animation.TimeInterpolator;
33 import android.animation.ValueAnimator;
34 import android.annotation.DrawableRes;
35 import android.app.StatusBarManager;
36 import android.content.Context;
37 import android.content.res.Configuration;
38 import android.graphics.Canvas;
39 import android.graphics.Point;
40 import android.graphics.Rect;
41 import android.media.permission.SafeCloseable;
42 import android.os.Bundle;
43 import android.os.RemoteException;
44 import android.util.AttributeSet;
45 import android.util.Log;
46 import android.util.SparseArray;
47 import android.view.ContextThemeWrapper;
48 import android.view.Display;
49 import android.view.MotionEvent;
50 import android.view.Surface;
51 import android.view.View;
52 import android.view.ViewGroup;
53 import android.view.WindowInsets;
54 import android.view.WindowInsetsController.Behavior;
55 import android.view.WindowManager;
56 import android.view.WindowManagerGlobal;
57 import android.view.accessibility.AccessibilityNodeInfo;
58 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
59 import android.widget.FrameLayout;
60 
61 import androidx.annotation.Nullable;
62 
63 import com.android.app.animation.Interpolators;
64 import com.android.app.viewcapture.ViewCaptureFactory;
65 import com.android.internal.annotations.VisibleForTesting;
66 import com.android.settingslib.Utils;
67 import com.android.systemui.Gefingerpoken;
68 import com.android.systemui.model.SysUiState;
69 import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
70 import com.android.systemui.navigationbar.buttons.ContextualButton;
71 import com.android.systemui.navigationbar.buttons.ContextualButtonGroup;
72 import com.android.systemui.navigationbar.buttons.DeadZone;
73 import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
74 import com.android.systemui.navigationbar.buttons.NearestTouchFrame;
75 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
76 import com.android.systemui.recents.Recents;
77 import com.android.systemui.res.R;
78 import com.android.systemui.settings.DisplayTracker;
79 import com.android.systemui.shade.ShadeViewController;
80 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
81 import com.android.systemui.shared.rotation.FloatingRotationButton;
82 import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
83 import com.android.systemui.shared.rotation.RotationButtonController;
84 import com.android.systemui.shared.system.QuickStepContract;
85 import com.android.systemui.statusbar.phone.AutoHideController;
86 import com.android.systemui.statusbar.phone.CentralSurfaces;
87 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
88 import com.android.wm.shell.back.BackAnimation;
89 import com.android.wm.shell.pip.Pip;
90 
91 import java.io.PrintWriter;
92 import java.util.Map;
93 import java.util.Optional;
94 import java.util.concurrent.Executor;
95 import java.util.function.Consumer;
96 
97 /** */
98 public class NavigationBarView extends FrameLayout {
99     final static boolean DEBUG = false;
100     final static String TAG = "NavBarView";
101 
102     final static boolean ALTERNATE_CAR_MODE_UI = false;
103 
104     private Executor mBgExecutor;
105 
106     // The current view is one of mHorizontal or mVertical depending on the current configuration
107     View mCurrentView = null;
108     private View mVertical;
109     private View mHorizontal;
110 
111     /** Indicates that navigation bar is vertical. */
112     private boolean mIsVertical;
113     private int mCurrentRotation = -1;
114 
115     boolean mLongClickableAccessibilityButton;
116     int mDisabledFlags = 0;
117     int mNavigationIconHints = 0;
118     private int mNavBarMode;
119     private boolean mImeDrawsImeNavBar;
120 
121     private KeyButtonDrawable mBackIcon;
122     private KeyButtonDrawable mHomeDefaultIcon;
123     private KeyButtonDrawable mRecentIcon;
124     private KeyButtonDrawable mDockedIcon;
125     private Context mLightContext;
126     private int mLightIconColor;
127     private int mDarkIconColor;
128 
129     private EdgeBackGestureHandler mEdgeBackGestureHandler;
130     private DisplayTracker mDisplayTracker;
131     private final DeadZone mDeadZone;
132     private NavigationBarTransitions mBarTransitions;
133     @Nullable
134     private AutoHideController mAutoHideController;
135 
136     // performs manual animation in sync with layout transitions
137     private final NavTransitionListener mTransitionListener = new NavTransitionListener();
138 
139     private OnVerticalChangedListener mOnVerticalChangedListener;
140     private boolean mLayoutTransitionsEnabled = true;
141     private boolean mWakeAndUnlocking;
142     private boolean mUseCarModeUi = false;
143     private boolean mInCarMode = false;
144     private boolean mDockedStackExists;
145     private boolean mScreenOn = true;
146 
147     private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
148     private final ContextualButtonGroup mContextualButtonGroup;
149     private Configuration mConfiguration;
150     private Configuration mTmpLastConfiguration;
151 
152     private NavigationBarInflaterView mNavigationInflaterView;
153     private Optional<Recents> mRecentsOptional = Optional.empty();
154     @Nullable
155     private ShadeViewController mShadeViewController;
156     @Nullable
157     private PanelExpansionInteractor mPanelExpansionInteractor;
158     private FloatingRotationButton mFloatingRotationButton;
159     private RotationButtonController mRotationButtonController;
160 
161     /**
162      * Helper that is responsible for showing the right toast when a disallowed activity operation
163      * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
164      * fully locked mode we only show that unlocking is blocked.
165      */
166     private ScreenPinningNotify mScreenPinningNotify;
167     private boolean mScreenPinningActive = false;
168 
169     /**
170      * {@code true} if the IME can render the back button and the IME switcher button.
171      *
172      * <p>The value must be used when and only when
173      * {@link com.android.systemui.shared.system.QuickStepContract#isGesturalMode(int)} returns
174      * {@code true}</p>
175      *
176      * <p>Cache the value here for better performance.</p>
177      */
178     private final boolean mImeCanRenderGesturalNavButtons = canImeRenderGesturalNavButtons();
179     private Gefingerpoken mTouchHandler;
180     private boolean mOverviewProxyEnabled;
181     private boolean mShowSwipeUpUi;
182     private UpdateActiveTouchRegionsCallback mUpdateActiveTouchRegionsCallback;
183     private SafeCloseable mViewCaptureCloseable;
184 
185     private class NavTransitionListener implements TransitionListener {
186         private boolean mBackTransitioning;
187         private boolean mHomeAppearing;
188         private long mStartDelay;
189         private long mDuration;
190         private TimeInterpolator mInterpolator;
191 
192         @Override
startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)193         public void startTransition(LayoutTransition transition, ViewGroup container,
194                 View view, int transitionType) {
195             if (view.getId() == R.id.back) {
196                 mBackTransitioning = true;
197             } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
198                 mHomeAppearing = true;
199                 mStartDelay = transition.getStartDelay(transitionType);
200                 mDuration = transition.getDuration(transitionType);
201                 mInterpolator = transition.getInterpolator(transitionType);
202             }
203         }
204 
205         @Override
endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType)206         public void endTransition(LayoutTransition transition, ViewGroup container,
207                 View view, int transitionType) {
208             if (view.getId() == R.id.back) {
209                 mBackTransitioning = false;
210             } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
211                 mHomeAppearing = false;
212             }
213         }
214 
onBackAltCleared()215         public void onBackAltCleared() {
216             ButtonDispatcher backButton = getBackButton();
217 
218             // When dismissing ime during unlock, force the back button to run the same appearance
219             // animation as home (if we catch this condition early enough).
220             if (!mBackTransitioning && backButton.getVisibility() == VISIBLE
221                     && mHomeAppearing && getHomeButton().getAlpha() == 0) {
222                 getBackButton().setAlpha(0);
223                 ValueAnimator a = ObjectAnimator.ofFloat(backButton, "alpha", 0, 1);
224                 a.setStartDelay(mStartDelay);
225                 a.setDuration(mDuration);
226                 a.setInterpolator(mInterpolator);
227                 a.start();
228             }
229         }
230     }
231 
232     private final AccessibilityDelegate mQuickStepAccessibilityDelegate =
233             new AccessibilityDelegate() {
234                 private AccessibilityAction mToggleOverviewAction;
235 
236                 @Override
237                 public void onInitializeAccessibilityNodeInfo(View host,
238                         AccessibilityNodeInfo info) {
239                     super.onInitializeAccessibilityNodeInfo(host, info);
240                     if (mToggleOverviewAction == null) {
241                         mToggleOverviewAction = new AccessibilityAction(
242                                 R.id.action_toggle_overview, getContext().getString(
243                                 R.string.quick_step_accessibility_toggle_overview));
244                     }
245                     info.addAction(mToggleOverviewAction);
246                 }
247 
248                 @Override
249                 public boolean performAccessibilityAction(View host, int action, Bundle args) {
250                     if (action == R.id.action_toggle_overview) {
251                         mRecentsOptional.ifPresent(Recents::toggleRecentApps);
252                     } else {
253                         return super.performAccessibilityAction(host, action, args);
254                     }
255                     return true;
256                 }
257             };
258 
259     private final RotationButtonUpdatesCallback mRotationButtonListener =
260             new RotationButtonUpdatesCallback() {
261                 @Override
262                 public void onVisibilityChanged(boolean visible) {
263                     if (visible && mAutoHideController != null) {
264                         // If the button will actually become visible and the navbar is about
265                         // to hide, tell the statusbar to keep it around for longer
266                         mAutoHideController.touchAutoHide();
267                     }
268                     notifyActiveTouchRegions();
269                 }
270 
271                 @Override
272                 public void onPositionChanged() {
273                     notifyActiveTouchRegions();
274                 }
275             };
276 
NavigationBarView(Context context, AttributeSet attrs)277     public NavigationBarView(Context context, AttributeSet attrs) {
278         super(context, attrs);
279 
280         final Context darkContext = new ContextThemeWrapper(context,
281                 Utils.getThemeAttr(context, R.attr.darkIconTheme));
282         mLightContext = new ContextThemeWrapper(context,
283                 Utils.getThemeAttr(context, R.attr.lightIconTheme));
284         mLightIconColor = Utils.getColorAttrDefaultColor(mLightContext, R.attr.singleToneColor);
285         mDarkIconColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor);
286         mIsVertical = false;
287         mLongClickableAccessibilityButton = false;
288 
289         // Set up the context group of buttons
290         mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
291         final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
292                 mLightContext, R.drawable.ic_ime_switcher_default);
293         final ContextualButton accessibilityButton =
294                 new ContextualButton(R.id.accessibility_button, mLightContext,
295                         R.drawable.ic_sysbar_accessibility_button);
296         mContextualButtonGroup.addButton(imeSwitcherButton);
297         mContextualButtonGroup.addButton(accessibilityButton);
298         mFloatingRotationButton = new FloatingRotationButton(mContext,
299                 R.string.accessibility_rotate_button,
300                 R.layout.rotate_suggestion,
301                 R.id.rotate_suggestion,
302                 R.dimen.floating_rotation_button_min_margin,
303                 R.dimen.rounded_corner_content_padding,
304                 R.dimen.floating_rotation_button_taskbar_left_margin,
305                 R.dimen.floating_rotation_button_taskbar_bottom_margin,
306                 R.dimen.floating_rotation_button_diameter,
307                 R.dimen.key_button_ripple_max_width,
308                 R.bool.floating_rotation_button_position_left);
309         mRotationButtonController = new RotationButtonController(mLightContext, mLightIconColor,
310                 mDarkIconColor, R.drawable.ic_sysbar_rotate_button_ccw_start_0,
311                 R.drawable.ic_sysbar_rotate_button_ccw_start_90,
312                 R.drawable.ic_sysbar_rotate_button_cw_start_0,
313                 R.drawable.ic_sysbar_rotate_button_cw_start_90,
314                 () -> mCurrentRotation);
315 
316         mConfiguration = new Configuration();
317         mTmpLastConfiguration = new Configuration();
318         mConfiguration.updateFrom(context.getResources().getConfiguration());
319 
320         mScreenPinningNotify = new ScreenPinningNotify(mContext);
321 
322         mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
323         mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
324         mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle));
325         mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
326         mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton);
327         mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);
328         mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
329         mDeadZone = new DeadZone(this);
330     }
331 
setEdgeBackGestureHandler(EdgeBackGestureHandler edgeBackGestureHandler)332     public void setEdgeBackGestureHandler(EdgeBackGestureHandler edgeBackGestureHandler) {
333         mEdgeBackGestureHandler = edgeBackGestureHandler;
334     }
335 
setBarTransitions(NavigationBarTransitions navigationBarTransitions)336     void setBarTransitions(NavigationBarTransitions navigationBarTransitions) {
337         mBarTransitions = navigationBarTransitions;
338     }
339 
setAutoHideController(AutoHideController autoHideController)340     public void setAutoHideController(AutoHideController autoHideController) {
341         mAutoHideController = autoHideController;
342     }
343 
getLightTransitionsController()344     public LightBarTransitionsController getLightTransitionsController() {
345         return mBarTransitions.getLightTransitionsController();
346     }
347 
setComponents(Optional<Recents> recentsOptional)348     public void setComponents(Optional<Recents> recentsOptional) {
349         mRecentsOptional = recentsOptional;
350     }
351 
352     /** */
setComponents(ShadeViewController svc, PanelExpansionInteractor pei)353     public void setComponents(ShadeViewController svc, PanelExpansionInteractor pei) {
354         mShadeViewController = svc;
355         mPanelExpansionInteractor = pei;
356         updatePanelSystemUiStateFlags();
357     }
358 
setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener)359     public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
360         mOnVerticalChangedListener = onVerticalChangedListener;
361         notifyVerticalChangedListener(mIsVertical);
362     }
363 
setBackgroundExecutor(Executor bgExecutor)364     public void setBackgroundExecutor(Executor bgExecutor) {
365         mBgExecutor = bgExecutor;
366         mRotationButtonController.setBgExecutor(bgExecutor);
367     }
368 
setDisplayTracker(DisplayTracker displayTracker)369     public void setDisplayTracker(DisplayTracker displayTracker) {
370         mDisplayTracker = displayTracker;
371     }
372 
setTouchHandler(Gefingerpoken touchHandler)373     public void setTouchHandler(Gefingerpoken touchHandler) {
374         mTouchHandler = touchHandler;
375     }
376 
377     @Override
onInterceptTouchEvent(MotionEvent event)378     public boolean onInterceptTouchEvent(MotionEvent event) {
379         return mTouchHandler.onInterceptTouchEvent(event) || super.onInterceptTouchEvent(event);
380     }
381 
382     @Override
onTouchEvent(MotionEvent event)383     public boolean onTouchEvent(MotionEvent event) {
384         mTouchHandler.onTouchEvent(event);
385         return super.onTouchEvent(event);
386     }
387 
abortCurrentGesture()388     public void abortCurrentGesture() {
389         getHomeButton().abortCurrentGesture();
390     }
391 
getCurrentView()392     public View getCurrentView() {
393         return mCurrentView;
394     }
395 
396     /**
397      * Applies {@param consumer} to each of the nav bar views.
398      */
forEachView(Consumer<View> consumer)399     public void forEachView(Consumer<View> consumer) {
400         if (mVertical != null) {
401             consumer.accept(mVertical);
402         }
403         if (mHorizontal != null) {
404             consumer.accept(mHorizontal);
405         }
406     }
407 
getRotationButtonController()408     public RotationButtonController getRotationButtonController() {
409         return mRotationButtonController;
410     }
411 
getFloatingRotationButton()412     public FloatingRotationButton getFloatingRotationButton() {
413         return mFloatingRotationButton;
414     }
415 
getRecentsButton()416     public ButtonDispatcher getRecentsButton() {
417         return mButtonDispatchers.get(R.id.recent_apps);
418     }
419 
getBackButton()420     public ButtonDispatcher getBackButton() {
421         return mButtonDispatchers.get(R.id.back);
422     }
423 
getHomeButton()424     public ButtonDispatcher getHomeButton() {
425         return mButtonDispatchers.get(R.id.home);
426     }
427 
getImeSwitchButton()428     public ButtonDispatcher getImeSwitchButton() {
429         return mButtonDispatchers.get(R.id.ime_switcher);
430     }
431 
getAccessibilityButton()432     public ButtonDispatcher getAccessibilityButton() {
433         return mButtonDispatchers.get(R.id.accessibility_button);
434     }
435 
getHomeHandle()436     public ButtonDispatcher getHomeHandle() {
437         return mButtonDispatchers.get(R.id.home_handle);
438     }
439 
getButtonDispatchers()440     public SparseArray<ButtonDispatcher> getButtonDispatchers() {
441         return mButtonDispatchers;
442     }
443 
isRecentsButtonVisible()444     public boolean isRecentsButtonVisible() {
445         return getRecentsButton().getVisibility() == View.VISIBLE;
446     }
447 
isOverviewEnabled()448     public boolean isOverviewEnabled() {
449         return (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) == 0;
450     }
451 
isQuickStepSwipeUpEnabled()452     private boolean isQuickStepSwipeUpEnabled() {
453         return mShowSwipeUpUi && isOverviewEnabled();
454     }
455 
reloadNavIcons()456     private void reloadNavIcons() {
457         updateIcons(Configuration.EMPTY);
458     }
459 
updateIcons(Configuration oldConfig)460     private void updateIcons(Configuration oldConfig) {
461         final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation;
462         final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi;
463         final boolean dirChange = oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection();
464 
465         if (orientationChange || densityChange) {
466             mDockedIcon = getDrawable(R.drawable.ic_sysbar_docked);
467             mHomeDefaultIcon = getHomeDrawable();
468         }
469         if (densityChange || dirChange) {
470             mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent);
471             mContextualButtonGroup.updateIcons(mLightIconColor, mDarkIconColor);
472         }
473         if (orientationChange || densityChange || dirChange) {
474             mBackIcon = getBackDrawable();
475         }
476     }
477 
478     /**
479      * Updates the rotation button based on the current navigation mode.
480      */
updateRotationButton()481     void updateRotationButton() {
482         mRotationButtonController.setRotationButton(mFloatingRotationButton,
483                 mRotationButtonListener);
484     }
485 
getBackDrawable()486     public KeyButtonDrawable getBackDrawable() {
487         KeyButtonDrawable drawable = getDrawable(R.drawable.ic_sysbar_back);
488         orientBackButton(drawable);
489         return drawable;
490     }
491 
getHomeDrawable()492     public KeyButtonDrawable getHomeDrawable() {
493         KeyButtonDrawable drawable = mShowSwipeUpUi
494                 ? getDrawable(R.drawable.ic_sysbar_home_quick_step)
495                 : getDrawable(R.drawable.ic_sysbar_home);
496         orientHomeButton(drawable);
497         return drawable;
498     }
499 
orientBackButton(KeyButtonDrawable drawable)500     private void orientBackButton(KeyButtonDrawable drawable) {
501         final boolean useAltBack =
502                 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
503         final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
504         float degrees = useAltBack ? (isRtl ? 90 : -90) : 0;
505         if (drawable.getRotation() == degrees) {
506             return;
507         }
508 
509         if (isGesturalMode(mNavBarMode)) {
510             drawable.setRotation(degrees);
511             return;
512         }
513 
514         // Animate the back button's rotation to the new degrees and only in portrait move up the
515         // back button to line up with the other buttons
516         float targetY = !mShowSwipeUpUi && !mIsVertical && useAltBack
517                 ? - getResources().getDimension(R.dimen.navbar_back_button_ime_offset)
518                 : 0;
519         ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable,
520                 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_ROTATE, degrees),
521                 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_TRANSLATE_Y, targetY));
522         navBarAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
523         navBarAnimator.setDuration(200);
524         navBarAnimator.start();
525     }
526 
orientHomeButton(KeyButtonDrawable drawable)527     private void orientHomeButton(KeyButtonDrawable drawable) {
528         drawable.setRotation(mIsVertical ? 90 : 0);
529     }
530 
getDrawable(@rawableRes int icon)531     private KeyButtonDrawable getDrawable(@DrawableRes int icon) {
532         return KeyButtonDrawable.create(mLightContext, mLightIconColor, mDarkIconColor, icon,
533                 true /* hasShadow */, null /* ovalBackgroundColor */);
534     }
535 
536     /** To be called when screen lock/unlock state changes */
onScreenStateChanged(boolean isScreenOn)537     public void onScreenStateChanged(boolean isScreenOn) {
538         mScreenOn = isScreenOn;
539     }
540 
setWindowVisible(boolean visible)541     public void setWindowVisible(boolean visible) {
542         mRotationButtonController.onNavigationBarWindowVisibilityChange(visible);
543     }
544 
setBehavior(@ehavior int behavior)545     public void setBehavior(@Behavior int behavior) {
546         mRotationButtonController.onBehaviorChanged(mDisplayTracker.getDefaultDisplayId(),
547                 behavior);
548     }
549 
550     @Override
setLayoutDirection(int layoutDirection)551     public void setLayoutDirection(int layoutDirection) {
552         reloadNavIcons();
553 
554         super.setLayoutDirection(layoutDirection);
555     }
556 
setNavigationIconHints(int hints)557     void setNavigationIconHints(int hints) {
558         if (hints == mNavigationIconHints) return;
559         mNavigationIconHints = hints;
560         updateNavButtonIcons();
561     }
562 
onImeVisibilityChanged(boolean visible)563     void onImeVisibilityChanged(boolean visible) {
564         if (!visible) {
565             mTransitionListener.onBackAltCleared();
566         }
567     }
568 
setDisabledFlags(int disabledFlags, SysUiState sysUiState)569     void setDisabledFlags(int disabledFlags, SysUiState sysUiState) {
570         if (mDisabledFlags == disabledFlags) return;
571 
572         final boolean overviewEnabledBefore = isOverviewEnabled();
573         mDisabledFlags = disabledFlags;
574 
575         // Update icons if overview was just enabled to ensure the correct icons are present
576         if (!overviewEnabledBefore && isOverviewEnabled()) {
577             reloadNavIcons();
578         }
579 
580         updateNavButtonIcons();
581         updateSlippery();
582         updateDisabledSystemUiStateFlags(sysUiState);
583     }
584 
updateNavButtonIcons()585     public void updateNavButtonIcons() {
586         // We have to replace or restore the back and home button icons when exiting or entering
587         // carmode, respectively. Recents are not available in CarMode in nav bar so change
588         // to recent icon is not required.
589         final boolean useAltBack =
590                 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
591         KeyButtonDrawable backIcon = mBackIcon;
592         orientBackButton(backIcon);
593         KeyButtonDrawable homeIcon = mHomeDefaultIcon;
594         if (!mUseCarModeUi) {
595             orientHomeButton(homeIcon);
596         }
597         getHomeButton().setImageDrawable(homeIcon);
598         getBackButton().setImageDrawable(backIcon);
599 
600         updateRecentsIcon();
601 
602         // Update IME button visibility, a11y and rotate button always overrides the appearance
603         boolean disableImeSwitcher =
604                 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) == 0
605                 || isImeRenderingNavButtons();
606         mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher);
607 
608         mBarTransitions.reapplyDarkIntensity();
609 
610         boolean disableHome = isGesturalMode(mNavBarMode)
611                 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
612 
613         // Always disable recents when alternate car mode UI is active and for secondary displays.
614         boolean disableRecent = isRecentsButtonDisabled();
615 
616         // Disable the home handle if both hone and recents are disabled
617         boolean disableHomeHandle = disableRecent
618                 && ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
619 
620         boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures()
621                 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0))
622                 || isImeRenderingNavButtons();
623 
624         // When screen pinning, don't hide back and home when connected service or back and
625         // recents buttons when disconnected from launcher service in screen pinning mode,
626         // as they are used for exiting.
627         if (mOverviewProxyEnabled) {
628             // Force disable recents when not in legacy mode
629             disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode);
630             if (mScreenPinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) {
631                 disableBack = disableHome = false;
632             }
633         } else if (mScreenPinningActive) {
634             disableBack = disableRecent = false;
635         }
636 
637         ViewGroup navButtons = getCurrentView().findViewById(R.id.nav_buttons);
638         if (navButtons != null) {
639             LayoutTransition lt = navButtons.getLayoutTransition();
640             if (lt != null) {
641                 if (!lt.getTransitionListeners().contains(mTransitionListener)) {
642                     lt.addTransitionListener(mTransitionListener);
643                 }
644             }
645         }
646 
647         getBackButton().setVisibility(disableBack       ? View.INVISIBLE : View.VISIBLE);
648         getHomeButton().setVisibility(disableHome       ? View.INVISIBLE : View.VISIBLE);
649         getRecentsButton().setVisibility(disableRecent  ? View.INVISIBLE : View.VISIBLE);
650         getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE);
651         notifyActiveTouchRegions();
652     }
653 
654     /**
655      * Returns whether the IME is currently visible and drawing the nav buttons.
656      */
isImeRenderingNavButtons()657     boolean isImeRenderingNavButtons() {
658         return mImeDrawsImeNavBar
659                 && mImeCanRenderGesturalNavButtons
660                 && (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
661     }
662 
663     @VisibleForTesting
isRecentsButtonDisabled()664     boolean isRecentsButtonDisabled() {
665         return mUseCarModeUi || !isOverviewEnabled()
666                 || getContext().getDisplayId() != mDisplayTracker.getDefaultDisplayId();
667     }
668 
getContextDisplay()669     private Display getContextDisplay() {
670         return getContext().getDisplay();
671     }
672 
setLayoutTransitionsEnabled(boolean enabled)673     public void setLayoutTransitionsEnabled(boolean enabled) {
674         mLayoutTransitionsEnabled = enabled;
675         updateLayoutTransitionsEnabled();
676     }
677 
setWakeAndUnlocking(boolean wakeAndUnlocking)678     public void setWakeAndUnlocking(boolean wakeAndUnlocking) {
679         setUseFadingAnimations(wakeAndUnlocking);
680         mWakeAndUnlocking = wakeAndUnlocking;
681         updateLayoutTransitionsEnabled();
682     }
683 
updateLayoutTransitionsEnabled()684     private void updateLayoutTransitionsEnabled() {
685         boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled;
686         ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
687         LayoutTransition lt = navButtons.getLayoutTransition();
688         if (lt != null) {
689             if (enabled) {
690                 lt.enableTransitionType(LayoutTransition.APPEARING);
691                 lt.enableTransitionType(LayoutTransition.DISAPPEARING);
692                 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING);
693                 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
694             } else {
695                 lt.disableTransitionType(LayoutTransition.APPEARING);
696                 lt.disableTransitionType(LayoutTransition.DISAPPEARING);
697                 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
698                 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
699             }
700         }
701     }
702 
setUseFadingAnimations(boolean useFadingAnimations)703     private void setUseFadingAnimations(boolean useFadingAnimations) {
704         WindowManager.LayoutParams lp = (WindowManager.LayoutParams) ((ViewGroup) getParent())
705                 .getLayoutParams();
706         if (lp != null) {
707             boolean old = lp.windowAnimations != 0;
708             if (!old && useFadingAnimations) {
709                 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn;
710             } else if (old && !useFadingAnimations) {
711                 lp.windowAnimations = 0;
712             } else {
713                 return;
714             }
715             WindowManager wm = getContext().getSystemService(WindowManager.class);
716             wm.updateViewLayout((View) getParent(), lp);
717         }
718     }
719 
onStatusBarPanelStateChanged()720     public void onStatusBarPanelStateChanged() {
721         updateSlippery();
722     }
723 
724     /** */
updateDisabledSystemUiStateFlags(SysUiState sysUiState)725     public void updateDisabledSystemUiStateFlags(SysUiState sysUiState) {
726         int displayId = mContext.getDisplayId();
727 
728         sysUiState.setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
729                         (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
730                 .setFlag(SYSUI_STATE_HOME_DISABLED,
731                         (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0)
732                 .setFlag(SYSUI_STATE_SEARCH_DISABLED,
733                         (mDisabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0)
734                 .commitUpdate(displayId);
735     }
736 
setInScreenPinning(boolean active)737     public void setInScreenPinning(boolean active) {
738         mScreenPinningActive = active;
739     }
740 
updatePanelSystemUiStateFlags()741     private void updatePanelSystemUiStateFlags() {
742         if (SysUiState.DEBUG) {
743             Log.d(TAG, "Updating panel sysui state flags: panelView=" + mShadeViewController);
744         }
745         if (mShadeViewController != null) {
746             mShadeViewController.updateSystemUiStateFlags();
747         }
748     }
749 
onOverviewProxyConnectionChange(boolean enabled)750     void onOverviewProxyConnectionChange(boolean enabled) {
751         mOverviewProxyEnabled = enabled;
752     }
753 
setShouldShowSwipeUpUi(boolean showSwipeUpUi)754     void setShouldShowSwipeUpUi(boolean showSwipeUpUi) {
755         mShowSwipeUpUi = showSwipeUpUi;
756         updateStates();
757     }
758 
759     /** */
updateStates()760     public void updateStates() {
761         if (mNavigationInflaterView != null) {
762             // Reinflate the navbar if needed, no-op unless the swipe up state changes
763             mNavigationInflaterView.onLikelyDefaultLayoutChange();
764         }
765 
766         updateSlippery();
767         reloadNavIcons();
768         updateNavButtonIcons();
769         mBgExecutor.execute(() -> setNavBarVirtualKeyHapticFeedbackEnabled(!mShowSwipeUpUi));
770         getHomeButton().setAccessibilityDelegate(
771                 mShowSwipeUpUi ? mQuickStepAccessibilityDelegate : null);
772     }
773 
774     /**
775      * Enable or disable haptic feedback on the navigation bar buttons.
776      */
setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled)777     private void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) {
778         try {
779             WindowManagerGlobal.getWindowManagerService()
780                     .setNavBarVirtualKeyHapticFeedbackEnabled(enabled);
781         } catch (RemoteException e) {
782             Log.w(TAG, "Failed to enable or disable navigation bar button haptics: ", e);
783         }
784     }
785 
786     /**
787      * Updates the {@link WindowManager.LayoutParams.FLAG_SLIPPERY} state dependent on if swipe up
788      * is enabled, or the notifications is fully opened without being in an animated state. If
789      * slippery is enabled, touch events will leave the nav bar window and enter into the fullscreen
790      * app/home window, if not nav bar will receive a cancelled touch event once gesture leaves bar.
791      */
updateSlippery()792     void updateSlippery() {
793         setSlippery(!isQuickStepSwipeUpEnabled() ||
794                 (mPanelExpansionInteractor != null && mPanelExpansionInteractor.isFullyExpanded()
795                         && !mPanelExpansionInteractor.isCollapsing()));
796     }
797 
setSlippery(boolean slippery)798     void setSlippery(boolean slippery) {
799         setWindowFlag(WindowManager.LayoutParams.FLAG_SLIPPERY, slippery);
800     }
801 
setWindowFlag(int flags, boolean enable)802     private void setWindowFlag(int flags, boolean enable) {
803         final ViewGroup navbarView = ((ViewGroup) getParent());
804         if (navbarView == null) {
805             return;
806         }
807         WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView.getLayoutParams();
808         if (lp == null || enable == ((lp.flags & flags) != 0)) {
809             return;
810         }
811         if (enable) {
812             lp.flags |= flags;
813         } else {
814             lp.flags &= ~flags;
815         }
816         WindowManager wm = getContext().getSystemService(WindowManager.class);
817         wm.updateViewLayout(navbarView, lp);
818     }
819 
setNavBarMode(int mode, boolean imeDrawsImeNavBar)820     void setNavBarMode(int mode, boolean imeDrawsImeNavBar) {
821         mNavBarMode = mode;
822         mImeDrawsImeNavBar = imeDrawsImeNavBar;
823         mBarTransitions.onNavigationModeChanged(mNavBarMode);
824         mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode);
825         mRotationButtonController.onNavigationModeChanged(mNavBarMode);
826         updateRotationButton();
827     }
828 
setAccessibilityButtonState(final boolean visible, final boolean longClickable)829     public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
830         mLongClickableAccessibilityButton = longClickable;
831         getAccessibilityButton().setLongClickable(longClickable);
832         mContextualButtonGroup.setButtonVisibility(R.id.accessibility_button, visible);
833     }
834 
835     @Override
onFinishInflate()836     public void onFinishInflate() {
837         super.onFinishInflate();
838         mNavigationInflaterView = findViewById(R.id.navigation_inflater);
839         mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
840 
841         updateOrientationViews();
842         reloadNavIcons();
843     }
844 
845     @Override
onDraw(Canvas canvas)846     protected void onDraw(Canvas canvas) {
847         mDeadZone.onDraw(canvas);
848         super.onDraw(canvas);
849     }
850 
851     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)852     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
853         super.onLayout(changed, left, top, right, bottom);
854 
855         notifyActiveTouchRegions();
856     }
857 
858     /**
859      * Notifies the overview service of the active touch regions.
860      */
notifyActiveTouchRegions()861     public void notifyActiveTouchRegions() {
862         if (mUpdateActiveTouchRegionsCallback != null) {
863             mUpdateActiveTouchRegionsCallback.update();
864         }
865     }
866 
setUpdateActiveTouchRegionsCallback(UpdateActiveTouchRegionsCallback callback)867     void setUpdateActiveTouchRegionsCallback(UpdateActiveTouchRegionsCallback callback) {
868         mUpdateActiveTouchRegionsCallback = callback;
869         notifyActiveTouchRegions();
870     }
871 
getButtonTouchRegionCache()872     Map<View, Rect> getButtonTouchRegionCache() {
873         FrameLayout navBarLayout = mIsVertical
874                 ? mNavigationInflaterView.mVertical
875                 : mNavigationInflaterView.mHorizontal;
876         return ((NearestTouchFrame) navBarLayout
877                 .findViewById(R.id.nav_buttons)).getFullTouchableChildRegions();
878     }
879 
updateOrientationViews()880     private void updateOrientationViews() {
881         mHorizontal = findViewById(R.id.horizontal);
882         mVertical = findViewById(R.id.vertical);
883 
884         updateCurrentView();
885     }
886 
needsReorient(int rotation)887     boolean needsReorient(int rotation) {
888         return mCurrentRotation != rotation;
889     }
890 
updateCurrentRotation()891     private void updateCurrentRotation() {
892         final int rotation = mConfiguration.windowConfiguration.getDisplayRotation();
893         if (mCurrentRotation == rotation) {
894             return;
895         }
896         mCurrentRotation = rotation;
897         mNavigationInflaterView.setAlternativeOrder(mCurrentRotation == Surface.ROTATION_90);
898         mDeadZone.onConfigurationChanged(mCurrentRotation);
899         if (DEBUG) {
900             Log.d(TAG, "updateCurrentRotation(): rot=" + mCurrentRotation);
901         }
902     }
903 
updateCurrentView()904     private void updateCurrentView() {
905         resetViews();
906         mCurrentView = mIsVertical ? mVertical : mHorizontal;
907         mCurrentView.setVisibility(View.VISIBLE);
908         mNavigationInflaterView.setVertical(mIsVertical);
909         mNavigationInflaterView.updateButtonDispatchersCurrentView();
910         updateLayoutTransitionsEnabled();
911         updateCurrentRotation();
912     }
913 
resetViews()914     private void resetViews() {
915         mHorizontal.setVisibility(View.GONE);
916         mVertical.setVisibility(View.GONE);
917     }
918 
updateRecentsIcon()919     private void updateRecentsIcon() {
920         mDockedIcon.setRotation(mDockedStackExists && mIsVertical ? 90 : 0);
921         getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon);
922         mBarTransitions.reapplyDarkIntensity();
923     }
924 
showPinningEnterExitToast(boolean entering)925     public void showPinningEnterExitToast(boolean entering) {
926         if (entering) {
927             mScreenPinningNotify.showPinningStartToast();
928         } else {
929             mScreenPinningNotify.showPinningExitToast();
930         }
931     }
932 
showPinningEscapeToast()933     public void showPinningEscapeToast() {
934         mScreenPinningNotify.showEscapeToast(
935                 mNavBarMode == NAV_BAR_MODE_GESTURAL, isRecentsButtonVisible());
936     }
937 
isVertical()938     public boolean isVertical() {
939         return mIsVertical;
940     }
941 
reorient()942     public void reorient() {
943         updateCurrentView();
944         ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
945 
946         // force the low profile & disabled states into compliance
947         mBarTransitions.init();
948 
949         // Resolve layout direction if not resolved since components changing layout direction such
950         // as changing languages will recreate this view and the direction will be resolved later
951         if (!isLayoutDirectionResolved()) {
952             resolveLayoutDirection();
953         }
954         updateNavButtonIcons();
955 
956         getHomeButton().setVertical(mIsVertical);
957     }
958 
959     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)960     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
961         int w = MeasureSpec.getSize(widthMeasureSpec);
962         int h = MeasureSpec.getSize(heightMeasureSpec);
963         if (DEBUG) Log.d(TAG, String.format(
964                 "onMeasure: (%dx%d) old: (%dx%d)", w, h, getMeasuredWidth(), getMeasuredHeight()));
965 
966         final boolean newVertical = w > 0 && h > w
967                 && !isGesturalMode(mNavBarMode);
968         if (newVertical != mIsVertical) {
969             mIsVertical = newVertical;
970             if (DEBUG) {
971                 Log.d(TAG, String.format("onMeasure: h=%d, w=%d, vert=%s", h, w,
972                         mIsVertical ? "y" : "n"));
973             }
974             reorient();
975             notifyVerticalChangedListener(newVertical);
976         }
977 
978         if (isGesturalMode(mNavBarMode)) {
979             // Update the nav bar background to match the height of the visible nav bar
980             int height = mIsVertical
981                     ? getResources().getDimensionPixelSize(
982                             com.android.internal.R.dimen.navigation_bar_height_landscape)
983                     : getResources().getDimensionPixelSize(
984                             com.android.internal.R.dimen.navigation_bar_height);
985             int frameHeight = getResources().getDimensionPixelSize(
986                     com.android.internal.R.dimen.navigation_bar_frame_height);
987             mBarTransitions.setBackgroundFrame(new Rect(0, frameHeight - height, w, h));
988         } else {
989             mBarTransitions.setBackgroundFrame(null);
990         }
991 
992         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
993     }
994 
getNavBarHeight()995     int getNavBarHeight() {
996         return mIsVertical
997                 ? getResources().getDimensionPixelSize(
998                 com.android.internal.R.dimen.navigation_bar_height_landscape)
999                 : getResources().getDimensionPixelSize(
1000                         com.android.internal.R.dimen.navigation_bar_height);
1001     }
1002 
notifyVerticalChangedListener(boolean newVertical)1003     private void notifyVerticalChangedListener(boolean newVertical) {
1004         if (mOnVerticalChangedListener != null) {
1005             mOnVerticalChangedListener.onVerticalChanged(newVertical);
1006         }
1007     }
1008 
1009     @Override
onConfigurationChanged(Configuration newConfig)1010     protected void onConfigurationChanged(Configuration newConfig) {
1011         super.onConfigurationChanged(newConfig);
1012         mTmpLastConfiguration.updateFrom(mConfiguration);
1013         final int changes = mConfiguration.updateFrom(newConfig);
1014         mFloatingRotationButton.onConfigurationChanged(changes);
1015 
1016         boolean uiCarModeChanged = updateCarMode();
1017         updateIcons(mTmpLastConfiguration);
1018         updateRecentsIcon();
1019         updateCurrentRotation();
1020         if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi
1021                 || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) {
1022             // If car mode or density changes, we need to reset the icons.
1023             updateNavButtonIcons();
1024         }
1025     }
1026 
1027     /**
1028      * If the configuration changed, update the carmode and return that it was updated.
1029      */
updateCarMode()1030     private boolean updateCarMode() {
1031         boolean uiCarModeChanged = false;
1032         if (mConfiguration != null) {
1033             int uiMode = mConfiguration.uiMode & Configuration.UI_MODE_TYPE_MASK;
1034             final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR);
1035 
1036             if (isCarMode != mInCarMode) {
1037                 mInCarMode = isCarMode;
1038                 if (ALTERNATE_CAR_MODE_UI) {
1039                     mUseCarModeUi = isCarMode;
1040                     uiCarModeChanged = true;
1041                 } else {
1042                     // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set.
1043                     mUseCarModeUi = false;
1044                 }
1045             }
1046         }
1047         return uiCarModeChanged;
1048     }
1049 
getResourceName(int resId)1050     private String getResourceName(int resId) {
1051         if (resId != 0) {
1052             final android.content.res.Resources res = getContext().getResources();
1053             try {
1054                 return res.getResourceName(resId);
1055             } catch (android.content.res.Resources.NotFoundException ex) {
1056                 return "(unknown)";
1057             }
1058         } else {
1059             return "(null)";
1060         }
1061     }
1062 
visibilityToString(int vis)1063     private static String visibilityToString(int vis) {
1064         switch (vis) {
1065             case View.INVISIBLE:
1066                 return "INVISIBLE";
1067             case View.GONE:
1068                 return "GONE";
1069             default:
1070                 return "VISIBLE";
1071         }
1072     }
1073 
1074     @Override
onAttachedToWindow()1075     protected void onAttachedToWindow() {
1076         super.onAttachedToWindow();
1077         requestApplyInsets();
1078         reorient();
1079         if (mRotationButtonController != null) {
1080             mRotationButtonController.registerListeners(false /* registerRotationWatcher */);
1081         }
1082 
1083         updateNavButtonIcons();
1084         if (enableViewCaptureTracing()) {
1085             mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
1086                     .startCapture(getRootView(), ".NavigationBarView");
1087         }
1088     }
1089 
1090     @Override
onDetachedFromWindow()1091     protected void onDetachedFromWindow() {
1092         super.onDetachedFromWindow();
1093         for (int i = 0; i < mButtonDispatchers.size(); ++i) {
1094             mButtonDispatchers.valueAt(i).onDestroy();
1095         }
1096         if (mRotationButtonController != null) {
1097             mFloatingRotationButton.hide();
1098             mRotationButtonController.unregisterListeners();
1099         }
1100         if (mViewCaptureCloseable != null) {
1101             mViewCaptureCloseable.close();
1102         }
1103     }
1104 
dump(PrintWriter pw)1105     void dump(PrintWriter pw) {
1106         final Rect r = new Rect();
1107         final Point size = new Point();
1108         getContextDisplay().getRealSize(size);
1109 
1110         pw.println("NavigationBarView:");
1111         pw.println(String.format("      this: " + CentralSurfaces.viewInfo(this)
1112                         + " " + visibilityToString(getVisibility())));
1113 
1114         getWindowVisibleDisplayFrame(r);
1115         final boolean offscreen = r.right > size.x || r.bottom > size.y;
1116         pw.println("      window: "
1117                 + r.toShortString()
1118                 + " " + visibilityToString(getWindowVisibility())
1119                 + (offscreen ? " OFFSCREEN!" : ""));
1120 
1121         pw.println(String.format("      mCurrentView: id=%s (%dx%d) %s %f",
1122                         getResourceName(getCurrentView().getId()),
1123                         getCurrentView().getWidth(), getCurrentView().getHeight(),
1124                         visibilityToString(getCurrentView().getVisibility()),
1125                         getCurrentView().getAlpha()));
1126 
1127         pw.println(String.format("      disabled=0x%08x vertical=%s darkIntensity=%.2f",
1128                         mDisabledFlags,
1129                         mIsVertical ? "true" : "false",
1130                         getLightTransitionsController().getCurrentDarkIntensity()));
1131 
1132         pw.println("    mScreenOn: " + mScreenOn);
1133 
1134 
1135         dumpButton(pw, "back", getBackButton());
1136         dumpButton(pw, "home", getHomeButton());
1137         dumpButton(pw, "handle", getHomeHandle());
1138         dumpButton(pw, "rcnt", getRecentsButton());
1139         dumpButton(pw, "a11y", getAccessibilityButton());
1140         dumpButton(pw, "ime", getImeSwitchButton());
1141 
1142         if (mNavigationInflaterView != null) {
1143             mNavigationInflaterView.dump(pw);
1144         }
1145         mBarTransitions.dump(pw);
1146         mContextualButtonGroup.dump(pw);
1147         mEdgeBackGestureHandler.dump(pw);
1148     }
1149 
1150     @Override
onApplyWindowInsets(WindowInsets insets)1151     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
1152         int leftInset = insets.getSystemWindowInsetLeft();
1153         int rightInset = insets.getSystemWindowInsetRight();
1154         setPadding(leftInset, insets.getSystemWindowInsetTop(), rightInset,
1155                 insets.getSystemWindowInsetBottom());
1156         // we're passing the insets onto the gesture handler since the back arrow is only
1157         // conditionally added and doesn't always get all the insets.
1158         mEdgeBackGestureHandler.setInsets(leftInset, rightInset);
1159 
1160         // this allows assist handle to be drawn outside its bound so that it can align screen
1161         // bottom by translating its y position.
1162         final boolean shouldClip =
1163                 !isGesturalMode(mNavBarMode) || insets.getSystemWindowInsetBottom() == 0;
1164         setClipChildren(shouldClip);
1165         setClipToPadding(shouldClip);
1166 
1167         return super.onApplyWindowInsets(insets);
1168     }
1169 
addPipExclusionBoundsChangeListener(Pip pip)1170     void addPipExclusionBoundsChangeListener(Pip pip) {
1171         pip.addPipExclusionBoundsChangeListener(mPipListener);
1172     }
1173 
removePipExclusionBoundsChangeListener(Pip pip)1174     void removePipExclusionBoundsChangeListener(Pip pip) {
1175         pip.removePipExclusionBoundsChangeListener(mPipListener);
1176     }
1177 
registerBackAnimation(BackAnimation backAnimation)1178     void registerBackAnimation(BackAnimation backAnimation) {
1179         mEdgeBackGestureHandler.setBackAnimation(backAnimation);
1180     }
1181 
dumpButton(PrintWriter pw, String caption, ButtonDispatcher button)1182     private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
1183         pw.print("      " + caption + ": ");
1184         if (button == null) {
1185             pw.print("null");
1186         } else {
1187             pw.print(visibilityToString(button.getVisibility())
1188                     + " alpha=" + button.getAlpha()
1189                     );
1190         }
1191         pw.println();
1192     }
1193 
1194     public interface OnVerticalChangedListener {
onVerticalChanged(boolean isVertical)1195         void onVerticalChanged(boolean isVertical);
1196     }
1197 
1198     private final Consumer<Boolean> mDockedListener = exists -> post(() -> {
1199         mDockedStackExists = exists;
1200         updateRecentsIcon();
1201     });
1202 
1203     private final Consumer<Rect> mPipListener = bounds -> post(() -> {
1204         mEdgeBackGestureHandler.setPipStashExclusionBounds(bounds);
1205     });
1206 
1207 
1208     interface UpdateActiveTouchRegionsCallback {
update()1209         void update();
1210     }
1211 }
1212