blob: 0cf1b3ddc21303dd7376db2d7b90d17806d72340 [file] [log] [blame]
Jason Monk49fa0162017-01-11 09:21:56 -05001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.systemui.statusbar.phone;
16
17import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
18import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
19import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
20import static android.app.StatusBarManager.windowStateToString;
21
Winson Chung2dbcf092018-10-24 13:00:41 -070022import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
Matthew Ng8f25fb962018-01-16 17:17:24 -080023import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
Tracy Zhou1ac592c2018-07-25 13:47:37 -070024import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
Jason Monk49fa0162017-01-11 09:21:56 -050025import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
Tracy Zhou1ac592c2018-07-25 13:47:37 -070026import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050027import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
28import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
Jason Monk49fa0162017-01-11 09:21:56 -050029
Casey Burkhardt048c2bc2016-12-08 16:09:20 -080030import android.accessibilityservice.AccessibilityServiceInfo;
Matthew Ng9c3bce52018-02-01 22:00:31 +000031import android.annotation.IdRes;
Jason Monk49fa0162017-01-11 09:21:56 -050032import android.annotation.Nullable;
Winson Chung67f5c8b2018-09-24 12:09:19 -070033import android.app.ActivityManager;
Wale Ogunwale65ebd952018-04-25 15:41:44 -070034import android.app.ActivityTaskManager;
Jason Monk49fa0162017-01-11 09:21:56 -050035import android.app.Fragment;
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070036import android.app.IActivityTaskManager;
Jason Monk49fa0162017-01-11 09:21:56 -050037import android.app.StatusBarManager;
38import android.content.BroadcastReceiver;
Casey Burkhardtb9dcd662017-03-20 15:10:16 -070039import android.content.ContentResolver;
Jason Monk49fa0162017-01-11 09:21:56 -050040import android.content.Context;
41import android.content.Intent;
42import android.content.IntentFilter;
43import android.content.res.Configuration;
Casey Burkhardt74922c62017-02-13 12:43:16 -080044import android.database.ContentObserver;
Jason Monk49fa0162017-01-11 09:21:56 -050045import android.graphics.PixelFormat;
46import android.graphics.Rect;
47import android.inputmethodservice.InputMethodService;
48import android.os.Binder;
49import android.os.Bundle;
50import android.os.Handler;
51import android.os.IBinder;
Jason Monk49fa0162017-01-11 09:21:56 -050052import android.os.RemoteException;
53import android.os.UserHandle;
Casey Burkhardt74922c62017-02-13 12:43:16 -080054import android.provider.Settings;
Jason Monk49fa0162017-01-11 09:21:56 -050055import android.telecom.TelecomManager;
56import android.text.TextUtils;
57import android.util.Log;
Andrii Kulian3ddd7de2018-10-10 00:33:19 -070058import android.view.Display;
59import android.view.Gravity;
Jason Monk49fa0162017-01-11 09:21:56 -050060import android.view.KeyEvent;
61import android.view.LayoutInflater;
62import android.view.MotionEvent;
Mike Digman85ff7fa2018-01-23 14:59:52 -080063import android.view.Surface;
Jason Monk49fa0162017-01-11 09:21:56 -050064import android.view.View;
65import android.view.ViewGroup;
66import android.view.WindowManager;
67import android.view.WindowManager.LayoutParams;
Jason Monk49fa0162017-01-11 09:21:56 -050068import android.view.accessibility.AccessibilityEvent;
69import android.view.accessibility.AccessibilityManager;
Jason Monk91e587e2017-04-13 13:41:23 -040070import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
Jason Monk49fa0162017-01-11 09:21:56 -050071
Matthew Ng761562d2018-09-17 11:13:21 -070072import androidx.annotation.VisibleForTesting;
73
Jason Monk49fa0162017-01-11 09:21:56 -050074import com.android.internal.logging.MetricsLogger;
75import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monkea03be12017-12-04 11:08:41 -050076import com.android.internal.util.LatencyTracker;
Jason Monk9c7844c2017-01-18 15:21:53 -050077import com.android.systemui.Dependency;
Jason Monk49fa0162017-01-11 09:21:56 -050078import com.android.systemui.R;
Jason Monk9c7844c2017-01-18 15:21:53 -050079import com.android.systemui.SysUiServiceProvider;
Jason Monk49fa0162017-01-11 09:21:56 -050080import com.android.systemui.assist.AssistManager;
81import com.android.systemui.fragments.FragmentHostManager;
82import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
Andrii Kulian3ddd7de2018-10-10 00:33:19 -070083import com.android.systemui.recents.OverviewProxyService;
Jason Monk49fa0162017-01-11 09:21:56 -050084import com.android.systemui.recents.Recents;
Mike Digman7d092772018-01-11 12:10:32 -080085import com.android.systemui.shared.system.ActivityManagerWrapper;
Jason Monk49fa0162017-01-11 09:21:56 -050086import com.android.systemui.stackdivider.Divider;
87import com.android.systemui.statusbar.CommandQueue;
88import com.android.systemui.statusbar.CommandQueue.Callbacks;
Matthew Ng761562d2018-09-17 11:13:21 -070089import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
90import com.android.systemui.statusbar.phone.ContextualButton.ContextButtonListener;
Jason Monk91e587e2017-04-13 13:41:23 -040091import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
Jason Monk297c04e2018-08-23 17:16:59 -040092import com.android.systemui.statusbar.policy.DeviceProvisionedController;
Jason Monk49fa0162017-01-11 09:21:56 -050093import com.android.systemui.statusbar.policy.KeyButtonView;
Jason Monk49fa0162017-01-11 09:21:56 -050094
95import java.io.FileDescriptor;
96import java.io.PrintWriter;
Casey Burkhardt048c2bc2016-12-08 16:09:20 -080097import java.util.List;
Jason Monk49fa0162017-01-11 09:21:56 -050098import java.util.Locale;
Matthew Ng761562d2018-09-17 11:13:21 -070099import java.util.function.Consumer;
Jason Monk49fa0162017-01-11 09:21:56 -0500100
101/**
102 * Fragment containing the NavigationBarFragment. Contains logic for what happens
103 * on clicks and view states of the nav bar.
104 */
105public class NavigationBarFragment extends Fragment implements Callbacks {
106
Jason Monkd4afe152017-05-01 15:37:43 -0400107 public static final String TAG = "NavigationBar";
Jason Monk49fa0162017-01-11 09:21:56 -0500108 private static final boolean DEBUG = false;
109 private static final String EXTRA_DISABLE_STATE = "disabled_state";
Mike Digmandd2f49e2018-03-16 10:54:22 -0700110 private static final String EXTRA_DISABLE2_STATE = "disabled2_state";
Jason Monk49fa0162017-01-11 09:21:56 -0500111
112 /** Allow some time inbetween the long press for back and recents. */
113 private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
114
Jason Monk297c04e2018-08-23 17:16:59 -0400115 private final DeviceProvisionedController mDeviceProvisionedController =
116 Dependency.get(DeviceProvisionedController.class);
117
Jason Monk49fa0162017-01-11 09:21:56 -0500118 protected NavigationBarView mNavigationBarView = null;
119 protected AssistManager mAssistManager;
120
121 private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
122
123 private int mNavigationIconHints = 0;
124 private int mNavigationBarMode;
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800125 private AccessibilityManager mAccessibilityManager;
Casey Burkhardt74922c62017-02-13 12:43:16 -0800126 private MagnificationContentObserver mMagnificationObserver;
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700127 private ContentResolver mContentResolver;
Mike Digmanc94759d2018-01-23 11:01:21 -0800128 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500129
130 private int mDisabledFlags1;
Mike Digmandd2f49e2018-03-16 10:54:22 -0700131 private int mDisabledFlags2;
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500132 private StatusBar mStatusBar;
Jason Monk49fa0162017-01-11 09:21:56 -0500133 private Recents mRecents;
134 private Divider mDivider;
135 private WindowManager mWindowManager;
136 private CommandQueue mCommandQueue;
137 private long mLastLockToAppLongPress;
138
139 private Locale mLocale;
140 private int mLayoutDirection;
141
142 private int mSystemUiVisibility;
143 private LightBarController mLightBarController;
Jason Monk49fa0162017-01-11 09:21:56 -0500144
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800145 private OverviewProxyService mOverviewProxyService;
146
Riddle Hsu19607772018-11-01 18:11:26 +0800147 private boolean mIsOnDefaultDisplay = true;
Jason Monk49fa0162017-01-11 09:21:56 -0500148 public boolean mHomeBlockedThisTouch;
149
Matthew Ng9c3bce52018-02-01 22:00:31 +0000150 private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
151 @Override
152 public void onConnectionChanged(boolean isConnected) {
Winson Chungf9e30272018-03-26 17:25:36 -0700153 mNavigationBarView.updateStates();
Matthew Ng9c3bce52018-02-01 22:00:31 +0000154 updateScreenPinningGestures();
155 }
156
157 @Override
Matthew Ng2ea93b72018-03-14 19:43:18 +0000158 public void onQuickStepStarted() {
Mike Digman85a9bea2018-02-23 15:08:53 -0800159 // Use navbar dragging as a signal to hide the rotate button
Matthew Ng761562d2018-09-17 11:13:21 -0700160 mNavigationBarView.getRotateSuggestionButton().setRotateSuggestionButtonState(false);
Matthew Ng90ef0632018-08-15 13:53:15 -0700161
162 // Hide the notifications panel when quick step starts
163 mStatusBar.collapsePanel(true /* animate */);
Matthew Ng9c3bce52018-02-01 22:00:31 +0000164 }
Matthew Ng8f25fb962018-01-16 17:17:24 -0800165
166 @Override
167 public void onInteractionFlagsChanged(@InteractionType int flags) {
168 mNavigationBarView.updateStates();
Winson Chungf9e30272018-03-26 17:25:36 -0700169 updateScreenPinningGestures();
Matthew Ng8f25fb962018-01-16 17:17:24 -0800170 }
Matthew Ng96985e72018-05-08 15:46:13 -0700171
172 @Override
173 public void onBackButtonAlphaChanged(float alpha, boolean animate) {
174 final ButtonDispatcher backButton = mNavigationBarView.getBackButton();
Matthew Ngb687c8a2018-10-02 11:31:38 -0700175 if (QuickStepController.shouldhideBackButton(getContext())) {
Matthew Ngc83b9892018-08-21 16:31:13 -0700176 // If property was changed to hide/show back button, going home will trigger
177 // launcher to to change the back button alpha to reflect property change
178 backButton.setVisibility(View.GONE);
179 } else {
180 backButton.setVisibility(alpha > 0 ? View.VISIBLE : View.INVISIBLE);
181 backButton.setAlpha(alpha, animate);
182 }
Matthew Ng96985e72018-05-08 15:46:13 -0700183 }
Matthew Ng9c3bce52018-02-01 22:00:31 +0000184 };
Mike Digman7d092772018-01-11 12:10:32 -0800185
Matthew Ng761562d2018-09-17 11:13:21 -0700186 private final ContextButtonListener mRotationButtonListener = new ContextButtonListener() {
187 @Override
188 public void onVisibilityChanged(ContextualButton button, boolean visible) {
189 if (visible) {
190 // If the button will actually become visible and the navbar is about to hide,
191 // tell the statusbar to keep it around for longer
192 mStatusBar.touchAutoHide();
193 }
194 }
195 };
196
Jason Monk49fa0162017-01-11 09:21:56 -0500197 // ----- Fragment Lifecycle Callbacks -----
198
199 @Override
200 public void onCreate(@Nullable Bundle savedInstanceState) {
201 super.onCreate(savedInstanceState);
Jason Monk9c7844c2017-01-18 15:21:53 -0500202 mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500203 mCommandQueue.addCallbacks(this);
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500204 mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
Jason Monk9c7844c2017-01-18 15:21:53 -0500205 mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class);
206 mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500207 mWindowManager = getContext().getSystemService(WindowManager.class);
208 mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
Jason Monk91e587e2017-04-13 13:41:23 -0400209 Dependency.get(AccessibilityManagerWrapper.class).addCallback(
210 mAccessibilityListener);
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700211 mContentResolver = getContext().getContentResolver();
Casey Burkhardt74922c62017-02-13 12:43:16 -0800212 mMagnificationObserver = new MagnificationContentObserver(
213 getContext().getMainThreadHandler());
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700214 mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
Casey Burkhardt74922c62017-02-13 12:43:16 -0800215 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false,
Casey Burkhardt5d614402017-04-06 13:46:50 -0700216 mMagnificationObserver, UserHandle.USER_ALL);
Casey Burkhardt74922c62017-02-13 12:43:16 -0800217
Jason Monk49fa0162017-01-11 09:21:56 -0500218 if (savedInstanceState != null) {
219 mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
Mike Digmandd2f49e2018-03-16 10:54:22 -0700220 mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0);
Jason Monk49fa0162017-01-11 09:21:56 -0500221 }
Jason Monk9c7844c2017-01-18 15:21:53 -0500222 mAssistManager = Dependency.get(AssistManager.class);
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800223 mOverviewProxyService = Dependency.get(OverviewProxyService.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500224 }
225
226 @Override
227 public void onDestroy() {
228 super.onDestroy();
229 mCommandQueue.removeCallbacks(this);
Jason Monk91e587e2017-04-13 13:41:23 -0400230 Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
231 mAccessibilityListener);
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700232 mContentResolver.unregisterContentObserver(mMagnificationObserver);
Jason Monk49fa0162017-01-11 09:21:56 -0500233 }
234
235 @Override
236 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
237 Bundle savedInstanceState) {
238 return inflater.inflate(R.layout.navigation_bar, container, false);
239 }
240
241 @Override
242 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
243 super.onViewCreated(view, savedInstanceState);
244 mNavigationBarView = (NavigationBarView) view;
Riddle Hsu19607772018-11-01 18:11:26 +0800245 final Display display = view.getDisplay();
246 // It may not have display when running unit test.
247 if (display != null) {
248 mIsOnDefaultDisplay = display.getDisplayId() == Display.DEFAULT_DISPLAY;
249 }
Jason Monk49fa0162017-01-11 09:21:56 -0500250
Winson Chung67f5c8b2018-09-24 12:09:19 -0700251 mNavigationBarView.setComponents(mStatusBar.getPanel());
Matthew Ngf0f84542018-10-15 11:53:22 -0700252 mNavigationBarView.setDisabledFlags(mDisabledFlags1);
Jason Monk49fa0162017-01-11 09:21:56 -0500253 mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
254 mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
255 if (savedInstanceState != null) {
256 mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
257 }
258
259 prepareNavigationBarView();
260 checkNavBarModes();
261
262 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
263 filter.addAction(Intent.ACTION_SCREEN_ON);
Phil Weavera858c5f2018-04-26 13:27:30 -0700264 filter.addAction(Intent.ACTION_USER_SWITCHED);
Jason Monk49fa0162017-01-11 09:21:56 -0500265 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
Siarhei Vishniakoud002a0a2017-06-05 22:44:37 +0100266 notifyNavigationBarScreenOn();
Matthew Ng9c3bce52018-02-01 22:00:31 +0000267 mOverviewProxyService.addCallback(mOverviewProxyListener);
Matthew Ng761562d2018-09-17 11:13:21 -0700268
Riddle Hsu19607772018-11-01 18:11:26 +0800269 // Currently there is no accelerometer sensor on non-default display.
270 if (mIsOnDefaultDisplay) {
271 final RotationContextButton rotationButton =
272 mNavigationBarView.getRotateSuggestionButton();
273 rotationButton.setListener(mRotationButtonListener);
274 rotationButton.addRotationCallback(mRotationWatcher);
Matthew Ng761562d2018-09-17 11:13:21 -0700275
Riddle Hsu19607772018-11-01 18:11:26 +0800276 // Reset user rotation pref to match that of the WindowManager if starting in locked
277 // mode. This will automatically happen when switching from auto-rotate to locked mode.
278 if (display != null && rotationButton.isRotationLocked()) {
279 final int winRotation = display.getRotation();
280 rotationButton.setRotationLockedAtAngle(winRotation);
281 }
282 } else {
283 mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
Matthew Ng761562d2018-09-17 11:13:21 -0700284 }
Riddle Hsu19607772018-11-01 18:11:26 +0800285 setDisabled2Flags(mDisabledFlags2);
Jason Monk49fa0162017-01-11 09:21:56 -0500286 }
287
288 @Override
289 public void onDestroyView() {
290 super.onDestroyView();
Winson Chungb03d44d2018-09-18 12:42:45 -0700291 if (mNavigationBarView != null) {
292 mNavigationBarView.getBarTransitions().destroy();
293 mNavigationBarView.getLightTransitionsController().destroy(getContext());
294 }
Matthew Ng9c3bce52018-02-01 22:00:31 +0000295 mOverviewProxyService.removeCallback(mOverviewProxyListener);
Jason Monk49fa0162017-01-11 09:21:56 -0500296 getContext().unregisterReceiver(mBroadcastReceiver);
297 }
298
299 @Override
300 public void onSaveInstanceState(Bundle outState) {
301 super.onSaveInstanceState(outState);
302 outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1);
Mike Digmandd2f49e2018-03-16 10:54:22 -0700303 outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2);
Jason Monk49fa0162017-01-11 09:21:56 -0500304 if (mNavigationBarView != null) {
305 mNavigationBarView.getLightTransitionsController().saveState(outState);
306 }
307 }
308
309 @Override
310 public void onConfigurationChanged(Configuration newConfig) {
311 super.onConfigurationChanged(newConfig);
312 final Locale locale = getContext().getResources().getConfiguration().locale;
313 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
314 if (!locale.equals(mLocale) || ld != mLayoutDirection) {
315 if (DEBUG) {
316 Log.v(TAG, String.format(
317 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
318 locale, ld));
319 }
320 mLocale = locale;
321 mLayoutDirection = ld;
322 refreshLayout(ld);
323 }
324 repositionNavigationBar();
325 }
326
327 @Override
328 public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) {
329 if (mNavigationBarView != null) {
330 pw.print(" mNavigationBarWindowState=");
331 pw.println(windowStateToString(mNavigationBarWindowState));
332 pw.print(" mNavigationBarMode=");
333 pw.println(BarTransitions.modeToString(mNavigationBarMode));
334 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
335 }
336
337 pw.print(" mNavigationBarView=");
338 if (mNavigationBarView == null) {
339 pw.println("null");
340 } else {
341 mNavigationBarView.dump(fd, pw, args);
342 }
343 }
344
345 // ----- CommandQueue Callbacks -----
346
347 @Override
348 public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
349 boolean showImeSwitcher) {
350 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
351 int hints = mNavigationIconHints;
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700352 switch (backDisposition) {
353 case InputMethodService.BACK_DISPOSITION_DEFAULT:
354 case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
355 case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
356 if (imeShown) {
357 hints |= NAVIGATION_HINT_BACK_ALT;
358 } else {
359 hints &= ~NAVIGATION_HINT_BACK_ALT;
360 }
361 break;
362 case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING:
363 hints &= ~NAVIGATION_HINT_BACK_ALT;
364 break;
Jason Monk49fa0162017-01-11 09:21:56 -0500365 }
366 if (showImeSwitcher) {
367 hints |= NAVIGATION_HINT_IME_SHOWN;
368 } else {
369 hints &= ~NAVIGATION_HINT_IME_SHOWN;
370 }
371 if (hints == mNavigationIconHints) return;
372
373 mNavigationIconHints = hints;
374
375 if (mNavigationBarView != null) {
376 mNavigationBarView.setNavigationIconHints(hints);
377 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500378 mStatusBar.checkBarModes();
Jason Monk49fa0162017-01-11 09:21:56 -0500379 }
380
381 @Override
382 public void topAppWindowChanged(boolean showMenu) {
383 if (mNavigationBarView != null) {
384 mNavigationBarView.setMenuVisibility(showMenu);
385 }
386 }
387
388 @Override
389 public void setWindowState(int window, int state) {
390 if (mNavigationBarView != null
391 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
392 && mNavigationBarWindowState != state) {
393 mNavigationBarWindowState = state;
394 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
Mike Digman5aeca792018-03-05 11:14:39 -0800395
Matthew Ng761562d2018-09-17 11:13:21 -0700396 mNavigationBarView.getRotateSuggestionButton()
397 .onNavigationBarWindowVisibilityChange(state == WINDOW_STATE_SHOWING);
Jason Monk49fa0162017-01-11 09:21:56 -0500398 }
399 }
400
Mike Digman7d092772018-01-11 12:10:32 -0800401 @Override
Mike Digmane0777312018-01-19 12:41:51 -0800402 public void onRotationProposal(final int rotation, boolean isValid) {
Riddle Hsu19607772018-11-01 18:11:26 +0800403 final int winRotation = mNavigationBarView.getDisplay().getRotation();
Matthew Ng761562d2018-09-17 11:13:21 -0700404 final boolean rotateSuggestionsDisabled = RotationContextButton
405 .hasDisable2RotateSuggestionFlag(mDisabledFlags2);
406 if (RotationContextButton.DEBUG_ROTATION) {
Mike Digman3e33da62018-03-14 16:25:11 -0700407 Log.v(TAG, "onRotationProposal proposedRotation=" + Surface.rotationToString(rotation)
408 + ", winRotation=" + Surface.rotationToString(winRotation)
409 + ", isValid=" + isValid + ", mNavBarWindowState="
410 + StatusBarManager.windowStateToString(mNavigationBarWindowState)
Mike Digmandd2f49e2018-03-16 10:54:22 -0700411 + ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled
Mike Digman3e33da62018-03-14 16:25:11 -0700412 + ", isRotateButtonVisible=" + (mNavigationBarView == null ? "null" :
Matthew Ng761562d2018-09-17 11:13:21 -0700413 mNavigationBarView.getRotateSuggestionButton().isVisible()));
Mike Digman3e33da62018-03-14 16:25:11 -0700414 }
415
Mike Digmandd2f49e2018-03-16 10:54:22 -0700416 // Respect the disabled flag, no need for action as flag change callback will handle hiding
417 if (rotateSuggestionsDisabled) return;
418
Matthew Ng761562d2018-09-17 11:13:21 -0700419 mNavigationBarView.getRotateSuggestionButton()
420 .onRotationProposal(rotation, winRotation, isValid);
Mike Digman50752642018-02-15 13:36:09 -0800421 }
422
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500423 // Injected from StatusBar at creation.
Jason Monk49fa0162017-01-11 09:21:56 -0500424 public void setCurrentSysuiVisibility(int systemUiVisibility) {
425 mSystemUiVisibility = systemUiVisibility;
Matthew Ng5a250202018-10-09 16:09:27 -0700426 final int barMode = mStatusBar.computeBarMode(0, mSystemUiVisibility,
Jason Monk49fa0162017-01-11 09:21:56 -0500427 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
428 View.NAVIGATION_BAR_TRANSPARENT);
Matthew Ng5a250202018-10-09 16:09:27 -0700429 if (barMode != -1) {
430 mNavigationBarMode = barMode;
431 }
Jason Monk49fa0162017-01-11 09:21:56 -0500432 checkNavBarModes();
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500433 mStatusBar.touchAutoHide();
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700434
435 // TODO(115978725): Support light bar controller on external nav bars.
436 if (mLightBarController != null) {
437 mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
438 true /* nbModeChanged */, mNavigationBarMode);
439 }
Jason Monk49fa0162017-01-11 09:21:56 -0500440 }
441
442 @Override
443 public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
444 int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
445 final int oldVal = mSystemUiVisibility;
446 final int newVal = (oldVal & ~mask) | (vis & mask);
447 final int diff = newVal ^ oldVal;
448 boolean nbModeChanged = false;
449 if (diff != 0) {
450 mSystemUiVisibility = newVal;
451
452 // update navigation bar mode
453 final int nbMode = getView() == null
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500454 ? -1 : mStatusBar.computeBarMode(oldVal, newVal,
Jason Monk49fa0162017-01-11 09:21:56 -0500455 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
456 View.NAVIGATION_BAR_TRANSPARENT);
457 nbModeChanged = nbMode != -1;
458 if (nbModeChanged) {
459 if (mNavigationBarMode != nbMode) {
Tracy Zhou1ac592c2018-07-25 13:47:37 -0700460 if (mNavigationBarMode == MODE_TRANSPARENT
461 || mNavigationBarMode == MODE_LIGHTS_OUT_TRANSPARENT) {
462 mNavigationBarView.hideRecentsOnboarding();
463 }
Jason Monk49fa0162017-01-11 09:21:56 -0500464 mNavigationBarMode = nbMode;
465 checkNavBarModes();
466 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500467 mStatusBar.touchAutoHide();
Jason Monk49fa0162017-01-11 09:21:56 -0500468 }
469 }
470
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700471 // TODO(115978725): Support light bar controller on external nav bars.
472 if (mLightBarController != null) {
473 mLightBarController.onNavigationVisibilityChanged(vis, mask, nbModeChanged,
474 mNavigationBarMode);
475 }
Jason Monk49fa0162017-01-11 09:21:56 -0500476 }
477
478 @Override
479 public void disable(int state1, int state2, boolean animate) {
Mike Digmandd2f49e2018-03-16 10:54:22 -0700480 // Navigation bar flags are in both state1 and state2.
481 final int masked = state1 & (StatusBarManager.DISABLE_HOME
Jason Monk49fa0162017-01-11 09:21:56 -0500482 | StatusBarManager.DISABLE_RECENT
483 | StatusBarManager.DISABLE_BACK
484 | StatusBarManager.DISABLE_SEARCH);
485 if (masked != mDisabledFlags1) {
486 mDisabledFlags1 = masked;
487 if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1);
Matthew Ng9c3bce52018-02-01 22:00:31 +0000488 updateScreenPinningGestures();
Jason Monk49fa0162017-01-11 09:21:56 -0500489 }
Mike Digmandd2f49e2018-03-16 10:54:22 -0700490
Riddle Hsu19607772018-11-01 18:11:26 +0800491 // Only default display supports rotation suggestions.
492 if (mIsOnDefaultDisplay) {
493 final int masked2 = state2 & (StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS);
494 if (masked2 != mDisabledFlags2) {
495 mDisabledFlags2 = masked2;
496 setDisabled2Flags(masked2);
497 }
Mike Digmandd2f49e2018-03-16 10:54:22 -0700498 }
499 }
500
501 private void setDisabled2Flags(int state2) {
502 // Method only called on change of disable2 flags
Evan Lairdb8b717f2018-10-25 14:08:32 -0400503 if (mNavigationBarView != null) {
504 mNavigationBarView.getRotateSuggestionButton().onDisable2FlagChanged(state2);
505 }
Jason Monk49fa0162017-01-11 09:21:56 -0500506 }
507
508 // ----- Internal stuffz -----
509
510 private void refreshLayout(int layoutDirection) {
511 if (mNavigationBarView != null) {
512 mNavigationBarView.setLayoutDirection(layoutDirection);
513 }
514 }
515
516 private boolean shouldDisableNavbarGestures() {
Jason Monk297c04e2018-08-23 17:16:59 -0400517 return !mDeviceProvisionedController.isDeviceProvisioned()
Matthew Ngfee0b5b2018-02-21 15:38:21 -0800518 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
Jason Monk49fa0162017-01-11 09:21:56 -0500519 }
520
521 private void repositionNavigationBar() {
522 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
523
524 prepareNavigationBarView();
525
526 mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(),
527 ((View) mNavigationBarView.getParent()).getLayoutParams());
528 }
529
Matthew Ng9c3bce52018-02-01 22:00:31 +0000530 private void updateScreenPinningGestures() {
531 if (mNavigationBarView == null) {
532 return;
533 }
534
535 // Change the cancel pin gesture to home and back if recents button is invisible
536 boolean recentsVisible = mNavigationBarView.isRecentsButtonVisible();
Matthew Ng9c3bce52018-02-01 22:00:31 +0000537 ButtonDispatcher backButton = mNavigationBarView.getBackButton();
538 if (recentsVisible) {
Matthew Ng9c3bce52018-02-01 22:00:31 +0000539 backButton.setOnLongClickListener(this::onLongPressBackRecents);
540 } else {
Matthew Ng9c3bce52018-02-01 22:00:31 +0000541 backButton.setOnLongClickListener(this::onLongPressBackHome);
542 }
543 }
544
Siarhei Vishniakoud002a0a2017-06-05 22:44:37 +0100545 private void notifyNavigationBarScreenOn() {
Matthew Ngd0a73e72018-03-02 15:16:03 -0800546 mNavigationBarView.updateNavButtonIcons();
Jason Monk49fa0162017-01-11 09:21:56 -0500547 }
548
549 private void prepareNavigationBarView() {
550 mNavigationBarView.reorient();
551
552 ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
553 recentsButton.setOnClickListener(this::onRecentsClick);
554 recentsButton.setOnTouchListener(this::onRecentsTouch);
555 recentsButton.setLongClickable(true);
556 recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
557
558 ButtonDispatcher backButton = mNavigationBarView.getBackButton();
559 backButton.setLongClickable(true);
Jason Monk49fa0162017-01-11 09:21:56 -0500560
561 ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
562 homeButton.setOnTouchListener(this::onHomeTouch);
Matthew Ngfee0b5b2018-02-21 15:38:21 -0800563 homeButton.setOnLongClickListener(this::onHomeLongClick);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800564
565 ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
566 accessibilityButton.setOnClickListener(this::onAccessibilityClick);
567 accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
Phil Weaverdb9a7742017-04-18 08:15:06 -0700568 updateAccessibilityServicesState(mAccessibilityManager);
Mike Digman7d092772018-01-11 12:10:32 -0800569
Matthew Ng9c3bce52018-02-01 22:00:31 +0000570 updateScreenPinningGestures();
Jason Monk49fa0162017-01-11 09:21:56 -0500571 }
572
573 private boolean onHomeTouch(View v, MotionEvent event) {
574 if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
575 return true;
576 }
577 // If an incoming call is ringing, HOME is totally disabled.
578 // (The user is already on the InCallUI at this point,
579 // and his ONLY options are to answer or reject the call.)
580 switch (event.getAction()) {
581 case MotionEvent.ACTION_DOWN:
582 mHomeBlockedThisTouch = false;
583 TelecomManager telecomManager =
584 getContext().getSystemService(TelecomManager.class);
585 if (telecomManager != null && telecomManager.isRinging()) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500586 if (mStatusBar.isKeyguardShowing()) {
Jason Monk49fa0162017-01-11 09:21:56 -0500587 Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
588 "No heads up");
589 mHomeBlockedThisTouch = true;
590 return true;
591 }
592 }
593 break;
594 case MotionEvent.ACTION_UP:
595 case MotionEvent.ACTION_CANCEL:
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500596 mStatusBar.awakenDreams();
Jason Monk49fa0162017-01-11 09:21:56 -0500597 break;
598 }
599 return false;
600 }
601
602 private void onVerticalChanged(boolean isVertical) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500603 mStatusBar.setQsScrimEnabled(!isVertical);
Jason Monk49fa0162017-01-11 09:21:56 -0500604 }
605
606 private boolean onNavigationTouch(View v, MotionEvent event) {
Eliot Courtneycb5d3162017-08-09 16:53:15 +0900607 mStatusBar.checkUserAutohide(event);
Jason Monk49fa0162017-01-11 09:21:56 -0500608 return false;
609 }
610
Jason Monk865246d2017-01-19 08:27:01 -0500611 @VisibleForTesting
612 boolean onHomeLongClick(View v) {
Matthew Ng6ff33b72018-02-27 13:47:38 -0800613 if (!mNavigationBarView.isRecentsButtonVisible()
614 && ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
Matthew Ngfee0b5b2018-02-21 15:38:21 -0800615 return onLongPressBackHome(v);
616 }
Jason Monk49fa0162017-01-11 09:21:56 -0500617 if (shouldDisableNavbarGestures()) {
618 return false;
619 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000620 mNavigationBarView.onNavigationButtonLongPress(v);
Mike Digmanc94759d2018-01-23 11:01:21 -0800621 mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS);
Jason Monk49fa0162017-01-11 09:21:56 -0500622 mAssistManager.startAssist(new Bundle() /* args */);
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500623 mStatusBar.awakenDreams();
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800624
Jason Monk49fa0162017-01-11 09:21:56 -0500625 if (mNavigationBarView != null) {
626 mNavigationBarView.abortCurrentGesture();
627 }
628 return true;
629 }
630
631 // additional optimization when we have software system buttons - start loading the recent
632 // tasks on touch down
633 private boolean onRecentsTouch(View v, MotionEvent event) {
634 int action = event.getAction() & MotionEvent.ACTION_MASK;
635 if (action == MotionEvent.ACTION_DOWN) {
636 mCommandQueue.preloadRecentApps();
637 } else if (action == MotionEvent.ACTION_CANCEL) {
638 mCommandQueue.cancelPreloadRecentApps();
639 } else if (action == MotionEvent.ACTION_UP) {
640 if (!v.isPressed()) {
641 mCommandQueue.cancelPreloadRecentApps();
642 }
643 }
644 return false;
645 }
646
647 private void onRecentsClick(View v) {
648 if (LatencyTracker.isEnabled(getContext())) {
649 LatencyTracker.getInstance(getContext()).onActionStart(
650 LatencyTracker.ACTION_TOGGLE_RECENTS);
651 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500652 mStatusBar.awakenDreams();
Jason Monk49fa0162017-01-11 09:21:56 -0500653 mCommandQueue.toggleRecentApps();
654 }
655
Matthew Ng9c3bce52018-02-01 22:00:31 +0000656 private boolean onLongPressBackHome(View v) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000657 mNavigationBarView.onNavigationButtonLongPress(v);
Matthew Ng9c3bce52018-02-01 22:00:31 +0000658 return onLongPressNavigationButtons(v, R.id.back, R.id.home);
659 }
660
661 private boolean onLongPressBackRecents(View v) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000662 mNavigationBarView.onNavigationButtonLongPress(v);
Matthew Ng9c3bce52018-02-01 22:00:31 +0000663 return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps);
664 }
665
Jason Monk49fa0162017-01-11 09:21:56 -0500666 /**
Matthew Ng9c3bce52018-02-01 22:00:31 +0000667 * This handles long-press of both back and recents/home. Back is the common button with
668 * combination of recents if it is visible or home if recents is invisible.
669 * They are handled together to capture them both being long-pressed
Jason Monk49fa0162017-01-11 09:21:56 -0500670 * at the same time to exit screen pinning (lock task).
671 *
Matthew Ng9c3bce52018-02-01 22:00:31 +0000672 * When accessibility mode is on, only a long-press from recents/home
Jason Monk49fa0162017-01-11 09:21:56 -0500673 * is required to exit.
674 *
675 * In all other circumstances we try to pass through long-press events
676 * for Back, so that apps can still use it. Which can be from two things.
677 * 1) Not currently in screen pinning (lock task).
Matthew Ng9c3bce52018-02-01 22:00:31 +0000678 * 2) Back is long-pressed without recents/home.
Jason Monk49fa0162017-01-11 09:21:56 -0500679 */
Matthew Ng9c3bce52018-02-01 22:00:31 +0000680 private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) {
Jason Monk49fa0162017-01-11 09:21:56 -0500681 try {
682 boolean sendBackLongPress = false;
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700683 IActivityTaskManager activityManager = ActivityTaskManager.getService();
Jason Monk49fa0162017-01-11 09:21:56 -0500684 boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
685 boolean inLockTaskMode = activityManager.isInLockTaskMode();
686 if (inLockTaskMode && !touchExplorationEnabled) {
687 long time = System.currentTimeMillis();
Matthew Ng9c3bce52018-02-01 22:00:31 +0000688
Jason Monk49fa0162017-01-11 09:21:56 -0500689 // If we recently long-pressed the other button then they were
690 // long-pressed 'together'
691 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
Benjamin Franza83859f2017-07-03 16:34:14 +0100692 activityManager.stopSystemLockTaskMode();
Jason Monk49fa0162017-01-11 09:21:56 -0500693 // When exiting refresh disabled flags.
Matthew Ngd0a73e72018-03-02 15:16:03 -0800694 mNavigationBarView.updateNavButtonIcons();
Jason Monk49fa0162017-01-11 09:21:56 -0500695 return true;
Matthew Ng9c3bce52018-02-01 22:00:31 +0000696 } else if (v.getId() == btnId1) {
697 ButtonDispatcher button = btnId2 == R.id.recent_apps
698 ? mNavigationBarView.getRecentsButton()
699 : mNavigationBarView.getHomeButton();
700 if (!button.getCurrentView().isPressed()) {
701 // If we aren't pressing recents/home right now then they presses
702 // won't be together, so send the standard long-press action.
703 sendBackLongPress = true;
704 }
Jason Monk49fa0162017-01-11 09:21:56 -0500705 }
706 mLastLockToAppLongPress = time;
707 } else {
708 // If this is back still need to handle sending the long-press event.
Matthew Ng9c3bce52018-02-01 22:00:31 +0000709 if (v.getId() == btnId1) {
Jason Monk49fa0162017-01-11 09:21:56 -0500710 sendBackLongPress = true;
711 } else if (touchExplorationEnabled && inLockTaskMode) {
Matthew Ng9c3bce52018-02-01 22:00:31 +0000712 // When in accessibility mode a long press that is recents/home (not back)
Jason Monk49fa0162017-01-11 09:21:56 -0500713 // should stop lock task.
Benjamin Franza83859f2017-07-03 16:34:14 +0100714 activityManager.stopSystemLockTaskMode();
Jason Monk49fa0162017-01-11 09:21:56 -0500715 // When exiting refresh disabled flags.
Matthew Ngd0a73e72018-03-02 15:16:03 -0800716 mNavigationBarView.updateNavButtonIcons();
Jason Monk49fa0162017-01-11 09:21:56 -0500717 return true;
Matthew Ng9c3bce52018-02-01 22:00:31 +0000718 } else if (v.getId() == btnId2) {
719 return btnId2 == R.id.recent_apps
720 ? onLongPressRecents()
721 : onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView());
Jason Monk49fa0162017-01-11 09:21:56 -0500722 }
723 }
724 if (sendBackLongPress) {
725 KeyButtonView keyButtonView = (KeyButtonView) v;
726 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
727 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
728 return true;
729 }
730 } catch (RemoteException e) {
731 Log.d(TAG, "Unable to reach activity manager", e);
732 }
733 return false;
734 }
735
736 private boolean onLongPressRecents() {
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700737 if (mRecents == null || !ActivityTaskManager.supportsMultiWindow(getContext())
Matthew Ng43db6d22017-06-27 15:29:39 -0700738 || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()
Winson Chung67f5c8b2018-09-24 12:09:19 -0700739 || ActivityManager.isLowRamDeviceStatic()
Winson Chung074c4342018-03-26 17:27:19 -0700740 // If we are connected to the overview service, then disable the recents button
741 || mOverviewProxyService.getProxy() != null) {
Jason Monk49fa0162017-01-11 09:21:56 -0500742 return false;
743 }
744
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500745 return mStatusBar.toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
Jason Monk49fa0162017-01-11 09:21:56 -0500746 MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
747 }
748
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800749 private void onAccessibilityClick(View v) {
750 mAccessibilityManager.notifyAccessibilityButtonClicked();
751 }
752
753 private boolean onAccessibilityLongClick(View v) {
Casey Burkhardt5e8b9802017-03-24 10:07:20 -0700754 Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
755 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
Casey Burkhardt5d614402017-04-06 13:46:50 -0700756 v.getContext().startActivityAsUser(intent, UserHandle.CURRENT);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800757 return true;
758 }
759
Phil Weaverdb9a7742017-04-18 08:15:06 -0700760 private void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800761 int requestingServices = 0;
762 try {
Casey Burkhardt5d614402017-04-06 13:46:50 -0700763 if (Settings.Secure.getIntForUser(mContentResolver,
764 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
765 UserHandle.USER_CURRENT) == 1) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800766 requestingServices++;
767 }
768 } catch (Settings.SettingNotFoundException e) {
769 }
770
Mike Digman90402952018-01-22 16:05:51 -0800771 boolean feedbackEnabled = false;
Casey Burkhardt5d614402017-04-06 13:46:50 -0700772 // AccessibilityManagerService resolves services for the current user since the local
773 // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800774 final List<AccessibilityServiceInfo> services =
Phil Weaverdb9a7742017-04-18 08:15:06 -0700775 accessibilityManager.getEnabledAccessibilityServiceList(
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800776 AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800777 for (int i = services.size() - 1; i >= 0; --i) {
778 AccessibilityServiceInfo info = services.get(i);
779 if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
780 requestingServices++;
781 }
Mike Digman90402952018-01-22 16:05:51 -0800782
783 if (info.feedbackType != 0 && info.feedbackType !=
784 AccessibilityServiceInfo.FEEDBACK_GENERIC) {
785 feedbackEnabled = true;
786 }
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800787 }
788
Matthew Ng761562d2018-09-17 11:13:21 -0700789 mNavigationBarView.getRotateSuggestionButton()
790 .setAccessibilityFeedbackEnabled(feedbackEnabled);
Mike Digman90402952018-01-22 16:05:51 -0800791
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800792 final boolean showAccessibilityButton = requestingServices >= 1;
793 final boolean targetSelection = requestingServices >= 2;
794 mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
795 }
796
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500797 // ----- Methods that StatusBar talks to (should be minimized) -----
Jason Monk49fa0162017-01-11 09:21:56 -0500798
Jason Monk49fa0162017-01-11 09:21:56 -0500799 public void setLightBarController(LightBarController lightBarController) {
800 mLightBarController = lightBarController;
801 mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController());
802 }
803
804 public boolean isSemiTransparent() {
805 return mNavigationBarMode == MODE_SEMI_TRANSPARENT;
806 }
807
Jason Monk49fa0162017-01-11 09:21:56 -0500808 public void disableAnimationsDuringHide(long delay) {
809 mNavigationBarView.setLayoutTransitionsEnabled(false);
810 mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true),
811 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
812 }
813
Jason Monk49fa0162017-01-11 09:21:56 -0500814 public BarTransitions getBarTransitions() {
815 return mNavigationBarView.getBarTransitions();
816 }
817
818 public void checkNavBarModes() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500819 mStatusBar.checkBarMode(mNavigationBarMode,
Jason Monk49fa0162017-01-11 09:21:56 -0500820 mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
821 }
822
823 public void finishBarAnimations() {
824 mNavigationBarView.getBarTransitions().finishAnimations();
825 }
826
Jason Monk91e587e2017-04-13 13:41:23 -0400827 private final AccessibilityServicesStateChangeListener mAccessibilityListener =
828 this::updateAccessibilityServicesState;
829
Casey Burkhardt74922c62017-02-13 12:43:16 -0800830 private class MagnificationContentObserver extends ContentObserver {
831
832 public MagnificationContentObserver(Handler handler) {
833 super(handler);
834 }
835
836 @Override
837 public void onChange(boolean selfChange) {
Phil Weaverdb9a7742017-04-18 08:15:06 -0700838 NavigationBarFragment.this.updateAccessibilityServicesState(mAccessibilityManager);
Casey Burkhardt74922c62017-02-13 12:43:16 -0800839 }
840 }
841
Matthew Ng761562d2018-09-17 11:13:21 -0700842 private final Consumer<Integer> mRotationWatcher = rotation -> {
843 if (mNavigationBarView != null
844 && mNavigationBarView.needsReorient(rotation)) {
845 repositionNavigationBar();
Mike Digman85ff7fa2018-01-23 14:59:52 -0800846 }
Jason Monk49fa0162017-01-11 09:21:56 -0500847 };
848
849 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
850 @Override
851 public void onReceive(Context context, Intent intent) {
852 String action = intent.getAction();
Siarhei Vishniakoud002a0a2017-06-05 22:44:37 +0100853 if (Intent.ACTION_SCREEN_OFF.equals(action)
854 || Intent.ACTION_SCREEN_ON.equals(action)) {
855 notifyNavigationBarScreenOn();
Jason Monk49fa0162017-01-11 09:21:56 -0500856 }
Phil Weavera858c5f2018-04-26 13:27:30 -0700857 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
858 // The accessibility settings may be different for the new user
859 updateAccessibilityServicesState(mAccessibilityManager);
860 };
Jason Monk49fa0162017-01-11 09:21:56 -0500861 }
862 };
863
864 public static View create(Context context, FragmentListener listener) {
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700865 final int displayId = context.getDisplay().getDisplayId();
866 final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
867 final int height = isDefaultDisplay
868 ? LayoutParams.MATCH_PARENT
869 : context.getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
Jason Monk49fa0162017-01-11 09:21:56 -0500870 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700871 LayoutParams.MATCH_PARENT, height,
872 // TODO(b/117478341): Resolve one status bar/ navigation bar assumption
873 isDefaultDisplay
874 ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
875 : WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
Jason Monk49fa0162017-01-11 09:21:56 -0500876 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
877 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
878 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
879 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
880 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
881 | WindowManager.LayoutParams.FLAG_SLIPPERY,
882 PixelFormat.TRANSLUCENT);
883 lp.token = new Binder();
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700884 lp.setTitle("NavigationBar" + displayId);
Phil Weaver8583ae82018-02-13 11:01:24 -0800885 lp.accessibilityTitle = context.getString(R.string.nav_bar);
Jason Monk49fa0162017-01-11 09:21:56 -0500886 lp.windowAnimations = 0;
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700887 if (!isDefaultDisplay) {
888 lp.flags |= LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
889 lp.gravity = Gravity.BOTTOM;
890 }
Jason Monk49fa0162017-01-11 09:21:56 -0500891
892 View navigationBarView = LayoutInflater.from(context).inflate(
893 R.layout.navigation_bar_window, null);
894
895 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
896 if (navigationBarView == null) return null;
897
Riddle Hsu19607772018-11-01 18:11:26 +0800898 final NavigationBarFragment fragment = new NavigationBarFragment();
899 navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
900 @Override
901 public void onViewAttachedToWindow(View v) {
902 final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
903 fragmentHost.getFragmentManager().beginTransaction()
904 .replace(R.id.navigation_bar_frame, fragment, TAG)
905 .commit();
906 fragmentHost.addTagListener(TAG, listener);
907 }
908
909 @Override
910 public void onViewDetachedFromWindow(View v) {
911 FragmentHostManager.removeAndDestroy(v);
912 }
913 });
Jason Monk49fa0162017-01-11 09:21:56 -0500914 context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
Jason Monk49fa0162017-01-11 09:21:56 -0500915 return navigationBarView;
916 }
917}