• 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.app.animation.Interpolators;
62 import com.android.internal.annotations.VisibleForTesting;
63 import com.android.settingslib.Utils;
64 import com.android.systemui.Gefingerpoken;
65 import com.android.systemui.R;
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.ShadeViewController;
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 ShadeViewController 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 
349     /** */
setComponents(ShadeViewController panel)350     public void setComponents(ShadeViewController panel) {
351         mPanelView = panel;
352         updatePanelSystemUiStateFlags();
353     }
354 
setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener)355     public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
356         mOnVerticalChangedListener = onVerticalChangedListener;
357         notifyVerticalChangedListener(mIsVertical);
358     }
359 
setBackgroundExecutor(Executor bgExecutor)360     public void setBackgroundExecutor(Executor bgExecutor) {
361         mBgExecutor = bgExecutor;
362         mRotationButtonController.setBgExecutor(bgExecutor);
363     }
364 
setDisplayTracker(DisplayTracker displayTracker)365     public void setDisplayTracker(DisplayTracker displayTracker) {
366         mDisplayTracker = displayTracker;
367     }
368 
setTouchHandler(Gefingerpoken touchHandler)369     public void setTouchHandler(Gefingerpoken touchHandler) {
370         mTouchHandler = touchHandler;
371     }
372 
373     @Override
onInterceptTouchEvent(MotionEvent event)374     public boolean onInterceptTouchEvent(MotionEvent event) {
375         return mTouchHandler.onInterceptTouchEvent(event) || super.onInterceptTouchEvent(event);
376     }
377 
378     @Override
onTouchEvent(MotionEvent event)379     public boolean onTouchEvent(MotionEvent event) {
380         mTouchHandler.onTouchEvent(event);
381         return super.onTouchEvent(event);
382     }
383 
abortCurrentGesture()384     public void abortCurrentGesture() {
385         getHomeButton().abortCurrentGesture();
386     }
387 
getCurrentView()388     public View getCurrentView() {
389         return mCurrentView;
390     }
391 
392     /**
393      * Applies {@param consumer} to each of the nav bar views.
394      */
forEachView(Consumer<View> consumer)395     public void forEachView(Consumer<View> consumer) {
396         if (mVertical != null) {
397             consumer.accept(mVertical);
398         }
399         if (mHorizontal != null) {
400             consumer.accept(mHorizontal);
401         }
402     }
403 
getRotationButtonController()404     public RotationButtonController getRotationButtonController() {
405         return mRotationButtonController;
406     }
407 
getFloatingRotationButton()408     public FloatingRotationButton getFloatingRotationButton() {
409         return mFloatingRotationButton;
410     }
411 
getRecentsButton()412     public ButtonDispatcher getRecentsButton() {
413         return mButtonDispatchers.get(R.id.recent_apps);
414     }
415 
getBackButton()416     public ButtonDispatcher getBackButton() {
417         return mButtonDispatchers.get(R.id.back);
418     }
419 
getHomeButton()420     public ButtonDispatcher getHomeButton() {
421         return mButtonDispatchers.get(R.id.home);
422     }
423 
getImeSwitchButton()424     public ButtonDispatcher getImeSwitchButton() {
425         return mButtonDispatchers.get(R.id.ime_switcher);
426     }
427 
getAccessibilityButton()428     public ButtonDispatcher getAccessibilityButton() {
429         return mButtonDispatchers.get(R.id.accessibility_button);
430     }
431 
getRotateSuggestionButton()432     public RotationContextButton getRotateSuggestionButton() {
433         return (RotationContextButton) mButtonDispatchers.get(R.id.rotate_suggestion);
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         if (isGesturalMode(mNavBarMode)) {
483             mContextualButtonGroup.removeButton(R.id.rotate_suggestion);
484             mButtonDispatchers.remove(R.id.rotate_suggestion);
485             mRotationButtonController.setRotationButton(mFloatingRotationButton,
486                     mRotationButtonListener);
487         } else if (mContextualButtonGroup.getContextButton(R.id.rotate_suggestion) == null) {
488             mContextualButtonGroup.addButton(mRotationContextButton);
489             mButtonDispatchers.put(R.id.rotate_suggestion, mRotationContextButton);
490             mRotationButtonController.setRotationButton(mRotationContextButton,
491                     mRotationButtonListener);
492         }
493         mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
494     }
495 
getBackDrawable()496     public KeyButtonDrawable getBackDrawable() {
497         KeyButtonDrawable drawable = getDrawable(getBackDrawableRes());
498         orientBackButton(drawable);
499         return drawable;
500     }
501 
getBackDrawableRes()502     public @DrawableRes int getBackDrawableRes() {
503         return chooseNavigationIconDrawableRes(R.drawable.ic_sysbar_back,
504                 R.drawable.ic_sysbar_back_quick_step);
505     }
506 
getHomeDrawable()507     public KeyButtonDrawable getHomeDrawable() {
508         KeyButtonDrawable drawable = mShowSwipeUpUi
509                 ? getDrawable(R.drawable.ic_sysbar_home_quick_step)
510                 : getDrawable(R.drawable.ic_sysbar_home);
511         orientHomeButton(drawable);
512         return drawable;
513     }
514 
orientBackButton(KeyButtonDrawable drawable)515     private void orientBackButton(KeyButtonDrawable drawable) {
516         final boolean useAltBack =
517                 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
518         final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
519         float degrees = useAltBack ? (isRtl ? 90 : -90) : 0;
520         if (drawable.getRotation() == degrees) {
521             return;
522         }
523 
524         if (isGesturalMode(mNavBarMode)) {
525             drawable.setRotation(degrees);
526             return;
527         }
528 
529         // Animate the back button's rotation to the new degrees and only in portrait move up the
530         // back button to line up with the other buttons
531         float targetY = !mShowSwipeUpUi && !mIsVertical && useAltBack
532                 ? - getResources().getDimension(R.dimen.navbar_back_button_ime_offset)
533                 : 0;
534         ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable,
535                 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_ROTATE, degrees),
536                 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_TRANSLATE_Y, targetY));
537         navBarAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
538         navBarAnimator.setDuration(200);
539         navBarAnimator.start();
540     }
541 
orientHomeButton(KeyButtonDrawable drawable)542     private void orientHomeButton(KeyButtonDrawable drawable) {
543         drawable.setRotation(mIsVertical ? 90 : 0);
544     }
545 
chooseNavigationIconDrawableRes(@rawableRes int icon, @DrawableRes int quickStepIcon)546     private @DrawableRes int chooseNavigationIconDrawableRes(@DrawableRes int icon,
547             @DrawableRes int quickStepIcon) {
548         return mShowSwipeUpUi ? quickStepIcon : icon;
549     }
550 
getDrawable(@rawableRes int icon)551     private KeyButtonDrawable getDrawable(@DrawableRes int icon) {
552         return KeyButtonDrawable.create(mLightContext, mLightIconColor, mDarkIconColor, icon,
553                 true /* hasShadow */, null /* ovalBackgroundColor */);
554     }
555 
556     /** To be called when screen lock/unlock state changes */
onScreenStateChanged(boolean isScreenOn)557     public void onScreenStateChanged(boolean isScreenOn) {
558         mScreenOn = isScreenOn;
559     }
560 
setWindowVisible(boolean visible)561     public void setWindowVisible(boolean visible) {
562         mRotationButtonController.onNavigationBarWindowVisibilityChange(visible);
563     }
564 
setBehavior(@ehavior int behavior)565     public void setBehavior(@Behavior int behavior) {
566         mRotationButtonController.onBehaviorChanged(mDisplayTracker.getDefaultDisplayId(),
567                 behavior);
568     }
569 
570     @Override
setLayoutDirection(int layoutDirection)571     public void setLayoutDirection(int layoutDirection) {
572         reloadNavIcons();
573 
574         super.setLayoutDirection(layoutDirection);
575     }
576 
setNavigationIconHints(int hints)577     void setNavigationIconHints(int hints) {
578         if (hints == mNavigationIconHints) return;
579         mNavigationIconHints = hints;
580         updateNavButtonIcons();
581     }
582 
onImeVisibilityChanged(boolean visible)583     void onImeVisibilityChanged(boolean visible) {
584         if (!visible) {
585             mTransitionListener.onBackAltCleared();
586         }
587         mRotationButtonController.getRotationButton().setCanShowRotationButton(!visible);
588     }
589 
setDisabledFlags(int disabledFlags, SysUiState sysUiState)590     void setDisabledFlags(int disabledFlags, SysUiState sysUiState) {
591         if (mDisabledFlags == disabledFlags) return;
592 
593         final boolean overviewEnabledBefore = isOverviewEnabled();
594         mDisabledFlags = disabledFlags;
595 
596         // Update icons if overview was just enabled to ensure the correct icons are present
597         if (!overviewEnabledBefore && isOverviewEnabled()) {
598             reloadNavIcons();
599         }
600 
601         updateNavButtonIcons();
602         updateSlippery();
603         updateDisabledSystemUiStateFlags(sysUiState);
604     }
605 
updateNavButtonIcons()606     public void updateNavButtonIcons() {
607         // We have to replace or restore the back and home button icons when exiting or entering
608         // carmode, respectively. Recents are not available in CarMode in nav bar so change
609         // to recent icon is not required.
610         final boolean useAltBack =
611                 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
612         KeyButtonDrawable backIcon = mBackIcon;
613         orientBackButton(backIcon);
614         KeyButtonDrawable homeIcon = mHomeDefaultIcon;
615         if (!mUseCarModeUi) {
616             orientHomeButton(homeIcon);
617         }
618         getHomeButton().setImageDrawable(homeIcon);
619         getBackButton().setImageDrawable(backIcon);
620 
621         updateRecentsIcon();
622 
623         // Update IME button visibility, a11y and rotate button always overrides the appearance
624         boolean disableImeSwitcher =
625                 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) == 0
626                 || isImeRenderingNavButtons();
627         mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher);
628 
629         mBarTransitions.reapplyDarkIntensity();
630 
631         boolean disableHome = isGesturalMode(mNavBarMode)
632                 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
633 
634         // Always disable recents when alternate car mode UI is active and for secondary displays.
635         boolean disableRecent = isRecentsButtonDisabled();
636 
637         // Disable the home handle if both hone and recents are disabled
638         boolean disableHomeHandle = disableRecent
639                 && ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
640 
641         boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures()
642                 || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0))
643                 || isImeRenderingNavButtons();
644 
645         // When screen pinning, don't hide back and home when connected service or back and
646         // recents buttons when disconnected from launcher service in screen pinning mode,
647         // as they are used for exiting.
648         if (mOverviewProxyEnabled) {
649             // Force disable recents when not in legacy mode
650             disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode);
651             if (mScreenPinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) {
652                 disableBack = disableHome = false;
653             }
654         } else if (mScreenPinningActive) {
655             disableBack = disableRecent = false;
656         }
657 
658         ViewGroup navButtons = getCurrentView().findViewById(R.id.nav_buttons);
659         if (navButtons != null) {
660             LayoutTransition lt = navButtons.getLayoutTransition();
661             if (lt != null) {
662                 if (!lt.getTransitionListeners().contains(mTransitionListener)) {
663                     lt.addTransitionListener(mTransitionListener);
664                 }
665             }
666         }
667 
668         getBackButton().setVisibility(disableBack       ? View.INVISIBLE : View.VISIBLE);
669         getHomeButton().setVisibility(disableHome       ? View.INVISIBLE : View.VISIBLE);
670         getRecentsButton().setVisibility(disableRecent  ? View.INVISIBLE : View.VISIBLE);
671         getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE);
672         notifyActiveTouchRegions();
673     }
674 
675     /**
676      * Returns whether the IME is currently visible and drawing the nav buttons.
677      */
isImeRenderingNavButtons()678     boolean isImeRenderingNavButtons() {
679         return mImeDrawsImeNavBar
680                 && mImeCanRenderGesturalNavButtons
681                 && (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
682     }
683 
684     @VisibleForTesting
isRecentsButtonDisabled()685     boolean isRecentsButtonDisabled() {
686         return mUseCarModeUi || !isOverviewEnabled()
687                 || getContext().getDisplayId() != mDisplayTracker.getDefaultDisplayId();
688     }
689 
getContextDisplay()690     private Display getContextDisplay() {
691         return getContext().getDisplay();
692     }
693 
setLayoutTransitionsEnabled(boolean enabled)694     public void setLayoutTransitionsEnabled(boolean enabled) {
695         mLayoutTransitionsEnabled = enabled;
696         updateLayoutTransitionsEnabled();
697     }
698 
setWakeAndUnlocking(boolean wakeAndUnlocking)699     public void setWakeAndUnlocking(boolean wakeAndUnlocking) {
700         setUseFadingAnimations(wakeAndUnlocking);
701         mWakeAndUnlocking = wakeAndUnlocking;
702         updateLayoutTransitionsEnabled();
703     }
704 
updateLayoutTransitionsEnabled()705     private void updateLayoutTransitionsEnabled() {
706         boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled;
707         ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
708         LayoutTransition lt = navButtons.getLayoutTransition();
709         if (lt != null) {
710             if (enabled) {
711                 lt.enableTransitionType(LayoutTransition.APPEARING);
712                 lt.enableTransitionType(LayoutTransition.DISAPPEARING);
713                 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING);
714                 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
715             } else {
716                 lt.disableTransitionType(LayoutTransition.APPEARING);
717                 lt.disableTransitionType(LayoutTransition.DISAPPEARING);
718                 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
719                 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
720             }
721         }
722     }
723 
setUseFadingAnimations(boolean useFadingAnimations)724     private void setUseFadingAnimations(boolean useFadingAnimations) {
725         WindowManager.LayoutParams lp = (WindowManager.LayoutParams) ((ViewGroup) getParent())
726                 .getLayoutParams();
727         if (lp != null) {
728             boolean old = lp.windowAnimations != 0;
729             if (!old && useFadingAnimations) {
730                 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn;
731             } else if (old && !useFadingAnimations) {
732                 lp.windowAnimations = 0;
733             } else {
734                 return;
735             }
736             WindowManager wm = getContext().getSystemService(WindowManager.class);
737             wm.updateViewLayout((View) getParent(), lp);
738         }
739     }
740 
onStatusBarPanelStateChanged()741     public void onStatusBarPanelStateChanged() {
742         updateSlippery();
743     }
744 
745     /** */
updateDisabledSystemUiStateFlags(SysUiState sysUiState)746     public void updateDisabledSystemUiStateFlags(SysUiState sysUiState) {
747         int displayId = mContext.getDisplayId();
748 
749         sysUiState.setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
750                         (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
751                 .setFlag(SYSUI_STATE_HOME_DISABLED,
752                         (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0)
753                 .setFlag(SYSUI_STATE_SEARCH_DISABLED,
754                         (mDisabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0)
755                 .commitUpdate(displayId);
756     }
757 
setInScreenPinning(boolean active)758     public void setInScreenPinning(boolean active) {
759         mScreenPinningActive = active;
760     }
761 
updatePanelSystemUiStateFlags()762     private void updatePanelSystemUiStateFlags() {
763         if (SysUiState.DEBUG) {
764             Log.d(TAG, "Updating panel sysui state flags: panelView=" + mPanelView);
765         }
766         if (mPanelView != null) {
767             mPanelView.updateSystemUiStateFlags();
768         }
769     }
770 
onOverviewProxyConnectionChange(boolean enabled)771     void onOverviewProxyConnectionChange(boolean enabled) {
772         mOverviewProxyEnabled = enabled;
773     }
774 
setShouldShowSwipeUpUi(boolean showSwipeUpUi)775     void setShouldShowSwipeUpUi(boolean showSwipeUpUi) {
776         mShowSwipeUpUi = showSwipeUpUi;
777         updateStates();
778     }
779 
780     /** */
updateStates()781     public void updateStates() {
782         if (mNavigationInflaterView != null) {
783             // Reinflate the navbar if needed, no-op unless the swipe up state changes
784             mNavigationInflaterView.onLikelyDefaultLayoutChange();
785         }
786 
787         updateSlippery();
788         reloadNavIcons();
789         updateNavButtonIcons();
790         mBgExecutor.execute(() -> setNavBarVirtualKeyHapticFeedbackEnabled(!mShowSwipeUpUi));
791         getHomeButton().setAccessibilityDelegate(
792                 mShowSwipeUpUi ? mQuickStepAccessibilityDelegate : null);
793     }
794 
795     /**
796      * Enable or disable haptic feedback on the navigation bar buttons.
797      */
setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled)798     private void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) {
799         try {
800             WindowManagerGlobal.getWindowManagerService()
801                     .setNavBarVirtualKeyHapticFeedbackEnabled(enabled);
802         } catch (RemoteException e) {
803             Log.w(TAG, "Failed to enable or disable navigation bar button haptics: ", e);
804         }
805     }
806 
807     /**
808      * Updates the {@link WindowManager.LayoutParams.FLAG_SLIPPERY} state dependent on if swipe up
809      * is enabled, or the notifications is fully opened without being in an animated state. If
810      * slippery is enabled, touch events will leave the nav bar window and enter into the fullscreen
811      * app/home window, if not nav bar will receive a cancelled touch event once gesture leaves bar.
812      */
updateSlippery()813     void updateSlippery() {
814         setSlippery(!isQuickStepSwipeUpEnabled() ||
815                 (mPanelView != null && mPanelView.isFullyExpanded() && !mPanelView.isCollapsing()));
816     }
817 
setSlippery(boolean slippery)818     void setSlippery(boolean slippery) {
819         setWindowFlag(WindowManager.LayoutParams.FLAG_SLIPPERY, slippery);
820     }
821 
setWindowFlag(int flags, boolean enable)822     private void setWindowFlag(int flags, boolean enable) {
823         final ViewGroup navbarView = ((ViewGroup) getParent());
824         if (navbarView == null) {
825             return;
826         }
827         WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView.getLayoutParams();
828         if (lp == null || enable == ((lp.flags & flags) != 0)) {
829             return;
830         }
831         if (enable) {
832             lp.flags |= flags;
833         } else {
834             lp.flags &= ~flags;
835         }
836         WindowManager wm = getContext().getSystemService(WindowManager.class);
837         wm.updateViewLayout(navbarView, lp);
838     }
839 
setNavBarMode(int mode, boolean imeDrawsImeNavBar)840     void setNavBarMode(int mode, boolean imeDrawsImeNavBar) {
841         mNavBarMode = mode;
842         mImeDrawsImeNavBar = imeDrawsImeNavBar;
843         mBarTransitions.onNavigationModeChanged(mNavBarMode);
844         mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode);
845         mRotationButtonController.onNavigationModeChanged(mNavBarMode);
846         updateRotationButton();
847     }
848 
setAccessibilityButtonState(final boolean visible, final boolean longClickable)849     public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
850         mLongClickableAccessibilityButton = longClickable;
851         getAccessibilityButton().setLongClickable(longClickable);
852         mContextualButtonGroup.setButtonVisibility(R.id.accessibility_button, visible);
853     }
854 
855     @Override
onFinishInflate()856     public void onFinishInflate() {
857         super.onFinishInflate();
858         mNavigationInflaterView = findViewById(R.id.navigation_inflater);
859         mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
860 
861         updateOrientationViews();
862         reloadNavIcons();
863     }
864 
865     @Override
onDraw(Canvas canvas)866     protected void onDraw(Canvas canvas) {
867         mDeadZone.onDraw(canvas);
868         super.onDraw(canvas);
869     }
870 
871     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)872     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
873         super.onLayout(changed, left, top, right, bottom);
874 
875         notifyActiveTouchRegions();
876     }
877 
878     /**
879      * Notifies the overview service of the active touch regions.
880      */
notifyActiveTouchRegions()881     public void notifyActiveTouchRegions() {
882         if (mUpdateActiveTouchRegionsCallback != null) {
883             mUpdateActiveTouchRegionsCallback.update();
884         }
885     }
886 
setUpdateActiveTouchRegionsCallback(UpdateActiveTouchRegionsCallback callback)887     void setUpdateActiveTouchRegionsCallback(UpdateActiveTouchRegionsCallback callback) {
888         mUpdateActiveTouchRegionsCallback = callback;
889         notifyActiveTouchRegions();
890     }
891 
getButtonTouchRegionCache()892     Map<View, Rect> getButtonTouchRegionCache() {
893         FrameLayout navBarLayout = mIsVertical
894                 ? mNavigationInflaterView.mVertical
895                 : mNavigationInflaterView.mHorizontal;
896         return ((NearestTouchFrame) navBarLayout
897                 .findViewById(R.id.nav_buttons)).getFullTouchableChildRegions();
898     }
899 
updateOrientationViews()900     private void updateOrientationViews() {
901         mHorizontal = findViewById(R.id.horizontal);
902         mVertical = findViewById(R.id.vertical);
903 
904         updateCurrentView();
905     }
906 
needsReorient(int rotation)907     boolean needsReorient(int rotation) {
908         return mCurrentRotation != rotation;
909     }
910 
updateCurrentRotation()911     private void updateCurrentRotation() {
912         final int rotation = mConfiguration.windowConfiguration.getDisplayRotation();
913         if (mCurrentRotation == rotation) {
914             return;
915         }
916         mCurrentRotation = rotation;
917         mNavigationInflaterView.setAlternativeOrder(mCurrentRotation == Surface.ROTATION_90);
918         mDeadZone.onConfigurationChanged(mCurrentRotation);
919         if (DEBUG) {
920             Log.d(TAG, "updateCurrentRotation(): rot=" + mCurrentRotation);
921         }
922     }
923 
updateCurrentView()924     private void updateCurrentView() {
925         resetViews();
926         mCurrentView = mIsVertical ? mVertical : mHorizontal;
927         mCurrentView.setVisibility(View.VISIBLE);
928         mNavigationInflaterView.setVertical(mIsVertical);
929         mNavigationInflaterView.updateButtonDispatchersCurrentView();
930         updateLayoutTransitionsEnabled();
931         updateCurrentRotation();
932     }
933 
resetViews()934     private void resetViews() {
935         mHorizontal.setVisibility(View.GONE);
936         mVertical.setVisibility(View.GONE);
937     }
938 
updateRecentsIcon()939     private void updateRecentsIcon() {
940         mDockedIcon.setRotation(mDockedStackExists && mIsVertical ? 90 : 0);
941         getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon);
942         mBarTransitions.reapplyDarkIntensity();
943     }
944 
showPinningEnterExitToast(boolean entering)945     public void showPinningEnterExitToast(boolean entering) {
946         if (entering) {
947             mScreenPinningNotify.showPinningStartToast();
948         } else {
949             mScreenPinningNotify.showPinningExitToast();
950         }
951     }
952 
showPinningEscapeToast()953     public void showPinningEscapeToast() {
954         mScreenPinningNotify.showEscapeToast(
955                 mNavBarMode == NAV_BAR_MODE_GESTURAL, isRecentsButtonVisible());
956     }
957 
isVertical()958     public boolean isVertical() {
959         return mIsVertical;
960     }
961 
reorient()962     public void reorient() {
963         updateCurrentView();
964         ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
965 
966         // force the low profile & disabled states into compliance
967         mBarTransitions.init();
968 
969         // Resolve layout direction if not resolved since components changing layout direction such
970         // as changing languages will recreate this view and the direction will be resolved later
971         if (!isLayoutDirectionResolved()) {
972             resolveLayoutDirection();
973         }
974         updateNavButtonIcons();
975 
976         getHomeButton().setVertical(mIsVertical);
977     }
978 
979     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)980     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
981         int w = MeasureSpec.getSize(widthMeasureSpec);
982         int h = MeasureSpec.getSize(heightMeasureSpec);
983         if (DEBUG) Log.d(TAG, String.format(
984                 "onMeasure: (%dx%d) old: (%dx%d)", w, h, getMeasuredWidth(), getMeasuredHeight()));
985 
986         final boolean newVertical = w > 0 && h > w
987                 && !isGesturalMode(mNavBarMode);
988         if (newVertical != mIsVertical) {
989             mIsVertical = newVertical;
990             if (DEBUG) {
991                 Log.d(TAG, String.format("onMeasure: h=%d, w=%d, vert=%s", h, w,
992                         mIsVertical ? "y" : "n"));
993             }
994             reorient();
995             notifyVerticalChangedListener(newVertical);
996         }
997 
998         if (isGesturalMode(mNavBarMode)) {
999             // Update the nav bar background to match the height of the visible nav bar
1000             int height = mIsVertical
1001                     ? getResources().getDimensionPixelSize(
1002                             com.android.internal.R.dimen.navigation_bar_height_landscape)
1003                     : getResources().getDimensionPixelSize(
1004                             com.android.internal.R.dimen.navigation_bar_height);
1005             int frameHeight = getResources().getDimensionPixelSize(
1006                     com.android.internal.R.dimen.navigation_bar_frame_height);
1007             mBarTransitions.setBackgroundFrame(new Rect(0, frameHeight - height, w, h));
1008         } else {
1009             mBarTransitions.setBackgroundFrame(null);
1010         }
1011 
1012         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
1013     }
1014 
getNavBarHeight()1015     int getNavBarHeight() {
1016         return mIsVertical
1017                 ? getResources().getDimensionPixelSize(
1018                 com.android.internal.R.dimen.navigation_bar_height_landscape)
1019                 : getResources().getDimensionPixelSize(
1020                         com.android.internal.R.dimen.navigation_bar_height);
1021     }
1022 
notifyVerticalChangedListener(boolean newVertical)1023     private void notifyVerticalChangedListener(boolean newVertical) {
1024         if (mOnVerticalChangedListener != null) {
1025             mOnVerticalChangedListener.onVerticalChanged(newVertical);
1026         }
1027     }
1028 
1029     @Override
onConfigurationChanged(Configuration newConfig)1030     protected void onConfigurationChanged(Configuration newConfig) {
1031         super.onConfigurationChanged(newConfig);
1032         mTmpLastConfiguration.updateFrom(mConfiguration);
1033         final int changes = mConfiguration.updateFrom(newConfig);
1034         mFloatingRotationButton.onConfigurationChanged(changes);
1035 
1036         boolean uiCarModeChanged = updateCarMode();
1037         updateIcons(mTmpLastConfiguration);
1038         updateRecentsIcon();
1039         updateCurrentRotation();
1040         mEdgeBackGestureHandler.onConfigurationChanged(mConfiguration);
1041         if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi
1042                 || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) {
1043             // If car mode or density changes, we need to reset the icons.
1044             updateNavButtonIcons();
1045         }
1046     }
1047 
1048     /**
1049      * If the configuration changed, update the carmode and return that it was updated.
1050      */
updateCarMode()1051     private boolean updateCarMode() {
1052         boolean uiCarModeChanged = false;
1053         if (mConfiguration != null) {
1054             int uiMode = mConfiguration.uiMode & Configuration.UI_MODE_TYPE_MASK;
1055             final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR);
1056 
1057             if (isCarMode != mInCarMode) {
1058                 mInCarMode = isCarMode;
1059                 if (ALTERNATE_CAR_MODE_UI) {
1060                     mUseCarModeUi = isCarMode;
1061                     uiCarModeChanged = true;
1062                 } else {
1063                     // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set.
1064                     mUseCarModeUi = false;
1065                 }
1066             }
1067         }
1068         return uiCarModeChanged;
1069     }
1070 
getResourceName(int resId)1071     private String getResourceName(int resId) {
1072         if (resId != 0) {
1073             final android.content.res.Resources res = getContext().getResources();
1074             try {
1075                 return res.getResourceName(resId);
1076             } catch (android.content.res.Resources.NotFoundException ex) {
1077                 return "(unknown)";
1078             }
1079         } else {
1080             return "(null)";
1081         }
1082     }
1083 
visibilityToString(int vis)1084     private static String visibilityToString(int vis) {
1085         switch (vis) {
1086             case View.INVISIBLE:
1087                 return "INVISIBLE";
1088             case View.GONE:
1089                 return "GONE";
1090             default:
1091                 return "VISIBLE";
1092         }
1093     }
1094 
1095     @Override
onAttachedToWindow()1096     protected void onAttachedToWindow() {
1097         super.onAttachedToWindow();
1098         requestApplyInsets();
1099         reorient();
1100         if (mRotationButtonController != null) {
1101             mRotationButtonController.registerListeners(false /* registerRotationWatcher */);
1102         }
1103 
1104         updateNavButtonIcons();
1105     }
1106 
1107     @Override
onDetachedFromWindow()1108     protected void onDetachedFromWindow() {
1109         super.onDetachedFromWindow();
1110         for (int i = 0; i < mButtonDispatchers.size(); ++i) {
1111             mButtonDispatchers.valueAt(i).onDestroy();
1112         }
1113         if (mRotationButtonController != null) {
1114             mFloatingRotationButton.hide();
1115             mRotationButtonController.unregisterListeners();
1116         }
1117     }
1118 
dump(PrintWriter pw)1119     void dump(PrintWriter pw) {
1120         final Rect r = new Rect();
1121         final Point size = new Point();
1122         getContextDisplay().getRealSize(size);
1123 
1124         pw.println("NavigationBarView:");
1125         pw.println(String.format("      this: " + CentralSurfaces.viewInfo(this)
1126                         + " " + visibilityToString(getVisibility())));
1127 
1128         getWindowVisibleDisplayFrame(r);
1129         final boolean offscreen = r.right > size.x || r.bottom > size.y;
1130         pw.println("      window: "
1131                 + r.toShortString()
1132                 + " " + visibilityToString(getWindowVisibility())
1133                 + (offscreen ? " OFFSCREEN!" : ""));
1134 
1135         pw.println(String.format("      mCurrentView: id=%s (%dx%d) %s %f",
1136                         getResourceName(getCurrentView().getId()),
1137                         getCurrentView().getWidth(), getCurrentView().getHeight(),
1138                         visibilityToString(getCurrentView().getVisibility()),
1139                         getCurrentView().getAlpha()));
1140 
1141         pw.println(String.format("      disabled=0x%08x vertical=%s darkIntensity=%.2f",
1142                         mDisabledFlags,
1143                         mIsVertical ? "true" : "false",
1144                         getLightTransitionsController().getCurrentDarkIntensity()));
1145 
1146         pw.println("    mScreenOn: " + mScreenOn);
1147 
1148 
1149         dumpButton(pw, "back", getBackButton());
1150         dumpButton(pw, "home", getHomeButton());
1151         dumpButton(pw, "handle", getHomeHandle());
1152         dumpButton(pw, "rcnt", getRecentsButton());
1153         dumpButton(pw, "rota", getRotateSuggestionButton());
1154         dumpButton(pw, "a11y", getAccessibilityButton());
1155         dumpButton(pw, "ime", getImeSwitchButton());
1156 
1157         if (mNavigationInflaterView != null) {
1158             mNavigationInflaterView.dump(pw);
1159         }
1160         mBarTransitions.dump(pw);
1161         mContextualButtonGroup.dump(pw);
1162         mEdgeBackGestureHandler.dump(pw);
1163     }
1164 
1165     @Override
onApplyWindowInsets(WindowInsets insets)1166     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
1167         int leftInset = insets.getSystemWindowInsetLeft();
1168         int rightInset = insets.getSystemWindowInsetRight();
1169         setPadding(leftInset, insets.getSystemWindowInsetTop(), rightInset,
1170                 insets.getSystemWindowInsetBottom());
1171         // we're passing the insets onto the gesture handler since the back arrow is only
1172         // conditionally added and doesn't always get all the insets.
1173         mEdgeBackGestureHandler.setInsets(leftInset, rightInset);
1174 
1175         // this allows assist handle to be drawn outside its bound so that it can align screen
1176         // bottom by translating its y position.
1177         final boolean shouldClip =
1178                 !isGesturalMode(mNavBarMode) || insets.getSystemWindowInsetBottom() == 0;
1179         setClipChildren(shouldClip);
1180         setClipToPadding(shouldClip);
1181 
1182         return super.onApplyWindowInsets(insets);
1183     }
1184 
addPipExclusionBoundsChangeListener(Pip pip)1185     void addPipExclusionBoundsChangeListener(Pip pip) {
1186         pip.addPipExclusionBoundsChangeListener(mPipListener);
1187     }
1188 
removePipExclusionBoundsChangeListener(Pip pip)1189     void removePipExclusionBoundsChangeListener(Pip pip) {
1190         pip.removePipExclusionBoundsChangeListener(mPipListener);
1191     }
1192 
registerBackAnimation(BackAnimation backAnimation)1193     void registerBackAnimation(BackAnimation backAnimation) {
1194         mEdgeBackGestureHandler.setBackAnimation(backAnimation);
1195     }
1196 
dumpButton(PrintWriter pw, String caption, ButtonDispatcher button)1197     private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
1198         pw.print("      " + caption + ": ");
1199         if (button == null) {
1200             pw.print("null");
1201         } else {
1202             pw.print(visibilityToString(button.getVisibility())
1203                     + " alpha=" + button.getAlpha()
1204                     );
1205         }
1206         pw.println();
1207     }
1208 
1209     public interface OnVerticalChangedListener {
onVerticalChanged(boolean isVertical)1210         void onVerticalChanged(boolean isVertical);
1211     }
1212 
1213     private final Consumer<Boolean> mDockedListener = exists -> post(() -> {
1214         mDockedStackExists = exists;
1215         updateRecentsIcon();
1216     });
1217 
1218     private final Consumer<Rect> mPipListener = bounds -> post(() -> {
1219         mEdgeBackGestureHandler.setPipStashExclusionBounds(bounds);
1220     });
1221 
1222 
1223     interface UpdateActiveTouchRegionsCallback {
update()1224         void update();
1225     }
1226 }
1227