blob: 42258439190e5544f272f0acc14af08e37e1c0ed [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
22import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
Jason Monk2a6ea9c2017-01-26 11:14:51 -050023import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
24import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
Jason Monk49fa0162017-01-11 09:21:56 -050025
Casey Burkhardt048c2bc2016-12-08 16:09:20 -080026import android.accessibilityservice.AccessibilityServiceInfo;
Mike Digman7d092772018-01-11 12:10:32 -080027import android.animation.Animator;
28import android.animation.AnimatorListenerAdapter;
29import android.animation.AnimatorSet;
30import android.animation.ObjectAnimator;
Jason Monk49fa0162017-01-11 09:21:56 -050031import android.annotation.Nullable;
32import android.app.ActivityManager;
33import android.app.ActivityManagerNative;
34import android.app.Fragment;
35import android.app.IActivityManager;
36import android.app.StatusBarManager;
37import android.content.BroadcastReceiver;
Casey Burkhardtb9dcd662017-03-20 15:10:16 -070038import android.content.ContentResolver;
Jason Monk49fa0162017-01-11 09:21:56 -050039import android.content.Context;
40import android.content.Intent;
41import android.content.IntentFilter;
42import android.content.res.Configuration;
Casey Burkhardt74922c62017-02-13 12:43:16 -080043import android.database.ContentObserver;
Jason Monk49fa0162017-01-11 09:21:56 -050044import android.graphics.PixelFormat;
45import android.graphics.Rect;
Mike Digman7d092772018-01-11 12:10:32 -080046import android.graphics.drawable.AnimatedVectorDrawable;
Jason Monk49fa0162017-01-11 09:21:56 -050047import android.inputmethodservice.InputMethodService;
48import android.os.Binder;
49import android.os.Bundle;
50import android.os.Handler;
51import android.os.IBinder;
52import android.os.Message;
Jason Monk49fa0162017-01-11 09:21:56 -050053import android.os.RemoteException;
54import android.os.UserHandle;
Casey Burkhardt74922c62017-02-13 12:43:16 -080055import android.provider.Settings;
Jason Monk865246d2017-01-19 08:27:01 -050056import android.support.annotation.VisibleForTesting;
Jason Monk49fa0162017-01-11 09:21:56 -050057import android.telecom.TelecomManager;
58import android.text.TextUtils;
59import android.util.Log;
60import android.view.IRotationWatcher.Stub;
61import android.view.KeyEvent;
62import android.view.LayoutInflater;
63import android.view.MotionEvent;
64import android.view.View;
65import android.view.ViewGroup;
66import android.view.WindowManager;
67import android.view.WindowManager.LayoutParams;
68import android.view.WindowManagerGlobal;
69import android.view.accessibility.AccessibilityEvent;
70import android.view.accessibility.AccessibilityManager;
Jason Monk91e587e2017-04-13 13:41:23 -040071import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
Jason Monk49fa0162017-01-11 09:21:56 -050072
73import com.android.internal.logging.MetricsLogger;
74import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monkea03be12017-12-04 11:08:41 -050075import com.android.internal.util.LatencyTracker;
Jason Monk9c7844c2017-01-18 15:21:53 -050076import com.android.systemui.Dependency;
Matthew Ngdc79e5c2017-12-14 17:37:35 -080077import com.android.systemui.OverviewProxyService;
Mike Digman7d092772018-01-11 12:10:32 -080078import com.android.systemui.Interpolators;
Jason Monk49fa0162017-01-11 09:21:56 -050079import com.android.systemui.R;
Jason Monk9c7844c2017-01-18 15:21:53 -050080import com.android.systemui.SysUiServiceProvider;
Jason Monk49fa0162017-01-11 09:21:56 -050081import com.android.systemui.assist.AssistManager;
82import com.android.systemui.fragments.FragmentHostManager;
83import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
84import com.android.systemui.recents.Recents;
Mike Digman7d092772018-01-11 12:10:32 -080085import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
86import com.android.systemui.shared.system.ActivityManagerWrapper;
Jason Monk49fa0162017-01-11 09:21:56 -050087import com.android.systemui.stackdivider.Divider;
88import com.android.systemui.statusbar.CommandQueue;
89import com.android.systemui.statusbar.CommandQueue.Callbacks;
Jason Monk91e587e2017-04-13 13:41:23 -040090import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
Mike Digman7d092772018-01-11 12:10:32 -080091import com.android.systemui.statusbar.policy.KeyButtonDrawable;
Jason Monk49fa0162017-01-11 09:21:56 -050092import com.android.systemui.statusbar.policy.KeyButtonView;
Mike Digman7d092772018-01-11 12:10:32 -080093import com.android.systemui.statusbar.policy.RotationLockController;
Jason Monk49fa0162017-01-11 09:21:56 -050094import com.android.systemui.statusbar.stack.StackStateAnimator;
95
96import java.io.FileDescriptor;
97import java.io.PrintWriter;
Casey Burkhardt048c2bc2016-12-08 16:09:20 -080098import java.util.List;
Jason Monk49fa0162017-01-11 09:21:56 -050099import java.util.Locale;
100
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";
110
111 /** Allow some time inbetween the long press for back and recents. */
112 private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
113
Mike Digman7d092772018-01-11 12:10:32 -0800114 private static final int ROTATE_SUGGESTION_TIMEOUT_MS = 4000;
115
Jason Monk49fa0162017-01-11 09:21:56 -0500116 protected NavigationBarView mNavigationBarView = null;
117 protected AssistManager mAssistManager;
118
119 private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
120
121 private int mNavigationIconHints = 0;
122 private int mNavigationBarMode;
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800123 private AccessibilityManager mAccessibilityManager;
Casey Burkhardt74922c62017-02-13 12:43:16 -0800124 private MagnificationContentObserver mMagnificationObserver;
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700125 private ContentResolver mContentResolver;
Jason Monk49fa0162017-01-11 09:21:56 -0500126
127 private int mDisabledFlags1;
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500128 private StatusBar mStatusBar;
Jason Monk49fa0162017-01-11 09:21:56 -0500129 private Recents mRecents;
130 private Divider mDivider;
131 private WindowManager mWindowManager;
132 private CommandQueue mCommandQueue;
133 private long mLastLockToAppLongPress;
134
135 private Locale mLocale;
136 private int mLayoutDirection;
137
138 private int mSystemUiVisibility;
139 private LightBarController mLightBarController;
Jason Monk49fa0162017-01-11 09:21:56 -0500140
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800141 private OverviewProxyService mOverviewProxyService;
142
Jason Monk49fa0162017-01-11 09:21:56 -0500143 public boolean mHomeBlockedThisTouch;
144
Mike Digman7d092772018-01-11 12:10:32 -0800145 private int mLastRotationSuggestion;
146 private RotationLockController mRotationLockController;
147 private TaskStackListenerImpl mTaskStackListener;
148
149 private final Runnable mRemoveRotationProposal = () -> setRotateSuggestionButtonState(false);
150 private Animator mRotateShowAnimator;
151 private Animator mRotateHideAnimator;
152
153
Jason Monk49fa0162017-01-11 09:21:56 -0500154 // ----- Fragment Lifecycle Callbacks -----
155
156 @Override
157 public void onCreate(@Nullable Bundle savedInstanceState) {
158 super.onCreate(savedInstanceState);
Jason Monk9c7844c2017-01-18 15:21:53 -0500159 mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500160 mCommandQueue.addCallbacks(this);
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500161 mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
Jason Monk9c7844c2017-01-18 15:21:53 -0500162 mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class);
163 mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500164 mWindowManager = getContext().getSystemService(WindowManager.class);
165 mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
Jason Monk91e587e2017-04-13 13:41:23 -0400166 Dependency.get(AccessibilityManagerWrapper.class).addCallback(
167 mAccessibilityListener);
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700168 mContentResolver = getContext().getContentResolver();
Casey Burkhardt74922c62017-02-13 12:43:16 -0800169 mMagnificationObserver = new MagnificationContentObserver(
170 getContext().getMainThreadHandler());
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700171 mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
Casey Burkhardt74922c62017-02-13 12:43:16 -0800172 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false,
Casey Burkhardt5d614402017-04-06 13:46:50 -0700173 mMagnificationObserver, UserHandle.USER_ALL);
Casey Burkhardt74922c62017-02-13 12:43:16 -0800174
Jason Monk49fa0162017-01-11 09:21:56 -0500175 if (savedInstanceState != null) {
176 mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
177 }
Jason Monk9c7844c2017-01-18 15:21:53 -0500178 mAssistManager = Dependency.get(AssistManager.class);
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800179 mOverviewProxyService = Dependency.get(OverviewProxyService.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500180
181 try {
182 WindowManagerGlobal.getWindowManagerService()
Andrii Kulian35fa3c22017-03-11 09:37:28 -0800183 .watchRotation(mRotationWatcher, getContext().getDisplay().getDisplayId());
Jason Monk49fa0162017-01-11 09:21:56 -0500184 } catch (RemoteException e) {
185 throw e.rethrowFromSystemServer();
186 }
Mike Digman7d092772018-01-11 12:10:32 -0800187
188 mRotationLockController = Dependency.get(RotationLockController.class);
189
190 // Register the task stack listener
191 mTaskStackListener = new TaskStackListenerImpl();
192 ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
Jason Monk49fa0162017-01-11 09:21:56 -0500193 }
194
195 @Override
196 public void onDestroy() {
197 super.onDestroy();
198 mCommandQueue.removeCallbacks(this);
Jason Monk91e587e2017-04-13 13:41:23 -0400199 Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
200 mAccessibilityListener);
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700201 mContentResolver.unregisterContentObserver(mMagnificationObserver);
Jason Monk49fa0162017-01-11 09:21:56 -0500202 try {
203 WindowManagerGlobal.getWindowManagerService()
204 .removeRotationWatcher(mRotationWatcher);
205 } catch (RemoteException e) {
206 throw e.rethrowFromSystemServer();
207 }
Mike Digman7d092772018-01-11 12:10:32 -0800208
209 // Unregister the task stack listener
210 ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
Jason Monk49fa0162017-01-11 09:21:56 -0500211 }
212
213 @Override
214 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
215 Bundle savedInstanceState) {
216 return inflater.inflate(R.layout.navigation_bar, container, false);
217 }
218
219 @Override
220 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
221 super.onViewCreated(view, savedInstanceState);
222 mNavigationBarView = (NavigationBarView) view;
223
224 mNavigationBarView.setDisabledFlags(mDisabledFlags1);
225 mNavigationBarView.setComponents(mRecents, mDivider);
226 mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
227 mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
228 if (savedInstanceState != null) {
229 mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
230 }
231
232 prepareNavigationBarView();
233 checkNavBarModes();
234
235 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
236 filter.addAction(Intent.ACTION_SCREEN_ON);
237 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
Siarhei Vishniakoud002a0a2017-06-05 22:44:37 +0100238 notifyNavigationBarScreenOn();
Jason Monk49fa0162017-01-11 09:21:56 -0500239 }
240
241 @Override
242 public void onDestroyView() {
243 super.onDestroyView();
Jason Monkaa573e92017-01-27 17:00:29 -0500244 mNavigationBarView.getLightTransitionsController().destroy(getContext());
Jason Monk49fa0162017-01-11 09:21:56 -0500245 getContext().unregisterReceiver(mBroadcastReceiver);
246 }
247
248 @Override
249 public void onSaveInstanceState(Bundle outState) {
250 super.onSaveInstanceState(outState);
251 outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1);
252 if (mNavigationBarView != null) {
253 mNavigationBarView.getLightTransitionsController().saveState(outState);
254 }
255 }
256
257 @Override
258 public void onConfigurationChanged(Configuration newConfig) {
259 super.onConfigurationChanged(newConfig);
260 final Locale locale = getContext().getResources().getConfiguration().locale;
261 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
262 if (!locale.equals(mLocale) || ld != mLayoutDirection) {
263 if (DEBUG) {
264 Log.v(TAG, String.format(
265 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
266 locale, ld));
267 }
268 mLocale = locale;
269 mLayoutDirection = ld;
270 refreshLayout(ld);
271 }
272 repositionNavigationBar();
273 }
274
275 @Override
276 public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) {
277 if (mNavigationBarView != null) {
278 pw.print(" mNavigationBarWindowState=");
279 pw.println(windowStateToString(mNavigationBarWindowState));
280 pw.print(" mNavigationBarMode=");
281 pw.println(BarTransitions.modeToString(mNavigationBarMode));
282 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
283 }
284
285 pw.print(" mNavigationBarView=");
286 if (mNavigationBarView == null) {
287 pw.println("null");
288 } else {
289 mNavigationBarView.dump(fd, pw, args);
290 }
291 }
292
293 // ----- CommandQueue Callbacks -----
294
295 @Override
296 public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
297 boolean showImeSwitcher) {
298 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
299 int hints = mNavigationIconHints;
300 if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
301 hints |= NAVIGATION_HINT_BACK_ALT;
302 } else {
303 hints &= ~NAVIGATION_HINT_BACK_ALT;
304 }
305 if (showImeSwitcher) {
306 hints |= NAVIGATION_HINT_IME_SHOWN;
307 } else {
308 hints &= ~NAVIGATION_HINT_IME_SHOWN;
309 }
310 if (hints == mNavigationIconHints) return;
311
312 mNavigationIconHints = hints;
313
314 if (mNavigationBarView != null) {
315 mNavigationBarView.setNavigationIconHints(hints);
316 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500317 mStatusBar.checkBarModes();
Jason Monk49fa0162017-01-11 09:21:56 -0500318 }
319
320 @Override
321 public void topAppWindowChanged(boolean showMenu) {
322 if (mNavigationBarView != null) {
323 mNavigationBarView.setMenuVisibility(showMenu);
324 }
325 }
326
327 @Override
328 public void setWindowState(int window, int state) {
329 if (mNavigationBarView != null
330 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
331 && mNavigationBarWindowState != state) {
332 mNavigationBarWindowState = state;
333 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
334 }
335 }
336
Mike Digman7d092772018-01-11 12:10:32 -0800337 @Override
Mike Digmane0777312018-01-19 12:41:51 -0800338 public void onRotationProposal(final int rotation, boolean isValid) {
339 // This method will be called on rotation suggestion changes even if the proposed rotation
340 // is not valid for the top app. Use invalid rotation choices as a signal to remove the
341 // rotate button if shown.
342
343 if (!isValid) {
344 setRotateSuggestionButtonState(false);
345 return;
346 }
347
Mike Digman7d092772018-01-11 12:10:32 -0800348 Handler h = getView().getHandler();
349 if (rotation == mWindowManager.getDefaultDisplay().getRotation()) {
350 // Use this as a signal to remove any current suggestions
351 h.removeCallbacks(mRemoveRotationProposal);
352 setRotateSuggestionButtonState(false);
353 } else {
354 mLastRotationSuggestion = rotation; // Remember rotation for click
355 setRotateSuggestionButtonState(true);
356 h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal
357 h.postDelayed(mRemoveRotationProposal,
358 ROTATE_SUGGESTION_TIMEOUT_MS); // Schedule timeout
359 }
360 }
361
362 public void setRotateSuggestionButtonState(final boolean visible) {
363 setRotateSuggestionButtonState(visible, false);
364 }
365
366 public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) {
367 ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton();
368 boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE;
369
370 // Rerun a show animation to indicate change but don't rerun a hide animation
371 if (!visible && !currentlyVisible) return;
372
373 View currentView = mNavigationBarView.getRotateSuggestionButton().getCurrentView();
374 if (currentView == null) return;
375
376 KeyButtonDrawable kbd = mNavigationBarView.getRotateSuggestionButton().getImageDrawable();
377 if (kbd == null) return;
378
379 AnimatedVectorDrawable animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
380 if (visible) { // Appear and change
381 rotBtn.setVisibility(View.VISIBLE);
382
383 if (skipAnim) {
384 currentView.setAlpha(1f);
385 return;
386 }
387
388 // Start a new animation if running
389 if (mRotateShowAnimator != null) mRotateShowAnimator.pause();
390 if (mRotateHideAnimator != null) mRotateHideAnimator.pause();
391
392 ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha",
393 0f, 1f);
394 appearFade.setDuration(100);
395 appearFade.setInterpolator(Interpolators.LINEAR);
396 mRotateShowAnimator = appearFade;
397 appearFade.start();
398
399 // Run the rotate icon's animation
400 animIcon.reset();
401 animIcon.start();
402 } else { // Hide
403
404 if (skipAnim) {
405 rotBtn.setVisibility(View.INVISIBLE);
406 return;
407 }
408
409 // Don't start any new hide animations if one is running
410 if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
411 // Pause any active show animations but don't reset the AVD to avoid jumps
412 if (mRotateShowAnimator != null) mRotateShowAnimator.pause();
413
414 ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha",
415 0f);
416 fadeOut.setDuration(100);
417 fadeOut.setInterpolator(Interpolators.LINEAR);
418 fadeOut.addListener(new AnimatorListenerAdapter() {
419 @Override
420 public void onAnimationEnd(Animator animation) {
421 rotBtn.setVisibility(View.INVISIBLE);
422 }
423 });
424
425 mRotateHideAnimator = fadeOut;
426 fadeOut.start();
427 }
428 }
429
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500430 // Injected from StatusBar at creation.
Jason Monk49fa0162017-01-11 09:21:56 -0500431 public void setCurrentSysuiVisibility(int systemUiVisibility) {
432 mSystemUiVisibility = systemUiVisibility;
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500433 mNavigationBarMode = mStatusBar.computeBarMode(0, mSystemUiVisibility,
Jason Monk49fa0162017-01-11 09:21:56 -0500434 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
435 View.NAVIGATION_BAR_TRANSPARENT);
436 checkNavBarModes();
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500437 mStatusBar.touchAutoHide();
Jason Monk49fa0162017-01-11 09:21:56 -0500438 mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
439 true /* nbModeChanged */, mNavigationBarMode);
440 }
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) {
460 mNavigationBarMode = nbMode;
461 checkNavBarModes();
462 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500463 mStatusBar.touchAutoHide();
Jason Monk49fa0162017-01-11 09:21:56 -0500464 }
465 }
466
467 mLightBarController.onNavigationVisibilityChanged(vis, mask, nbModeChanged,
468 mNavigationBarMode);
469 }
470
471 @Override
472 public void disable(int state1, int state2, boolean animate) {
473 // All navigation bar flags are in state1.
474 int masked = state1 & (StatusBarManager.DISABLE_HOME
475 | StatusBarManager.DISABLE_RECENT
476 | StatusBarManager.DISABLE_BACK
477 | StatusBarManager.DISABLE_SEARCH);
478 if (masked != mDisabledFlags1) {
479 mDisabledFlags1 = masked;
480 if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1);
481 }
482 }
483
484 // ----- Internal stuffz -----
485
486 private void refreshLayout(int layoutDirection) {
487 if (mNavigationBarView != null) {
488 mNavigationBarView.setLayoutDirection(layoutDirection);
489 }
490 }
491
492 private boolean shouldDisableNavbarGestures() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500493 return !mStatusBar.isDeviceProvisioned()
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800494 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0
495 || mOverviewProxyService.getProxy() != null;
Jason Monk49fa0162017-01-11 09:21:56 -0500496 }
497
498 private void repositionNavigationBar() {
499 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
500
501 prepareNavigationBarView();
502
503 mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(),
504 ((View) mNavigationBarView.getParent()).getLayoutParams());
505 }
506
Siarhei Vishniakoud002a0a2017-06-05 22:44:37 +0100507 private void notifyNavigationBarScreenOn() {
508 mNavigationBarView.notifyScreenOn();
Jason Monk49fa0162017-01-11 09:21:56 -0500509 }
510
511 private void prepareNavigationBarView() {
512 mNavigationBarView.reorient();
513
514 ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
515 recentsButton.setOnClickListener(this::onRecentsClick);
516 recentsButton.setOnTouchListener(this::onRecentsTouch);
517 recentsButton.setLongClickable(true);
518 recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
519
520 ButtonDispatcher backButton = mNavigationBarView.getBackButton();
521 backButton.setLongClickable(true);
522 backButton.setOnLongClickListener(this::onLongPressBackRecents);
523
524 ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
525 homeButton.setOnTouchListener(this::onHomeTouch);
526 homeButton.setOnLongClickListener(this::onHomeLongClick);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800527
528 ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
529 accessibilityButton.setOnClickListener(this::onAccessibilityClick);
530 accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
Phil Weaverdb9a7742017-04-18 08:15:06 -0700531 updateAccessibilityServicesState(mAccessibilityManager);
Mike Digman7d092772018-01-11 12:10:32 -0800532
533 ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
534 rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
Jason Monk49fa0162017-01-11 09:21:56 -0500535 }
536
537 private boolean onHomeTouch(View v, MotionEvent event) {
538 if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
539 return true;
540 }
541 // If an incoming call is ringing, HOME is totally disabled.
542 // (The user is already on the InCallUI at this point,
543 // and his ONLY options are to answer or reject the call.)
544 switch (event.getAction()) {
545 case MotionEvent.ACTION_DOWN:
546 mHomeBlockedThisTouch = false;
547 TelecomManager telecomManager =
548 getContext().getSystemService(TelecomManager.class);
549 if (telecomManager != null && telecomManager.isRinging()) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500550 if (mStatusBar.isKeyguardShowing()) {
Jason Monk49fa0162017-01-11 09:21:56 -0500551 Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
552 "No heads up");
553 mHomeBlockedThisTouch = true;
554 return true;
555 }
556 }
557 break;
558 case MotionEvent.ACTION_UP:
559 case MotionEvent.ACTION_CANCEL:
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500560 mStatusBar.awakenDreams();
Jason Monk49fa0162017-01-11 09:21:56 -0500561 break;
562 }
563 return false;
564 }
565
566 private void onVerticalChanged(boolean isVertical) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500567 mStatusBar.setQsScrimEnabled(!isVertical);
Jason Monk49fa0162017-01-11 09:21:56 -0500568 }
569
570 private boolean onNavigationTouch(View v, MotionEvent event) {
Eliot Courtneycb5d3162017-08-09 16:53:15 +0900571 mStatusBar.checkUserAutohide(event);
Jason Monk49fa0162017-01-11 09:21:56 -0500572 return false;
573 }
574
Jason Monk865246d2017-01-19 08:27:01 -0500575 @VisibleForTesting
576 boolean onHomeLongClick(View v) {
Jason Monk49fa0162017-01-11 09:21:56 -0500577 if (shouldDisableNavbarGestures()) {
578 return false;
579 }
580 MetricsLogger.action(getContext(), MetricsEvent.ACTION_ASSIST_LONG_PRESS);
581 mAssistManager.startAssist(new Bundle() /* args */);
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500582 mStatusBar.awakenDreams();
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800583
Jason Monk49fa0162017-01-11 09:21:56 -0500584 if (mNavigationBarView != null) {
585 mNavigationBarView.abortCurrentGesture();
586 }
587 return true;
588 }
589
590 // additional optimization when we have software system buttons - start loading the recent
591 // tasks on touch down
592 private boolean onRecentsTouch(View v, MotionEvent event) {
593 int action = event.getAction() & MotionEvent.ACTION_MASK;
594 if (action == MotionEvent.ACTION_DOWN) {
595 mCommandQueue.preloadRecentApps();
596 } else if (action == MotionEvent.ACTION_CANCEL) {
597 mCommandQueue.cancelPreloadRecentApps();
598 } else if (action == MotionEvent.ACTION_UP) {
599 if (!v.isPressed()) {
600 mCommandQueue.cancelPreloadRecentApps();
601 }
602 }
603 return false;
604 }
605
606 private void onRecentsClick(View v) {
607 if (LatencyTracker.isEnabled(getContext())) {
608 LatencyTracker.getInstance(getContext()).onActionStart(
609 LatencyTracker.ACTION_TOGGLE_RECENTS);
610 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500611 mStatusBar.awakenDreams();
Jason Monk49fa0162017-01-11 09:21:56 -0500612 mCommandQueue.toggleRecentApps();
613 }
614
615 /**
616 * This handles long-press of both back and recents. They are
617 * handled together to capture them both being long-pressed
618 * at the same time to exit screen pinning (lock task).
619 *
620 * When accessibility mode is on, only a long-press from recents
621 * is required to exit.
622 *
623 * In all other circumstances we try to pass through long-press events
624 * for Back, so that apps can still use it. Which can be from two things.
625 * 1) Not currently in screen pinning (lock task).
626 * 2) Back is long-pressed without recents.
627 */
628 private boolean onLongPressBackRecents(View v) {
629 try {
630 boolean sendBackLongPress = false;
631 IActivityManager activityManager = ActivityManagerNative.getDefault();
632 boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
633 boolean inLockTaskMode = activityManager.isInLockTaskMode();
634 if (inLockTaskMode && !touchExplorationEnabled) {
635 long time = System.currentTimeMillis();
636 // If we recently long-pressed the other button then they were
637 // long-pressed 'together'
638 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
Benjamin Franza83859f2017-07-03 16:34:14 +0100639 activityManager.stopSystemLockTaskMode();
Jason Monk49fa0162017-01-11 09:21:56 -0500640 // When exiting refresh disabled flags.
641 mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
642 return true;
643 } else if ((v.getId() == R.id.back)
644 && !mNavigationBarView.getRecentsButton().getCurrentView().isPressed()) {
645 // If we aren't pressing recents right now then they presses
646 // won't be together, so send the standard long-press action.
647 sendBackLongPress = true;
648 }
649 mLastLockToAppLongPress = time;
650 } else {
651 // If this is back still need to handle sending the long-press event.
652 if (v.getId() == R.id.back) {
653 sendBackLongPress = true;
654 } else if (touchExplorationEnabled && inLockTaskMode) {
655 // When in accessibility mode a long press that is recents (not back)
656 // should stop lock task.
Benjamin Franza83859f2017-07-03 16:34:14 +0100657 activityManager.stopSystemLockTaskMode();
Jason Monk49fa0162017-01-11 09:21:56 -0500658 // When exiting refresh disabled flags.
659 mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
660 return true;
661 } else if (v.getId() == R.id.recent_apps) {
662 return onLongPressRecents();
663 }
664 }
665 if (sendBackLongPress) {
666 KeyButtonView keyButtonView = (KeyButtonView) v;
667 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
668 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
669 return true;
670 }
671 } catch (RemoteException e) {
672 Log.d(TAG, "Unable to reach activity manager", e);
673 }
674 return false;
675 }
676
677 private boolean onLongPressRecents() {
Erik Wolsheimer9be3a062017-05-31 14:59:57 -0700678 if (mRecents == null || !ActivityManager.supportsMultiWindow(getContext())
Matthew Ng43db6d22017-06-27 15:29:39 -0700679 || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()
680 || Recents.getConfiguration().isLowRamDevice) {
Jason Monk49fa0162017-01-11 09:21:56 -0500681 return false;
682 }
683
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500684 return mStatusBar.toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
Jason Monk49fa0162017-01-11 09:21:56 -0500685 MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
686 }
687
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800688 private void onAccessibilityClick(View v) {
689 mAccessibilityManager.notifyAccessibilityButtonClicked();
690 }
691
692 private boolean onAccessibilityLongClick(View v) {
Casey Burkhardt5e8b9802017-03-24 10:07:20 -0700693 Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
694 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
Casey Burkhardt5d614402017-04-06 13:46:50 -0700695 v.getContext().startActivityAsUser(intent, UserHandle.CURRENT);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800696 return true;
697 }
698
Phil Weaverdb9a7742017-04-18 08:15:06 -0700699 private void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800700 int requestingServices = 0;
701 try {
Casey Burkhardt5d614402017-04-06 13:46:50 -0700702 if (Settings.Secure.getIntForUser(mContentResolver,
703 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
704 UserHandle.USER_CURRENT) == 1) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800705 requestingServices++;
706 }
707 } catch (Settings.SettingNotFoundException e) {
708 }
709
Casey Burkhardt5d614402017-04-06 13:46:50 -0700710 // AccessibilityManagerService resolves services for the current user since the local
711 // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800712 final List<AccessibilityServiceInfo> services =
Phil Weaverdb9a7742017-04-18 08:15:06 -0700713 accessibilityManager.getEnabledAccessibilityServiceList(
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800714 AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800715 for (int i = services.size() - 1; i >= 0; --i) {
716 AccessibilityServiceInfo info = services.get(i);
717 if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
718 requestingServices++;
719 }
720 }
721
722 final boolean showAccessibilityButton = requestingServices >= 1;
723 final boolean targetSelection = requestingServices >= 2;
724 mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
725 }
726
Mike Digman7d092772018-01-11 12:10:32 -0800727 private void onRotateSuggestionClick(View v) {
728 mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion);
729 }
730
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500731 // ----- Methods that StatusBar talks to (should be minimized) -----
Jason Monk49fa0162017-01-11 09:21:56 -0500732
Jason Monk49fa0162017-01-11 09:21:56 -0500733 public void setLightBarController(LightBarController lightBarController) {
734 mLightBarController = lightBarController;
735 mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController());
736 }
737
738 public boolean isSemiTransparent() {
739 return mNavigationBarMode == MODE_SEMI_TRANSPARENT;
740 }
741
Jason Monk49fa0162017-01-11 09:21:56 -0500742 public void disableAnimationsDuringHide(long delay) {
743 mNavigationBarView.setLayoutTransitionsEnabled(false);
744 mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true),
745 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
746 }
747
Jason Monk49fa0162017-01-11 09:21:56 -0500748 public BarTransitions getBarTransitions() {
749 return mNavigationBarView.getBarTransitions();
750 }
751
752 public void checkNavBarModes() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500753 mStatusBar.checkBarMode(mNavigationBarMode,
Jason Monk49fa0162017-01-11 09:21:56 -0500754 mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
755 }
756
757 public void finishBarAnimations() {
758 mNavigationBarView.getBarTransitions().finishAnimations();
759 }
760
Jason Monk91e587e2017-04-13 13:41:23 -0400761 private final AccessibilityServicesStateChangeListener mAccessibilityListener =
762 this::updateAccessibilityServicesState;
763
Casey Burkhardt74922c62017-02-13 12:43:16 -0800764 private class MagnificationContentObserver extends ContentObserver {
765
766 public MagnificationContentObserver(Handler handler) {
767 super(handler);
768 }
769
770 @Override
771 public void onChange(boolean selfChange) {
Phil Weaverdb9a7742017-04-18 08:15:06 -0700772 NavigationBarFragment.this.updateAccessibilityServicesState(mAccessibilityManager);
Casey Burkhardt74922c62017-02-13 12:43:16 -0800773 }
774 }
775
Jason Monk49fa0162017-01-11 09:21:56 -0500776 private final Stub mRotationWatcher = new Stub() {
777 @Override
778 public void onRotationChanged(int rotation) throws RemoteException {
Mike Digman7d092772018-01-11 12:10:32 -0800779 // If the screen rotation changes while locked, update lock rotation to flow with
780 // new screen rotation and hide any showing suggestions.
781 if (mRotationLockController.isRotationLocked()) {
782 mRotationLockController.setRotationLockedAtAngle(true, rotation);
783 setRotateSuggestionButtonState(false, true);
784 }
785
Jason Monk49fa0162017-01-11 09:21:56 -0500786 // We need this to be scheduled as early as possible to beat the redrawing of
787 // window in response to the orientation change.
788 Handler h = getView().getHandler();
789 Message msg = Message.obtain(h, () -> {
790 if (mNavigationBarView != null
791 && mNavigationBarView.needsReorient(rotation)) {
792 repositionNavigationBar();
793 }
794 });
795 msg.setAsynchronous(true);
796 h.sendMessageAtFrontOfQueue(msg);
797 }
798 };
799
800 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
801 @Override
802 public void onReceive(Context context, Intent intent) {
803 String action = intent.getAction();
Siarhei Vishniakoud002a0a2017-06-05 22:44:37 +0100804 if (Intent.ACTION_SCREEN_OFF.equals(action)
805 || Intent.ACTION_SCREEN_ON.equals(action)) {
806 notifyNavigationBarScreenOn();
Jason Monk49fa0162017-01-11 09:21:56 -0500807 }
808 }
809 };
810
Mike Digman7d092772018-01-11 12:10:32 -0800811 class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
812 // Invalidate any rotation suggestion on task change or activity orientation change
813 // Note: all callbacks happen on main thread
814
815 @Override
816 public void onTaskStackChanged() {
817 setRotateSuggestionButtonState(false);
818 }
819
820 @Override
821 public void onTaskRemoved(int taskId) {
822 setRotateSuggestionButtonState(false);
823 }
824
825 @Override
826 public void onTaskMovedToFront(int taskId) {
827 setRotateSuggestionButtonState(false);
828 }
829
830 @Override
831 public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
832 setRotateSuggestionButtonState(false);
833 }
834 }
835
Jason Monk49fa0162017-01-11 09:21:56 -0500836 public static View create(Context context, FragmentListener listener) {
837 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
838 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
839 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
840 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
841 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
842 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
843 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
844 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
845 | WindowManager.LayoutParams.FLAG_SLIPPERY,
846 PixelFormat.TRANSLUCENT);
847 lp.token = new Binder();
Jason Monk49fa0162017-01-11 09:21:56 -0500848 lp.setTitle("NavigationBar");
849 lp.windowAnimations = 0;
850
851 View navigationBarView = LayoutInflater.from(context).inflate(
852 R.layout.navigation_bar_window, null);
853
854 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
855 if (navigationBarView == null) return null;
856
857 context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
858 FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView);
859 NavigationBarFragment fragment = new NavigationBarFragment();
860 fragmentHost.getFragmentManager().beginTransaction()
861 .replace(R.id.navigation_bar_frame, fragment, TAG)
862 .commit();
863 fragmentHost.addTagListener(TAG, listener);
864 return navigationBarView;
865 }
866}