blob: f82b05e78ff9295abb2c78e7dc2b49236cfffd74 [file] [log] [blame]
Daniel Sandler8956dbb2011-04-22 07:55:02 -04001/*
2 * Copyright (C) 2008 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
17package com.android.systemui.statusbar.phone;
18
Winson Chungc4e06202018-02-13 10:37:35 -080019import static android.view.MotionEvent.ACTION_DOWN;
Tiger Huang44c04552018-11-29 20:06:45 +080020import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
Matthew Ngb831fb42019-01-30 11:20:48 -080021import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
22import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
Gus Prevasab336792018-11-14 13:52:20 -050023
24import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
25import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
Winson Chungc4e06202018-02-13 10:37:35 -080026import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
Matthew Ng472d3e42018-06-14 15:16:55 -070027import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
Winson Chungc4e06202018-02-13 10:37:35 -080028import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
29import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
Gus Prevasab336792018-11-14 13:52:20 -050030import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
31import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION;
Matthew Ngb55c02c2019-02-15 16:20:31 -080032import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_VIEWS;
Winson Chungc4e06202018-02-13 10:37:35 -080033
John Spurlock1bbd49d2012-10-19 11:09:32 -040034import android.animation.LayoutTransition;
John Spurlock56d007b2013-10-28 18:40:56 -040035import android.animation.LayoutTransition.TransitionListener;
36import android.animation.ObjectAnimator;
Matthew Ngca4592b2018-08-06 14:12:37 -070037import android.animation.PropertyValuesHolder;
John Spurlock56d007b2013-10-28 18:40:56 -040038import android.animation.TimeInterpolator;
39import android.animation.ValueAnimator;
Jorim Jaggi40db0292016-06-27 17:58:03 -070040import android.annotation.DrawableRes;
Matthew Ngb831fb42019-01-30 11:20:48 -080041import android.annotation.IntDef;
42import android.annotation.SuppressLint;
Daniel Sandler328310c2011-09-23 15:56:52 -040043import android.app.StatusBarManager;
Daniel Sandler8956dbb2011-04-22 07:55:02 -040044import android.content.Context;
Selim Cinek17a327a2014-08-25 15:03:48 +020045import android.content.res.Configuration;
Matthew Nga8f24262017-12-19 11:54:24 -080046import android.graphics.Canvas;
Jeff Browna8b9def2012-07-23 14:22:49 -070047import android.graphics.Point;
Daniel Sandler48852952011-12-01 14:34:23 -050048import android.graphics.Rect;
Sunny Goyalaac6c882019-02-11 11:57:58 -080049import android.graphics.Region;
50import android.graphics.Region.Op;
Matthew Ng44b14502018-05-04 11:10:14 -070051import android.os.Bundle;
Daniel Sandler0b69b592012-01-23 21:08:36 -050052import android.os.Handler;
53import android.os.Message;
Matthew Ng86a436e2018-10-26 16:00:53 -070054import android.os.RemoteException;
Matthew Ng8f25fb962018-01-16 17:17:24 -080055import android.os.SystemProperties;
Daniel Sandler8956dbb2011-04-22 07:55:02 -040056import android.util.AttributeSet;
John Spurlockcd686b52013-06-05 10:13:46 -040057import android.util.Log;
Matthew Ng86a436e2018-10-26 16:00:53 -070058import android.util.Slog;
Jason Monka2081822016-01-18 14:41:03 -050059import android.util.SparseArray;
Daniel Sandler8956dbb2011-04-22 07:55:02 -040060import android.view.Display;
Matthew Ngb831fb42019-01-30 11:20:48 -080061import android.view.Gravity;
Daniel Sandler1d4d30a2011-04-28 12:35:29 -040062import android.view.MotionEvent;
Daniel Sandler8956dbb2011-04-22 07:55:02 -040063import android.view.Surface;
John Spurlockde84f0e2013-06-12 12:41:00 -040064import android.view.View;
John Spurlock1bbd49d2012-10-19 11:09:32 -040065import android.view.ViewGroup;
Adrian Roos7e58a082018-03-09 16:40:56 +010066import android.view.WindowInsets;
Daniel Sandler8956dbb2011-04-22 07:55:02 -040067import android.view.WindowManager;
Matthew Ng86a436e2018-10-26 16:00:53 -070068import android.view.WindowManagerGlobal;
Matthew Ng44b14502018-05-04 11:10:14 -070069import android.view.accessibility.AccessibilityNodeInfo;
70import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
Jason Monkf1ff2092014-04-29 16:50:53 -040071import android.view.inputmethod.InputMethodManager;
Jason Monk86bc3312016-08-16 13:17:56 -040072import android.widget.FrameLayout;
Jason Monk67e6c802016-08-30 14:09:21 -040073
Jeff Chang2aaa91a2018-11-08 18:04:16 +080074import com.android.internal.annotations.VisibleForTesting;
Jason Monkde850bb2017-02-01 19:26:30 -050075import com.android.systemui.Dependency;
Jason Monkf85fc962017-04-19 17:13:41 -040076import com.android.systemui.DockedStackExistsListener;
Matthew Ngca4592b2018-08-06 14:12:37 -070077import com.android.systemui.Interpolators;
Daniel Sandler8956dbb2011-04-22 07:55:02 -040078import com.android.systemui.R;
Matthew Ng44b14502018-05-04 11:10:14 -070079import com.android.systemui.SysUiServiceProvider;
Matthew Ng0548fbc2019-01-11 12:24:13 -080080import com.android.systemui.assist.AssistManager;
Jason Monk67e6c802016-08-30 14:09:21 -040081import com.android.systemui.plugins.PluginListener;
Jason Monk67e6c802016-08-30 14:09:21 -040082import com.android.systemui.plugins.statusbar.phone.NavGesture;
83import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
Gus Prevasab336792018-11-14 13:52:20 -050084import com.android.systemui.recents.OverviewProxyService;
Matthew Ng44b14502018-05-04 11:10:14 -070085import com.android.systemui.recents.Recents;
Tony Wickham05c1f852018-02-06 12:32:54 -080086import com.android.systemui.recents.RecentsOnboarding;
Gus Prevasab336792018-11-14 13:52:20 -050087import com.android.systemui.shared.plugins.PluginManager;
Matthew Ng87cbf342018-02-15 12:21:18 -080088import com.android.systemui.shared.system.ActivityManagerWrapper;
Winson Chungc4e06202018-02-13 10:37:35 -080089import com.android.systemui.shared.system.NavigationBarCompat;
Matthew Ng84db1612018-04-17 16:48:21 -070090import com.android.systemui.shared.system.WindowManagerWrapper;
Matthew Ng1cf6eac2018-11-27 15:06:55 -080091import com.android.systemui.statusbar.phone.NavigationPrototypeController.GestureAction;
92import com.android.systemui.statusbar.phone.NavigationPrototypeController.OnPrototypeChangedListener;
Daniel Sandlerc26185b2012-08-29 15:49:53 -040093import com.android.systemui.statusbar.policy.DeadZone;
Jorim Jaggi40db0292016-06-27 17:58:03 -070094import com.android.systemui.statusbar.policy.KeyButtonDrawable;
Jorim Jaggi2fdeeab2015-04-01 15:13:03 -070095
John Spurlockde84f0e2013-06-12 12:41:00 -040096import java.io.FileDescriptor;
97import java.io.PrintWriter;
Matthew Ngb831fb42019-01-30 11:20:48 -080098import java.lang.annotation.Retention;
99import java.lang.annotation.RetentionPolicy;
Jason Monk32e3bb52017-07-27 12:35:41 -0400100import java.util.function.Consumer;
John Spurlockde84f0e2013-06-12 12:41:00 -0400101
Jason Monk67e6c802016-08-30 14:09:21 -0400102public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture> {
Daniel Sandler60ee2562011-07-22 12:34:33 -0400103 final static boolean DEBUG = false;
Adrian Roosa98b32c2016-08-11 10:41:08 -0700104 final static String TAG = "StatusBar/NavBarView";
Daniel Sandler60ee2562011-07-22 12:34:33 -0400105
Matthew Ngb831fb42019-01-30 11:20:48 -0800106 @Retention(RetentionPolicy.SOURCE)
107 @IntDef({WINDOW_TARGET_BOTTOM, WINDOW_TARGET_LEFT, WINDOW_TARGET_RIGHT})
108 public @interface WindowTarget{}
109 public static final int WINDOW_TARGET_BOTTOM = 0;
110 public static final int WINDOW_TARGET_LEFT = 1;
111 public static final int WINDOW_TARGET_RIGHT = 2;
112
Daniel Sandlerc3fc3222012-10-25 13:28:33 -0400113 // slippery nav bar when everything is disabled, e.g. during setup
John Spurlock7edfbca2013-09-14 11:58:55 -0400114 final static boolean SLIPPERY_WHEN_DISABLED = true;
Daniel Sandlerc3fc3222012-10-25 13:28:33 -0400115
Justin Paupore01915a12016-09-28 17:41:26 -0700116 final static boolean ALTERNATE_CAR_MODE_UI = false;
117
Daniel Sandler5c8da942011-06-28 00:29:04 -0400118 View mCurrentView = null;
Charles Chenaa94d95b2019-02-19 14:18:19 +0800119 private View mVertical;
120 private View mHorizontal;
Daniel Sandler60ee2562011-07-22 12:34:33 -0400121
Charles Chenaa94d95b2019-02-19 14:18:19 +0800122 /** Indicates that navigation bar is vertical. */
123 private boolean mIsVertical;
Adrian Roos090b7d82016-08-02 18:36:12 -0700124 private int mCurrentRotation = -1;
Daniel Sandler60ee2562011-07-22 12:34:33 -0400125
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800126 boolean mLongClickableAccessibilityButton;
Daniel Sandler6da2b762011-09-14 16:04:59 -0400127 int mDisabledFlags = 0;
Daniel Sandler328310c2011-09-23 15:56:52 -0400128 int mNavigationIconHints = 0;
Daniel Sandler8956dbb2011-04-22 07:55:02 -0400129
Winson Chungc4e06202018-02-13 10:37:35 -0800130 private @NavigationBarCompat.HitTarget int mDownHitTarget = HIT_TARGET_NONE;
Matthew Ngb831fb42019-01-30 11:20:48 -0800131 private @WindowTarget int mWindowHitTarget = WINDOW_TARGET_BOTTOM;
Winson Chungc4e06202018-02-13 10:37:35 -0800132 private Rect mHomeButtonBounds = new Rect();
133 private Rect mBackButtonBounds = new Rect();
Matthew Ng8a8c89c2018-03-01 13:21:43 -0800134 private Rect mRecentsButtonBounds = new Rect();
Winson Chung8079af72018-05-10 11:12:55 -0700135 private Rect mRotationButtonBounds = new Rect();
Sunny Goyalaac6c882019-02-11 11:57:58 -0800136 private final Region mActiveRegion = new Region();
Winson Chungc4e06202018-02-13 10:37:35 -0800137 private int[] mTmpPosition = new int[2];
138
Matthew Ngeb5ce832018-05-15 17:50:37 -0700139 private KeyButtonDrawable mBackIcon;
Matthew Ngec5b8412018-08-22 10:45:08 -0700140 private KeyButtonDrawable mHomeDefaultIcon;
Jorim Jaggi40db0292016-06-27 17:58:03 -0700141 private KeyButtonDrawable mRecentIcon;
142 private KeyButtonDrawable mDockedIcon;
Fabrice Di Meglio18d98242013-01-17 10:57:40 -0800143
Jason Monk67e6c802016-08-30 14:09:21 -0400144 private GestureHelper mGestureHelper;
Matthew Ng603b3292018-03-30 17:15:39 -0700145 private final DeadZone mDeadZone;
Matthew Ng1e14e962018-05-03 12:51:43 -0700146 private boolean mDeadZoneConsuming = false;
John Spurlock89835dd2013-08-16 15:06:51 -0400147 private final NavigationBarTransitions mBarTransitions;
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800148 private final OverviewProxyService mOverviewProxyService;
Jim Millere898ac52012-04-06 17:10:57 -0700149
Daniel Sandler0b69b592012-01-23 21:08:36 -0500150 // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
151 final static boolean WORKAROUND_INVALID_LAYOUT = true;
152 final static int MSG_CHECK_INVALID_LAYOUT = 8686;
153
John Spurlock56d007b2013-10-28 18:40:56 -0400154 // performs manual animation in sync with layout transitions
155 private final NavTransitionListener mTransitionListener = new NavTransitionListener();
156
Jorim Jaggif4797922014-08-04 22:49:41 +0200157 private OnVerticalChangedListener mOnVerticalChangedListener;
Jorim Jaggi37c11802015-08-18 20:27:54 -0700158 private boolean mLayoutTransitionsEnabled = true;
159 private boolean mWakeAndUnlocking;
Justin Paupore01915a12016-09-28 17:41:26 -0700160 private boolean mUseCarModeUi = false;
161 private boolean mInCarMode = false;
Jorim Jaggid30d95d2016-02-17 20:27:22 -0800162 private boolean mDockedStackExists;
Jorim Jaggif4797922014-08-04 22:49:41 +0200163
Felipe Leme15f915c2016-10-31 12:47:15 -0700164 private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
Matthew Ng25593cc2018-09-12 16:05:41 -0700165 private final ContextualButtonGroup mContextualButtonGroup;
Winsonfde2e6a2016-03-22 16:03:10 -0700166 private Configuration mConfiguration;
Matthew Ng8c056c12018-10-15 15:16:46 -0700167 private Configuration mTmpLastConfiguration;
Jason Monka2081822016-01-18 14:41:03 -0500168
Adrian Roosdb12b152016-07-12 15:38:55 -0700169 private NavigationBarInflaterView mNavigationInflaterView;
Tony Wickham05c1f852018-02-06 12:32:54 -0800170 private RecentsOnboarding mRecentsOnboarding;
Matthew Ng78f88d12018-01-23 12:39:55 -0800171 private NotificationPanelView mPanelView;
Adrian Roosdb12b152016-07-12 15:38:55 -0700172
Matthew Ng355fe212018-12-14 17:42:38 -0800173 private NavBarTintController mColorAdaptionController;
Matthew Ng1cf6eac2018-11-27 15:06:55 -0800174 private NavigationPrototypeController mPrototypeController;
175 private NavigationGestureAction[] mDefaultGestureMap;
Matthew Ng86a436e2018-10-26 16:00:53 -0700176 private QuickScrubAction mQuickScrubAction;
177 private QuickStepAction mQuickStepAction;
178 private NavigationBackAction mBackAction;
Matthew Ngb9c84282018-12-06 17:15:27 -0800179 private QuickSwitchAction mQuickSwitchAction;
Matthew Ng0548fbc2019-01-11 12:24:13 -0800180 private NavigationAssistantAction mAssistantAction;
Matthew Ngf8869462019-01-18 13:31:47 -0800181 private NavigationNotificationPanelAction mNotificationPanelAction;
Matthew Ng86a436e2018-10-26 16:00:53 -0700182
Matthew Ngb831fb42019-01-30 11:20:48 -0800183 private NavigationBarEdgePanel mLeftEdgePanel;
184 private NavigationBarEdgePanel mRightEdgePanel;
185
Matthew Ng20136e62018-05-30 12:20:31 -0700186 /**
187 * Helper that is responsible for showing the right toast when a disallowed activity operation
188 * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
189 * fully locked mode we only show that unlocking is blocked.
190 */
191 private ScreenPinningNotify mScreenPinningNotify;
192
John Spurlock56d007b2013-10-28 18:40:56 -0400193 private class NavTransitionListener implements TransitionListener {
194 private boolean mBackTransitioning;
195 private boolean mHomeAppearing;
196 private long mStartDelay;
197 private long mDuration;
198 private TimeInterpolator mInterpolator;
199
200 @Override
201 public void startTransition(LayoutTransition transition, ViewGroup container,
202 View view, int transitionType) {
203 if (view.getId() == R.id.back) {
204 mBackTransitioning = true;
205 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
206 mHomeAppearing = true;
207 mStartDelay = transition.getStartDelay(transitionType);
208 mDuration = transition.getDuration(transitionType);
209 mInterpolator = transition.getInterpolator(transitionType);
210 }
211 }
212
213 @Override
214 public void endTransition(LayoutTransition transition, ViewGroup container,
215 View view, int transitionType) {
216 if (view.getId() == R.id.back) {
217 mBackTransitioning = false;
218 } else if (view.getId() == R.id.home && transitionType == LayoutTransition.APPEARING) {
219 mHomeAppearing = false;
220 }
221 }
222
223 public void onBackAltCleared() {
Jason Monka2081822016-01-18 14:41:03 -0500224 ButtonDispatcher backButton = getBackButton();
Anthony Chenada13042016-01-19 16:57:20 -0800225
John Spurlock56d007b2013-10-28 18:40:56 -0400226 // When dismissing ime during unlock, force the back button to run the same appearance
227 // animation as home (if we catch this condition early enough).
Jason Monka2081822016-01-18 14:41:03 -0500228 if (!mBackTransitioning && backButton.getVisibility() == VISIBLE
229 && mHomeAppearing && getHomeButton().getAlpha() == 0) {
John Spurlock56d007b2013-10-28 18:40:56 -0400230 getBackButton().setAlpha(0);
Anthony Chenada13042016-01-19 16:57:20 -0800231 ValueAnimator a = ObjectAnimator.ofFloat(backButton, "alpha", 0, 1);
John Spurlock56d007b2013-10-28 18:40:56 -0400232 a.setStartDelay(mStartDelay);
233 a.setDuration(mDuration);
234 a.setInterpolator(mInterpolator);
235 a.start();
236 }
237 }
238 }
239
Jason Monkf1ff2092014-04-29 16:50:53 -0400240 private final OnClickListener mImeSwitcherClickListener = new OnClickListener() {
241 @Override
242 public void onClick(View view) {
lumark0b05f9e2018-11-26 15:09:06 +0800243 mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
244 true /* showAuxiliarySubtypes */, getContext().getDisplayId());
Jason Monkf1ff2092014-04-29 16:50:53 -0400245 }
246 };
247
Matthew Ngb831fb42019-01-30 11:20:48 -0800248 private final OnTouchListener mEdgePanelTouchListener = new OnTouchListener() {
249 @SuppressLint("ClickableViewAccessibility")
250 @Override
251 public boolean onTouch(View v, MotionEvent event) {
252 if (event.getActionMasked() == ACTION_DOWN) {
253 mWindowHitTarget = v == mLeftEdgePanel ? WINDOW_TARGET_LEFT : WINDOW_TARGET_RIGHT;
254 mDownHitTarget = HIT_TARGET_NONE;
255 }
256 return mGestureHelper.onTouchEvent(event);
257 }
258 };
259
Daniel Sandler0b69b592012-01-23 21:08:36 -0500260 private class H extends Handler {
261 public void handleMessage(Message m) {
262 switch (m.what) {
263 case MSG_CHECK_INVALID_LAYOUT:
264 final String how = "" + m.obj;
265 final int w = getWidth();
266 final int h = getHeight();
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800267 final int vw = getCurrentView().getWidth();
268 final int vh = getCurrentView().getHeight();
Daniel Sandler0b69b592012-01-23 21:08:36 -0500269
270 if (h != vh || w != vw) {
John Spurlockcd686b52013-06-05 10:13:46 -0400271 Log.w(TAG, String.format(
Daniel Sandler0b69b592012-01-23 21:08:36 -0500272 "*** Invalid layout in navigation bar (%s this=%dx%d cur=%dx%d)",
273 how, w, h, vw, vh));
274 if (WORKAROUND_INVALID_LAYOUT) {
275 requestLayout();
276 }
277 }
278 break;
279 }
280 }
281 }
282
Matthew Ng44b14502018-05-04 11:10:14 -0700283 private final AccessibilityDelegate mQuickStepAccessibilityDelegate
284 = new AccessibilityDelegate() {
285 private AccessibilityAction mToggleOverviewAction;
286
287 @Override
288 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
289 super.onInitializeAccessibilityNodeInfo(host, info);
290 if (mToggleOverviewAction == null) {
291 mToggleOverviewAction = new AccessibilityAction(R.id.action_toggle_overview,
292 getContext().getString(R.string.quick_step_accessibility_toggle_overview));
293 }
294 info.addAction(mToggleOverviewAction);
295 }
296
297 @Override
298 public boolean performAccessibilityAction(View host, int action, Bundle args) {
Jason Monk05dd5672018-08-09 09:38:21 -0400299 if (action == R.id.action_toggle_overview) {
300 SysUiServiceProvider.getComponent(getContext(), Recents.class)
301 .toggleRecentApps();
302 } else {
303 return super.performAccessibilityAction(host, action, args);
Matthew Ng44b14502018-05-04 11:10:14 -0700304 }
305 return true;
306 }
307 };
308
Matthew Ng1cf6eac2018-11-27 15:06:55 -0800309 private OnPrototypeChangedListener mPrototypeListener = new OnPrototypeChangedListener() {
310 @Override
311 public void onGestureRemap(int[] actions) {
312 updateNavigationGestures();
313 }
314
315 @Override
316 public void onBackButtonVisibilityChanged(boolean visible) {
Matthew Ng8a10fb52018-12-20 16:43:56 -0800317 if (!inScreenPinning()) {
318 getBackButton().setVisibility(visible ? VISIBLE : GONE);
319 }
Matthew Ng1cf6eac2018-11-27 15:06:55 -0800320 }
Matthew Ng355fe212018-12-14 17:42:38 -0800321
322 @Override
Matthew Ngb8c30bb2018-12-21 15:25:48 -0800323 public void onHomeButtonVisibilityChanged(boolean visible) {
324 getHomeButton().setVisibility(visible ? VISIBLE : GONE);
325 }
326
327 @Override
Matthew Ng355fe212018-12-14 17:42:38 -0800328 public void onColorAdaptChanged(boolean enabled) {
329 if (enabled) {
330 mColorAdaptionController.start();
331 } else {
332 mColorAdaptionController.end();
333 }
334 }
Matthew Ngb831fb42019-01-30 11:20:48 -0800335
336 @Override
337 public void onEdgeSensitivityChanged(int width, int height) {
338 if (mLeftEdgePanel != null) {
339 mLeftEdgePanel.setDimensions(width, height);
340 }
341 if (mRightEdgePanel != null) {
342 mRightEdgePanel.setDimensions(width, height);
343 }
344 }
Matthew Ngb55c02c2019-02-15 16:20:31 -0800345
346 @Override
347 public void onHomeHandleVisiblilityChanged(boolean visible) {
348 showHomeHandle(visible);
349 }
Matthew Ng1cf6eac2018-11-27 15:06:55 -0800350 };
351
John Spurlocke932e302013-08-12 10:16:29 -0400352 public NavigationBarView(Context context, AttributeSet attrs) {
353 super(context, attrs);
354
Charles Chenaa94d95b2019-02-19 14:18:19 +0800355 mIsVertical = false;
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800356 mLongClickableAccessibilityButton = false;
357
Matthew Ng25593cc2018-09-12 16:05:41 -0700358 // Set up the context group of buttons
359 mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
360 final ContextualButton menuButton = new ContextualButton(R.id.menu,
361 R.drawable.ic_sysbar_menu);
362 final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
363 R.drawable.ic_ime_switcher_default);
364 final RotationContextButton rotateSuggestionButton = new RotationContextButton(
Matthew Ng761562d2018-09-17 11:13:21 -0700365 R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button, getContext(),
Matthew Ng25593cc2018-09-12 16:05:41 -0700366 R.style.RotateButtonCCWStart90);
367 final ContextualButton accessibilityButton =
368 new ContextualButton(R.id.accessibility_button,
369 R.drawable.ic_sysbar_accessibility_button);
370 mContextualButtonGroup.addButton(menuButton);
371 mContextualButtonGroup.addButton(imeSwitcherButton);
372 mContextualButtonGroup.addButton(rotateSuggestionButton);
373 mContextualButtonGroup.addButton(accessibilityButton);
374
Matthew Ngf2946542018-02-12 14:13:18 -0800375 mOverviewProxyService = Dependency.get(OverviewProxyService.class);
376 mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
377
Winsonfde2e6a2016-03-22 16:03:10 -0700378 mConfiguration = new Configuration();
Matthew Ng8c056c12018-10-15 15:16:46 -0700379 mTmpLastConfiguration = new Configuration();
Winsonfde2e6a2016-03-22 16:03:10 -0700380 mConfiguration.updateFrom(context.getResources().getConfiguration());
John Spurlocke932e302013-08-12 10:16:29 -0400381
Matthew Ng20136e62018-05-30 12:20:31 -0700382 mScreenPinningNotify = new ScreenPinningNotify(mContext);
John Spurlock7edfbca2013-09-14 11:58:55 -0400383 mBarTransitions = new NavigationBarTransitions(this);
Jason Monka2081822016-01-18 14:41:03 -0500384
Felipe Leme15f915c2016-10-31 12:47:15 -0700385 mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
386 mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
Matthew Ngb55c02c2019-02-15 16:20:31 -0800387 mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle));
Felipe Leme15f915c2016-10-31 12:47:15 -0700388 mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
Matthew Ng25593cc2018-09-12 16:05:41 -0700389 mButtonDispatchers.put(R.id.menu, menuButton);
390 mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton);
391 mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);
392 mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton);
393 mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
Matthew Ng603b3292018-03-30 17:15:39 -0700394 mDeadZone = new DeadZone(this);
Matthew Ng86a436e2018-10-26 16:00:53 -0700395
396 mQuickScrubAction = new QuickScrubAction(this, mOverviewProxyService);
397 mQuickStepAction = new QuickStepAction(this, mOverviewProxyService);
398 mBackAction = new NavigationBackAction(this, mOverviewProxyService);
Matthew Ngb9c84282018-12-06 17:15:27 -0800399 mQuickSwitchAction = new QuickSwitchAction(this, mOverviewProxyService);
Matthew Ng1cf6eac2018-11-27 15:06:55 -0800400 mDefaultGestureMap = new NavigationGestureAction[] {
401 mQuickStepAction, null /* swipeDownAction*/, null /* swipeLeftAction */,
Matthew Ngb9c84282018-12-06 17:15:27 -0800402 mQuickScrubAction, null /* swipeLeftEdgeAction */, null /* swipeRightEdgeAction */
Matthew Ng1cf6eac2018-11-27 15:06:55 -0800403 };
404
405 mPrototypeController = new NavigationPrototypeController(mHandler, mContext);
406 mPrototypeController.register();
407 mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
Matthew Ng355fe212018-12-14 17:42:38 -0800408 mColorAdaptionController = new NavBarTintController(this, getLightTransitionsController());
409 }
410
411 public NavBarTintController getColorAdaptionController() {
412 return mColorAdaptionController;
John Spurlocke932e302013-08-12 10:16:29 -0400413 }
414
415 public BarTransitions getBarTransitions() {
416 return mBarTransitions;
417 }
418
Jorim Jaggi40db0292016-06-27 17:58:03 -0700419 public LightBarTransitionsController getLightTransitionsController() {
420 return mBarTransitions.getLightTransitionsController();
421 }
422
Matthew Ng0548fbc2019-01-11 12:24:13 -0800423 public void setComponents(NotificationPanelView panel, AssistManager assistManager) {
Matthew Ng78f88d12018-01-23 12:39:55 -0800424 mPanelView = panel;
Matthew Ng0548fbc2019-01-11 12:24:13 -0800425 if (mAssistantAction == null) {
426 mAssistantAction = new NavigationAssistantAction(this, mOverviewProxyService,
427 assistManager);
428 }
Matthew Ngf8869462019-01-18 13:31:47 -0800429 if (mNotificationPanelAction == null) {
430 mNotificationPanelAction = new NavigationNotificationPanelAction(this,
431 mOverviewProxyService, panel);
432 }
Winson Chung0d3815d2018-09-25 18:11:54 -0700433 if (mGestureHelper instanceof QuickStepController) {
434 ((QuickStepController) mGestureHelper).setComponents(this);
Matthew Ng1cf6eac2018-11-27 15:06:55 -0800435 updateNavigationGestures();
436 }
437 }
438
439 private void updateNavigationGestures() {
440 if (mGestureHelper instanceof QuickStepController) {
441 final int[] assignedMap = mPrototypeController.getGestureActionMap();
442 ((QuickStepController) mGestureHelper).setGestureActions(
443 getNavigationActionFromType(assignedMap[0], mDefaultGestureMap[0]),
444 getNavigationActionFromType(assignedMap[1], mDefaultGestureMap[1]),
445 getNavigationActionFromType(assignedMap[2], mDefaultGestureMap[2]),
Matthew Ngb9c84282018-12-06 17:15:27 -0800446 getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3]),
447 getNavigationActionFromType(assignedMap[4], mDefaultGestureMap[4]),
448 getNavigationActionFromType(assignedMap[5], mDefaultGestureMap[5]));
Matthew Ng1cf6eac2018-11-27 15:06:55 -0800449 }
450 }
451
452 private NavigationGestureAction getNavigationActionFromType(@GestureAction int actionType,
453 NavigationGestureAction defaultAction) {
454 switch(actionType) {
455 case NavigationPrototypeController.ACTION_QUICKSTEP:
456 return mQuickStepAction;
457 case NavigationPrototypeController.ACTION_QUICKSCRUB:
458 return mQuickScrubAction;
459 case NavigationPrototypeController.ACTION_BACK:
460 return mBackAction;
Matthew Ngb9c84282018-12-06 17:15:27 -0800461 case NavigationPrototypeController.ACTION_QUICKSWITCH:
462 return mQuickSwitchAction;
Matthew Ng0548fbc2019-01-11 12:24:13 -0800463 case NavigationPrototypeController.ACTION_ASSISTANT:
464 return mAssistantAction;
Matthew Ngf8869462019-01-18 13:31:47 -0800465 case NavigationPrototypeController.ACTION_EXPAND_NOTIFICATION:
466 return mNotificationPanelAction;
Matthew Ng0548fbc2019-01-11 12:24:13 -0800467 case NavigationPrototypeController.ACTION_NOTHING:
468 return null;
Matthew Ng1cf6eac2018-11-27 15:06:55 -0800469 default:
470 return defaultAction;
Jason Monk67e6c802016-08-30 14:09:21 -0400471 }
Jim Millere898ac52012-04-06 17:10:57 -0700472 }
473
Jorim Jaggif4797922014-08-04 22:49:41 +0200474 public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
475 mOnVerticalChangedListener = onVerticalChangedListener;
Charles Chenaa94d95b2019-02-19 14:18:19 +0800476 notifyVerticalChangedListener(mIsVertical);
Jorim Jaggif4797922014-08-04 22:49:41 +0200477 }
478
Jim Millere898ac52012-04-06 17:10:57 -0700479 @Override
480 public boolean onInterceptTouchEvent(MotionEvent event) {
Matthew Ng472d3e42018-06-14 15:16:55 -0700481 final boolean deadZoneConsumed = shouldDeadZoneConsumeTouchEvents(event);
Winson Chungc4e06202018-02-13 10:37:35 -0800482 switch (event.getActionMasked()) {
483 case ACTION_DOWN:
484 int x = (int) event.getX();
485 int y = (int) event.getY();
486 mDownHitTarget = HIT_TARGET_NONE;
Matthew Ngb831fb42019-01-30 11:20:48 -0800487 mWindowHitTarget = WINDOW_TARGET_BOTTOM;
Matthew Ng472d3e42018-06-14 15:16:55 -0700488 if (deadZoneConsumed) {
489 mDownHitTarget = HIT_TARGET_DEAD_ZONE;
490 } else if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) {
Winson Chungc4e06202018-02-13 10:37:35 -0800491 mDownHitTarget = HIT_TARGET_BACK;
Winson Chungc6c9d402018-05-25 18:25:52 -0700492 } else if (getHomeButton().isVisible() && mHomeButtonBounds.contains(x, y)) {
Winson Chungc4e06202018-02-13 10:37:35 -0800493 mDownHitTarget = HIT_TARGET_HOME;
Winson Chungc6c9d402018-05-25 18:25:52 -0700494 } else if (getRecentsButton().isVisible() && mRecentsButtonBounds.contains(x, y)) {
Matthew Ng8a8c89c2018-03-01 13:21:43 -0800495 mDownHitTarget = HIT_TARGET_OVERVIEW;
Winson Chungc6c9d402018-05-25 18:25:52 -0700496 } else if (getRotateSuggestionButton().isVisible()
497 && mRotationButtonBounds.contains(x, y)) {
Winson Chung8079af72018-05-10 11:12:55 -0700498 mDownHitTarget = HIT_TARGET_ROTATION;
Winson Chungc4e06202018-02-13 10:37:35 -0800499 }
500 break;
501 }
Winson Chung49658842018-02-08 12:52:21 -0800502 return mGestureHelper.onInterceptTouchEvent(event);
Jim Millere898ac52012-04-06 17:10:57 -0700503 }
504
Winson Chung4faf38a2018-02-06 08:53:37 -0800505 @Override
506 public boolean onTouchEvent(MotionEvent event) {
Matthew Ng472d3e42018-06-14 15:16:55 -0700507 shouldDeadZoneConsumeTouchEvents(event);
Jason Monk297c04e2018-08-23 17:16:59 -0400508 if (mGestureHelper != null && mGestureHelper.onTouchEvent(event)) {
Winson Chung4faf38a2018-02-06 08:53:37 -0800509 return true;
510 }
Winson Chung49658842018-02-08 12:52:21 -0800511 return super.onTouchEvent(event);
Winson Chung4faf38a2018-02-06 08:53:37 -0800512 }
513
Matthew Ng1e14e962018-05-03 12:51:43 -0700514 private boolean shouldDeadZoneConsumeTouchEvents(MotionEvent event) {
515 if (mDeadZone.onTouchEvent(event) || mDeadZoneConsuming) {
516 switch (event.getActionMasked()) {
517 case MotionEvent.ACTION_DOWN:
518 // Allow gestures starting in the deadzone to be slippery
519 setSlippery(true);
520 mDeadZoneConsuming = true;
521 break;
522 case MotionEvent.ACTION_CANCEL:
523 case MotionEvent.ACTION_UP:
524 // When a gesture started in the deadzone is finished, restore slippery state
525 updateSlippery();
526 mDeadZoneConsuming = false;
527 break;
528 }
529 return true;
530 }
531 return false;
532 }
533
Winson Chungc4e06202018-02-13 10:37:35 -0800534 public @NavigationBarCompat.HitTarget int getDownHitTarget() {
535 return mDownHitTarget;
536 }
537
Matthew Ngb831fb42019-01-30 11:20:48 -0800538 public @WindowTarget int getWindowTarget() {
539 return mWindowHitTarget;
540 }
541
Xiyuan Xiaee7dd052015-05-15 09:46:27 -0700542 public void abortCurrentGesture() {
Jason Monka2081822016-01-18 14:41:03 -0500543 getHomeButton().abortCurrentGesture();
Xiyuan Xiaee7dd052015-05-15 09:46:27 -0700544 }
545
Daniel Sandler0b69b592012-01-23 21:08:36 -0500546 private H mHandler = new H();
547
John Spurlock7edfbca2013-09-14 11:58:55 -0400548 public View getCurrentView() {
549 return mCurrentView;
550 }
551
Jason Monka2081822016-01-18 14:41:03 -0500552 public ButtonDispatcher getRecentsButton() {
Felipe Leme15f915c2016-10-31 12:47:15 -0700553 return mButtonDispatchers.get(R.id.recent_apps);
Daniel Sandler5c8da942011-06-28 00:29:04 -0400554 }
555
Jason Monka2081822016-01-18 14:41:03 -0500556 public ButtonDispatcher getMenuButton() {
Felipe Leme15f915c2016-10-31 12:47:15 -0700557 return mButtonDispatchers.get(R.id.menu);
Daniel Sandler5c8da942011-06-28 00:29:04 -0400558 }
559
Jason Monka2081822016-01-18 14:41:03 -0500560 public ButtonDispatcher getBackButton() {
Felipe Leme15f915c2016-10-31 12:47:15 -0700561 return mButtonDispatchers.get(R.id.back);
Mike Lockwoode3646dd2011-09-01 12:46:28 -0400562 }
563
Jason Monka2081822016-01-18 14:41:03 -0500564 public ButtonDispatcher getHomeButton() {
Felipe Leme15f915c2016-10-31 12:47:15 -0700565 return mButtonDispatchers.get(R.id.home);
Mike Lockwoode3646dd2011-09-01 12:46:28 -0400566 }
567
Jason Monka2081822016-01-18 14:41:03 -0500568 public ButtonDispatcher getImeSwitchButton() {
Felipe Leme15f915c2016-10-31 12:47:15 -0700569 return mButtonDispatchers.get(R.id.ime_switcher);
Jason Monkf1ff2092014-04-29 16:50:53 -0400570 }
571
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800572 public ButtonDispatcher getAccessibilityButton() {
573 return mButtonDispatchers.get(R.id.accessibility_button);
574 }
575
Matthew Ng761562d2018-09-17 11:13:21 -0700576 public RotationContextButton getRotateSuggestionButton() {
577 return (RotationContextButton) mContextualButtonGroup
578 .getContextButton(R.id.rotate_suggestion);
Mike Digman7d092772018-01-11 12:10:32 -0800579 }
580
Matthew Ngb55c02c2019-02-15 16:20:31 -0800581 public ButtonDispatcher getHomeHandle() {
582 return mButtonDispatchers.get(R.id.home_handle);
583 }
584
Jorim Jaggi40db0292016-06-27 17:58:03 -0700585 public SparseArray<ButtonDispatcher> getButtonDispatchers() {
586 return mButtonDispatchers;
587 }
588
Matthew Ng9c3bce52018-02-01 22:00:31 +0000589 public boolean isRecentsButtonVisible() {
590 return getRecentsButton().getVisibility() == View.VISIBLE;
591 }
592
Matthew Ng6ff33b72018-02-27 13:47:38 -0800593 public boolean isOverviewEnabled() {
594 return (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) == 0;
595 }
596
Matthew Ng8f25fb962018-01-16 17:17:24 -0800597 public boolean isQuickStepSwipeUpEnabled() {
Matthew Ngc1a97b12018-03-28 14:02:00 -0700598 return mOverviewProxyService.shouldShowSwipeUpUI() && isOverviewEnabled();
Matthew Ng8f25fb962018-01-16 17:17:24 -0800599 }
600
601 public boolean isQuickScrubEnabled() {
602 return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true)
Winson Chungd95a2252018-04-04 17:02:29 +0000603 && mOverviewProxyService.isEnabled() && isOverviewEnabled()
Matthew Ngc1a97b12018-03-28 14:02:00 -0700604 && ((mOverviewProxyService.getInteractionFlags() & FLAG_DISABLE_QUICK_SCRUB) == 0);
Matthew Ng8f25fb962018-01-16 17:17:24 -0800605 }
606
Matthew Ngd0a73e72018-03-02 15:16:03 -0800607 private void reloadNavIcons() {
Matthew Ng8c056c12018-10-15 15:16:46 -0700608 updateIcons(Configuration.EMPTY);
Matthew Ngd0a73e72018-03-02 15:16:03 -0800609 }
610
Matthew Ng8c056c12018-10-15 15:16:46 -0700611 private void updateIcons(Configuration oldConfig) {
612 final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation;
613 final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi;
614 final boolean dirChange = oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection();
Matthew Ngca4592b2018-08-06 14:12:37 -0700615
616 if (orientationChange || densityChange) {
Matthew Ngd6865ba2018-08-27 17:58:41 -0700617 mDockedIcon = getDrawable(R.drawable.ic_sysbar_docked);
618 mHomeDefaultIcon = getHomeDrawable();
Winsonfde2e6a2016-03-22 16:03:10 -0700619 }
Matthew Ngca4592b2018-08-06 14:12:37 -0700620 if (densityChange || dirChange) {
Matthew Ngd6865ba2018-08-27 17:58:41 -0700621 mRecentIcon = getDrawable(R.drawable.ic_sysbar_recent);
Matthew Ng25593cc2018-09-12 16:05:41 -0700622 mContextualButtonGroup.updateIcons();
Winsonfde2e6a2016-03-22 16:03:10 -0700623 }
Matthew Ngca4592b2018-08-06 14:12:37 -0700624 if (orientationChange || densityChange || dirChange) {
Matthew Ngd6865ba2018-08-27 17:58:41 -0700625 mBackIcon = getBackDrawable();
Matthew Ngca4592b2018-08-06 14:12:37 -0700626 }
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700627 }
628
Matthew Ngd6865ba2018-08-27 17:58:41 -0700629 public KeyButtonDrawable getBackDrawable() {
630 KeyButtonDrawable drawable = chooseNavigationIconDrawable(R.drawable.ic_sysbar_back,
631 R.drawable.ic_sysbar_back_quick_step);
Matthew Ngeb5ce832018-05-15 17:50:37 -0700632 orientBackButton(drawable);
633 return drawable;
Matthew Ng87cbf342018-02-15 12:21:18 -0800634 }
635
Matthew Ngd6865ba2018-08-27 17:58:41 -0700636 public KeyButtonDrawable getHomeDrawable() {
Matthew Ngeb5ce832018-05-15 17:50:37 -0700637 final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI();
638 KeyButtonDrawable drawable = quickStepEnabled
Matthew Ngd6865ba2018-08-27 17:58:41 -0700639 ? getDrawable(R.drawable.ic_sysbar_home_quick_step)
640 : getDrawable(R.drawable.ic_sysbar_home);
Matthew Ngeb5ce832018-05-15 17:50:37 -0700641 orientHomeButton(drawable);
642 return drawable;
Matthew Ng87cbf342018-02-15 12:21:18 -0800643 }
644
Matthew Ngeb5ce832018-05-15 17:50:37 -0700645 private void orientBackButton(KeyButtonDrawable drawable) {
646 final boolean useAltBack =
Matthew Ngca4592b2018-08-06 14:12:37 -0700647 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
Matthew Ng8c056c12018-10-15 15:16:46 -0700648 final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
Matthew Ngca4592b2018-08-06 14:12:37 -0700649 float degrees = useAltBack
650 ? (isRtl ? 270 : -90)
651 : (isRtl ? 180 : 0);
652 if (drawable.getRotation() == degrees) {
653 return;
654 }
655
656 // Animate the back button's rotation to the new degrees and only in portrait move up the
657 // back button to line up with the other buttons
Charles Chenaa94d95b2019-02-19 14:18:19 +0800658 float targetY = !mOverviewProxyService.shouldShowSwipeUpUI() && !mIsVertical && useAltBack
Matthew Ngca4592b2018-08-06 14:12:37 -0700659 ? - getResources().getDimension(R.dimen.navbar_back_button_ime_offset)
660 : 0;
661 ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable,
662 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_ROTATE, degrees),
663 PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_TRANSLATE_Y, targetY));
664 navBarAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
665 navBarAnimator.setDuration(200);
666 navBarAnimator.start();
Matthew Ng87cbf342018-02-15 12:21:18 -0800667 }
668
Matthew Ngeb5ce832018-05-15 17:50:37 -0700669 private void orientHomeButton(KeyButtonDrawable drawable) {
Charles Chenaa94d95b2019-02-19 14:18:19 +0800670 drawable.setRotation(mIsVertical ? 90 : 0);
Matthew Ngeb5ce832018-05-15 17:50:37 -0700671 }
672
Matthew Ngd6865ba2018-08-27 17:58:41 -0700673 private KeyButtonDrawable chooseNavigationIconDrawable(@DrawableRes int icon,
674 @DrawableRes int quickStepIcon) {
Matthew Ngc1a97b12018-03-28 14:02:00 -0700675 final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI();
Matthew Ngd6865ba2018-08-27 17:58:41 -0700676 return quickStepEnabled ? getDrawable(quickStepIcon) : getDrawable(icon);
Matthew Ngeb5ce832018-05-15 17:50:37 -0700677 }
678
Matthew Ngd6865ba2018-08-27 17:58:41 -0700679 private KeyButtonDrawable getDrawable(@DrawableRes int icon) {
Matthew Ng25593cc2018-09-12 16:05:41 -0700680 return KeyButtonDrawable.create(mContext, icon, true /* hasShadow */);
Matthew Ngeb5ce832018-05-15 17:50:37 -0700681 }
682
Matthew Ngd6865ba2018-08-27 17:58:41 -0700683 private KeyButtonDrawable getDrawable(@DrawableRes int icon, boolean hasShadow) {
Matthew Ng25593cc2018-09-12 16:05:41 -0700684 return KeyButtonDrawable.create(mContext, icon, hasShadow);
Mike Digman7d092772018-01-11 12:10:32 -0800685 }
686
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700687 @Override
688 public void setLayoutDirection(int layoutDirection) {
Matthew Ngd0a73e72018-03-02 15:16:03 -0800689 reloadNavIcons();
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700690
691 super.setLayoutDirection(layoutDirection);
Daniel Sandler1d4d30a2011-04-28 12:35:29 -0400692 }
693
Matthew Ngd0a73e72018-03-02 15:16:03 -0800694 public void setNavigationIconHints(int hints) {
695 if (hints == mNavigationIconHints) return;
John Spurlock56d007b2013-10-28 18:40:56 -0400696 final boolean backAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
697 if ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0 && !backAlt) {
698 mTransitionListener.onBackAltCleared();
699 }
Daniel Sandler328310c2011-09-23 15:56:52 -0400700 if (DEBUG) {
John Spurlock01534782014-01-13 11:59:22 -0500701 android.widget.Toast.makeText(getContext(),
Daniel Sandler328310c2011-09-23 15:56:52 -0400702 "Navigation icon hints = " + hints,
703 500).show();
704 }
Daniel Sandler328310c2011-09-23 15:56:52 -0400705 mNavigationIconHints = hints;
Matthew Ngd0a73e72018-03-02 15:16:03 -0800706 updateNavButtonIcons();
707 }
Daniel Sandler328310c2011-09-23 15:56:52 -0400708
Matthew Ngd0a73e72018-03-02 15:16:03 -0800709 public void setDisabledFlags(int disabledFlags) {
710 if (mDisabledFlags == disabledFlags) return;
711
712 final boolean overviewEnabledBefore = isOverviewEnabled();
713 mDisabledFlags = disabledFlags;
714
715 // Update icons if overview was just enabled to ensure the correct icons are present
716 if (!overviewEnabledBefore && isOverviewEnabled()) {
717 reloadNavIcons();
718 }
719
720 updateNavButtonIcons();
Winson Chung1be4c7e2018-05-23 18:24:13 -0700721 updateSlippery();
Tracy Zhou98140912018-06-05 12:09:14 -0700722 setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
Matthew Ngd0a73e72018-03-02 15:16:03 -0800723 }
724
725 public void updateNavButtonIcons() {
Sriram Viswanathan9ebbe6a2015-11-16 17:59:22 -0800726 // We have to replace or restore the back and home button icons when exiting or entering
727 // carmode, respectively. Recents are not available in CarMode in nav bar so change
728 // to recent icon is not required.
Matthew Ngeb5ce832018-05-15 17:50:37 -0700729 final boolean useAltBack =
730 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
Matthew Ngd6865ba2018-08-27 17:58:41 -0700731 KeyButtonDrawable backIcon = mBackIcon;
Matthew Ngec5b8412018-08-22 10:45:08 -0700732 orientBackButton(backIcon);
Matthew Ngd6865ba2018-08-27 17:58:41 -0700733 KeyButtonDrawable homeIcon = mHomeDefaultIcon;
Matthew Ngeb5ce832018-05-15 17:50:37 -0700734 if (!mUseCarModeUi) {
Matthew Ngeb5ce832018-05-15 17:50:37 -0700735 orientHomeButton(homeIcon);
736 }
737 getHomeButton().setImageDrawable(homeIcon);
Jason Monka2081822016-01-18 14:41:03 -0500738 getBackButton().setImageDrawable(backIcon);
Sriram Viswanathan9ebbe6a2015-11-16 17:59:22 -0800739
Jorim Jaggid30d95d2016-02-17 20:27:22 -0800740 updateRecentsIcon();
Sriram Viswanathan9ebbe6a2015-11-16 17:59:22 -0800741
Mike Digmana48cf192018-02-12 17:52:48 -0800742 // Update IME button visibility, a11y and rotate button always overrides the appearance
Matthew Ng25593cc2018-09-12 16:05:41 -0700743 mContextualButtonGroup.setButtonVisiblity(R.id.ime_switcher,
744 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800745
Jorim Jaggi40db0292016-06-27 17:58:03 -0700746 mBarTransitions.reapplyDarkIntensity();
Daniel Sandler328310c2011-09-23 15:56:52 -0400747
Matthew Ngd0a73e72018-03-02 15:16:03 -0800748 boolean disableHome = ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
Sriram Viswanathan469caae2016-03-22 13:11:50 -0700749
Matthew Ng78ba6e22018-09-24 16:55:36 -0700750 // TODO(b/113914868): investigation log for disappearing home button
751 Log.i(TAG, "updateNavButtonIcons (b/113914868): home disabled=" + disableHome
752 + " mDisabledFlags=" + mDisabledFlags);
Matthew Ngb8c30bb2018-12-21 15:25:48 -0800753 disableHome |= mPrototypeController.hideHomeButton();
Matthew Ng78ba6e22018-09-24 16:55:36 -0700754
Jeff Chang2aaa91a2018-11-08 18:04:16 +0800755 // Always disable recents when alternate car mode UI is active and for secondary displays.
756 boolean disableRecent = isRecentsButtonDisabled();
Charles Hee57ff812017-10-26 10:01:35 +0100757
Matthew Ngb687c8a2018-10-02 11:31:38 -0700758 boolean disableBack = QuickStepController.shouldhideBackButton(getContext())
Matthew Ngc83b9892018-08-21 16:31:13 -0700759 || (((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0) && !useAltBack);
Daniel Sandler029d5872011-09-12 00:58:58 -0400760
Matthew Ng87cbf342018-02-15 12:21:18 -0800761 // When screen pinning, don't hide back and home when connected service or back and
762 // recents buttons when disconnected from launcher service in screen pinning mode,
763 // as they are used for exiting.
Matthew Ng6ff33b72018-02-27 13:47:38 -0800764 final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
Winson Chungd95a2252018-04-04 17:02:29 +0000765 if (mOverviewProxyService.isEnabled()) {
Matthew Ng87cbf342018-02-15 12:21:18 -0800766 // Use interaction flags to show/hide navigation buttons but will be shown if required
767 // to exit screen pinning.
Matthew Ng8f25fb962018-01-16 17:17:24 -0800768 final int flags = mOverviewProxyService.getInteractionFlags();
769 disableRecent |= (flags & FLAG_SHOW_OVERVIEW_BUTTON) == 0;
Matthew Ng6ff33b72018-02-27 13:47:38 -0800770 if (pinningActive) {
Matthew Ng87cbf342018-02-15 12:21:18 -0800771 disableBack = disableHome = false;
Matthew Ng87cbf342018-02-15 12:21:18 -0800772 }
Matthew Ng6ff33b72018-02-27 13:47:38 -0800773 } else if (pinningActive) {
Matthew Ng87cbf342018-02-15 12:21:18 -0800774 disableBack = disableRecent = false;
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800775 }
Charles Hee57ff812017-10-26 10:01:35 +0100776
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800777 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
John Spurlock56d007b2013-10-28 18:40:56 -0400778 if (navButtons != null) {
779 LayoutTransition lt = navButtons.getLayoutTransition();
John Spurlock1bbd49d2012-10-19 11:09:32 -0400780 if (lt != null) {
John Spurlock56d007b2013-10-28 18:40:56 -0400781 if (!lt.getTransitionListeners().contains(mTransitionListener)) {
782 lt.addTransitionListener(mTransitionListener);
783 }
John Spurlock1bbd49d2012-10-19 11:09:32 -0400784 }
785 }
786
Jason Monka2081822016-01-18 14:41:03 -0500787 getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
Jason Monk188908f2016-01-22 10:23:51 -0500788 getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
Jason Monka2081822016-01-18 14:41:03 -0500789 getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
John Spurlock56d007b2013-10-28 18:40:56 -0400790 }
791
Jeff Chang2aaa91a2018-11-08 18:04:16 +0800792 @VisibleForTesting
793 boolean isRecentsButtonDisabled() {
794 return mUseCarModeUi || !isOverviewEnabled()
795 || getContext().getDisplayId() != Display.DEFAULT_DISPLAY;
796 }
797
798 private Display getContextDisplay() {
799 return getContext().getDisplay();
800 }
801
Matthew Ng87cbf342018-02-15 12:21:18 -0800802 public boolean inScreenPinning() {
Matthew Ng6ff33b72018-02-27 13:47:38 -0800803 return ActivityManagerWrapper.getInstance().isScreenPinningActive();
Jason Monk17488b92014-11-06 11:26:14 -0500804 }
805
Jorim Jaggi37c11802015-08-18 20:27:54 -0700806 public void setLayoutTransitionsEnabled(boolean enabled) {
807 mLayoutTransitionsEnabled = enabled;
808 updateLayoutTransitionsEnabled();
Jorim Jaggi0d210f62015-07-10 14:24:44 -0700809 }
810
Jorim Jaggi37c11802015-08-18 20:27:54 -0700811 public void setWakeAndUnlocking(boolean wakeAndUnlocking) {
812 setUseFadingAnimations(wakeAndUnlocking);
813 mWakeAndUnlocking = wakeAndUnlocking;
814 updateLayoutTransitionsEnabled();
815 }
816
817 private void updateLayoutTransitionsEnabled() {
818 boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled;
Rakesh Iyer1186faa2015-12-07 16:48:46 -0800819 ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
Jorim Jaggi0d210f62015-07-10 14:24:44 -0700820 LayoutTransition lt = navButtons.getLayoutTransition();
Julia Reynolds8478aba2015-07-31 09:17:20 -0400821 if (lt != null) {
822 if (enabled) {
823 lt.enableTransitionType(LayoutTransition.APPEARING);
824 lt.enableTransitionType(LayoutTransition.DISAPPEARING);
825 lt.enableTransitionType(LayoutTransition.CHANGE_APPEARING);
826 lt.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
827 } else {
828 lt.disableTransitionType(LayoutTransition.APPEARING);
829 lt.disableTransitionType(LayoutTransition.DISAPPEARING);
830 lt.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
831 lt.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
832 }
Jorim Jaggi0d210f62015-07-10 14:24:44 -0700833 }
834 }
835
836 private void setUseFadingAnimations(boolean useFadingAnimations) {
Jason Monk49fa0162017-01-11 09:21:56 -0500837 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) ((ViewGroup) getParent())
838 .getLayoutParams();
Jorim Jaggi0d210f62015-07-10 14:24:44 -0700839 if (lp != null) {
840 boolean old = lp.windowAnimations != 0;
841 if (!old && useFadingAnimations) {
842 lp.windowAnimations = R.style.Animation_NavigationBarFadeIn;
843 } else if (old && !useFadingAnimations) {
844 lp.windowAnimations = 0;
845 } else {
846 return;
847 }
848 WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
Jason Monk49fa0162017-01-11 09:21:56 -0500849 wm.updateViewLayout((View) getParent(), lp);
Jorim Jaggi0d210f62015-07-10 14:24:44 -0700850 }
851 }
852
Matthew Ng2ea93b72018-03-14 19:43:18 +0000853 public void onNavigationButtonLongPress(View v) {
Jason Monk297c04e2018-08-23 17:16:59 -0400854 if (mGestureHelper != null) {
855 mGestureHelper.onNavigationButtonLongPress(v);
856 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000857 }
858
Matthew Ng78f88d12018-01-23 12:39:55 -0800859 public void onPanelExpandedChange(boolean expanded) {
860 updateSlippery();
861 }
862
Matthew Ng8f25fb962018-01-16 17:17:24 -0800863 public void updateStates() {
Matthew Ng44b14502018-05-04 11:10:14 -0700864 final boolean showSwipeUpUI = mOverviewProxyService.shouldShowSwipeUpUI();
Mike Digmanf77fb912018-05-21 11:21:30 -0700865
866 if (mNavigationInflaterView != null) {
Matthew Ngb55c02c2019-02-15 16:20:31 -0800867 if (mPrototypeController.showHomeHandle()) {
868 showHomeHandle(true /* visible */);
869 }
870
Mike Digmanf77fb912018-05-21 11:21:30 -0700871 // Reinflate the navbar if needed, no-op unless the swipe up state changes
872 mNavigationInflaterView.onLikelyDefaultLayoutChange();
873 }
874
Matthew Ng8f25fb962018-01-16 17:17:24 -0800875 updateSlippery();
Winson Chung770d7e92018-03-20 17:51:39 -0700876 reloadNavIcons();
Matthew Ngd0a73e72018-03-02 15:16:03 -0800877 updateNavButtonIcons();
Winson Chungf9e30272018-03-26 17:25:36 -0700878 setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
Matthew Ng44b14502018-05-04 11:10:14 -0700879 WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled(!showSwipeUpUI);
880 getHomeButton().setAccessibilityDelegate(
881 showSwipeUpUI ? mQuickStepAccessibilityDelegate : null);
Matthew Ng8f25fb962018-01-16 17:17:24 -0800882 }
883
Matthew Ng90ef0632018-08-15 13:53:15 -0700884 public boolean isNotificationsFullyCollapsed() {
885 return mPanelView.isFullyCollapsed();
886 }
887
888 /**
889 * Updates the {@link WindowManager.LayoutParams.FLAG_SLIPPERY} state dependent on if swipe up
890 * is enabled, or the notifications is fully opened without being in an animated state. If
891 * slippery is enabled, touch events will leave the nav bar window and enter into the fullscreen
892 * app/home window, if not nav bar will receive a cancelled touch event once gesture leaves bar.
893 */
Matthew Ng472d3e42018-06-14 15:16:55 -0700894 public void updateSlippery() {
Matthew Ng90ef0632018-08-15 13:53:15 -0700895 setSlippery(!isQuickStepSwipeUpEnabled() ||
896 (mPanelView.isFullyExpanded() && !mPanelView.isCollapsing()));
Matthew Ng78f88d12018-01-23 12:39:55 -0800897 }
898
Matthew Ng7d05e772017-11-09 14:41:07 -0800899 private void setSlippery(boolean slippery) {
Matthew Ngb831fb42019-01-30 11:20:48 -0800900 setWindowFlag(WindowManager.LayoutParams.FLAG_SLIPPERY, slippery);
901 }
902
903 public void setWindowTouchable(boolean flag) {
904 setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !flag);
905 if (mLeftEdgePanel != null) {
906 mLeftEdgePanel.setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !flag);
907 }
908 if (mRightEdgePanel != null) {
909 mRightEdgePanel.setWindowFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, !flag);
910 }
911 }
912
913 private void setWindowFlag(int flags, boolean enable) {
Matthew Ng7d05e772017-11-09 14:41:07 -0800914 final ViewGroup navbarView = ((ViewGroup) getParent());
Matthew Ngb55c02c2019-02-15 16:20:31 -0800915 if (navbarView == null) {
916 return;
917 }
Matthew Ngb831fb42019-01-30 11:20:48 -0800918 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView.getLayoutParams();
919 if (lp == null || enable == ((lp.flags & flags) != 0)) {
Matthew Ng9c3bce52018-02-01 22:00:31 +0000920 return;
921 }
Matthew Ngb831fb42019-01-30 11:20:48 -0800922 if (enable) {
923 lp.flags |= flags;
924 } else {
925 lp.flags &= ~flags;
Matthew Ng7d05e772017-11-09 14:41:07 -0800926 }
Matthew Ngb831fb42019-01-30 11:20:48 -0800927 WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
928 wm.updateViewLayout(navbarView, lp);
Matthew Ng7d05e772017-11-09 14:41:07 -0800929 }
930
Matthew Ngb55c02c2019-02-15 16:20:31 -0800931 private void showHomeHandle(boolean visible) {
932 mNavigationInflaterView.onTuningChanged(NAV_BAR_VIEWS,
933 visible ? getContext().getString(R.string.config_navBarLayoutHandle) : null);
934
935 // Color adaption is tied with showing home handle, only avaliable if visible
936 if (visible) {
937 mColorAdaptionController.start();
938 } else {
939 mColorAdaptionController.end();
940 }
941 }
942
Daniel Sandler56598cd2011-09-15 16:02:56 -0400943 public void setMenuVisibility(final boolean show) {
Matthew Ng25593cc2018-09-12 16:05:41 -0700944 mContextualButtonGroup.setButtonVisiblity(R.id.menu, show);
Daniel Sandler56598cd2011-09-15 16:02:56 -0400945 }
946
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800947 public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800948 mLongClickableAccessibilityButton = longClickable;
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800949 getAccessibilityButton().setLongClickable(longClickable);
Matthew Ng25593cc2018-09-12 16:05:41 -0700950 mContextualButtonGroup.setButtonVisiblity(R.id.accessibility_button, visible);
Mike Digmana48cf192018-02-12 17:52:48 -0800951 }
952
Tracy Zhou1ac592c2018-07-25 13:47:37 -0700953 void hideRecentsOnboarding() {
954 mRecentsOnboarding.hide(true);
955 }
956
Jim Miller960892c2012-05-23 15:50:04 -0700957 @Override
Daniel Sandler8956dbb2011-04-22 07:55:02 -0400958 public void onFinishInflate() {
Winson Chung661d5f92018-05-21 18:41:39 -0700959 mNavigationInflaterView = findViewById(R.id.navigation_inflater);
Felipe Leme15f915c2016-10-31 12:47:15 -0700960 mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
Jason Monka2081822016-01-18 14:41:03 -0500961
962 getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
Selim Cinek17a327a2014-08-25 15:03:48 +0200963
Jason Monk32e3bb52017-07-27 12:35:41 -0400964 DockedStackExistsListener.register(mDockedListener);
Charles Chenaa94d95b2019-02-19 14:18:19 +0800965 updateOrientationViews();
Matthew Ng25593cc2018-09-12 16:05:41 -0700966 reloadNavIcons();
Jorim Jaggia6c934e2015-12-21 13:22:31 +0100967 }
968
Matthew Nga8f24262017-12-19 11:54:24 -0800969 public void onDarkIntensityChange(float intensity) {
970 if (mGestureHelper != null) {
971 mGestureHelper.onDarkIntensityChange(intensity);
972 }
973 }
974
975 @Override
976 protected void onDraw(Canvas canvas) {
Jason Monk297c04e2018-08-23 17:16:59 -0400977 if (mGestureHelper != null) {
978 mGestureHelper.onDraw(canvas);
979 }
Matthew Ng603b3292018-03-30 17:15:39 -0700980 mDeadZone.onDraw(canvas);
Matthew Nga8f24262017-12-19 11:54:24 -0800981 super.onDraw(canvas);
982 }
983
984 @Override
985 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
986 super.onLayout(changed, left, top, right, bottom);
Sunny Goyalaac6c882019-02-11 11:57:58 -0800987
988 mActiveRegion.setEmpty();
989 updateButtonLocation(getBackButton(), mBackButtonBounds, true);
990 updateButtonLocation(getHomeButton(), mHomeButtonBounds, false);
991 updateButtonLocation(getRecentsButton(), mRecentsButtonBounds, false);
992 updateButtonLocation(getRotateSuggestionButton(), mRotationButtonBounds, true);
993 // TODO: Handle button visibility changes
994 mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
Jason Monk297c04e2018-08-23 17:16:59 -0400995 if (mGestureHelper != null) {
996 mGestureHelper.onLayout(changed, left, top, right, bottom);
997 }
Tracy Zhou559d97c2018-04-07 23:54:46 -0700998 mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
Matthew Nga8f24262017-12-19 11:54:24 -0800999 }
1000
Sunny Goyalaac6c882019-02-11 11:57:58 -08001001 private void updateButtonLocation(ButtonDispatcher button, Rect buttonBounds,
1002 boolean isActive) {
Winson Chungc4e06202018-02-13 10:37:35 -08001003 View view = button.getCurrentView();
1004 if (view == null) {
1005 buttonBounds.setEmpty();
1006 return;
1007 }
Matthew Ng3b7e3902018-03-22 16:06:09 -07001008 // Temporarily reset the translation back to origin to get the position in window
1009 final float posX = view.getTranslationX();
1010 final float posY = view.getTranslationY();
1011 view.setTranslationX(0);
1012 view.setTranslationY(0);
Sunny Goyalaac6c882019-02-11 11:57:58 -08001013
1014 if (isActive) {
1015 view.getLocationOnScreen(mTmpPosition);
1016 buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
1017 mTmpPosition[0] + view.getMeasuredWidth(),
1018 mTmpPosition[1] + view.getMeasuredHeight());
1019 mActiveRegion.op(buttonBounds, Op.UNION);
1020 }
Winson Chungc4e06202018-02-13 10:37:35 -08001021 view.getLocationInWindow(mTmpPosition);
1022 buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
1023 mTmpPosition[0] + view.getMeasuredWidth(),
1024 mTmpPosition[1] + view.getMeasuredHeight());
Matthew Ng3b7e3902018-03-22 16:06:09 -07001025 view.setTranslationX(posX);
1026 view.setTranslationY(posY);
Winson Chungc4e06202018-02-13 10:37:35 -08001027 }
1028
Charles Chenaa94d95b2019-02-19 14:18:19 +08001029 private void updateOrientationViews() {
1030 mHorizontal = findViewById(R.id.horizontal);
1031 mVertical = findViewById(R.id.vertical);
Jason Monk9a6552d2016-05-20 11:21:59 -04001032
Jason Monk199a2d02017-07-28 11:08:27 -04001033 updateCurrentView();
Daniel Sandler8956dbb2011-04-22 07:55:02 -04001034 }
1035
Charles Chenaa94d95b2019-02-19 14:18:19 +08001036 boolean needsReorient(int rotation) {
Adrian Roosa98b32c2016-08-11 10:41:08 -07001037 return mCurrentRotation != rotation;
Adrian Roos090b7d82016-08-02 18:36:12 -07001038 }
1039
Jason Monk199a2d02017-07-28 11:08:27 -04001040 private void updateCurrentView() {
Charles Chenaa94d95b2019-02-19 14:18:19 +08001041 resetViews();
1042 mCurrentView = mIsVertical ? mVertical : mHorizontal;
Daniel Sandler5c8da942011-06-28 00:29:04 -04001043 mCurrentView.setVisibility(View.VISIBLE);
Charles Chenaa94d95b2019-02-19 14:18:19 +08001044 mNavigationInflaterView.setVertical(mIsVertical);
1045 mCurrentRotation = getContextDisplay().getRotation();
1046 mNavigationInflaterView.setAlternativeOrder(mCurrentRotation == Surface.ROTATION_90);
Matthew Ng1efc9c32018-04-11 18:13:56 -07001047 mNavigationInflaterView.updateButtonDispatchersCurrentView();
Jorim Jaggi37c11802015-08-18 20:27:54 -07001048 updateLayoutTransitionsEnabled();
Charles Chenaa94d95b2019-02-19 14:18:19 +08001049 }
1050
1051 private void resetViews() {
1052 mHorizontal.setVisibility(View.GONE);
1053 mVertical.setVisibility(View.GONE);
Jason Monk9a6552d2016-05-20 11:21:59 -04001054 }
1055
1056 private void updateRecentsIcon() {
Charles Chenaa94d95b2019-02-19 14:18:19 +08001057 mDockedIcon.setRotation(mDockedStackExists && mIsVertical ? 90 : 0);
Jason Monk9a6552d2016-05-20 11:21:59 -04001058 getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon);
Jorim Jaggi40db0292016-06-27 17:58:03 -07001059 mBarTransitions.reapplyDarkIntensity();
Jason Monk9a6552d2016-05-20 11:21:59 -04001060 }
1061
Matthew Ng20136e62018-05-30 12:20:31 -07001062 public void showPinningEnterExitToast(boolean entering) {
1063 if (entering) {
1064 mScreenPinningNotify.showPinningStartToast();
Matthew Ng8a10fb52018-12-20 16:43:56 -08001065
1066 // TODO(b/112934365): remove after prototype finished, only needed to escape from pin
1067 getBackButton().setVisibility(VISIBLE);
Matthew Ngb8c30bb2018-12-21 15:25:48 -08001068 getHomeButton().setVisibility(VISIBLE);
Matthew Ng20136e62018-05-30 12:20:31 -07001069 } else {
1070 mScreenPinningNotify.showPinningExitToast();
1071 }
1072 }
1073
1074 public void showPinningEscapeToast() {
1075 mScreenPinningNotify.showEscapeToast(isRecentsButtonVisible());
1076 }
1077
Jason Monk9a6552d2016-05-20 11:21:59 -04001078 public boolean isVertical() {
Charles Chenaa94d95b2019-02-19 14:18:19 +08001079 return mIsVertical;
Jason Monk9a6552d2016-05-20 11:21:59 -04001080 }
1081
1082 public void reorient() {
Jason Monk199a2d02017-07-28 11:08:27 -04001083 updateCurrentView();
Daniel Sandler8956dbb2011-04-22 07:55:02 -04001084
Siarhei Vishniakoufdb42772017-06-29 10:32:11 -07001085 ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
Matthew Ng603b3292018-03-30 17:15:39 -07001086 mDeadZone.onConfigurationChanged(mCurrentRotation);
Daniel Sandlerc26185b2012-08-29 15:49:53 -04001087
Daniel Sandler6da2b762011-09-14 16:04:59 -04001088 // force the low profile & disabled states into compliance
Adrian Roos8a8ffd42015-05-26 18:30:37 -07001089 mBarTransitions.init();
Daniel Sandler6da2b762011-09-14 16:04:59 -04001090
Daniel Sandler60ee2562011-07-22 12:34:33 -04001091 if (DEBUG) {
Adrian Roosa98b32c2016-08-11 10:41:08 -07001092 Log.d(TAG, "reorient(): rot=" + mCurrentRotation);
Daniel Sandler60ee2562011-07-22 12:34:33 -04001093 }
Michael Jurkaa5d0ddb2012-03-09 17:41:41 -08001094
Matthew Ng5fd80412018-02-14 14:40:31 -08001095 // Resolve layout direction if not resolved since components changing layout direction such
1096 // as changing languages will recreate this view and the direction will be resolved later
1097 if (!isLayoutDirectionResolved()) {
1098 resolveLayoutDirection();
1099 }
Selim Cinek92d892c2014-09-11 15:11:00 +02001100 updateTaskSwitchHelper();
Matthew Ngd0a73e72018-03-02 15:16:03 -08001101 updateNavButtonIcons();
Xiaohui Chen9efa5de2016-11-22 10:34:38 -08001102
Charles Chenaa94d95b2019-02-19 14:18:19 +08001103 getHomeButton().setVertical(mIsVertical);
Daniel Sandler8956dbb2011-04-22 07:55:02 -04001104 }
Daniel Sandler48852952011-12-01 14:34:23 -05001105
Selim Cinek92d892c2014-09-11 15:11:00 +02001106 private void updateTaskSwitchHelper() {
Jason Monkde850bb2017-02-01 19:26:30 -05001107 if (mGestureHelper == null) return;
Selim Cinek92d892c2014-09-11 15:11:00 +02001108 boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
Tiger Huang44c04552018-11-29 20:06:45 +08001109 int navBarPos = NAV_BAR_INVALID;
Matthew Ng86a436e2018-10-26 16:00:53 -07001110 try {
Tiger Huang44c04552018-11-29 20:06:45 +08001111 navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition(
Jeff Chang2aaa91a2018-11-08 18:04:16 +08001112 getContext().getDisplayId());
Matthew Ng86a436e2018-10-26 16:00:53 -07001113 } catch (RemoteException e) {
1114 Slog.e(TAG, "Failed to get nav bar position.", e);
1115 }
Matthew Ngb831fb42019-01-30 11:20:48 -08001116
1117 // For landscape, hide the panel that would interfere with navigation bar layout
1118 if (mLeftEdgePanel != null && mRightEdgePanel != null) {
1119 mLeftEdgePanel.setVisibility(VISIBLE);
1120 mRightEdgePanel.setVisibility(VISIBLE);
1121 if (navBarPos == NAV_BAR_LEFT) {
1122 mLeftEdgePanel.setVisibility(GONE);
1123 } else if (navBarPos == NAV_BAR_RIGHT) {
1124 mRightEdgePanel.setVisibility(GONE);
1125 }
1126 }
Matthew Ng86a436e2018-10-26 16:00:53 -07001127 mGestureHelper.setBarState(isRtl, navBarPos);
Selim Cinek92d892c2014-09-11 15:11:00 +02001128 }
1129
Daniel Sandler0b69b592012-01-23 21:08:36 -05001130 @Override
1131 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
John Spurlockcd686b52013-06-05 10:13:46 -04001132 if (DEBUG) Log.d(TAG, String.format(
Daniel Sandler0b69b592012-01-23 21:08:36 -05001133 "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh));
Daniel Sandlere03bc952012-04-27 16:11:22 -04001134
1135 final boolean newVertical = w > 0 && h > w;
Charles Chenaa94d95b2019-02-19 14:18:19 +08001136 if (newVertical != mIsVertical) {
1137 mIsVertical = newVertical;
1138 if (DEBUG) {
1139 Log.d(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w,
1140 mIsVertical ? "y" : "n"));
1141 }
Daniel Sandlere03bc952012-04-27 16:11:22 -04001142 reorient();
Selim Cinek92d892c2014-09-11 15:11:00 +02001143 notifyVerticalChangedListener(newVertical);
Daniel Sandlere03bc952012-04-27 16:11:22 -04001144 }
1145
Daniel Sandler0b69b592012-01-23 21:08:36 -05001146 postCheckForInvalidLayout("sizeChanged");
1147 super.onSizeChanged(w, h, oldw, oldh);
1148 }
1149
Selim Cinek92d892c2014-09-11 15:11:00 +02001150 private void notifyVerticalChangedListener(boolean newVertical) {
1151 if (mOnVerticalChangedListener != null) {
1152 mOnVerticalChangedListener.onVerticalChanged(newVertical);
1153 }
1154 }
1155
Selim Cinek17a327a2014-08-25 15:03:48 +02001156 @Override
1157 protected void onConfigurationChanged(Configuration newConfig) {
1158 super.onConfigurationChanged(newConfig);
Matthew Ng8c056c12018-10-15 15:16:46 -07001159 mTmpLastConfiguration.updateFrom(mConfiguration);
1160 mConfiguration.updateFrom(newConfig);
1161 boolean uiCarModeChanged = updateCarMode();
Selim Cinek92d892c2014-09-11 15:11:00 +02001162 updateTaskSwitchHelper();
Matthew Ng8c056c12018-10-15 15:16:46 -07001163 updateIcons(mTmpLastConfiguration);
Winsonfde2e6a2016-03-22 16:03:10 -07001164 updateRecentsIcon();
Matthew Ng8c056c12018-10-15 15:16:46 -07001165 mRecentsOnboarding.onConfigurationChanged(mConfiguration);
1166 if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi
1167 || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) {
Jorim Jaggi11c62e12016-04-05 20:41:21 -07001168 // If car mode or density changes, we need to reset the icons.
Matthew Ngd0a73e72018-03-02 15:16:03 -08001169 updateNavButtonIcons();
Jorim Jaggi11c62e12016-04-05 20:41:21 -07001170 }
Matthew Ngb55c02c2019-02-15 16:20:31 -08001171
1172 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
1173 mColorAdaptionController.start();
1174 } else {
1175 mColorAdaptionController.end();
1176 }
Sriram Viswanathan9ebbe6a2015-11-16 17:59:22 -08001177 }
1178
1179 /**
1180 * If the configuration changed, update the carmode and return that it was updated.
1181 */
Matthew Ng8c056c12018-10-15 15:16:46 -07001182 private boolean updateCarMode() {
Sriram Viswanathan9ebbe6a2015-11-16 17:59:22 -08001183 boolean uiCarModeChanged = false;
Matthew Ng8c056c12018-10-15 15:16:46 -07001184 if (mConfiguration != null) {
1185 int uiMode = mConfiguration.uiMode & Configuration.UI_MODE_TYPE_MASK;
Justin Paupore01915a12016-09-28 17:41:26 -07001186 final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR);
1187
1188 if (isCarMode != mInCarMode) {
1189 mInCarMode = isCarMode;
Justin Paupore01915a12016-09-28 17:41:26 -07001190 if (ALTERNATE_CAR_MODE_UI) {
1191 mUseCarModeUi = isCarMode;
1192 uiCarModeChanged = true;
1193 } else {
1194 // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set.
1195 mUseCarModeUi = false;
1196 }
Sriram Viswanathan9ebbe6a2015-11-16 17:59:22 -08001197 }
1198 }
1199 return uiCarModeChanged;
Selim Cinek17a327a2014-08-25 15:03:48 +02001200 }
1201
Daniel Sandler0b69b592012-01-23 21:08:36 -05001202 /*
1203 @Override
1204 protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
John Spurlockcd686b52013-06-05 10:13:46 -04001205 if (DEBUG) Log.d(TAG, String.format(
John Spurlock209bede2013-07-17 12:23:27 -04001206 "onLayout: %s (%d,%d,%d,%d)",
Daniel Sandler0b69b592012-01-23 21:08:36 -05001207 changed?"changed":"notchanged", left, top, right, bottom));
1208 super.onLayout(changed, left, top, right, bottom);
1209 }
1210
1211 // uncomment this for extra defensiveness in WORKAROUND_INVALID_LAYOUT situations: if all else
1212 // fails, any touch on the display will fix the layout.
1213 @Override
1214 public boolean onInterceptTouchEvent(MotionEvent ev) {
John Spurlockcd686b52013-06-05 10:13:46 -04001215 if (DEBUG) Log.d(TAG, "onInterceptTouchEvent: " + ev.toString());
Daniel Sandler0b69b592012-01-23 21:08:36 -05001216 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
1217 postCheckForInvalidLayout("touch");
1218 }
1219 return super.onInterceptTouchEvent(ev);
1220 }
1221 */
John Spurlock209bede2013-07-17 12:23:27 -04001222
Daniel Sandler0b69b592012-01-23 21:08:36 -05001223
Daniel Sandler48852952011-12-01 14:34:23 -05001224 private String getResourceName(int resId) {
1225 if (resId != 0) {
John Spurlock01534782014-01-13 11:59:22 -05001226 final android.content.res.Resources res = getContext().getResources();
Daniel Sandler48852952011-12-01 14:34:23 -05001227 try {
1228 return res.getResourceName(resId);
1229 } catch (android.content.res.Resources.NotFoundException ex) {
1230 return "(unknown)";
1231 }
1232 } else {
1233 return "(null)";
1234 }
1235 }
1236
Daniel Sandler0b69b592012-01-23 21:08:36 -05001237 private void postCheckForInvalidLayout(final String how) {
1238 mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget();
1239 }
1240
Daniel Sandler48852952011-12-01 14:34:23 -05001241 private static String visibilityToString(int vis) {
1242 switch (vis) {
1243 case View.INVISIBLE:
1244 return "INVISIBLE";
1245 case View.GONE:
1246 return "GONE";
1247 default:
1248 return "VISIBLE";
1249 }
1250 }
1251
Jason Monk67e6c802016-08-30 14:09:21 -04001252 @Override
1253 protected void onAttachedToWindow() {
1254 super.onAttachedToWindow();
Adrian Roos7e58a082018-03-09 16:40:56 +01001255 requestApplyInsets();
Siarhei Vishniakoufdb42772017-06-29 10:32:11 -07001256 reorient();
Jason Monkde850bb2017-02-01 19:26:30 -05001257 onPluginDisconnected(null); // Create default gesture helper
Jason Monk5bec68f2017-02-08 20:45:10 -08001258 Dependency.get(PluginManager.class).addPluginListener(this,
1259 NavGesture.class, false /* Only one */);
Winson Chungf9e30272018-03-26 17:25:36 -07001260 setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
Matthew Ng355fe212018-12-14 17:42:38 -08001261 mColorAdaptionController.start();
Matthew Ngb831fb42019-01-30 11:20:48 -08001262
1263 if (mPrototypeController.isEnabled()) {
1264 WindowManager wm = (WindowManager) getContext()
1265 .getSystemService(Context.WINDOW_SERVICE);
1266 int width = mPrototypeController.getEdgeSensitivityWidth();
1267 int height = mPrototypeController.getEdgeSensitivityHeight();
Zak Cohen8d8d6772019-02-07 16:50:24 -08001268 // Explicitly left and right, not start and end as this is device relative.
Matthew Ngb831fb42019-01-30 11:20:48 -08001269 mLeftEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
Zak Cohen8d8d6772019-02-07 16:50:24 -08001270 Gravity.LEFT | Gravity.BOTTOM);
Matthew Ngb831fb42019-01-30 11:20:48 -08001271 mRightEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
Zak Cohen8d8d6772019-02-07 16:50:24 -08001272 Gravity.RIGHT | Gravity.BOTTOM);
Matthew Ngb831fb42019-01-30 11:20:48 -08001273 mLeftEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
1274 mRightEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
1275 wm.addView(mLeftEdgePanel, mLeftEdgePanel.getLayoutParams());
1276 wm.addView(mRightEdgePanel, mRightEdgePanel.getLayoutParams());
1277 }
Jason Monk67e6c802016-08-30 14:09:21 -04001278 }
1279
1280 @Override
1281 protected void onDetachedFromWindow() {
1282 super.onDetachedFromWindow();
Jason Monkde850bb2017-02-01 19:26:30 -05001283 Dependency.get(PluginManager.class).removePluginListener(this);
1284 if (mGestureHelper != null) {
1285 mGestureHelper.destroy();
1286 }
Matthew Ng1cf6eac2018-11-27 15:06:55 -08001287 mPrototypeController.unregister();
Matthew Ng355fe212018-12-14 17:42:38 -08001288 mColorAdaptionController.stop();
Tony Wickhamfb63fe82018-01-16 12:14:06 -08001289 setUpSwipeUpOnboarding(false);
Matthew Ng761562d2018-09-17 11:13:21 -07001290 for (int i = 0; i < mButtonDispatchers.size(); ++i) {
1291 mButtonDispatchers.valueAt(i).onDestroy();
1292 }
Matthew Ngb831fb42019-01-30 11:20:48 -08001293
Matthew Ng33fa2752019-02-14 12:12:50 -08001294 WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
1295 if (mLeftEdgePanel != null) {
1296 wm.removeView(mLeftEdgePanel);
1297 }
1298 if (mRightEdgePanel != null) {
1299 wm.removeView(mRightEdgePanel);
Matthew Ngb831fb42019-01-30 11:20:48 -08001300 }
Tony Wickhamfb63fe82018-01-16 12:14:06 -08001301 }
1302
1303 private void setUpSwipeUpOnboarding(boolean connectedToOverviewProxy) {
1304 if (connectedToOverviewProxy) {
Tony Wickham05c1f852018-02-06 12:32:54 -08001305 mRecentsOnboarding.onConnectedToLauncher();
Tony Wickhamfb63fe82018-01-16 12:14:06 -08001306 } else {
Tony Wickham05c1f852018-02-06 12:32:54 -08001307 mRecentsOnboarding.onDisconnectedFromLauncher();
Tony Wickhamfb63fe82018-01-16 12:14:06 -08001308 }
Jason Monk67e6c802016-08-30 14:09:21 -04001309 }
1310
1311 @Override
Jason Monk20ff3f92017-01-09 15:13:23 -05001312 public void onPluginConnected(NavGesture plugin, Context context) {
Jason Monk67e6c802016-08-30 14:09:21 -04001313 mGestureHelper = plugin.getGestureHelper();
1314 updateTaskSwitchHelper();
1315 }
1316
1317 @Override
1318 public void onPluginDisconnected(NavGesture plugin) {
Winson Chung0d3815d2018-09-25 18:11:54 -07001319 QuickStepController defaultHelper = new QuickStepController(getContext());
Winson Chung67f5c8b2018-09-24 12:09:19 -07001320 defaultHelper.setComponents(this);
Jason Monkde850bb2017-02-01 19:26:30 -05001321 if (mGestureHelper != null) {
1322 mGestureHelper.destroy();
1323 }
Jason Monk67e6c802016-08-30 14:09:21 -04001324 mGestureHelper = defaultHelper;
1325 updateTaskSwitchHelper();
1326 }
1327
Daniel Sandler48852952011-12-01 14:34:23 -05001328 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1329 pw.println("NavigationBarView {");
1330 final Rect r = new Rect();
Jeff Browna8b9def2012-07-23 14:22:49 -07001331 final Point size = new Point();
Jeff Chang2aaa91a2018-11-08 18:04:16 +08001332 getContextDisplay().getRealSize(size);
Daniel Sandler48852952011-12-01 14:34:23 -05001333
Jason Monk2a6ea9c2017-01-26 11:14:51 -05001334 pw.println(String.format(" this: " + StatusBar.viewInfo(this)
Daniel Sandler48852952011-12-01 14:34:23 -05001335 + " " + visibilityToString(getVisibility())));
1336
1337 getWindowVisibleDisplayFrame(r);
Jeff Browna8b9def2012-07-23 14:22:49 -07001338 final boolean offscreen = r.right > size.x || r.bottom > size.y;
John Spurlock209bede2013-07-17 12:23:27 -04001339 pw.println(" window: "
Daniel Sandler48852952011-12-01 14:34:23 -05001340 + r.toShortString()
1341 + " " + visibilityToString(getWindowVisibility())
1342 + (offscreen ? " OFFSCREEN!" : ""));
1343
Winson Chung661d5f92018-05-21 18:41:39 -07001344 pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s %f",
Rakesh Iyerf51fe4e2016-02-09 10:51:50 -08001345 getResourceName(getCurrentView().getId()),
1346 getCurrentView().getWidth(), getCurrentView().getHeight(),
Winson Chung661d5f92018-05-21 18:41:39 -07001347 visibilityToString(getCurrentView().getVisibility()),
1348 getCurrentView().getAlpha()));
Daniel Sandler48852952011-12-01 14:34:23 -05001349
Matthew Ngeb0b0942018-10-12 16:42:11 -07001350 pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s darkIntensity=%.2f",
Daniel Sandler48852952011-12-01 14:34:23 -05001351 mDisabledFlags,
Charles Chenaa94d95b2019-02-19 14:18:19 +08001352 mIsVertical ? "true" : "false",
Matthew Ngeb0b0942018-10-12 16:42:11 -07001353 getMenuButton().isVisible() ? "true" : "false",
1354 getLightTransitionsController().getCurrentDarkIntensity()));
Daniel Sandler48852952011-12-01 14:34:23 -05001355
John Spurlock56d007b2013-10-28 18:40:56 -04001356 dumpButton(pw, "back", getBackButton());
1357 dumpButton(pw, "home", getHomeButton());
1358 dumpButton(pw, "rcnt", getRecentsButton());
1359 dumpButton(pw, "menu", getMenuButton());
Matthew Ng25593cc2018-09-12 16:05:41 -07001360 dumpButton(pw, "rota", getRotateSuggestionButton());
Casey Burkhardt048c2bc2016-12-08 16:09:20 -08001361 dumpButton(pw, "a11y", getAccessibilityButton());
Daniel Sandler48852952011-12-01 14:34:23 -05001362
Daniel Sandler48852952011-12-01 14:34:23 -05001363 pw.println(" }");
Matthew Ng25593cc2018-09-12 16:05:41 -07001364
1365 mContextualButtonGroup.dump(pw);
Jason Monk297c04e2018-08-23 17:16:59 -04001366 if (mGestureHelper != null) {
Matthew Ng86a436e2018-10-26 16:00:53 -07001367 pw.println("Navigation Gesture Actions {");
1368 pw.print(" "); pw.println("QuickScrub Enabled=" + mQuickScrubAction.isEnabled());
1369 pw.print(" "); pw.println("QuickScrub Active=" + mQuickScrubAction.isActive());
1370 pw.print(" "); pw.println("QuickStep Enabled=" + mQuickStepAction.isEnabled());
1371 pw.print(" "); pw.println("QuickStep Active=" + mQuickStepAction.isActive());
1372 pw.print(" "); pw.println("Back Gesture Enabled=" + mBackAction.isEnabled());
1373 pw.print(" "); pw.println("Back Gesture Active=" + mBackAction.isActive());
1374 pw.println("}");
Jason Monk297c04e2018-08-23 17:16:59 -04001375 mGestureHelper.dump(pw);
1376 }
Matthew Ng25593cc2018-09-12 16:05:41 -07001377 mRecentsOnboarding.dump(pw);
Daniel Sandler48852952011-12-01 14:34:23 -05001378 }
Jim Millere898ac52012-04-06 17:10:57 -07001379
Adrian Roos7e58a082018-03-09 16:40:56 +01001380 @Override
1381 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
1382 setPadding(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
1383 insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
1384 return super.onApplyWindowInsets(insets);
1385 }
1386
Jason Monka2081822016-01-18 14:41:03 -05001387 private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
John Spurlock56d007b2013-10-28 18:40:56 -04001388 pw.print(" " + caption + ": ");
1389 if (button == null) {
1390 pw.print("null");
1391 } else {
Jason Monka2081822016-01-18 14:41:03 -05001392 pw.print(visibilityToString(button.getVisibility())
John Spurlock56d007b2013-10-28 18:40:56 -04001393 + " alpha=" + button.getAlpha()
1394 );
John Spurlock56d007b2013-10-28 18:40:56 -04001395 }
1396 pw.println();
1397 }
1398
Jorim Jaggif4797922014-08-04 22:49:41 +02001399 public interface OnVerticalChangedListener {
1400 void onVerticalChanged(boolean isVertical);
1401 }
Jason Monka2081822016-01-18 14:41:03 -05001402
Jason Monk32e3bb52017-07-27 12:35:41 -04001403 private final Consumer<Boolean> mDockedListener = exists -> mHandler.post(() -> {
1404 mDockedStackExists = exists;
1405 updateRecentsIcon();
1406 });
Daniel Sandler8956dbb2011-04-22 07:55:02 -04001407}