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