blob: 2b52b48a68191a2fc8d5d607cabed2d6481eb6ab [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;
Jason Monk49fa0162017-01-11 09:21:56 -050027import android.annotation.Nullable;
28import android.app.ActivityManager;
29import android.app.ActivityManagerNative;
30import android.app.Fragment;
31import android.app.IActivityManager;
32import android.app.StatusBarManager;
33import android.content.BroadcastReceiver;
34import android.content.Context;
35import android.content.Intent;
36import android.content.IntentFilter;
37import android.content.res.Configuration;
Casey Burkhardt74922c62017-02-13 12:43:16 -080038import android.database.ContentObserver;
Jason Monk49fa0162017-01-11 09:21:56 -050039import android.graphics.PixelFormat;
40import android.graphics.Rect;
41import android.inputmethodservice.InputMethodService;
42import android.os.Binder;
43import android.os.Bundle;
44import android.os.Handler;
45import android.os.IBinder;
46import android.os.Message;
47import android.os.PowerManager;
48import android.os.RemoteException;
49import android.os.UserHandle;
Casey Burkhardt74922c62017-02-13 12:43:16 -080050import android.provider.Settings;
Jason Monk865246d2017-01-19 08:27:01 -050051import android.support.annotation.VisibleForTesting;
Jason Monk49fa0162017-01-11 09:21:56 -050052import android.telecom.TelecomManager;
53import android.text.TextUtils;
54import android.util.Log;
55import android.view.IRotationWatcher.Stub;
56import android.view.KeyEvent;
57import android.view.LayoutInflater;
58import android.view.MotionEvent;
59import android.view.View;
60import android.view.ViewGroup;
61import android.view.WindowManager;
62import android.view.WindowManager.LayoutParams;
63import android.view.WindowManagerGlobal;
64import android.view.accessibility.AccessibilityEvent;
65import android.view.accessibility.AccessibilityManager;
66
67import com.android.internal.logging.MetricsLogger;
68import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
69import com.android.keyguard.LatencyTracker;
Jason Monk9c7844c2017-01-18 15:21:53 -050070import com.android.systemui.Dependency;
Jason Monk49fa0162017-01-11 09:21:56 -050071import com.android.systemui.R;
Jason Monk9c7844c2017-01-18 15:21:53 -050072import com.android.systemui.SysUiServiceProvider;
Jason Monk49fa0162017-01-11 09:21:56 -050073import com.android.systemui.assist.AssistManager;
74import com.android.systemui.fragments.FragmentHostManager;
75import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
76import com.android.systemui.recents.Recents;
77import com.android.systemui.stackdivider.Divider;
78import com.android.systemui.statusbar.CommandQueue;
79import com.android.systemui.statusbar.CommandQueue.Callbacks;
80import com.android.systemui.statusbar.policy.KeyButtonView;
81import com.android.systemui.statusbar.stack.StackStateAnimator;
82
83import java.io.FileDescriptor;
84import java.io.PrintWriter;
Casey Burkhardt048c2bc2016-12-08 16:09:20 -080085import java.util.List;
Jason Monk49fa0162017-01-11 09:21:56 -050086import java.util.Locale;
87
88/**
89 * Fragment containing the NavigationBarFragment. Contains logic for what happens
90 * on clicks and view states of the nav bar.
91 */
92public class NavigationBarFragment extends Fragment implements Callbacks {
93
94 private static final String TAG = "NavigationBar";
95 private static final boolean DEBUG = false;
96 private static final String EXTRA_DISABLE_STATE = "disabled_state";
97
98 /** Allow some time inbetween the long press for back and recents. */
99 private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
100
101 protected NavigationBarView mNavigationBarView = null;
102 protected AssistManager mAssistManager;
103
104 private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
105
106 private int mNavigationIconHints = 0;
107 private int mNavigationBarMode;
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800108 private AccessibilityManager mAccessibilityManager;
Casey Burkhardt74922c62017-02-13 12:43:16 -0800109 private MagnificationContentObserver mMagnificationObserver;
Jason Monk49fa0162017-01-11 09:21:56 -0500110
111 private int mDisabledFlags1;
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500112 private StatusBar mStatusBar;
Jason Monk49fa0162017-01-11 09:21:56 -0500113 private Recents mRecents;
114 private Divider mDivider;
115 private WindowManager mWindowManager;
116 private CommandQueue mCommandQueue;
117 private long mLastLockToAppLongPress;
118
119 private Locale mLocale;
120 private int mLayoutDirection;
121
122 private int mSystemUiVisibility;
123 private LightBarController mLightBarController;
Jason Monk49fa0162017-01-11 09:21:56 -0500124
125 public boolean mHomeBlockedThisTouch;
126
127 // ----- Fragment Lifecycle Callbacks -----
128
129 @Override
130 public void onCreate(@Nullable Bundle savedInstanceState) {
131 super.onCreate(savedInstanceState);
Jason Monk9c7844c2017-01-18 15:21:53 -0500132 mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500133 mCommandQueue.addCallbacks(this);
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500134 mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
Jason Monk9c7844c2017-01-18 15:21:53 -0500135 mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class);
136 mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500137 mWindowManager = getContext().getSystemService(WindowManager.class);
138 mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800139 mAccessibilityManager.addAccessibilityServicesStateChangeListener(
140 this::updateAccessibilityServicesState);
Casey Burkhardt74922c62017-02-13 12:43:16 -0800141 mMagnificationObserver = new MagnificationContentObserver(
142 getContext().getMainThreadHandler());
143 getContext().getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
144 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false,
145 mMagnificationObserver);
146
Jason Monk49fa0162017-01-11 09:21:56 -0500147 if (savedInstanceState != null) {
148 mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
149 }
Jason Monk9c7844c2017-01-18 15:21:53 -0500150 mAssistManager = Dependency.get(AssistManager.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500151
152 try {
153 WindowManagerGlobal.getWindowManagerService()
Andrii Kulian35fa3c22017-03-11 09:37:28 -0800154 .watchRotation(mRotationWatcher, getContext().getDisplay().getDisplayId());
Jason Monk49fa0162017-01-11 09:21:56 -0500155 } catch (RemoteException e) {
156 throw e.rethrowFromSystemServer();
157 }
158 }
159
160 @Override
161 public void onDestroy() {
162 super.onDestroy();
163 mCommandQueue.removeCallbacks(this);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800164 mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
165 this::updateAccessibilityServicesState);
Casey Burkhardt74922c62017-02-13 12:43:16 -0800166 getContext().getContentResolver().unregisterContentObserver(mMagnificationObserver);
Jason Monk49fa0162017-01-11 09:21:56 -0500167 try {
168 WindowManagerGlobal.getWindowManagerService()
169 .removeRotationWatcher(mRotationWatcher);
170 } catch (RemoteException e) {
171 throw e.rethrowFromSystemServer();
172 }
173 }
174
175 @Override
176 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
177 Bundle savedInstanceState) {
178 return inflater.inflate(R.layout.navigation_bar, container, false);
179 }
180
181 @Override
182 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
183 super.onViewCreated(view, savedInstanceState);
184 mNavigationBarView = (NavigationBarView) view;
185
186 mNavigationBarView.setDisabledFlags(mDisabledFlags1);
187 mNavigationBarView.setComponents(mRecents, mDivider);
188 mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
189 mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
190 if (savedInstanceState != null) {
191 mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
192 }
193
194 prepareNavigationBarView();
195 checkNavBarModes();
196
197 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
198 filter.addAction(Intent.ACTION_SCREEN_ON);
199 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
200 PowerManager pm = getContext().getSystemService(PowerManager.class);
201 notifyNavigationBarScreenOn(pm.isScreenOn());
202 }
203
204 @Override
205 public void onDestroyView() {
206 super.onDestroyView();
Jason Monkaa573e92017-01-27 17:00:29 -0500207 mNavigationBarView.getLightTransitionsController().destroy(getContext());
Jason Monk49fa0162017-01-11 09:21:56 -0500208 getContext().unregisterReceiver(mBroadcastReceiver);
209 }
210
211 @Override
212 public void onSaveInstanceState(Bundle outState) {
213 super.onSaveInstanceState(outState);
214 outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1);
215 if (mNavigationBarView != null) {
216 mNavigationBarView.getLightTransitionsController().saveState(outState);
217 }
218 }
219
220 @Override
221 public void onConfigurationChanged(Configuration newConfig) {
222 super.onConfigurationChanged(newConfig);
223 final Locale locale = getContext().getResources().getConfiguration().locale;
224 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
225 if (!locale.equals(mLocale) || ld != mLayoutDirection) {
226 if (DEBUG) {
227 Log.v(TAG, String.format(
228 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
229 locale, ld));
230 }
231 mLocale = locale;
232 mLayoutDirection = ld;
233 refreshLayout(ld);
234 }
235 repositionNavigationBar();
236 }
237
238 @Override
239 public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) {
240 if (mNavigationBarView != null) {
241 pw.print(" mNavigationBarWindowState=");
242 pw.println(windowStateToString(mNavigationBarWindowState));
243 pw.print(" mNavigationBarMode=");
244 pw.println(BarTransitions.modeToString(mNavigationBarMode));
245 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
246 }
247
248 pw.print(" mNavigationBarView=");
249 if (mNavigationBarView == null) {
250 pw.println("null");
251 } else {
252 mNavigationBarView.dump(fd, pw, args);
253 }
254 }
255
256 // ----- CommandQueue Callbacks -----
257
258 @Override
259 public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
260 boolean showImeSwitcher) {
261 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
262 int hints = mNavigationIconHints;
263 if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
264 hints |= NAVIGATION_HINT_BACK_ALT;
265 } else {
266 hints &= ~NAVIGATION_HINT_BACK_ALT;
267 }
268 if (showImeSwitcher) {
269 hints |= NAVIGATION_HINT_IME_SHOWN;
270 } else {
271 hints &= ~NAVIGATION_HINT_IME_SHOWN;
272 }
273 if (hints == mNavigationIconHints) return;
274
275 mNavigationIconHints = hints;
276
277 if (mNavigationBarView != null) {
278 mNavigationBarView.setNavigationIconHints(hints);
279 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500280 mStatusBar.checkBarModes();
Jason Monk49fa0162017-01-11 09:21:56 -0500281 }
282
283 @Override
284 public void topAppWindowChanged(boolean showMenu) {
285 if (mNavigationBarView != null) {
286 mNavigationBarView.setMenuVisibility(showMenu);
287 }
288 }
289
290 @Override
291 public void setWindowState(int window, int state) {
292 if (mNavigationBarView != null
293 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
294 && mNavigationBarWindowState != state) {
295 mNavigationBarWindowState = state;
296 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
297 }
298 }
299
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500300 // Injected from StatusBar at creation.
Jason Monk49fa0162017-01-11 09:21:56 -0500301 public void setCurrentSysuiVisibility(int systemUiVisibility) {
302 mSystemUiVisibility = systemUiVisibility;
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500303 mNavigationBarMode = mStatusBar.computeBarMode(0, mSystemUiVisibility,
Jason Monk49fa0162017-01-11 09:21:56 -0500304 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
305 View.NAVIGATION_BAR_TRANSPARENT);
306 checkNavBarModes();
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500307 mStatusBar.touchAutoHide();
Jason Monk49fa0162017-01-11 09:21:56 -0500308 mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
309 true /* nbModeChanged */, mNavigationBarMode);
310 }
311
312 @Override
313 public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
314 int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
315 final int oldVal = mSystemUiVisibility;
316 final int newVal = (oldVal & ~mask) | (vis & mask);
317 final int diff = newVal ^ oldVal;
318 boolean nbModeChanged = false;
319 if (diff != 0) {
320 mSystemUiVisibility = newVal;
321
322 // update navigation bar mode
323 final int nbMode = getView() == null
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500324 ? -1 : mStatusBar.computeBarMode(oldVal, newVal,
Jason Monk49fa0162017-01-11 09:21:56 -0500325 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
326 View.NAVIGATION_BAR_TRANSPARENT);
327 nbModeChanged = nbMode != -1;
328 if (nbModeChanged) {
329 if (mNavigationBarMode != nbMode) {
330 mNavigationBarMode = nbMode;
331 checkNavBarModes();
332 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500333 mStatusBar.touchAutoHide();
Jason Monk49fa0162017-01-11 09:21:56 -0500334 }
335 }
336
337 mLightBarController.onNavigationVisibilityChanged(vis, mask, nbModeChanged,
338 mNavigationBarMode);
339 }
340
341 @Override
342 public void disable(int state1, int state2, boolean animate) {
343 // All navigation bar flags are in state1.
344 int masked = state1 & (StatusBarManager.DISABLE_HOME
345 | StatusBarManager.DISABLE_RECENT
346 | StatusBarManager.DISABLE_BACK
347 | StatusBarManager.DISABLE_SEARCH);
348 if (masked != mDisabledFlags1) {
349 mDisabledFlags1 = masked;
350 if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1);
351 }
352 }
353
354 // ----- Internal stuffz -----
355
356 private void refreshLayout(int layoutDirection) {
357 if (mNavigationBarView != null) {
358 mNavigationBarView.setLayoutDirection(layoutDirection);
359 }
360 }
361
362 private boolean shouldDisableNavbarGestures() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500363 return !mStatusBar.isDeviceProvisioned()
Jason Monk49fa0162017-01-11 09:21:56 -0500364 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
365 }
366
367 private void repositionNavigationBar() {
368 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
369
370 prepareNavigationBarView();
371
372 mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(),
373 ((View) mNavigationBarView.getParent()).getLayoutParams());
374 }
375
376 private void notifyNavigationBarScreenOn(boolean screenOn) {
377 mNavigationBarView.notifyScreenOn(screenOn);
378 }
379
380 private void prepareNavigationBarView() {
381 mNavigationBarView.reorient();
382
383 ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
384 recentsButton.setOnClickListener(this::onRecentsClick);
385 recentsButton.setOnTouchListener(this::onRecentsTouch);
386 recentsButton.setLongClickable(true);
387 recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
388
389 ButtonDispatcher backButton = mNavigationBarView.getBackButton();
390 backButton.setLongClickable(true);
391 backButton.setOnLongClickListener(this::onLongPressBackRecents);
392
393 ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
394 homeButton.setOnTouchListener(this::onHomeTouch);
395 homeButton.setOnLongClickListener(this::onHomeLongClick);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800396
397 ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
398 accessibilityButton.setOnClickListener(this::onAccessibilityClick);
399 accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
Casey Burkhardt74922c62017-02-13 12:43:16 -0800400 updateAccessibilityServicesState();
Jason Monk49fa0162017-01-11 09:21:56 -0500401 }
402
403 private boolean onHomeTouch(View v, MotionEvent event) {
404 if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
405 return true;
406 }
407 // If an incoming call is ringing, HOME is totally disabled.
408 // (The user is already on the InCallUI at this point,
409 // and his ONLY options are to answer or reject the call.)
410 switch (event.getAction()) {
411 case MotionEvent.ACTION_DOWN:
412 mHomeBlockedThisTouch = false;
413 TelecomManager telecomManager =
414 getContext().getSystemService(TelecomManager.class);
415 if (telecomManager != null && telecomManager.isRinging()) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500416 if (mStatusBar.isKeyguardShowing()) {
Jason Monk49fa0162017-01-11 09:21:56 -0500417 Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
418 "No heads up");
419 mHomeBlockedThisTouch = true;
420 return true;
421 }
422 }
423 break;
424 case MotionEvent.ACTION_UP:
425 case MotionEvent.ACTION_CANCEL:
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500426 mStatusBar.awakenDreams();
Jason Monk49fa0162017-01-11 09:21:56 -0500427 break;
428 }
429 return false;
430 }
431
432 private void onVerticalChanged(boolean isVertical) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500433 mStatusBar.setQsScrimEnabled(!isVertical);
Jason Monk49fa0162017-01-11 09:21:56 -0500434 }
435
436 private boolean onNavigationTouch(View v, MotionEvent event) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500437 mStatusBar.checkUserAutohide(v, event);
Jason Monk49fa0162017-01-11 09:21:56 -0500438 return false;
439 }
440
Jason Monk865246d2017-01-19 08:27:01 -0500441 @VisibleForTesting
442 boolean onHomeLongClick(View v) {
Jason Monk49fa0162017-01-11 09:21:56 -0500443 if (shouldDisableNavbarGestures()) {
444 return false;
445 }
446 MetricsLogger.action(getContext(), MetricsEvent.ACTION_ASSIST_LONG_PRESS);
447 mAssistManager.startAssist(new Bundle() /* args */);
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500448 mStatusBar.awakenDreams();
Jason Monk49fa0162017-01-11 09:21:56 -0500449 if (mNavigationBarView != null) {
450 mNavigationBarView.abortCurrentGesture();
451 }
452 return true;
453 }
454
455 // additional optimization when we have software system buttons - start loading the recent
456 // tasks on touch down
457 private boolean onRecentsTouch(View v, MotionEvent event) {
458 int action = event.getAction() & MotionEvent.ACTION_MASK;
459 if (action == MotionEvent.ACTION_DOWN) {
460 mCommandQueue.preloadRecentApps();
461 } else if (action == MotionEvent.ACTION_CANCEL) {
462 mCommandQueue.cancelPreloadRecentApps();
463 } else if (action == MotionEvent.ACTION_UP) {
464 if (!v.isPressed()) {
465 mCommandQueue.cancelPreloadRecentApps();
466 }
467 }
468 return false;
469 }
470
471 private void onRecentsClick(View v) {
472 if (LatencyTracker.isEnabled(getContext())) {
473 LatencyTracker.getInstance(getContext()).onActionStart(
474 LatencyTracker.ACTION_TOGGLE_RECENTS);
475 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500476 mStatusBar.awakenDreams();
Jason Monk49fa0162017-01-11 09:21:56 -0500477 mCommandQueue.toggleRecentApps();
478 }
479
480 /**
481 * This handles long-press of both back and recents. They are
482 * handled together to capture them both being long-pressed
483 * at the same time to exit screen pinning (lock task).
484 *
485 * When accessibility mode is on, only a long-press from recents
486 * is required to exit.
487 *
488 * In all other circumstances we try to pass through long-press events
489 * for Back, so that apps can still use it. Which can be from two things.
490 * 1) Not currently in screen pinning (lock task).
491 * 2) Back is long-pressed without recents.
492 */
493 private boolean onLongPressBackRecents(View v) {
494 try {
495 boolean sendBackLongPress = false;
496 IActivityManager activityManager = ActivityManagerNative.getDefault();
497 boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
498 boolean inLockTaskMode = activityManager.isInLockTaskMode();
499 if (inLockTaskMode && !touchExplorationEnabled) {
500 long time = System.currentTimeMillis();
501 // If we recently long-pressed the other button then they were
502 // long-pressed 'together'
503 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
504 activityManager.stopLockTaskMode();
505 // When exiting refresh disabled flags.
506 mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
507 return true;
508 } else if ((v.getId() == R.id.back)
509 && !mNavigationBarView.getRecentsButton().getCurrentView().isPressed()) {
510 // If we aren't pressing recents right now then they presses
511 // won't be together, so send the standard long-press action.
512 sendBackLongPress = true;
513 }
514 mLastLockToAppLongPress = time;
515 } else {
516 // If this is back still need to handle sending the long-press event.
517 if (v.getId() == R.id.back) {
518 sendBackLongPress = true;
519 } else if (touchExplorationEnabled && inLockTaskMode) {
520 // When in accessibility mode a long press that is recents (not back)
521 // should stop lock task.
522 activityManager.stopLockTaskMode();
523 // When exiting refresh disabled flags.
524 mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
525 return true;
526 } else if (v.getId() == R.id.recent_apps) {
527 return onLongPressRecents();
528 }
529 }
530 if (sendBackLongPress) {
531 KeyButtonView keyButtonView = (KeyButtonView) v;
532 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
533 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
534 return true;
535 }
536 } catch (RemoteException e) {
537 Log.d(TAG, "Unable to reach activity manager", e);
538 }
539 return false;
540 }
541
542 private boolean onLongPressRecents() {
543 if (mRecents == null || !ActivityManager.supportsMultiWindow()
Winson Chungd3395382016-12-13 11:49:09 -0800544 || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()) {
Jason Monk49fa0162017-01-11 09:21:56 -0500545 return false;
546 }
547
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500548 return mStatusBar.toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
Jason Monk49fa0162017-01-11 09:21:56 -0500549 MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
550 }
551
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800552 private void onAccessibilityClick(View v) {
553 mAccessibilityManager.notifyAccessibilityButtonClicked();
554 }
555
556 private boolean onAccessibilityLongClick(View v) {
557 // TODO(b/34720082): Target service selection via long click
558 android.widget.Toast.makeText(getContext(), "Service selection coming soon...",
559 android.widget.Toast.LENGTH_LONG).show();
560 return true;
561 }
562
563 private void updateAccessibilityServicesState() {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800564 int requestingServices = 0;
565 try {
566 if (Settings.Secure.getInt(getContext().getContentResolver(),
567 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED) == 1) {
568 requestingServices++;
569 }
570 } catch (Settings.SettingNotFoundException e) {
571 }
572
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800573 final List<AccessibilityServiceInfo> services =
574 mAccessibilityManager.getEnabledAccessibilityServiceList(
575 AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800576 for (int i = services.size() - 1; i >= 0; --i) {
577 AccessibilityServiceInfo info = services.get(i);
578 if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
579 requestingServices++;
580 }
581 }
582
583 final boolean showAccessibilityButton = requestingServices >= 1;
584 final boolean targetSelection = requestingServices >= 2;
585 mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
586 }
587
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500588 // ----- Methods that StatusBar talks to (should be minimized) -----
Jason Monk49fa0162017-01-11 09:21:56 -0500589
Jason Monk49fa0162017-01-11 09:21:56 -0500590 public void setLightBarController(LightBarController lightBarController) {
591 mLightBarController = lightBarController;
592 mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController());
593 }
594
595 public boolean isSemiTransparent() {
596 return mNavigationBarMode == MODE_SEMI_TRANSPARENT;
597 }
598
599 public void onKeyguardOccludedChanged(boolean keyguardOccluded) {
600 mNavigationBarView.onKeyguardOccludedChanged(keyguardOccluded);
601 }
602
603 public void disableAnimationsDuringHide(long delay) {
604 mNavigationBarView.setLayoutTransitionsEnabled(false);
605 mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true),
606 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
607 }
608
Jason Monk49fa0162017-01-11 09:21:56 -0500609 public BarTransitions getBarTransitions() {
610 return mNavigationBarView.getBarTransitions();
611 }
612
613 public void checkNavBarModes() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500614 mStatusBar.checkBarMode(mNavigationBarMode,
Jason Monk49fa0162017-01-11 09:21:56 -0500615 mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
616 }
617
618 public void finishBarAnimations() {
619 mNavigationBarView.getBarTransitions().finishAnimations();
620 }
621
Casey Burkhardt74922c62017-02-13 12:43:16 -0800622 private class MagnificationContentObserver extends ContentObserver {
623
624 public MagnificationContentObserver(Handler handler) {
625 super(handler);
626 }
627
628 @Override
629 public void onChange(boolean selfChange) {
630 NavigationBarFragment.this.updateAccessibilityServicesState();
631 }
632 }
633
Jason Monk49fa0162017-01-11 09:21:56 -0500634 private final Stub mRotationWatcher = new Stub() {
635 @Override
636 public void onRotationChanged(int rotation) throws RemoteException {
637 // We need this to be scheduled as early as possible to beat the redrawing of
638 // window in response to the orientation change.
639 Handler h = getView().getHandler();
640 Message msg = Message.obtain(h, () -> {
641 if (mNavigationBarView != null
642 && mNavigationBarView.needsReorient(rotation)) {
643 repositionNavigationBar();
644 }
645 });
646 msg.setAsynchronous(true);
647 h.sendMessageAtFrontOfQueue(msg);
648 }
649 };
650
651 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
652 @Override
653 public void onReceive(Context context, Intent intent) {
654 String action = intent.getAction();
655 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
656 notifyNavigationBarScreenOn(false);
657 } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
658 notifyNavigationBarScreenOn(true);
659 }
660 }
661 };
662
663 public static View create(Context context, FragmentListener listener) {
664 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
665 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
666 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
667 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
668 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
669 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
670 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
671 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
672 | WindowManager.LayoutParams.FLAG_SLIPPERY,
673 PixelFormat.TRANSLUCENT);
674 lp.token = new Binder();
675 // this will allow the navbar to run in an overlay on devices that support this
676 if (ActivityManager.isHighEndGfx()) {
677 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
678 }
679 lp.setTitle("NavigationBar");
680 lp.windowAnimations = 0;
681
682 View navigationBarView = LayoutInflater.from(context).inflate(
683 R.layout.navigation_bar_window, null);
684
685 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
686 if (navigationBarView == null) return null;
687
688 context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
689 FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView);
690 NavigationBarFragment fragment = new NavigationBarFragment();
691 fragmentHost.getFragmentManager().beginTransaction()
692 .replace(R.id.navigation_bar_frame, fragment, TAG)
693 .commit();
694 fragmentHost.addTagListener(TAG, listener);
695 return navigationBarView;
696 }
697}