blob: 5912cd7b6433a22110c137e92a046fac7258de40 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.phone;
import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.view.IWindowManager;
import android.view.MotionEvent;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import javax.inject.Inject;
import javax.inject.Named;
/** A controller to control all auto-hide things. */
public class AutoHideController implements CommandQueue.Callbacks {
private static final String TAG = "AutoHideController";
private final IWindowManager mWindowManagerService;
private final Handler mHandler;
private final NotificationRemoteInputManager mRemoteInputManager;
private final CommandQueue mCommandQueue;
private StatusBar mStatusBar;
private NavigationBarFragment mNavigationBar;
@VisibleForTesting
int mDisplayId;
@VisibleForTesting
int mSystemUiVisibility;
// last value sent to window manager
private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
private boolean mAutoHideSuspended;
private static final long AUTOHIDE_TIMEOUT_MS = 2250;
private final Runnable mAutoHide = () -> {
int requested = mSystemUiVisibility & ~getTransientMask();
if (mSystemUiVisibility != requested) {
notifySystemUiVisibilityChanged(requested);
}
};
@Inject
public AutoHideController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
mCommandQueue.addCallback(this);
mHandler = handler;
mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
mWindowManagerService = Dependency.get(IWindowManager.class);
mDisplayId = context.getDisplayId();
}
@Override
public void onDisplayRemoved(int displayId) {
if (displayId == mDisplayId) {
mCommandQueue.removeCallback(this);
}
}
void setStatusBar(StatusBar statusBar) {
mStatusBar = statusBar;
}
void setNavigationBar(NavigationBarFragment navigationBar) {
mNavigationBar = navigationBar;
}
@Override
public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
boolean navbarColorManagedByIme) {
if (displayId != mDisplayId) {
return;
}
int oldVal = mSystemUiVisibility;
int newVal = (oldVal & ~mask) | (vis & mask);
int diff = newVal ^ oldVal;
if (diff != 0) {
mSystemUiVisibility = newVal;
// ready to unhide
if (hasStatusBar() && (vis & View.STATUS_BAR_UNHIDE) != 0) {
mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
}
if (hasNavigationBar() && (vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
}
// Re-send setSystemUiVisibility to update un-hide status.
if (mSystemUiVisibility != newVal) {
mCommandQueue.setSystemUiVisibility(mDisplayId, mSystemUiVisibility,
fullscreenStackVis, dockedStackVis, mask, fullscreenStackBounds,
dockedStackBounds, navbarColorManagedByIme);
}
notifySystemUiVisibilityChanged(mSystemUiVisibility);
}
}
@VisibleForTesting
void notifySystemUiVisibilityChanged(int vis) {
try {
if (mLastDispatchedSystemUiVisibility != vis) {
mWindowManagerService.statusBarVisibilityChanged(mDisplayId, vis);
mLastDispatchedSystemUiVisibility = vis;
}
} catch (RemoteException ex) {
Log.w(TAG, "Cannot get WindowManager");
}
}
void resumeSuspendedAutoHide() {
if (mAutoHideSuspended) {
scheduleAutoHide();
Runnable checkBarModesRunnable = getCheckBarModesRunnable();
if (checkBarModesRunnable != null) {
mHandler.postDelayed(checkBarModesRunnable, 500); // longer than home -> launcher
}
}
}
void suspendAutoHide() {
mHandler.removeCallbacks(mAutoHide);
Runnable checkBarModesRunnable = getCheckBarModesRunnable();
if (checkBarModesRunnable != null) {
mHandler.removeCallbacks(checkBarModesRunnable);
}
mAutoHideSuspended = (mSystemUiVisibility & getTransientMask()) != 0;
}
void touchAutoHide() {
// update transient bar auto hide
if ((hasStatusBar() && mStatusBar.getStatusBarMode() == MODE_SEMI_TRANSPARENT)
|| hasNavigationBar() && mNavigationBar.isSemiTransparent()) {
scheduleAutoHide();
} else {
cancelAutoHide();
}
}
private Runnable getCheckBarModesRunnable() {
if (hasStatusBar()) {
return () -> mStatusBar.checkBarModes();
} else if (hasNavigationBar()) {
return () -> mNavigationBar.checkNavBarModes();
} else {
return null;
}
}
private void cancelAutoHide() {
mAutoHideSuspended = false;
mHandler.removeCallbacks(mAutoHide);
}
private void scheduleAutoHide() {
cancelAutoHide();
mHandler.postDelayed(mAutoHide, AUTOHIDE_TIMEOUT_MS);
}
void checkUserAutoHide(MotionEvent event) {
boolean shouldAutoHide =
(mSystemUiVisibility & getTransientMask()) != 0 // a transient bar is revealed.
&& event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar.
&& event.getX() == 0 && event.getY() == 0;
if (hasStatusBar()) {
// a touch outside both bars
shouldAutoHide &= !mRemoteInputManager.getController().isRemoteInputActive();
}
if (shouldAutoHide) {
userAutoHide();
}
}
private void userAutoHide() {
cancelAutoHide();
mHandler.postDelayed(mAutoHide, 350); // longer than app gesture -> flag clear
}
private int getTransientMask() {
int mask = 0;
if (hasStatusBar()) {
mask |= View.STATUS_BAR_TRANSIENT;
}
if (hasNavigationBar()) {
mask |= View.NAVIGATION_BAR_TRANSIENT;
}
return mask;
}
boolean hasNavigationBar() {
return mNavigationBar != null;
}
@VisibleForTesting
boolean hasStatusBar() {
return mStatusBar != null;
}
}