blob: 402ec5976840e3c396cc3119cb50a15721884500 [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.server.wm;
import static android.view.InsetsState.TYPE_IME;
import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.ViewRootImpl.sNewInsetsMode;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import com.android.internal.util.function.TriConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
/**
* Controller for a specific inset source on the server. It's called provider as it provides the
* {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
*/
class InsetsSourceProvider {
private final Rect mTmpRect = new Rect();
private final @NonNull InsetsSource mSource;
private final DisplayContent mDisplayContent;
private final InsetsStateController mStateController;
private @Nullable InsetsSourceControl mControl;
private @Nullable WindowState mControllingWin;
private @Nullable ControlAdapter mAdapter;
private WindowState mWin;
private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
/** The visibility override from the current controlling window. */
private boolean mClientVisible;
/**
* Whether the window is available and considered visible as in {@link WindowState#isVisible}.
*/
private boolean mServerVisible;
private final boolean mControllable;
InsetsSourceProvider(InsetsSource source, InsetsStateController stateController,
DisplayContent displayContent) {
mClientVisible = InsetsState.getDefaultVisibility(source.getType());
mSource = source;
mDisplayContent = displayContent;
mStateController = stateController;
final int type = source.getType();
if (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR) {
mControllable = sNewInsetsMode == NEW_INSETS_MODE_FULL;
} else if (type == TYPE_IME) {
mControllable = sNewInsetsMode >= NEW_INSETS_MODE_IME;
} else {
mControllable = false;
}
}
InsetsSource getSource() {
return mSource;
}
/**
* @return Whether the current flag configuration allows to control this source.
*/
boolean isControllable() {
return mControllable;
}
/**
* Updates the window that currently backs this source.
*
* @param win The window that links to this source.
* @param frameProvider Based on display frame state and the window, calculates the resulting
* frame that should be reported to clients.
*/
void setWindow(@Nullable WindowState win,
@Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
if (mWin != null) {
mWin.setInsetProvider(null);
}
mWin = win;
mFrameProvider = frameProvider;
if (win == null) {
setServerVisible(false);
mSource.setFrame(new Rect());
} else {
mWin.setInsetProvider(this);
if (mControllingWin != null) {
updateControlForTarget(mControllingWin, true /* force */);
}
}
}
/**
* Called when a layout pass has occurred.
*/
void onPostLayout() {
if (mWin == null) {
return;
}
mTmpRect.set(mWin.getFrameLw());
if (mFrameProvider != null) {
mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect);
} else {
mTmpRect.inset(mWin.mGivenContentInsets);
}
mSource.setFrame(mTmpRect);
if (mControl != null) {
final Rect frame = mWin.getWindowFrames().mFrame;
if (mControl.setSurfacePosition(frame.left, frame.top)) {
mStateController.notifyControlChanged(mControllingWin);
}
}
setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy()
&& !mWin.mGivenInsetsPending);
}
void updateControlForTarget(@Nullable WindowState target, boolean force) {
if (mWin == null) {
mControllingWin = target;
return;
}
if (target == mControllingWin && !force) {
return;
}
if (target == null) {
// Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
mWin.cancelAnimation();
return;
}
mAdapter = new ControlAdapter();
setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter,
!mClientVisible /* hidden */);
mControllingWin = target;
mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash,
new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top));
}
boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) {
if (mControllingWin != caller || modifiedSource.isVisible() == mClientVisible) {
return false;
}
setClientVisible(modifiedSource.isVisible());
return true;
}
private void setClientVisible(boolean clientVisible) {
if (mClientVisible == clientVisible) {
return;
}
mClientVisible = clientVisible;
mDisplayContent.mWmService.mH.sendMessage(PooledLambda.obtainMessage(
DisplayContent::layoutAndAssignWindowLayersIfNeeded, mDisplayContent));
updateVisibility();
}
private void setServerVisible(boolean serverVisible) {
mServerVisible = serverVisible;
updateVisibility();
}
private void updateVisibility() {
mSource.setVisible(mServerVisible && mClientVisible);
}
InsetsSourceControl getControl() {
return mControl;
}
boolean isClientVisible() {
return sNewInsetsMode == NEW_INSETS_MODE_NONE || mClientVisible;
}
private class ControlAdapter implements AnimationAdapter {
private SurfaceControl mCapturedLeash;
@Override
public boolean getShowWallpaper() {
return false;
}
@Override
public int getBackgroundColor() {
return 0;
}
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
mCapturedLeash = animationLeash;
final Rect frame = mWin.getWindowFrames().mFrame;
t.setPosition(mCapturedLeash, frame.left, frame.top);
}
@Override
public void onAnimationCancelled(SurfaceControl animationLeash) {
if (mAdapter == this) {
mStateController.notifyControlRevoked(mControllingWin, InsetsSourceProvider.this);
setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
mControl = null;
mControllingWin = null;
mAdapter = null;
}
}
@Override
public long getDurationHint() {
return 0;
}
@Override
public long getStatusBarTransitionsStartTime() {
return 0;
}
@Override
public void dump(PrintWriter pw, String prefix) {
}
@Override
public void writeToProto(ProtoOutputStream proto) {
}
};
}