blob: fc51b467c8d4c8ba9d5689a371eec045eafea550 [file] [log] [blame]
/*
* Copyright (C) 2019 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.server.wm;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
import android.annotation.Nullable;
import android.app.StatusBarManager;
import android.util.IntArray;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetType;
import android.view.ViewRootImpl;
/**
* Policy that implements who gets control over the windows generating insets.
*/
class InsetsPolicy {
private final InsetsStateController mStateController;
private final DisplayContent mDisplayContent;
private final DisplayPolicy mPolicy;
private final TransientControlTarget mTransientControlTarget = new TransientControlTarget();
private final IntArray mShowingTransientTypes = new IntArray();
private WindowState mFocusedWin;
private BarWindow mTopBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
mStateController = stateController;
mDisplayContent = displayContent;
mPolicy = displayContent.getDisplayPolicy();
}
/** Updates the target which can control system bars. */
void updateBarControlTarget(@Nullable WindowState focusedWin) {
mFocusedWin = focusedWin;
mStateController.onBarControlTargetChanged(getTopControlTarget(focusedWin),
getFakeTopControlTarget(focusedWin),
getNavControlTarget(focusedWin),
getFakeNavControlTarget(focusedWin));
if (ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) {
return;
}
mTopBar.setVisible(focusedWin == null
|| focusedWin != getTopControlTarget(focusedWin)
|| focusedWin.getClientInsetsState().getSource(TYPE_TOP_BAR).isVisible());
mNavBar.setVisible(focusedWin == null
|| focusedWin != getNavControlTarget(focusedWin)
|| focusedWin.getClientInsetsState().getSource(TYPE_NAVIGATION_BAR).isVisible());
}
boolean isHidden(@InternalInsetType int type) {
final InsetsSourceProvider provider = mStateController.peekSourceProvider(type);
return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
}
void showTransient(IntArray types) {
boolean changed = false;
for (int i = types.size() - 1; i >= 0; i--) {
final int type = types.get(i);
if (mShowingTransientTypes.indexOf(type) != -1) {
continue;
}
if (!isHidden(type)) {
continue;
}
mShowingTransientTypes.add(type);
changed = true;
}
if (changed) {
updateBarControlTarget(mFocusedWin);
mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(),
mShowingTransientTypes.toArray());
mStateController.notifyInsetsChanged();
// TODO(b/118118435): Animation
}
}
void hideTransient() {
if (mShowingTransientTypes.size() == 0) {
return;
}
// TODO(b/118118435): Animation
mShowingTransientTypes.clear();
updateBarControlTarget(mFocusedWin);
mStateController.notifyInsetsChanged();
}
/**
* @see InsetsStateController#getInsetsForDispatch
*/
InsetsState getInsetsForDispatch(WindowState target) {
InsetsState state = mStateController.getInsetsForDispatch(target);
if (mShowingTransientTypes.size() == 0) {
return state;
}
for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
state.setSourceVisible(mShowingTransientTypes.get(i), false);
}
return state;
}
void onInsetsModified(WindowState windowState, InsetsState state) {
mStateController.onInsetsModified(windowState, state);
checkAbortTransient(windowState, state);
if (ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) {
return;
}
if (windowState == getTopControlTarget(mFocusedWin)) {
mTopBar.setVisible(state.getSource(TYPE_TOP_BAR).isVisible());
}
if (windowState == getNavControlTarget(mFocusedWin)) {
mNavBar.setVisible(state.getSource(TYPE_NAVIGATION_BAR).isVisible());
}
}
/**
* Called when a window modified the insets state. If the window set a insets source to visible
* while it is shown transiently, we need to abort the transient state.
*
* @param windowState who changed the insets state.
* @param state the modified insets state.
*/
private void checkAbortTransient(WindowState windowState, InsetsState state) {
if (mShowingTransientTypes.size() != 0) {
IntArray abortTypes = new IntArray();
for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
final int type = mShowingTransientTypes.get(i);
if (mStateController.isFakeTarget(type, windowState)
&& state.getSource(type).isVisible()) {
mShowingTransientTypes.remove(i);
abortTypes.add(type);
}
}
if (abortTypes.size() > 0) {
mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(),
abortTypes.toArray());
updateBarControlTarget(mFocusedWin);
}
}
}
private @Nullable InsetsControlTarget getFakeTopControlTarget(@Nullable WindowState focused) {
if (mShowingTransientTypes.indexOf(TYPE_TOP_BAR) != -1) {
return focused;
}
return null;
}
private @Nullable InsetsControlTarget getFakeNavControlTarget(@Nullable WindowState focused) {
if (mShowingTransientTypes.indexOf(TYPE_NAVIGATION_BAR) != -1) {
return focused;
}
return null;
}
private @Nullable InsetsControlTarget getTopControlTarget(@Nullable WindowState focusedWin) {
if (mShowingTransientTypes.indexOf(TYPE_TOP_BAR) != -1) {
return mTransientControlTarget;
}
if (areSystemBarsForciblyVisible() || isStatusBarForciblyVisible()) {
return null;
}
return focusedWin;
}
private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin) {
if (mShowingTransientTypes.indexOf(TYPE_NAVIGATION_BAR) != -1) {
return mTransientControlTarget;
}
if (areSystemBarsForciblyVisible() || isNavBarForciblyVisible()) {
return null;
}
return focusedWin;
}
private boolean isStatusBarForciblyVisible() {
final WindowState statusBar = mPolicy.getStatusBar();
if (statusBar == null) {
return false;
}
final int privateFlags = statusBar.mAttrs.privateFlags;
// TODO(b/118118435): Pretend to the app that it's still able to control it?
if ((privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
return true;
}
if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
return true;
}
return false;
}
private boolean isNavBarForciblyVisible() {
final WindowState statusBar = mPolicy.getStatusBar();
if (statusBar == null) {
return false;
}
if ((statusBar.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0) {
return true;
}
return false;
}
private boolean areSystemBarsForciblyVisible() {
final boolean isDockedStackVisible =
mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final boolean isFreeformStackVisible =
mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM);
final boolean isResizing = mDisplayContent.getDockedDividerController().isResizing();
// We need to force system bars when the docked stack is visible, when the freeform stack
// is visible but also when we are resizing for the transitions when docked stack
// visibility changes.
return isDockedStackVisible || isFreeformStackVisible || isResizing;
}
private class BarWindow {
private final int mId;
private @StatusBarManager.WindowVisibleState int mState =
StatusBarManager.WINDOW_STATE_SHOWING;
BarWindow(int id) {
mId = id;
}
private void setVisible(boolean visible) {
final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN;
if (mState != state) {
mState = state;
mPolicy.getStatusBarManagerInternal().setWindowState(
mDisplayContent.getDisplayId(), mId, state);
}
}
}
// TODO(b/118118435): Implement animations for it (with SurfaceAnimator)
private class TransientControlTarget implements InsetsControlTarget {
@Override
public void notifyInsetsControlChanged() {
}
}
}