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