blob: 3423a3c1b1b3dfa2d7a8abf1145b77cb2e960c55 [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;
23import static com.android.systemui.statusbar.phone.PhoneStatusBar.DEBUG_WINDOW_STATE;
24import static com.android.systemui.statusbar.phone.PhoneStatusBar.dumpBarTransitions;
25
26import android.annotation.Nullable;
27import android.app.ActivityManager;
28import android.app.ActivityManagerNative;
29import android.app.Fragment;
30import android.app.IActivityManager;
31import android.app.StatusBarManager;
32import android.content.BroadcastReceiver;
33import android.content.Context;
34import android.content.Intent;
35import android.content.IntentFilter;
36import android.content.res.Configuration;
37import android.graphics.PixelFormat;
38import android.graphics.Rect;
39import android.inputmethodservice.InputMethodService;
40import android.os.Binder;
41import android.os.Bundle;
42import android.os.Handler;
43import android.os.IBinder;
44import android.os.Message;
45import android.os.PowerManager;
46import android.os.RemoteException;
47import android.os.UserHandle;
Jason Monk865246d2017-01-19 08:27:01 -050048import android.support.annotation.VisibleForTesting;
Jason Monk49fa0162017-01-11 09:21:56 -050049import android.telecom.TelecomManager;
50import android.text.TextUtils;
51import android.util.Log;
52import android.view.IRotationWatcher.Stub;
53import android.view.KeyEvent;
54import android.view.LayoutInflater;
55import android.view.MotionEvent;
56import android.view.View;
57import android.view.ViewGroup;
58import android.view.WindowManager;
59import android.view.WindowManager.LayoutParams;
60import android.view.WindowManagerGlobal;
61import android.view.accessibility.AccessibilityEvent;
62import android.view.accessibility.AccessibilityManager;
63
64import com.android.internal.logging.MetricsLogger;
65import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
66import com.android.keyguard.LatencyTracker;
67import com.android.systemui.R;
68import com.android.systemui.SystemUIApplication;
69import com.android.systemui.assist.AssistManager;
70import com.android.systemui.fragments.FragmentHostManager;
71import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
72import com.android.systemui.recents.Recents;
73import com.android.systemui.stackdivider.Divider;
74import com.android.systemui.statusbar.CommandQueue;
75import com.android.systemui.statusbar.CommandQueue.Callbacks;
76import com.android.systemui.statusbar.policy.KeyButtonView;
77import com.android.systemui.statusbar.stack.StackStateAnimator;
78
79import java.io.FileDescriptor;
80import java.io.PrintWriter;
81import java.util.Locale;
82
83/**
84 * Fragment containing the NavigationBarFragment. Contains logic for what happens
85 * on clicks and view states of the nav bar.
86 */
87public class NavigationBarFragment extends Fragment implements Callbacks {
88
89 private static final String TAG = "NavigationBar";
90 private static final boolean DEBUG = false;
91 private static final String EXTRA_DISABLE_STATE = "disabled_state";
92
93 /** Allow some time inbetween the long press for back and recents. */
94 private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
95
96 protected NavigationBarView mNavigationBarView = null;
97 protected AssistManager mAssistManager;
98
99 private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
100
101 private int mNavigationIconHints = 0;
102 private int mNavigationBarMode;
103 protected AccessibilityManager mAccessibilityManager;
104
105 private int mDisabledFlags1;
106 private PhoneStatusBar mPhoneStatusBar;
107 private Recents mRecents;
108 private Divider mDivider;
109 private WindowManager mWindowManager;
110 private CommandQueue mCommandQueue;
111 private long mLastLockToAppLongPress;
112
113 private Locale mLocale;
114 private int mLayoutDirection;
115
116 private int mSystemUiVisibility;
117 private LightBarController mLightBarController;
118 private boolean mKeyguardGoingAway;
119
120 public boolean mHomeBlockedThisTouch;
121
122 // ----- Fragment Lifecycle Callbacks -----
123
124 @Override
125 public void onCreate(@Nullable Bundle savedInstanceState) {
126 super.onCreate(savedInstanceState);
127 mCommandQueue = SystemUIApplication.getComponent(getContext(), CommandQueue.class);
128 mCommandQueue.addCallbacks(this);
129 mPhoneStatusBar = SystemUIApplication.getComponent(getContext(), PhoneStatusBar.class);
130 mRecents = SystemUIApplication.getComponent(getContext(), Recents.class);
131 mDivider = SystemUIApplication.getComponent(getContext(), Divider.class);
132 mWindowManager = getContext().getSystemService(WindowManager.class);
133 mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
134 if (savedInstanceState != null) {
135 mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
136 }
137
138 try {
139 WindowManagerGlobal.getWindowManagerService()
140 .watchRotation(mRotationWatcher);
141 } catch (RemoteException e) {
142 throw e.rethrowFromSystemServer();
143 }
144 }
145
146 @Override
147 public void onDestroy() {
148 super.onDestroy();
149 mCommandQueue.removeCallbacks(this);
150 try {
151 WindowManagerGlobal.getWindowManagerService()
152 .removeRotationWatcher(mRotationWatcher);
153 } catch (RemoteException e) {
154 throw e.rethrowFromSystemServer();
155 }
156 }
157
158 @Override
159 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
160 Bundle savedInstanceState) {
161 return inflater.inflate(R.layout.navigation_bar, container, false);
162 }
163
164 @Override
165 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
166 super.onViewCreated(view, savedInstanceState);
167 mNavigationBarView = (NavigationBarView) view;
168
169 mNavigationBarView.setDisabledFlags(mDisabledFlags1);
170 mNavigationBarView.setComponents(mRecents, mDivider);
171 mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
172 mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
173 if (savedInstanceState != null) {
174 mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
175 }
176
177 prepareNavigationBarView();
178 checkNavBarModes();
179
180 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
181 filter.addAction(Intent.ACTION_SCREEN_ON);
182 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
183 PowerManager pm = getContext().getSystemService(PowerManager.class);
184 notifyNavigationBarScreenOn(pm.isScreenOn());
185 }
186
187 @Override
188 public void onDestroyView() {
189 super.onDestroyView();
190 getContext().unregisterReceiver(mBroadcastReceiver);
191 }
192
193 @Override
194 public void onSaveInstanceState(Bundle outState) {
195 super.onSaveInstanceState(outState);
196 outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1);
197 if (mNavigationBarView != null) {
198 mNavigationBarView.getLightTransitionsController().saveState(outState);
199 }
200 }
201
202 @Override
203 public void onConfigurationChanged(Configuration newConfig) {
204 super.onConfigurationChanged(newConfig);
205 final Locale locale = getContext().getResources().getConfiguration().locale;
206 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
207 if (!locale.equals(mLocale) || ld != mLayoutDirection) {
208 if (DEBUG) {
209 Log.v(TAG, String.format(
210 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
211 locale, ld));
212 }
213 mLocale = locale;
214 mLayoutDirection = ld;
215 refreshLayout(ld);
216 }
217 repositionNavigationBar();
218 }
219
220 @Override
221 public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) {
222 if (mNavigationBarView != null) {
223 pw.print(" mNavigationBarWindowState=");
224 pw.println(windowStateToString(mNavigationBarWindowState));
225 pw.print(" mNavigationBarMode=");
226 pw.println(BarTransitions.modeToString(mNavigationBarMode));
227 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
228 }
229
230 pw.print(" mNavigationBarView=");
231 if (mNavigationBarView == null) {
232 pw.println("null");
233 } else {
234 mNavigationBarView.dump(fd, pw, args);
235 }
236 }
237
238 // ----- CommandQueue Callbacks -----
239
240 @Override
241 public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
242 boolean showImeSwitcher) {
243 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
244 int hints = mNavigationIconHints;
245 if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
246 hints |= NAVIGATION_HINT_BACK_ALT;
247 } else {
248 hints &= ~NAVIGATION_HINT_BACK_ALT;
249 }
250 if (showImeSwitcher) {
251 hints |= NAVIGATION_HINT_IME_SHOWN;
252 } else {
253 hints &= ~NAVIGATION_HINT_IME_SHOWN;
254 }
255 if (hints == mNavigationIconHints) return;
256
257 mNavigationIconHints = hints;
258
259 if (mNavigationBarView != null) {
260 mNavigationBarView.setNavigationIconHints(hints);
261 }
262 mPhoneStatusBar.checkBarModes();
263 }
264
265 @Override
266 public void topAppWindowChanged(boolean showMenu) {
267 if (mNavigationBarView != null) {
268 mNavigationBarView.setMenuVisibility(showMenu);
269 }
270 }
271
272 @Override
273 public void setWindowState(int window, int state) {
274 if (mNavigationBarView != null
275 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
276 && mNavigationBarWindowState != state) {
277 mNavigationBarWindowState = state;
278 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
279 }
280 }
281
282 @Override
283 public void appTransitionPending() {
284 mNavigationBarView.getLightTransitionsController().appTransitionPending();
285 }
286
287 @Override
288 public void appTransitionCancelled() {
289 mNavigationBarView.getLightTransitionsController().appTransitionCancelled();
290 }
291
292 @Override
293 public void appTransitionStarting(long startTime, long duration) {
294 if (mKeyguardGoingAway) return;
295 doAppTransitionStarting(startTime, duration);
296 }
297
298 /**
299 * Calls appTransitionStarting for the nav bar regardless of whether keyguard is going away.
300 * public so PhoneStatusBar can force this when needed.
301 */
302 public void doAppTransitionStarting(long startTime, long duration) {
303 mNavigationBarView.getLightTransitionsController().appTransitionStarting(startTime,
304 duration);
305 }
306
307 // Injected from PhoneStatusBar at creation.
308 public void setCurrentSysuiVisibility(int systemUiVisibility) {
309 mSystemUiVisibility = systemUiVisibility;
310 mNavigationBarMode = mPhoneStatusBar.computeBarMode(0, mSystemUiVisibility,
311 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
312 View.NAVIGATION_BAR_TRANSPARENT);
313 checkNavBarModes();
314 mPhoneStatusBar.touchAutoHide();
315 mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
316 true /* nbModeChanged */, mNavigationBarMode);
317 }
318
319 @Override
320 public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
321 int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
322 final int oldVal = mSystemUiVisibility;
323 final int newVal = (oldVal & ~mask) | (vis & mask);
324 final int diff = newVal ^ oldVal;
325 boolean nbModeChanged = false;
326 if (diff != 0) {
327 mSystemUiVisibility = newVal;
328
329 // update navigation bar mode
330 final int nbMode = getView() == null
331 ? -1 : mPhoneStatusBar.computeBarMode(oldVal, newVal,
332 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
333 View.NAVIGATION_BAR_TRANSPARENT);
334 nbModeChanged = nbMode != -1;
335 if (nbModeChanged) {
336 if (mNavigationBarMode != nbMode) {
337 mNavigationBarMode = nbMode;
338 checkNavBarModes();
339 }
340 mPhoneStatusBar.touchAutoHide();
341 }
342 }
343
344 mLightBarController.onNavigationVisibilityChanged(vis, mask, nbModeChanged,
345 mNavigationBarMode);
346 }
347
348 @Override
349 public void disable(int state1, int state2, boolean animate) {
350 // All navigation bar flags are in state1.
351 int masked = state1 & (StatusBarManager.DISABLE_HOME
352 | StatusBarManager.DISABLE_RECENT
353 | StatusBarManager.DISABLE_BACK
354 | StatusBarManager.DISABLE_SEARCH);
355 if (masked != mDisabledFlags1) {
356 mDisabledFlags1 = masked;
357 if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1);
358 }
359 }
360
361 // ----- Internal stuffz -----
362
363 private void refreshLayout(int layoutDirection) {
364 if (mNavigationBarView != null) {
365 mNavigationBarView.setLayoutDirection(layoutDirection);
366 }
367 }
368
369 private boolean shouldDisableNavbarGestures() {
370 return !mPhoneStatusBar.isDeviceProvisioned()
371 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
372 }
373
374 private void repositionNavigationBar() {
375 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
376
377 prepareNavigationBarView();
378
379 mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(),
380 ((View) mNavigationBarView.getParent()).getLayoutParams());
381 }
382
383 private void notifyNavigationBarScreenOn(boolean screenOn) {
384 mNavigationBarView.notifyScreenOn(screenOn);
385 }
386
387 private void prepareNavigationBarView() {
388 mNavigationBarView.reorient();
389
390 ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
391 recentsButton.setOnClickListener(this::onRecentsClick);
392 recentsButton.setOnTouchListener(this::onRecentsTouch);
393 recentsButton.setLongClickable(true);
394 recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
395
396 ButtonDispatcher backButton = mNavigationBarView.getBackButton();
397 backButton.setLongClickable(true);
398 backButton.setOnLongClickListener(this::onLongPressBackRecents);
399
400 ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
401 homeButton.setOnTouchListener(this::onHomeTouch);
402 homeButton.setOnLongClickListener(this::onHomeLongClick);
403
404 if (mAssistManager != null) {
405 mAssistManager.onConfigurationChanged();
406 }
407 }
408
409 private boolean onHomeTouch(View v, MotionEvent event) {
410 if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
411 return true;
412 }
413 // If an incoming call is ringing, HOME is totally disabled.
414 // (The user is already on the InCallUI at this point,
415 // and his ONLY options are to answer or reject the call.)
416 switch (event.getAction()) {
417 case MotionEvent.ACTION_DOWN:
418 mHomeBlockedThisTouch = false;
419 TelecomManager telecomManager =
420 getContext().getSystemService(TelecomManager.class);
421 if (telecomManager != null && telecomManager.isRinging()) {
422 if (mPhoneStatusBar.isKeyguardShowing()) {
423 Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
424 "No heads up");
425 mHomeBlockedThisTouch = true;
426 return true;
427 }
428 }
429 break;
430 case MotionEvent.ACTION_UP:
431 case MotionEvent.ACTION_CANCEL:
432 mPhoneStatusBar.awakenDreams();
433 break;
434 }
435 return false;
436 }
437
438 private void onVerticalChanged(boolean isVertical) {
439 if (mAssistManager != null) {
440 // TODO: Clean this up.
441 mAssistManager.onConfigurationChanged();
442 }
443 mPhoneStatusBar.setQsScrimEnabled(!isVertical);
444 }
445
446 private boolean onNavigationTouch(View v, MotionEvent event) {
447 mPhoneStatusBar.checkUserAutohide(v, event);
448 return false;
449 }
450
Jason Monk865246d2017-01-19 08:27:01 -0500451 @VisibleForTesting
452 boolean onHomeLongClick(View v) {
Jason Monk49fa0162017-01-11 09:21:56 -0500453 if (shouldDisableNavbarGestures()) {
454 return false;
455 }
456 MetricsLogger.action(getContext(), MetricsEvent.ACTION_ASSIST_LONG_PRESS);
457 mAssistManager.startAssist(new Bundle() /* args */);
458 mPhoneStatusBar.awakenDreams();
459 if (mNavigationBarView != null) {
460 mNavigationBarView.abortCurrentGesture();
461 }
462 return true;
463 }
464
465 // additional optimization when we have software system buttons - start loading the recent
466 // tasks on touch down
467 private boolean onRecentsTouch(View v, MotionEvent event) {
468 int action = event.getAction() & MotionEvent.ACTION_MASK;
469 if (action == MotionEvent.ACTION_DOWN) {
470 mCommandQueue.preloadRecentApps();
471 } else if (action == MotionEvent.ACTION_CANCEL) {
472 mCommandQueue.cancelPreloadRecentApps();
473 } else if (action == MotionEvent.ACTION_UP) {
474 if (!v.isPressed()) {
475 mCommandQueue.cancelPreloadRecentApps();
476 }
477 }
478 return false;
479 }
480
481 private void onRecentsClick(View v) {
482 if (LatencyTracker.isEnabled(getContext())) {
483 LatencyTracker.getInstance(getContext()).onActionStart(
484 LatencyTracker.ACTION_TOGGLE_RECENTS);
485 }
486 mPhoneStatusBar.awakenDreams();
487 mCommandQueue.toggleRecentApps();
488 }
489
490 /**
491 * This handles long-press of both back and recents. They are
492 * handled together to capture them both being long-pressed
493 * at the same time to exit screen pinning (lock task).
494 *
495 * When accessibility mode is on, only a long-press from recents
496 * is required to exit.
497 *
498 * In all other circumstances we try to pass through long-press events
499 * for Back, so that apps can still use it. Which can be from two things.
500 * 1) Not currently in screen pinning (lock task).
501 * 2) Back is long-pressed without recents.
502 */
503 private boolean onLongPressBackRecents(View v) {
504 try {
505 boolean sendBackLongPress = false;
506 IActivityManager activityManager = ActivityManagerNative.getDefault();
507 boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
508 boolean inLockTaskMode = activityManager.isInLockTaskMode();
509 if (inLockTaskMode && !touchExplorationEnabled) {
510 long time = System.currentTimeMillis();
511 // If we recently long-pressed the other button then they were
512 // long-pressed 'together'
513 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
514 activityManager.stopLockTaskMode();
515 // When exiting refresh disabled flags.
516 mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
517 return true;
518 } else if ((v.getId() == R.id.back)
519 && !mNavigationBarView.getRecentsButton().getCurrentView().isPressed()) {
520 // If we aren't pressing recents right now then they presses
521 // won't be together, so send the standard long-press action.
522 sendBackLongPress = true;
523 }
524 mLastLockToAppLongPress = time;
525 } else {
526 // If this is back still need to handle sending the long-press event.
527 if (v.getId() == R.id.back) {
528 sendBackLongPress = true;
529 } else if (touchExplorationEnabled && inLockTaskMode) {
530 // When in accessibility mode a long press that is recents (not back)
531 // should stop lock task.
532 activityManager.stopLockTaskMode();
533 // When exiting refresh disabled flags.
534 mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
535 return true;
536 } else if (v.getId() == R.id.recent_apps) {
537 return onLongPressRecents();
538 }
539 }
540 if (sendBackLongPress) {
541 KeyButtonView keyButtonView = (KeyButtonView) v;
542 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
543 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
544 return true;
545 }
546 } catch (RemoteException e) {
547 Log.d(TAG, "Unable to reach activity manager", e);
548 }
549 return false;
550 }
551
552 private boolean onLongPressRecents() {
553 if (mRecents == null || !ActivityManager.supportsMultiWindow()
554 || !mDivider.getView().getSnapAlgorithm()
555 .isSplitScreenFeasible()) {
556 return false;
557 }
558
559 return mPhoneStatusBar.toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
560 MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
561 }
562
563 // ----- Methods that PhoneStatusBar talks to (should be minimized) -----
564
565 public void setAssistManager(AssistManager assistManager) {
566 mAssistManager = assistManager;
Jason Monk865246d2017-01-19 08:27:01 -0500567 mAssistManager.onConfigurationChanged();
Jason Monk49fa0162017-01-11 09:21:56 -0500568 }
569
570 public void setLightBarController(LightBarController lightBarController) {
571 mLightBarController = lightBarController;
572 mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController());
573 }
574
575 public boolean isSemiTransparent() {
576 return mNavigationBarMode == MODE_SEMI_TRANSPARENT;
577 }
578
579 public void onKeyguardOccludedChanged(boolean keyguardOccluded) {
580 mNavigationBarView.onKeyguardOccludedChanged(keyguardOccluded);
581 }
582
583 public void disableAnimationsDuringHide(long delay) {
584 mNavigationBarView.setLayoutTransitionsEnabled(false);
585 mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true),
586 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
587 }
588
589 public void setKeyguardGoingAway(boolean keyguardGoingAway) {
590 mKeyguardGoingAway = keyguardGoingAway;
591 }
592
593 public BarTransitions getBarTransitions() {
594 return mNavigationBarView.getBarTransitions();
595 }
596
597 public void checkNavBarModes() {
598 mPhoneStatusBar.checkBarMode(mNavigationBarMode,
599 mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
600 }
601
602 public void finishBarAnimations() {
603 mNavigationBarView.getBarTransitions().finishAnimations();
604 }
605
606 private final Stub mRotationWatcher = new Stub() {
607 @Override
608 public void onRotationChanged(int rotation) throws RemoteException {
609 // We need this to be scheduled as early as possible to beat the redrawing of
610 // window in response to the orientation change.
611 Handler h = getView().getHandler();
612 Message msg = Message.obtain(h, () -> {
613 if (mNavigationBarView != null
614 && mNavigationBarView.needsReorient(rotation)) {
615 repositionNavigationBar();
616 }
617 });
618 msg.setAsynchronous(true);
619 h.sendMessageAtFrontOfQueue(msg);
620 }
621 };
622
623 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
624 @Override
625 public void onReceive(Context context, Intent intent) {
626 String action = intent.getAction();
627 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
628 notifyNavigationBarScreenOn(false);
629 } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
630 notifyNavigationBarScreenOn(true);
631 }
632 }
633 };
634
635 public static View create(Context context, FragmentListener listener) {
636 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
637 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
638 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
639 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
640 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
641 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
642 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
643 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
644 | WindowManager.LayoutParams.FLAG_SLIPPERY,
645 PixelFormat.TRANSLUCENT);
646 lp.token = new Binder();
647 // this will allow the navbar to run in an overlay on devices that support this
648 if (ActivityManager.isHighEndGfx()) {
649 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
650 }
651 lp.setTitle("NavigationBar");
652 lp.windowAnimations = 0;
653
654 View navigationBarView = LayoutInflater.from(context).inflate(
655 R.layout.navigation_bar_window, null);
656
657 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
658 if (navigationBarView == null) return null;
659
660 context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
661 FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView);
662 NavigationBarFragment fragment = new NavigationBarFragment();
663 fragmentHost.getFragmentManager().beginTransaction()
664 .replace(R.id.navigation_bar_frame, fragment, TAG)
665 .commit();
666 fragmentHost.addTagListener(TAG, listener);
667 return navigationBarView;
668 }
669}