blob: dc51b1c3865d0459e5da235355812be91c5df84b [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;
Mike Digman7d092772018-01-11 12:10:32 -080029import android.animation.ObjectAnimator;
Jason Monk49fa0162017-01-11 09:21:56 -050030import android.annotation.Nullable;
31import android.app.ActivityManager;
32import android.app.ActivityManagerNative;
33import android.app.Fragment;
34import android.app.IActivityManager;
35import android.app.StatusBarManager;
36import android.content.BroadcastReceiver;
Casey Burkhardtb9dcd662017-03-20 15:10:16 -070037import android.content.ContentResolver;
Jason Monk49fa0162017-01-11 09:21:56 -050038import android.content.Context;
39import android.content.Intent;
40import android.content.IntentFilter;
41import android.content.res.Configuration;
Casey Burkhardt74922c62017-02-13 12:43:16 -080042import android.database.ContentObserver;
Jason Monk49fa0162017-01-11 09:21:56 -050043import android.graphics.PixelFormat;
44import android.graphics.Rect;
Mike Digman7d092772018-01-11 12:10:32 -080045import android.graphics.drawable.AnimatedVectorDrawable;
Jason Monk49fa0162017-01-11 09:21:56 -050046import android.inputmethodservice.InputMethodService;
47import android.os.Binder;
48import android.os.Bundle;
49import android.os.Handler;
50import android.os.IBinder;
51import android.os.Message;
Jason Monk49fa0162017-01-11 09:21:56 -050052import android.os.RemoteException;
53import android.os.UserHandle;
Casey Burkhardt74922c62017-02-13 12:43:16 -080054import android.provider.Settings;
Jason Monk865246d2017-01-19 08:27:01 -050055import android.support.annotation.VisibleForTesting;
Jason Monk49fa0162017-01-11 09:21:56 -050056import android.telecom.TelecomManager;
57import android.text.TextUtils;
58import android.util.Log;
59import android.view.IRotationWatcher.Stub;
60import android.view.KeyEvent;
61import android.view.LayoutInflater;
62import android.view.MotionEvent;
Mike Digman85ff7fa2018-01-23 14:59:52 -080063import android.view.Surface;
Jason Monk49fa0162017-01-11 09:21:56 -050064import android.view.View;
65import android.view.ViewGroup;
66import android.view.WindowManager;
67import android.view.WindowManager.LayoutParams;
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 Digman90402952018-01-22 16:05:51 -0800114 private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
Mike Digman7d092772018-01-11 12:10:32 -0800115
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;
Mike Digman90402952018-01-22 16:05:51 -0800123 private boolean mAccessibilityFeedbackEnabled;
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800124 private AccessibilityManager mAccessibilityManager;
Casey Burkhardt74922c62017-02-13 12:43:16 -0800125 private MagnificationContentObserver mMagnificationObserver;
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700126 private ContentResolver mContentResolver;
Jason Monk49fa0162017-01-11 09:21:56 -0500127
128 private int mDisabledFlags1;
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500129 private StatusBar mStatusBar;
Jason Monk49fa0162017-01-11 09:21:56 -0500130 private Recents mRecents;
131 private Divider mDivider;
132 private WindowManager mWindowManager;
133 private CommandQueue mCommandQueue;
134 private long mLastLockToAppLongPress;
135
136 private Locale mLocale;
137 private int mLayoutDirection;
138
139 private int mSystemUiVisibility;
140 private LightBarController mLightBarController;
Jason Monk49fa0162017-01-11 09:21:56 -0500141
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800142 private OverviewProxyService mOverviewProxyService;
143
Jason Monk49fa0162017-01-11 09:21:56 -0500144 public boolean mHomeBlockedThisTouch;
145
Mike Digman7d092772018-01-11 12:10:32 -0800146 private int mLastRotationSuggestion;
Mike Digman90402952018-01-22 16:05:51 -0800147 private boolean mHoveringRotationSuggestion;
Mike Digman7d092772018-01-11 12:10:32 -0800148 private RotationLockController mRotationLockController;
149 private TaskStackListenerImpl mTaskStackListener;
150
151 private final Runnable mRemoveRotationProposal = () -> setRotateSuggestionButtonState(false);
152 private Animator mRotateShowAnimator;
153 private Animator mRotateHideAnimator;
154
155
Jason Monk49fa0162017-01-11 09:21:56 -0500156 // ----- Fragment Lifecycle Callbacks -----
157
158 @Override
159 public void onCreate(@Nullable Bundle savedInstanceState) {
160 super.onCreate(savedInstanceState);
Jason Monk9c7844c2017-01-18 15:21:53 -0500161 mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500162 mCommandQueue.addCallbacks(this);
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500163 mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
Jason Monk9c7844c2017-01-18 15:21:53 -0500164 mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class);
165 mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500166 mWindowManager = getContext().getSystemService(WindowManager.class);
167 mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
Jason Monk91e587e2017-04-13 13:41:23 -0400168 Dependency.get(AccessibilityManagerWrapper.class).addCallback(
169 mAccessibilityListener);
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700170 mContentResolver = getContext().getContentResolver();
Casey Burkhardt74922c62017-02-13 12:43:16 -0800171 mMagnificationObserver = new MagnificationContentObserver(
172 getContext().getMainThreadHandler());
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700173 mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
Casey Burkhardt74922c62017-02-13 12:43:16 -0800174 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false,
Casey Burkhardt5d614402017-04-06 13:46:50 -0700175 mMagnificationObserver, UserHandle.USER_ALL);
Casey Burkhardt74922c62017-02-13 12:43:16 -0800176
Jason Monk49fa0162017-01-11 09:21:56 -0500177 if (savedInstanceState != null) {
178 mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
179 }
Jason Monk9c7844c2017-01-18 15:21:53 -0500180 mAssistManager = Dependency.get(AssistManager.class);
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800181 mOverviewProxyService = Dependency.get(OverviewProxyService.class);
Jason Monk49fa0162017-01-11 09:21:56 -0500182
183 try {
184 WindowManagerGlobal.getWindowManagerService()
Andrii Kulian35fa3c22017-03-11 09:37:28 -0800185 .watchRotation(mRotationWatcher, getContext().getDisplay().getDisplayId());
Jason Monk49fa0162017-01-11 09:21:56 -0500186 } catch (RemoteException e) {
187 throw e.rethrowFromSystemServer();
188 }
Mike Digman7d092772018-01-11 12:10:32 -0800189
190 mRotationLockController = Dependency.get(RotationLockController.class);
191
192 // Register the task stack listener
193 mTaskStackListener = new TaskStackListenerImpl();
194 ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
Jason Monk49fa0162017-01-11 09:21:56 -0500195 }
196
197 @Override
198 public void onDestroy() {
199 super.onDestroy();
200 mCommandQueue.removeCallbacks(this);
Jason Monk91e587e2017-04-13 13:41:23 -0400201 Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
202 mAccessibilityListener);
Casey Burkhardtb9dcd662017-03-20 15:10:16 -0700203 mContentResolver.unregisterContentObserver(mMagnificationObserver);
Jason Monk49fa0162017-01-11 09:21:56 -0500204 try {
205 WindowManagerGlobal.getWindowManagerService()
206 .removeRotationWatcher(mRotationWatcher);
207 } catch (RemoteException e) {
208 throw e.rethrowFromSystemServer();
209 }
Mike Digman7d092772018-01-11 12:10:32 -0800210
211 // Unregister the task stack listener
212 ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
Jason Monk49fa0162017-01-11 09:21:56 -0500213 }
214
215 @Override
216 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
217 Bundle savedInstanceState) {
218 return inflater.inflate(R.layout.navigation_bar, container, false);
219 }
220
221 @Override
222 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
223 super.onViewCreated(view, savedInstanceState);
224 mNavigationBarView = (NavigationBarView) view;
225
226 mNavigationBarView.setDisabledFlags(mDisabledFlags1);
227 mNavigationBarView.setComponents(mRecents, mDivider);
228 mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
229 mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
230 if (savedInstanceState != null) {
231 mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
232 }
233
234 prepareNavigationBarView();
235 checkNavBarModes();
236
237 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
238 filter.addAction(Intent.ACTION_SCREEN_ON);
239 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
Siarhei Vishniakoud002a0a2017-06-05 22:44:37 +0100240 notifyNavigationBarScreenOn();
Jason Monk49fa0162017-01-11 09:21:56 -0500241 }
242
243 @Override
244 public void onDestroyView() {
245 super.onDestroyView();
Jason Monkaa573e92017-01-27 17:00:29 -0500246 mNavigationBarView.getLightTransitionsController().destroy(getContext());
Jason Monk49fa0162017-01-11 09:21:56 -0500247 getContext().unregisterReceiver(mBroadcastReceiver);
248 }
249
250 @Override
251 public void onSaveInstanceState(Bundle outState) {
252 super.onSaveInstanceState(outState);
253 outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1);
254 if (mNavigationBarView != null) {
255 mNavigationBarView.getLightTransitionsController().saveState(outState);
256 }
257 }
258
259 @Override
260 public void onConfigurationChanged(Configuration newConfig) {
261 super.onConfigurationChanged(newConfig);
262 final Locale locale = getContext().getResources().getConfiguration().locale;
263 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
264 if (!locale.equals(mLocale) || ld != mLayoutDirection) {
265 if (DEBUG) {
266 Log.v(TAG, String.format(
267 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
268 locale, ld));
269 }
270 mLocale = locale;
271 mLayoutDirection = ld;
272 refreshLayout(ld);
273 }
274 repositionNavigationBar();
275 }
276
277 @Override
278 public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) {
279 if (mNavigationBarView != null) {
280 pw.print(" mNavigationBarWindowState=");
281 pw.println(windowStateToString(mNavigationBarWindowState));
282 pw.print(" mNavigationBarMode=");
283 pw.println(BarTransitions.modeToString(mNavigationBarMode));
284 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
285 }
286
287 pw.print(" mNavigationBarView=");
288 if (mNavigationBarView == null) {
289 pw.println("null");
290 } else {
291 mNavigationBarView.dump(fd, pw, args);
292 }
293 }
294
295 // ----- CommandQueue Callbacks -----
296
297 @Override
298 public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
299 boolean showImeSwitcher) {
300 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
301 int hints = mNavigationIconHints;
302 if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
303 hints |= NAVIGATION_HINT_BACK_ALT;
304 } else {
305 hints &= ~NAVIGATION_HINT_BACK_ALT;
306 }
307 if (showImeSwitcher) {
308 hints |= NAVIGATION_HINT_IME_SHOWN;
309 } else {
310 hints &= ~NAVIGATION_HINT_IME_SHOWN;
311 }
312 if (hints == mNavigationIconHints) return;
313
314 mNavigationIconHints = hints;
315
316 if (mNavigationBarView != null) {
317 mNavigationBarView.setNavigationIconHints(hints);
318 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500319 mStatusBar.checkBarModes();
Jason Monk49fa0162017-01-11 09:21:56 -0500320 }
321
322 @Override
323 public void topAppWindowChanged(boolean showMenu) {
324 if (mNavigationBarView != null) {
325 mNavigationBarView.setMenuVisibility(showMenu);
326 }
327 }
328
329 @Override
330 public void setWindowState(int window, int state) {
331 if (mNavigationBarView != null
332 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
333 && mNavigationBarWindowState != state) {
334 mNavigationBarWindowState = state;
335 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
336 }
337 }
338
Mike Digman7d092772018-01-11 12:10:32 -0800339 @Override
Mike Digmane0777312018-01-19 12:41:51 -0800340 public void onRotationProposal(final int rotation, boolean isValid) {
341 // This method will be called on rotation suggestion changes even if the proposed rotation
342 // is not valid for the top app. Use invalid rotation choices as a signal to remove the
343 // rotate button if shown.
344
345 if (!isValid) {
346 setRotateSuggestionButtonState(false);
347 return;
348 }
349
Mike Digman7d092772018-01-11 12:10:32 -0800350 if (rotation == mWindowManager.getDefaultDisplay().getRotation()) {
351 // Use this as a signal to remove any current suggestions
Mike Digman90402952018-01-22 16:05:51 -0800352 getView().getHandler().removeCallbacks(mRemoveRotationProposal);
Mike Digman7d092772018-01-11 12:10:32 -0800353 setRotateSuggestionButtonState(false);
354 } else {
355 mLastRotationSuggestion = rotation; // Remember rotation for click
356 setRotateSuggestionButtonState(true);
Mike Digman90402952018-01-22 16:05:51 -0800357 rescheduleRotationTimeout(false);
Mike Digman7d092772018-01-11 12:10:32 -0800358 }
359 }
360
Mike Digman90402952018-01-22 16:05:51 -0800361 private void rescheduleRotationTimeout(final boolean reasonHover) {
362 // May be called due to a new rotation proposal or a change in hover state
363 if (reasonHover) {
364 // Don't reschedule if a hide animator is running
365 if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
366 return;
367 }
368 // Don't reschedule if not visible
369 if (mNavigationBarView.getRotateSuggestionButton().getVisibility() != View.VISIBLE) {
370 return;
371 }
372 }
373
374 Handler h = getView().getHandler();
375 h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal
376 h.postDelayed(mRemoveRotationProposal,
377 computeRotationProposalTimeout()); // Schedule timeout
378 }
379
380 private int computeRotationProposalTimeout() {
381 if (mAccessibilityFeedbackEnabled) return 20000;
382 if (mHoveringRotationSuggestion) return 16000;
383 return 6000;
384 }
385
Mike Digman7d092772018-01-11 12:10:32 -0800386 public void setRotateSuggestionButtonState(final boolean visible) {
387 setRotateSuggestionButtonState(visible, false);
388 }
389
390 public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) {
391 ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton();
Mike Digman90402952018-01-22 16:05:51 -0800392 final boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE;
Mike Digman7d092772018-01-11 12:10:32 -0800393
394 // Rerun a show animation to indicate change but don't rerun a hide animation
395 if (!visible && !currentlyVisible) return;
396
Mike Digman90402952018-01-22 16:05:51 -0800397 View currentView = rotBtn.getCurrentView();
Mike Digman7d092772018-01-11 12:10:32 -0800398 if (currentView == null) return;
399
Mike Digman90402952018-01-22 16:05:51 -0800400 KeyButtonDrawable kbd = rotBtn.getImageDrawable();
Mike Digman7d092772018-01-11 12:10:32 -0800401 if (kbd == null) return;
402
Mike Digman90402952018-01-22 16:05:51 -0800403 AnimatedVectorDrawable animIcon = null;
404 if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) {
405 animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
406 }
407
Mike Digman7d092772018-01-11 12:10:32 -0800408 if (visible) { // Appear and change
409 rotBtn.setVisibility(View.VISIBLE);
Mike Digman90402952018-01-22 16:05:51 -0800410 mNavigationBarView.notifyAccessibilitySubtreeChanged();
Mike Digman7d092772018-01-11 12:10:32 -0800411
412 if (skipAnim) {
413 currentView.setAlpha(1f);
414 return;
415 }
416
417 // Start a new animation if running
418 if (mRotateShowAnimator != null) mRotateShowAnimator.pause();
419 if (mRotateHideAnimator != null) mRotateHideAnimator.pause();
420
421 ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha",
422 0f, 1f);
Mike Digman90402952018-01-22 16:05:51 -0800423 appearFade.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
Mike Digman7d092772018-01-11 12:10:32 -0800424 appearFade.setInterpolator(Interpolators.LINEAR);
425 mRotateShowAnimator = appearFade;
426 appearFade.start();
427
Mike Digman90402952018-01-22 16:05:51 -0800428 // Run the rotate icon's animation if it has one
429 if (animIcon != null) {
430 animIcon.reset();
431 animIcon.start();
432 }
433
Mike Digman7d092772018-01-11 12:10:32 -0800434 } else { // Hide
435
436 if (skipAnim) {
437 rotBtn.setVisibility(View.INVISIBLE);
Mike Digman90402952018-01-22 16:05:51 -0800438 mNavigationBarView.notifyAccessibilitySubtreeChanged();
Mike Digman7d092772018-01-11 12:10:32 -0800439 return;
440 }
441
442 // Don't start any new hide animations if one is running
443 if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
444 // Pause any active show animations but don't reset the AVD to avoid jumps
445 if (mRotateShowAnimator != null) mRotateShowAnimator.pause();
446
447 ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha",
448 0f);
Mike Digman90402952018-01-22 16:05:51 -0800449 fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
Mike Digman7d092772018-01-11 12:10:32 -0800450 fadeOut.setInterpolator(Interpolators.LINEAR);
451 fadeOut.addListener(new AnimatorListenerAdapter() {
452 @Override
453 public void onAnimationEnd(Animator animation) {
454 rotBtn.setVisibility(View.INVISIBLE);
Mike Digman90402952018-01-22 16:05:51 -0800455 mNavigationBarView.notifyAccessibilitySubtreeChanged();
Mike Digman7d092772018-01-11 12:10:32 -0800456 }
457 });
458
459 mRotateHideAnimator = fadeOut;
460 fadeOut.start();
461 }
462 }
463
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500464 // Injected from StatusBar at creation.
Jason Monk49fa0162017-01-11 09:21:56 -0500465 public void setCurrentSysuiVisibility(int systemUiVisibility) {
466 mSystemUiVisibility = systemUiVisibility;
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500467 mNavigationBarMode = mStatusBar.computeBarMode(0, mSystemUiVisibility,
Jason Monk49fa0162017-01-11 09:21:56 -0500468 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
469 View.NAVIGATION_BAR_TRANSPARENT);
470 checkNavBarModes();
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500471 mStatusBar.touchAutoHide();
Jason Monk49fa0162017-01-11 09:21:56 -0500472 mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
473 true /* nbModeChanged */, mNavigationBarMode);
474 }
475
476 @Override
477 public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
478 int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
479 final int oldVal = mSystemUiVisibility;
480 final int newVal = (oldVal & ~mask) | (vis & mask);
481 final int diff = newVal ^ oldVal;
482 boolean nbModeChanged = false;
483 if (diff != 0) {
484 mSystemUiVisibility = newVal;
485
486 // update navigation bar mode
487 final int nbMode = getView() == null
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500488 ? -1 : mStatusBar.computeBarMode(oldVal, newVal,
Jason Monk49fa0162017-01-11 09:21:56 -0500489 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
490 View.NAVIGATION_BAR_TRANSPARENT);
491 nbModeChanged = nbMode != -1;
492 if (nbModeChanged) {
493 if (mNavigationBarMode != nbMode) {
494 mNavigationBarMode = nbMode;
495 checkNavBarModes();
496 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500497 mStatusBar.touchAutoHide();
Jason Monk49fa0162017-01-11 09:21:56 -0500498 }
499 }
500
501 mLightBarController.onNavigationVisibilityChanged(vis, mask, nbModeChanged,
502 mNavigationBarMode);
503 }
504
505 @Override
506 public void disable(int state1, int state2, boolean animate) {
507 // All navigation bar flags are in state1.
508 int masked = state1 & (StatusBarManager.DISABLE_HOME
509 | StatusBarManager.DISABLE_RECENT
510 | StatusBarManager.DISABLE_BACK
511 | StatusBarManager.DISABLE_SEARCH);
512 if (masked != mDisabledFlags1) {
513 mDisabledFlags1 = masked;
514 if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1);
515 }
516 }
517
518 // ----- Internal stuffz -----
519
520 private void refreshLayout(int layoutDirection) {
521 if (mNavigationBarView != null) {
522 mNavigationBarView.setLayoutDirection(layoutDirection);
523 }
524 }
525
526 private boolean shouldDisableNavbarGestures() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500527 return !mStatusBar.isDeviceProvisioned()
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800528 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0
529 || mOverviewProxyService.getProxy() != null;
Jason Monk49fa0162017-01-11 09:21:56 -0500530 }
531
532 private void repositionNavigationBar() {
533 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
534
535 prepareNavigationBarView();
536
537 mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(),
538 ((View) mNavigationBarView.getParent()).getLayoutParams());
539 }
540
Siarhei Vishniakoud002a0a2017-06-05 22:44:37 +0100541 private void notifyNavigationBarScreenOn() {
542 mNavigationBarView.notifyScreenOn();
Jason Monk49fa0162017-01-11 09:21:56 -0500543 }
544
545 private void prepareNavigationBarView() {
546 mNavigationBarView.reorient();
547
548 ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
549 recentsButton.setOnClickListener(this::onRecentsClick);
550 recentsButton.setOnTouchListener(this::onRecentsTouch);
551 recentsButton.setLongClickable(true);
552 recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
553
554 ButtonDispatcher backButton = mNavigationBarView.getBackButton();
555 backButton.setLongClickable(true);
556 backButton.setOnLongClickListener(this::onLongPressBackRecents);
557
558 ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
559 homeButton.setOnTouchListener(this::onHomeTouch);
560 homeButton.setOnLongClickListener(this::onHomeLongClick);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800561
562 ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
563 accessibilityButton.setOnClickListener(this::onAccessibilityClick);
564 accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
Phil Weaverdb9a7742017-04-18 08:15:06 -0700565 updateAccessibilityServicesState(mAccessibilityManager);
Mike Digman7d092772018-01-11 12:10:32 -0800566
567 ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
568 rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
Mike Digman90402952018-01-22 16:05:51 -0800569 rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);
Jason Monk49fa0162017-01-11 09:21:56 -0500570 }
571
572 private boolean onHomeTouch(View v, MotionEvent event) {
573 if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
574 return true;
575 }
576 // If an incoming call is ringing, HOME is totally disabled.
577 // (The user is already on the InCallUI at this point,
578 // and his ONLY options are to answer or reject the call.)
579 switch (event.getAction()) {
580 case MotionEvent.ACTION_DOWN:
581 mHomeBlockedThisTouch = false;
582 TelecomManager telecomManager =
583 getContext().getSystemService(TelecomManager.class);
584 if (telecomManager != null && telecomManager.isRinging()) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500585 if (mStatusBar.isKeyguardShowing()) {
Jason Monk49fa0162017-01-11 09:21:56 -0500586 Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
587 "No heads up");
588 mHomeBlockedThisTouch = true;
589 return true;
590 }
591 }
592 break;
593 case MotionEvent.ACTION_UP:
594 case MotionEvent.ACTION_CANCEL:
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500595 mStatusBar.awakenDreams();
Jason Monk49fa0162017-01-11 09:21:56 -0500596 break;
597 }
598 return false;
599 }
600
601 private void onVerticalChanged(boolean isVertical) {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500602 mStatusBar.setQsScrimEnabled(!isVertical);
Jason Monk49fa0162017-01-11 09:21:56 -0500603 }
604
605 private boolean onNavigationTouch(View v, MotionEvent event) {
Eliot Courtneycb5d3162017-08-09 16:53:15 +0900606 mStatusBar.checkUserAutohide(event);
Jason Monk49fa0162017-01-11 09:21:56 -0500607 return false;
608 }
609
Jason Monk865246d2017-01-19 08:27:01 -0500610 @VisibleForTesting
611 boolean onHomeLongClick(View v) {
Jason Monk49fa0162017-01-11 09:21:56 -0500612 if (shouldDisableNavbarGestures()) {
613 return false;
614 }
615 MetricsLogger.action(getContext(), MetricsEvent.ACTION_ASSIST_LONG_PRESS);
616 mAssistManager.startAssist(new Bundle() /* args */);
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500617 mStatusBar.awakenDreams();
Matthew Ngdc79e5c2017-12-14 17:37:35 -0800618
Jason Monk49fa0162017-01-11 09:21:56 -0500619 if (mNavigationBarView != null) {
620 mNavigationBarView.abortCurrentGesture();
621 }
622 return true;
623 }
624
625 // additional optimization when we have software system buttons - start loading the recent
626 // tasks on touch down
627 private boolean onRecentsTouch(View v, MotionEvent event) {
628 int action = event.getAction() & MotionEvent.ACTION_MASK;
629 if (action == MotionEvent.ACTION_DOWN) {
630 mCommandQueue.preloadRecentApps();
631 } else if (action == MotionEvent.ACTION_CANCEL) {
632 mCommandQueue.cancelPreloadRecentApps();
633 } else if (action == MotionEvent.ACTION_UP) {
634 if (!v.isPressed()) {
635 mCommandQueue.cancelPreloadRecentApps();
636 }
637 }
638 return false;
639 }
640
641 private void onRecentsClick(View v) {
642 if (LatencyTracker.isEnabled(getContext())) {
643 LatencyTracker.getInstance(getContext()).onActionStart(
644 LatencyTracker.ACTION_TOGGLE_RECENTS);
645 }
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500646 mStatusBar.awakenDreams();
Jason Monk49fa0162017-01-11 09:21:56 -0500647 mCommandQueue.toggleRecentApps();
648 }
649
650 /**
651 * This handles long-press of both back and recents. They are
652 * handled together to capture them both being long-pressed
653 * at the same time to exit screen pinning (lock task).
654 *
655 * When accessibility mode is on, only a long-press from recents
656 * is required to exit.
657 *
658 * In all other circumstances we try to pass through long-press events
659 * for Back, so that apps can still use it. Which can be from two things.
660 * 1) Not currently in screen pinning (lock task).
661 * 2) Back is long-pressed without recents.
662 */
663 private boolean onLongPressBackRecents(View v) {
664 try {
665 boolean sendBackLongPress = false;
666 IActivityManager activityManager = ActivityManagerNative.getDefault();
667 boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
668 boolean inLockTaskMode = activityManager.isInLockTaskMode();
669 if (inLockTaskMode && !touchExplorationEnabled) {
670 long time = System.currentTimeMillis();
671 // If we recently long-pressed the other button then they were
672 // long-pressed 'together'
673 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
Benjamin Franza83859f2017-07-03 16:34:14 +0100674 activityManager.stopSystemLockTaskMode();
Jason Monk49fa0162017-01-11 09:21:56 -0500675 // When exiting refresh disabled flags.
676 mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
677 return true;
678 } else if ((v.getId() == R.id.back)
679 && !mNavigationBarView.getRecentsButton().getCurrentView().isPressed()) {
680 // If we aren't pressing recents right now then they presses
681 // won't be together, so send the standard long-press action.
682 sendBackLongPress = true;
683 }
684 mLastLockToAppLongPress = time;
685 } else {
686 // If this is back still need to handle sending the long-press event.
687 if (v.getId() == R.id.back) {
688 sendBackLongPress = true;
689 } else if (touchExplorationEnabled && inLockTaskMode) {
690 // When in accessibility mode a long press that is recents (not back)
691 // should stop lock task.
Benjamin Franza83859f2017-07-03 16:34:14 +0100692 activityManager.stopSystemLockTaskMode();
Jason Monk49fa0162017-01-11 09:21:56 -0500693 // When exiting refresh disabled flags.
694 mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
695 return true;
696 } else if (v.getId() == R.id.recent_apps) {
697 return onLongPressRecents();
698 }
699 }
700 if (sendBackLongPress) {
701 KeyButtonView keyButtonView = (KeyButtonView) v;
702 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
703 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
704 return true;
705 }
706 } catch (RemoteException e) {
707 Log.d(TAG, "Unable to reach activity manager", e);
708 }
709 return false;
710 }
711
712 private boolean onLongPressRecents() {
Erik Wolsheimer9be3a062017-05-31 14:59:57 -0700713 if (mRecents == null || !ActivityManager.supportsMultiWindow(getContext())
Matthew Ng43db6d22017-06-27 15:29:39 -0700714 || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()
715 || Recents.getConfiguration().isLowRamDevice) {
Jason Monk49fa0162017-01-11 09:21:56 -0500716 return false;
717 }
718
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500719 return mStatusBar.toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
Jason Monk49fa0162017-01-11 09:21:56 -0500720 MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
721 }
722
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800723 private void onAccessibilityClick(View v) {
724 mAccessibilityManager.notifyAccessibilityButtonClicked();
725 }
726
727 private boolean onAccessibilityLongClick(View v) {
Casey Burkhardt5e8b9802017-03-24 10:07:20 -0700728 Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
729 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
Casey Burkhardt5d614402017-04-06 13:46:50 -0700730 v.getContext().startActivityAsUser(intent, UserHandle.CURRENT);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800731 return true;
732 }
733
Phil Weaverdb9a7742017-04-18 08:15:06 -0700734 private void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800735 int requestingServices = 0;
736 try {
Casey Burkhardt5d614402017-04-06 13:46:50 -0700737 if (Settings.Secure.getIntForUser(mContentResolver,
738 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
739 UserHandle.USER_CURRENT) == 1) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800740 requestingServices++;
741 }
742 } catch (Settings.SettingNotFoundException e) {
743 }
744
Mike Digman90402952018-01-22 16:05:51 -0800745 boolean feedbackEnabled = false;
Casey Burkhardt5d614402017-04-06 13:46:50 -0700746 // AccessibilityManagerService resolves services for the current user since the local
747 // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800748 final List<AccessibilityServiceInfo> services =
Phil Weaverdb9a7742017-04-18 08:15:06 -0700749 accessibilityManager.getEnabledAccessibilityServiceList(
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800750 AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800751 for (int i = services.size() - 1; i >= 0; --i) {
752 AccessibilityServiceInfo info = services.get(i);
753 if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
754 requestingServices++;
755 }
Mike Digman90402952018-01-22 16:05:51 -0800756
757 if (info.feedbackType != 0 && info.feedbackType !=
758 AccessibilityServiceInfo.FEEDBACK_GENERIC) {
759 feedbackEnabled = true;
760 }
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800761 }
762
Mike Digman90402952018-01-22 16:05:51 -0800763 mAccessibilityFeedbackEnabled = feedbackEnabled;
764
Casey Burkhardt048c2bc2016-12-08 16:09:20 -0800765 final boolean showAccessibilityButton = requestingServices >= 1;
766 final boolean targetSelection = requestingServices >= 2;
767 mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
768 }
769
Mike Digman7d092772018-01-11 12:10:32 -0800770 private void onRotateSuggestionClick(View v) {
771 mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion);
772 }
773
Mike Digman90402952018-01-22 16:05:51 -0800774 private boolean onRotateSuggestionHover(View v, MotionEvent event) {
775 final int action = event.getActionMasked();
776 mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER)
777 || (action == MotionEvent.ACTION_HOVER_MOVE);
778 rescheduleRotationTimeout(true);
779 return false; // Must return false so a11y hover events are dispatched correctly.
780 }
781
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500782 // ----- Methods that StatusBar talks to (should be minimized) -----
Jason Monk49fa0162017-01-11 09:21:56 -0500783
Jason Monk49fa0162017-01-11 09:21:56 -0500784 public void setLightBarController(LightBarController lightBarController) {
785 mLightBarController = lightBarController;
786 mLightBarController.setNavigationBar(mNavigationBarView.getLightTransitionsController());
787 }
788
789 public boolean isSemiTransparent() {
790 return mNavigationBarMode == MODE_SEMI_TRANSPARENT;
791 }
792
Jason Monk49fa0162017-01-11 09:21:56 -0500793 public void disableAnimationsDuringHide(long delay) {
794 mNavigationBarView.setLayoutTransitionsEnabled(false);
795 mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true),
796 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
797 }
798
Jason Monk49fa0162017-01-11 09:21:56 -0500799 public BarTransitions getBarTransitions() {
800 return mNavigationBarView.getBarTransitions();
801 }
802
803 public void checkNavBarModes() {
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500804 mStatusBar.checkBarMode(mNavigationBarMode,
Jason Monk49fa0162017-01-11 09:21:56 -0500805 mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
806 }
807
808 public void finishBarAnimations() {
809 mNavigationBarView.getBarTransitions().finishAnimations();
810 }
811
Jason Monk91e587e2017-04-13 13:41:23 -0400812 private final AccessibilityServicesStateChangeListener mAccessibilityListener =
813 this::updateAccessibilityServicesState;
814
Casey Burkhardt74922c62017-02-13 12:43:16 -0800815 private class MagnificationContentObserver extends ContentObserver {
816
817 public MagnificationContentObserver(Handler handler) {
818 super(handler);
819 }
820
821 @Override
822 public void onChange(boolean selfChange) {
Phil Weaverdb9a7742017-04-18 08:15:06 -0700823 NavigationBarFragment.this.updateAccessibilityServicesState(mAccessibilityManager);
Casey Burkhardt74922c62017-02-13 12:43:16 -0800824 }
825 }
826
Jason Monk49fa0162017-01-11 09:21:56 -0500827 private final Stub mRotationWatcher = new Stub() {
828 @Override
Mike Digman90402952018-01-22 16:05:51 -0800829 public void onRotationChanged(final int rotation) throws RemoteException {
Jason Monk49fa0162017-01-11 09:21:56 -0500830 // We need this to be scheduled as early as possible to beat the redrawing of
831 // window in response to the orientation change.
832 Handler h = getView().getHandler();
833 Message msg = Message.obtain(h, () -> {
Mike Digman85ff7fa2018-01-23 14:59:52 -0800834
835 // If the screen rotation changes while locked, potentially update lock to flow with
Mike Digman90402952018-01-22 16:05:51 -0800836 // new screen rotation and hide any showing suggestions.
837 if (mRotationLockController.isRotationLocked()) {
Mike Digman85ff7fa2018-01-23 14:59:52 -0800838 if (shouldOverrideUserLockPrefs(rotation)) {
839 mRotationLockController.setRotationLockedAtAngle(true, rotation);
840 }
Mike Digman90402952018-01-22 16:05:51 -0800841 setRotateSuggestionButtonState(false, true);
842 }
843
Jason Monk49fa0162017-01-11 09:21:56 -0500844 if (mNavigationBarView != null
845 && mNavigationBarView.needsReorient(rotation)) {
846 repositionNavigationBar();
847 }
848 });
849 msg.setAsynchronous(true);
850 h.sendMessageAtFrontOfQueue(msg);
851 }
Mike Digman85ff7fa2018-01-23 14:59:52 -0800852
853 private boolean shouldOverrideUserLockPrefs(final int rotation) {
854 // Only override user prefs when returning to portrait.
855 // Don't let apps that force landscape or 180 alter user lock.
856 return rotation == Surface.ROTATION_0;
857 }
Jason Monk49fa0162017-01-11 09:21:56 -0500858 };
859
860 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
861 @Override
862 public void onReceive(Context context, Intent intent) {
863 String action = intent.getAction();
Siarhei Vishniakoud002a0a2017-06-05 22:44:37 +0100864 if (Intent.ACTION_SCREEN_OFF.equals(action)
865 || Intent.ACTION_SCREEN_ON.equals(action)) {
866 notifyNavigationBarScreenOn();
Jason Monk49fa0162017-01-11 09:21:56 -0500867 }
868 }
869 };
870
Mike Digman7d092772018-01-11 12:10:32 -0800871 class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
872 // Invalidate any rotation suggestion on task change or activity orientation change
873 // Note: all callbacks happen on main thread
874
875 @Override
876 public void onTaskStackChanged() {
877 setRotateSuggestionButtonState(false);
878 }
879
880 @Override
881 public void onTaskRemoved(int taskId) {
882 setRotateSuggestionButtonState(false);
883 }
884
885 @Override
886 public void onTaskMovedToFront(int taskId) {
887 setRotateSuggestionButtonState(false);
888 }
889
890 @Override
891 public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
892 setRotateSuggestionButtonState(false);
893 }
894 }
895
Jason Monk49fa0162017-01-11 09:21:56 -0500896 public static View create(Context context, FragmentListener listener) {
897 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
898 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
899 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
900 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
901 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
902 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
903 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
904 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
905 | WindowManager.LayoutParams.FLAG_SLIPPERY,
906 PixelFormat.TRANSLUCENT);
907 lp.token = new Binder();
Jason Monk49fa0162017-01-11 09:21:56 -0500908 lp.setTitle("NavigationBar");
909 lp.windowAnimations = 0;
910
911 View navigationBarView = LayoutInflater.from(context).inflate(
912 R.layout.navigation_bar_window, null);
913
914 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
915 if (navigationBarView == null) return null;
916
917 context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
918 FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView);
919 NavigationBarFragment fragment = new NavigationBarFragment();
920 fragmentHost.getFragmentManager().beginTransaction()
921 .replace(R.id.navigation_bar_frame, fragment, TAG)
922 .commit();
923 fragmentHost.addTagListener(TAG, listener);
924 return navigationBarView;
925 }
926}