blob: 4406b1495b2c7145c666d375246bdaf07628ac87 [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
Jason Monk49fa0162017-01-11 09:21:56 -0500147 public boolean mHomeBlockedThisTouch;
148
Matthew Ng9c3bce52018-02-01 22:00:31 +0000149 private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
150 @Override
151 public void onConnectionChanged(boolean isConnected) {
Winson Chungf9e30272018-03-26 17:25:36 -0700152 mNavigationBarView.updateStates();
Matthew Ng9c3bce52018-02-01 22:00:31 +0000153 updateScreenPinningGestures();
154 }
155
156 @Override
Matthew Ng2ea93b72018-03-14 19:43:18 +0000157 public void onQuickStepStarted() {
Mike Digman85a9bea2018-02-23 15:08:53 -0800158 // Use navbar dragging as a signal to hide the rotate button
Matthew Ng761562d2018-09-17 11:13:21 -0700159 mNavigationBarView.getRotateSuggestionButton().setRotateSuggestionButtonState(false);
Matthew Ng90ef0632018-08-15 13:53:15 -0700160
161 // Hide the notifications panel when quick step starts
162 mStatusBar.collapsePanel(true /* animate */);
Matthew Ng9c3bce52018-02-01 22:00:31 +0000163 }
Matthew Ng8f25fb962018-01-16 17:17:24 -0800164
165 @Override
166 public void onInteractionFlagsChanged(@InteractionType int flags) {
167 mNavigationBarView.updateStates();
Winson Chungf9e30272018-03-26 17:25:36 -0700168 updateScreenPinningGestures();
Matthew Ng8f25fb962018-01-16 17:17:24 -0800169 }
Matthew Ng96985e72018-05-08 15:46:13 -0700170
171 @Override
172 public void onBackButtonAlphaChanged(float alpha, boolean animate) {
173 final ButtonDispatcher backButton = mNavigationBarView.getBackButton();
Matthew Ngb687c8a2018-10-02 11:31:38 -0700174 if (QuickStepController.shouldhideBackButton(getContext())) {
Matthew Ngc83b9892018-08-21 16:31:13 -0700175 // If property was changed to hide/show back button, going home will trigger
176 // launcher to to change the back button alpha to reflect property change
177 backButton.setVisibility(View.GONE);
178 } else {
179 backButton.setVisibility(alpha > 0 ? View.VISIBLE : View.INVISIBLE);
180 backButton.setAlpha(alpha, animate);
181 }
Matthew Ng96985e72018-05-08 15:46:13 -0700182 }
Matthew Ng9c3bce52018-02-01 22:00:31 +0000183 };
Mike Digman7d092772018-01-11 12:10:32 -0800184
Matthew Ng761562d2018-09-17 11:13:21 -0700185 private final ContextButtonListener mRotationButtonListener = new ContextButtonListener() {
186 @Override
187 public void onVisibilityChanged(ContextualButton button, boolean visible) {
188 if (visible) {
189 // If the button will actually become visible and the navbar is about to hide,
190 // tell the statusbar to keep it around for longer
191 mStatusBar.touchAutoHide();
192 }
193 }
194 };
195
Jason Monk49fa0162017-01-11 09:21:56 -0500196 // ----- Fragment Lifecycle Callbacks -----
197
198 @Override
199 public void onCreate(@Nullable Bundle savedInstanceState) {
200 super.onCreate(savedInstanceState);
Jason Monk9c7844c2017-01-18 15:21:53 -0500201 mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500202 mCommandQueue.addCallbacks(this);
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500203 mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
Jason Monk9c7844c2017-01-18 15:21:53 -0500204 mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class);
205 mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500206 mWindowManager = getContext().getSystemService(WindowManager.class);
207 mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
Jason Monk91e587e2017-04-13 13:41:23 -0400208 Dependency.get(AccessibilityManagerWrapper.class).addCallback(
209 mAccessibilityListener);
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700210 mContentResolver = getContext().getContentResolver();
Casey Burkhardt74922c62017-02-13 12:43:16 -0800211 mMagnificationObserver = new MagnificationContentObserver(
212 getContext().getMainThreadHandler());
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700213 mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
Casey Burkhardt74922c62017-02-13 12:43:16 -0800214 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false,
Casey Burkhardt5d614402017-04-06 13:46:50 -0700215 mMagnificationObserver, UserHandle.USER_ALL);
Casey Burkhardt74922c62017-02-13 12:43:16 -0800216
Jason Monk49fa0162017-01-11 09:21:56 -0500217 if (savedInstanceState != null) {
218 mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
Mike Digmandd2f49e2018-03-16 10:54:22 -0700219 mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0);
Jason Monk49fa0162017-01-11 09:21:56 -0500220 }
Jason Monk9c7844c2017-01-18 15:21:53 -0500221 mAssistManager = Dependency.get(AssistManager.class);
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800222 mOverviewProxyService = Dependency.get(OverviewProxyService.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500223 }
224
225 @Override
226 public void onDestroy() {
227 super.onDestroy();
228 mCommandQueue.removeCallbacks(this);
Jason Monk91e587e2017-04-13 13:41:23 -0400229 Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
230 mAccessibilityListener);
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700231 mContentResolver.unregisterContentObserver(mMagnificationObserver);
Jason Monk49fa0162017-01-11 09:21:56 -0500232 }
233
234 @Override
235 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
236 Bundle savedInstanceState) {
237 return inflater.inflate(R.layout.navigation_bar, container, false);
238 }
239
240 @Override
241 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
242 super.onViewCreated(view, savedInstanceState);
243 mNavigationBarView = (NavigationBarView) view;
244
Winson Chung67f5c8b2018-09-24 12:09:19 -0700245 mNavigationBarView.setComponents(mStatusBar.getPanel());
Matthew Ngf0f84542018-10-15 11:53:22 -0700246 mNavigationBarView.setDisabledFlags(mDisabledFlags1);
Jason Monk49fa0162017-01-11 09:21:56 -0500247 mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
248 mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
249 if (savedInstanceState != null) {
250 mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
251 }
252
253 prepareNavigationBarView();
254 checkNavBarModes();
255
Mike Digmandd2f49e2018-03-16 10:54:22 -0700256 setDisabled2Flags(mDisabledFlags2);
257
Jason Monk49fa0162017-01-11 09:21:56 -0500258 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
259 filter.addAction(Intent.ACTION_SCREEN_ON);
Phil Weavera858c5f2018-04-26 13:27:30 -0700260 filter.addAction(Intent.ACTION_USER_SWITCHED);
Jason Monk49fa0162017-01-11 09:21:56 -0500261 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
Siarhei Vishniakoud002a0a2017-06-05 22:44:37 +0100262 notifyNavigationBarScreenOn();
Matthew Ng9c3bce52018-02-01 22:00:31 +0000263 mOverviewProxyService.addCallback(mOverviewProxyListener);
Matthew Ng761562d2018-09-17 11:13:21 -0700264
265 RotationContextButton rotationButton = mNavigationBarView.getRotateSuggestionButton();
266 rotationButton.setListener(mRotationButtonListener);
267 rotationButton.addRotationCallback(mRotationWatcher);
268
269 // Reset user rotation pref to match that of the WindowManager if starting in locked mode
270 // This will automatically happen when switching from auto-rotate to locked mode
271 if (rotationButton.isRotationLocked()) {
272 final int winRotation = mWindowManager.getDefaultDisplay().getRotation();
273 rotationButton.setRotationLockedAtAngle(winRotation);
274 }
Jason Monk49fa0162017-01-11 09:21:56 -0500275 }
276
277 @Override
278 public void onDestroyView() {
279 super.onDestroyView();
Winson Chungb03d44d2018-09-18 12:42:45 -0700280 if (mNavigationBarView != null) {
281 mNavigationBarView.getBarTransitions().destroy();
282 mNavigationBarView.getLightTransitionsController().destroy(getContext());
283 }
Matthew Ng9c3bce52018-02-01 22:00:31 +0000284 mOverviewProxyService.removeCallback(mOverviewProxyListener);
Jason Monk49fa0162017-01-11 09:21:56 -0500285 getContext().unregisterReceiver(mBroadcastReceiver);
286 }
287
288 @Override
289 public void onSaveInstanceState(Bundle outState) {
290 super.onSaveInstanceState(outState);
291 outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1);
Mike Digmandd2f49e2018-03-16 10:54:22 -0700292 outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2);
Jason Monk49fa0162017-01-11 09:21:56 -0500293 if (mNavigationBarView != null) {
294 mNavigationBarView.getLightTransitionsController().saveState(outState);
295 }
296 }
297
298 @Override
299 public void onConfigurationChanged(Configuration newConfig) {
300 super.onConfigurationChanged(newConfig);
301 final Locale locale = getContext().getResources().getConfiguration().locale;
302 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
303 if (!locale.equals(mLocale) || ld != mLayoutDirection) {
304 if (DEBUG) {
305 Log.v(TAG, String.format(
306 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
307 locale, ld));
308 }
309 mLocale = locale;
310 mLayoutDirection = ld;
311 refreshLayout(ld);
312 }
313 repositionNavigationBar();
314 }
315
316 @Override
317 public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) {
318 if (mNavigationBarView != null) {
319 pw.print(" mNavigationBarWindowState=");
320 pw.println(windowStateToString(mNavigationBarWindowState));
321 pw.print(" mNavigationBarMode=");
322 pw.println(BarTransitions.modeToString(mNavigationBarMode));
323 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
324 }
325
326 pw.print(" mNavigationBarView=");
327 if (mNavigationBarView == null) {
328 pw.println("null");
329 } else {
330 mNavigationBarView.dump(fd, pw, args);
331 }
332 }
333
334 // ----- CommandQueue Callbacks -----
335
336 @Override
337 public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
338 boolean showImeSwitcher) {
339 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
340 int hints = mNavigationIconHints;
Yohei Yukawa386f50e2018-03-14 13:03:42 -0700341 switch (backDisposition) {
342 case InputMethodService.BACK_DISPOSITION_DEFAULT:
343 case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
344 case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
345 if (imeShown) {
346 hints |= NAVIGATION_HINT_BACK_ALT;
347 } else {
348 hints &= ~NAVIGATION_HINT_BACK_ALT;
349 }
350 break;
351 case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING:
352 hints &= ~NAVIGATION_HINT_BACK_ALT;
353 break;
Jason Monk49fa0162017-01-11 09:21:56 -0500354 }
355 if (showImeSwitcher) {
356 hints |= NAVIGATION_HINT_IME_SHOWN;
357 } else {
358 hints &= ~NAVIGATION_HINT_IME_SHOWN;
359 }
360 if (hints == mNavigationIconHints) return;
361
362 mNavigationIconHints = hints;
363
364 if (mNavigationBarView != null) {
365 mNavigationBarView.setNavigationIconHints(hints);
366 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500367 mStatusBar.checkBarModes();
Jason Monk49fa0162017-01-11 09:21:56 -0500368 }
369
370 @Override
371 public void topAppWindowChanged(boolean showMenu) {
372 if (mNavigationBarView != null) {
373 mNavigationBarView.setMenuVisibility(showMenu);
374 }
375 }
376
377 @Override
378 public void setWindowState(int window, int state) {
379 if (mNavigationBarView != null
380 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
381 && mNavigationBarWindowState != state) {
382 mNavigationBarWindowState = state;
383 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
Mike Digman5aeca792018-03-05 11:14:39 -0800384
Matthew Ng761562d2018-09-17 11:13:21 -0700385 mNavigationBarView.getRotateSuggestionButton()
386 .onNavigationBarWindowVisibilityChange(state == WINDOW_STATE_SHOWING);
Jason Monk49fa0162017-01-11 09:21:56 -0500387 }
388 }
389
Mike Digman7d092772018-01-11 12:10:32 -0800390 @Override
Mike Digmane0777312018-01-19 12:41:51 -0800391 public void onRotationProposal(final int rotation, boolean isValid) {
Mike Digman3e33da62018-03-14 16:25:11 -0700392 final int winRotation = mWindowManager.getDefaultDisplay().getRotation();
Matthew Ng761562d2018-09-17 11:13:21 -0700393 final boolean rotateSuggestionsDisabled = RotationContextButton
394 .hasDisable2RotateSuggestionFlag(mDisabledFlags2);
395 if (RotationContextButton.DEBUG_ROTATION) {
Mike Digman3e33da62018-03-14 16:25:11 -0700396 Log.v(TAG, "onRotationProposal proposedRotation=" + Surface.rotationToString(rotation)
397 + ", winRotation=" + Surface.rotationToString(winRotation)
398 + ", isValid=" + isValid + ", mNavBarWindowState="
399 + StatusBarManager.windowStateToString(mNavigationBarWindowState)
Mike Digmandd2f49e2018-03-16 10:54:22 -0700400 + ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled
Mike Digman3e33da62018-03-14 16:25:11 -0700401 + ", isRotateButtonVisible=" + (mNavigationBarView == null ? "null" :
Matthew Ng761562d2018-09-17 11:13:21 -0700402 mNavigationBarView.getRotateSuggestionButton().isVisible()));
Mike Digman3e33da62018-03-14 16:25:11 -0700403 }
404
Mike Digmandd2f49e2018-03-16 10:54:22 -0700405 // Respect the disabled flag, no need for action as flag change callback will handle hiding
406 if (rotateSuggestionsDisabled) return;
407
Matthew Ng761562d2018-09-17 11:13:21 -0700408 mNavigationBarView.getRotateSuggestionButton()
409 .onRotationProposal(rotation, winRotation, isValid);
Mike Digman50752642018-02-15 13:36:09 -0800410 }
411
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500412 // Injected from StatusBar at creation.
Jason Monk49fa0162017-01-11 09:21:56 -0500413 public void setCurrentSysuiVisibility(int systemUiVisibility) {
414 mSystemUiVisibility = systemUiVisibility;
Matthew Ng5a250202018-10-09 16:09:27 -0700415 final int barMode = mStatusBar.computeBarMode(0, mSystemUiVisibility,
Jason Monk49fa0162017-01-11 09:21:56 -0500416 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
417 View.NAVIGATION_BAR_TRANSPARENT);
Matthew Ng5a250202018-10-09 16:09:27 -0700418 if (barMode != -1) {
419 mNavigationBarMode = barMode;
420 }
Jason Monk49fa0162017-01-11 09:21:56 -0500421 checkNavBarModes();
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500422 mStatusBar.touchAutoHide();
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700423
424 // TODO(115978725): Support light bar controller on external nav bars.
425 if (mLightBarController != null) {
426 mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
427 true /* nbModeChanged */, mNavigationBarMode);
428 }
Jason Monk49fa0162017-01-11 09:21:56 -0500429 }
430
431 @Override
432 public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
433 int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
434 final int oldVal = mSystemUiVisibility;
435 final int newVal = (oldVal & ~mask) | (vis & mask);
436 final int diff = newVal ^ oldVal;
437 boolean nbModeChanged = false;
438 if (diff != 0) {
439 mSystemUiVisibility = newVal;
440
441 // update navigation bar mode
442 final int nbMode = getView() == null
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500443 ? -1 : mStatusBar.computeBarMode(oldVal, newVal,
Jason Monk49fa0162017-01-11 09:21:56 -0500444 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
445 View.NAVIGATION_BAR_TRANSPARENT);
446 nbModeChanged = nbMode != -1;
447 if (nbModeChanged) {
448 if (mNavigationBarMode != nbMode) {
Tracy Zhou1ac592c2018-07-25 13:47:37 -0700449 if (mNavigationBarMode == MODE_TRANSPARENT
450 || mNavigationBarMode == MODE_LIGHTS_OUT_TRANSPARENT) {
451 mNavigationBarView.hideRecentsOnboarding();
452 }
Jason Monk49fa0162017-01-11 09:21:56 -0500453 mNavigationBarMode = nbMode;
454 checkNavBarModes();
455 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500456 mStatusBar.touchAutoHide();
Jason Monk49fa0162017-01-11 09:21:56 -0500457 }
458 }
459
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700460 // TODO(115978725): Support light bar controller on external nav bars.
461 if (mLightBarController != null) {
462 mLightBarController.onNavigationVisibilityChanged(vis, mask, nbModeChanged,
463 mNavigationBarMode);
464 }
Jason Monk49fa0162017-01-11 09:21:56 -0500465 }
466
467 @Override
468 public void disable(int state1, int state2, boolean animate) {
Mike Digmandd2f49e2018-03-16 10:54:22 -0700469 // Navigation bar flags are in both state1 and state2.
470 final int masked = state1 & (StatusBarManager.DISABLE_HOME
Jason Monk49fa0162017-01-11 09:21:56 -0500471 | StatusBarManager.DISABLE_RECENT
472 | StatusBarManager.DISABLE_BACK
473 | StatusBarManager.DISABLE_SEARCH);
474 if (masked != mDisabledFlags1) {
475 mDisabledFlags1 = masked;
476 if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1);
Matthew Ng9c3bce52018-02-01 22:00:31 +0000477 updateScreenPinningGestures();
Jason Monk49fa0162017-01-11 09:21:56 -0500478 }
Mike Digmandd2f49e2018-03-16 10:54:22 -0700479
480 final int masked2 = state2 & (StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS);
481 if (masked2 != mDisabledFlags2) {
482 mDisabledFlags2 = masked2;
483 setDisabled2Flags(masked2);
484 }
485 }
486
487 private void setDisabled2Flags(int state2) {
488 // Method only called on change of disable2 flags
Evan Lairdb8b717f2018-10-25 14:08:32 -0400489 if (mNavigationBarView != null) {
490 mNavigationBarView.getRotateSuggestionButton().onDisable2FlagChanged(state2);
491 }
Jason Monk49fa0162017-01-11 09:21:56 -0500492 }
493
494 // ----- Internal stuffz -----
495
496 private void refreshLayout(int layoutDirection) {
497 if (mNavigationBarView != null) {
498 mNavigationBarView.setLayoutDirection(layoutDirection);
499 }
500 }
501
502 private boolean shouldDisableNavbarGestures() {
Jason Monk297c04e2018-08-23 17:16:59 -0400503 return !mDeviceProvisionedController.isDeviceProvisioned()
Matthew Ngfee0b5b2018-02-21 15:38:21 -0800504 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
Jason Monk49fa0162017-01-11 09:21:56 -0500505 }
506
507 private void repositionNavigationBar() {
508 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
509
510 prepareNavigationBarView();
511
512 mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(),
513 ((View) mNavigationBarView.getParent()).getLayoutParams());
514 }
515
Matthew Ng9c3bce52018-02-01 22:00:31 +0000516 private void updateScreenPinningGestures() {
517 if (mNavigationBarView == null) {
518 return;
519 }
520
521 // Change the cancel pin gesture to home and back if recents button is invisible
522 boolean recentsVisible = mNavigationBarView.isRecentsButtonVisible();
Matthew Ng9c3bce52018-02-01 22:00:31 +0000523 ButtonDispatcher backButton = mNavigationBarView.getBackButton();
524 if (recentsVisible) {
Matthew Ng9c3bce52018-02-01 22:00:31 +0000525 backButton.setOnLongClickListener(this::onLongPressBackRecents);
526 } else {
Matthew Ng9c3bce52018-02-01 22:00:31 +0000527 backButton.setOnLongClickListener(this::onLongPressBackHome);
528 }
529 }
530
Siarhei Vishniakoud002a0a2017-06-05 22:44:37 +0100531 private void notifyNavigationBarScreenOn() {
Matthew Ngd0a73e72018-03-02 15:16:03 -0800532 mNavigationBarView.updateNavButtonIcons();
Jason Monk49fa0162017-01-11 09:21:56 -0500533 }
534
535 private void prepareNavigationBarView() {
536 mNavigationBarView.reorient();
537
538 ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
539 recentsButton.setOnClickListener(this::onRecentsClick);
540 recentsButton.setOnTouchListener(this::onRecentsTouch);
541 recentsButton.setLongClickable(true);
542 recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
543
544 ButtonDispatcher backButton = mNavigationBarView.getBackButton();
545 backButton.setLongClickable(true);
Jason Monk49fa0162017-01-11 09:21:56 -0500546
547 ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
548 homeButton.setOnTouchListener(this::onHomeTouch);
Matthew Ngfee0b5b2018-02-21 15:38:21 -0800549 homeButton.setOnLongClickListener(this::onHomeLongClick);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800550
551 ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
552 accessibilityButton.setOnClickListener(this::onAccessibilityClick);
553 accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
Phil Weaverdb9a7742017-04-18 08:15:06 -0700554 updateAccessibilityServicesState(mAccessibilityManager);
Mike Digman7d092772018-01-11 12:10:32 -0800555
Matthew Ng9c3bce52018-02-01 22:00:31 +0000556 updateScreenPinningGestures();
Jason Monk49fa0162017-01-11 09:21:56 -0500557 }
558
559 private boolean onHomeTouch(View v, MotionEvent event) {
560 if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
561 return true;
562 }
563 // If an incoming call is ringing, HOME is totally disabled.
564 // (The user is already on the InCallUI at this point,
565 // and his ONLY options are to answer or reject the call.)
566 switch (event.getAction()) {
567 case MotionEvent.ACTION_DOWN:
568 mHomeBlockedThisTouch = false;
569 TelecomManager telecomManager =
570 getContext().getSystemService(TelecomManager.class);
571 if (telecomManager != null && telecomManager.isRinging()) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500572 if (mStatusBar.isKeyguardShowing()) {
Jason Monk49fa0162017-01-11 09:21:56 -0500573 Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
574 "No heads up");
575 mHomeBlockedThisTouch = true;
576 return true;
577 }
578 }
579 break;
580 case MotionEvent.ACTION_UP:
581 case MotionEvent.ACTION_CANCEL:
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500582 mStatusBar.awakenDreams();
Jason Monk49fa0162017-01-11 09:21:56 -0500583 break;
584 }
585 return false;
586 }
587
588 private void onVerticalChanged(boolean isVertical) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500589 mStatusBar.setQsScrimEnabled(!isVertical);
Jason Monk49fa0162017-01-11 09:21:56 -0500590 }
591
592 private boolean onNavigationTouch(View v, MotionEvent event) {
Eliot Courtneycb5d3162017-08-09 16:53:15 +0900593 mStatusBar.checkUserAutohide(event);
Jason Monk49fa0162017-01-11 09:21:56 -0500594 return false;
595 }
596
Jason Monk865246d2017-01-19 08:27:01 -0500597 @VisibleForTesting
598 boolean onHomeLongClick(View v) {
Matthew Ng6ff33b72018-02-27 13:47:38 -0800599 if (!mNavigationBarView.isRecentsButtonVisible()
600 && ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
Matthew Ngfee0b5b2018-02-21 15:38:21 -0800601 return onLongPressBackHome(v);
602 }
Jason Monk49fa0162017-01-11 09:21:56 -0500603 if (shouldDisableNavbarGestures()) {
604 return false;
605 }
Matthew Ng2ea93b72018-03-14 19:43:18 +0000606 mNavigationBarView.onNavigationButtonLongPress(v);
Mike Digmanc94759d2018-01-23 11:01:21 -0800607 mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS);
Jason Monk49fa0162017-01-11 09:21:56 -0500608 mAssistManager.startAssist(new Bundle() /* args */);
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500609 mStatusBar.awakenDreams();
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800610
Jason Monk49fa0162017-01-11 09:21:56 -0500611 if (mNavigationBarView != null) {
612 mNavigationBarView.abortCurrentGesture();
613 }
614 return true;
615 }
616
617 // additional optimization when we have software system buttons - start loading the recent
618 // tasks on touch down
619 private boolean onRecentsTouch(View v, MotionEvent event) {
620 int action = event.getAction() & MotionEvent.ACTION_MASK;
621 if (action == MotionEvent.ACTION_DOWN) {
622 mCommandQueue.preloadRecentApps();
623 } else if (action == MotionEvent.ACTION_CANCEL) {
624 mCommandQueue.cancelPreloadRecentApps();
625 } else if (action == MotionEvent.ACTION_UP) {
626 if (!v.isPressed()) {
627 mCommandQueue.cancelPreloadRecentApps();
628 }
629 }
630 return false;
631 }
632
633 private void onRecentsClick(View v) {
634 if (LatencyTracker.isEnabled(getContext())) {
635 LatencyTracker.getInstance(getContext()).onActionStart(
636 LatencyTracker.ACTION_TOGGLE_RECENTS);
637 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500638 mStatusBar.awakenDreams();
Jason Monk49fa0162017-01-11 09:21:56 -0500639 mCommandQueue.toggleRecentApps();
640 }
641
Matthew Ng9c3bce52018-02-01 22:00:31 +0000642 private boolean onLongPressBackHome(View v) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000643 mNavigationBarView.onNavigationButtonLongPress(v);
Matthew Ng9c3bce52018-02-01 22:00:31 +0000644 return onLongPressNavigationButtons(v, R.id.back, R.id.home);
645 }
646
647 private boolean onLongPressBackRecents(View v) {
Matthew Ng2ea93b72018-03-14 19:43:18 +0000648 mNavigationBarView.onNavigationButtonLongPress(v);
Matthew Ng9c3bce52018-02-01 22:00:31 +0000649 return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps);
650 }
651
Jason Monk49fa0162017-01-11 09:21:56 -0500652 /**
Matthew Ng9c3bce52018-02-01 22:00:31 +0000653 * This handles long-press of both back and recents/home. Back is the common button with
654 * combination of recents if it is visible or home if recents is invisible.
655 * They are handled together to capture them both being long-pressed
Jason Monk49fa0162017-01-11 09:21:56 -0500656 * at the same time to exit screen pinning (lock task).
657 *
Matthew Ng9c3bce52018-02-01 22:00:31 +0000658 * When accessibility mode is on, only a long-press from recents/home
Jason Monk49fa0162017-01-11 09:21:56 -0500659 * is required to exit.
660 *
661 * In all other circumstances we try to pass through long-press events
662 * for Back, so that apps can still use it. Which can be from two things.
663 * 1) Not currently in screen pinning (lock task).
Matthew Ng9c3bce52018-02-01 22:00:31 +0000664 * 2) Back is long-pressed without recents/home.
Jason Monk49fa0162017-01-11 09:21:56 -0500665 */
Matthew Ng9c3bce52018-02-01 22:00:31 +0000666 private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) {
Jason Monk49fa0162017-01-11 09:21:56 -0500667 try {
668 boolean sendBackLongPress = false;
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700669 IActivityTaskManager activityManager = ActivityTaskManager.getService();
Jason Monk49fa0162017-01-11 09:21:56 -0500670 boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
671 boolean inLockTaskMode = activityManager.isInLockTaskMode();
672 if (inLockTaskMode && !touchExplorationEnabled) {
673 long time = System.currentTimeMillis();
Matthew Ng9c3bce52018-02-01 22:00:31 +0000674
Jason Monk49fa0162017-01-11 09:21:56 -0500675 // If we recently long-pressed the other button then they were
676 // long-pressed 'together'
677 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
Benjamin Franza83859f2017-07-03 16:34:14 +0100678 activityManager.stopSystemLockTaskMode();
Jason Monk49fa0162017-01-11 09:21:56 -0500679 // When exiting refresh disabled flags.
Matthew Ngd0a73e72018-03-02 15:16:03 -0800680 mNavigationBarView.updateNavButtonIcons();
Jason Monk49fa0162017-01-11 09:21:56 -0500681 return true;
Matthew Ng9c3bce52018-02-01 22:00:31 +0000682 } else if (v.getId() == btnId1) {
683 ButtonDispatcher button = btnId2 == R.id.recent_apps
684 ? mNavigationBarView.getRecentsButton()
685 : mNavigationBarView.getHomeButton();
686 if (!button.getCurrentView().isPressed()) {
687 // If we aren't pressing recents/home right now then they presses
688 // won't be together, so send the standard long-press action.
689 sendBackLongPress = true;
690 }
Jason Monk49fa0162017-01-11 09:21:56 -0500691 }
692 mLastLockToAppLongPress = time;
693 } else {
694 // If this is back still need to handle sending the long-press event.
Matthew Ng9c3bce52018-02-01 22:00:31 +0000695 if (v.getId() == btnId1) {
Jason Monk49fa0162017-01-11 09:21:56 -0500696 sendBackLongPress = true;
697 } else if (touchExplorationEnabled && inLockTaskMode) {
Matthew Ng9c3bce52018-02-01 22:00:31 +0000698 // When in accessibility mode a long press that is recents/home (not back)
Jason Monk49fa0162017-01-11 09:21:56 -0500699 // should stop lock task.
Benjamin Franza83859f2017-07-03 16:34:14 +0100700 activityManager.stopSystemLockTaskMode();
Jason Monk49fa0162017-01-11 09:21:56 -0500701 // When exiting refresh disabled flags.
Matthew Ngd0a73e72018-03-02 15:16:03 -0800702 mNavigationBarView.updateNavButtonIcons();
Jason Monk49fa0162017-01-11 09:21:56 -0500703 return true;
Matthew Ng9c3bce52018-02-01 22:00:31 +0000704 } else if (v.getId() == btnId2) {
705 return btnId2 == R.id.recent_apps
706 ? onLongPressRecents()
707 : onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView());
Jason Monk49fa0162017-01-11 09:21:56 -0500708 }
709 }
710 if (sendBackLongPress) {
711 KeyButtonView keyButtonView = (KeyButtonView) v;
712 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
713 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
714 return true;
715 }
716 } catch (RemoteException e) {
717 Log.d(TAG, "Unable to reach activity manager", e);
718 }
719 return false;
720 }
721
722 private boolean onLongPressRecents() {
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700723 if (mRecents == null || !ActivityTaskManager.supportsMultiWindow(getContext())
Matthew Ng43db6d22017-06-27 15:29:39 -0700724 || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()
Winson Chung67f5c8b2018-09-24 12:09:19 -0700725 || ActivityManager.isLowRamDeviceStatic()
Winson Chung074c4342018-03-26 17:27:19 -0700726 // If we are connected to the overview service, then disable the recents button
727 || mOverviewProxyService.getProxy() != null) {
Jason Monk49fa0162017-01-11 09:21:56 -0500728 return false;
729 }
730
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500731 return mStatusBar.toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
Jason Monk49fa0162017-01-11 09:21:56 -0500732 MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
733 }
734
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800735 private void onAccessibilityClick(View v) {
736 mAccessibilityManager.notifyAccessibilityButtonClicked();
737 }
738
739 private boolean onAccessibilityLongClick(View v) {
Casey Burkhardt5e8b9802017-03-24 10:07:20 -0700740 Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
741 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
Casey Burkhardt5d614402017-04-06 13:46:50 -0700742 v.getContext().startActivityAsUser(intent, UserHandle.CURRENT);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800743 return true;
744 }
745
Phil Weaverdb9a7742017-04-18 08:15:06 -0700746 private void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800747 int requestingServices = 0;
748 try {
Casey Burkhardt5d614402017-04-06 13:46:50 -0700749 if (Settings.Secure.getIntForUser(mContentResolver,
750 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
751 UserHandle.USER_CURRENT) == 1) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800752 requestingServices++;
753 }
754 } catch (Settings.SettingNotFoundException e) {
755 }
756
Mike Digman90402952018-01-22 16:05:51 -0800757 boolean feedbackEnabled = false;
Casey Burkhardt5d614402017-04-06 13:46:50 -0700758 // AccessibilityManagerService resolves services for the current user since the local
759 // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800760 final List<AccessibilityServiceInfo> services =
Phil Weaverdb9a7742017-04-18 08:15:06 -0700761 accessibilityManager.getEnabledAccessibilityServiceList(
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800762 AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800763 for (int i = services.size() - 1; i >= 0; --i) {
764 AccessibilityServiceInfo info = services.get(i);
765 if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
766 requestingServices++;
767 }
Mike Digman90402952018-01-22 16:05:51 -0800768
769 if (info.feedbackType != 0 && info.feedbackType !=
770 AccessibilityServiceInfo.FEEDBACK_GENERIC) {
771 feedbackEnabled = true;
772 }
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800773 }
774
Matthew Ng761562d2018-09-17 11:13:21 -0700775 mNavigationBarView.getRotateSuggestionButton()
776 .setAccessibilityFeedbackEnabled(feedbackEnabled);
Mike Digman90402952018-01-22 16:05:51 -0800777
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800778 final boolean showAccessibilityButton = requestingServices >= 1;
779 final boolean targetSelection = requestingServices >= 2;
780 mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
781 }
782
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500783 // ----- Methods that StatusBar talks to (should be minimized) -----
Jason Monk49fa0162017-01-11 09:21:56 -0500784
Jason Monk49fa0162017-01-11 09:21:56 -0500785 public void setLightBarController(LightBarController lightBarController) {
786 mLightBarController = lightBarController;
787 mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController());
788 }
789
790 public boolean isSemiTransparent() {
791 return mNavigationBarMode == MODE_SEMI_TRANSPARENT;
792 }
793
Jason Monk49fa0162017-01-11 09:21:56 -0500794 public void disableAnimationsDuringHide(long delay) {
795 mNavigationBarView.setLayoutTransitionsEnabled(false);
796 mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true),
797 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
798 }
799
Jason Monk49fa0162017-01-11 09:21:56 -0500800 public BarTransitions getBarTransitions() {
801 return mNavigationBarView.getBarTransitions();
802 }
803
804 public void checkNavBarModes() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500805 mStatusBar.checkBarMode(mNavigationBarMode,
Jason Monk49fa0162017-01-11 09:21:56 -0500806 mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
807 }
808
809 public void finishBarAnimations() {
810 mNavigationBarView.getBarTransitions().finishAnimations();
811 }
812
Jason Monk91e587e2017-04-13 13:41:23 -0400813 private final AccessibilityServicesStateChangeListener mAccessibilityListener =
814 this::updateAccessibilityServicesState;
815
Casey Burkhardt74922c62017-02-13 12:43:16 -0800816 private class MagnificationContentObserver extends ContentObserver {
817
818 public MagnificationContentObserver(Handler handler) {
819 super(handler);
820 }
821
822 @Override
823 public void onChange(boolean selfChange) {
Phil Weaverdb9a7742017-04-18 08:15:06 -0700824 NavigationBarFragment.this.updateAccessibilityServicesState(mAccessibilityManager);
Casey Burkhardt74922c62017-02-13 12:43:16 -0800825 }
826 }
827
Matthew Ng761562d2018-09-17 11:13:21 -0700828 private final Consumer<Integer> mRotationWatcher = rotation -> {
829 if (mNavigationBarView != null
830 && mNavigationBarView.needsReorient(rotation)) {
831 repositionNavigationBar();
Mike Digman85ff7fa2018-01-23 14:59:52 -0800832 }
Jason Monk49fa0162017-01-11 09:21:56 -0500833 };
834
835 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
836 @Override
837 public void onReceive(Context context, Intent intent) {
838 String action = intent.getAction();
Siarhei Vishniakoud002a0a2017-06-05 22:44:37 +0100839 if (Intent.ACTION_SCREEN_OFF.equals(action)
840 || Intent.ACTION_SCREEN_ON.equals(action)) {
841 notifyNavigationBarScreenOn();
Jason Monk49fa0162017-01-11 09:21:56 -0500842 }
Phil Weavera858c5f2018-04-26 13:27:30 -0700843 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
844 // The accessibility settings may be different for the new user
845 updateAccessibilityServicesState(mAccessibilityManager);
846 };
Jason Monk49fa0162017-01-11 09:21:56 -0500847 }
848 };
849
850 public static View create(Context context, FragmentListener listener) {
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700851 final int displayId = context.getDisplay().getDisplayId();
852 final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
853 final int height = isDefaultDisplay
854 ? LayoutParams.MATCH_PARENT
855 : context.getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
Jason Monk49fa0162017-01-11 09:21:56 -0500856 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700857 LayoutParams.MATCH_PARENT, height,
858 // TODO(b/117478341): Resolve one status bar/ navigation bar assumption
859 isDefaultDisplay
860 ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
861 : WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
Jason Monk49fa0162017-01-11 09:21:56 -0500862 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
863 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
864 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
865 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
866 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
867 | WindowManager.LayoutParams.FLAG_SLIPPERY,
868 PixelFormat.TRANSLUCENT);
869 lp.token = new Binder();
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700870 lp.setTitle("NavigationBar" + displayId);
Phil Weaver8583ae82018-02-13 11:01:24 -0800871 lp.accessibilityTitle = context.getString(R.string.nav_bar);
Jason Monk49fa0162017-01-11 09:21:56 -0500872 lp.windowAnimations = 0;
Andrii Kulian3ddd7de2018-10-10 00:33:19 -0700873 if (!isDefaultDisplay) {
874 lp.flags |= LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
875 lp.gravity = Gravity.BOTTOM;
876 }
Jason Monk49fa0162017-01-11 09:21:56 -0500877
878 View navigationBarView = LayoutInflater.from(context).inflate(
879 R.layout.navigation_bar_window, null);
880
881 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
882 if (navigationBarView == null) return null;
883
884 context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
885 FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView);
886 NavigationBarFragment fragment = new NavigationBarFragment();
887 fragmentHost.getFragmentManager().beginTransaction()
888 .replace(R.id.navigation_bar_frame, fragment, TAG)
889 .commit();
890 fragmentHost.addTagListener(TAG, listener);
891 return navigationBarView;
892 }
893}