/*
|
* Copyright (C) 2013 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.ActivityTaskManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
|
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
|
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
|
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
|
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
import static android.content.res.Configuration.EMPTY;
|
import static android.view.SurfaceControl.METADATA_TASK_ID;
|
|
import static com.android.server.EventLogTags.WM_TASK_REMOVED;
|
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
|
import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
|
import static com.android.server.wm.TaskProto.BOUNDS;
|
import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
|
import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
|
import static com.android.server.wm.TaskProto.FILLS_PARENT;
|
import static com.android.server.wm.TaskProto.ID;
|
import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
|
import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
|
import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
|
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
|
import android.annotation.CallSuper;
|
import android.app.ActivityManager;
|
import android.app.ActivityManager.TaskDescription;
|
import android.content.pm.ActivityInfo;
|
import android.content.res.Configuration;
|
import android.graphics.Rect;
|
import android.os.IBinder;
|
import android.util.EventLog;
|
import android.util.Slog;
|
import android.util.proto.ProtoOutputStream;
|
import android.view.Display;
|
import android.view.Surface;
|
import android.view.SurfaceControl;
|
|
import com.android.internal.annotations.VisibleForTesting;
|
|
import java.io.PrintWriter;
|
import java.util.function.Consumer;
|
|
class Task extends WindowContainer<AppWindowToken> implements ConfigurationContainerListener{
|
static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
|
|
// TODO: Track parent marks like this in WindowContainer.
|
TaskStack mStack;
|
final int mTaskId;
|
final int mUserId;
|
private boolean mDeferRemoval = false;
|
|
final Rect mPreparedFrozenBounds = new Rect();
|
final Configuration mPreparedFrozenMergedConfig = new Configuration();
|
|
// If non-empty, bounds used to display the task during animations/interactions.
|
private final Rect mOverrideDisplayedBounds = new Rect();
|
|
/** ID of the display which rotation {@link #mRotation} has. */
|
private int mLastRotationDisplayId = Display.INVALID_DISPLAY;
|
/**
|
* Display rotation as of the last time {@link #setBounds(Rect)} was called or this task was
|
* moved to a new display.
|
*/
|
private int mRotation;
|
|
// For comparison with DisplayContent bounds.
|
private Rect mTmpRect = new Rect();
|
// For handling display rotations.
|
private Rect mTmpRect2 = new Rect();
|
// For retrieving dim bounds
|
private Rect mTmpRect3 = new Rect();
|
|
// For retrieving dim bounds
|
private Rect mTmpRect4 = new Rect();
|
|
// Resize mode of the task. See {@link ActivityInfo#resizeMode}
|
private int mResizeMode;
|
|
// Whether the task supports picture-in-picture.
|
// See {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE}
|
private boolean mSupportsPictureInPicture;
|
|
// Whether the task is currently being drag-resized
|
private boolean mDragResizing;
|
private int mDragResizeMode;
|
|
private TaskDescription mTaskDescription;
|
|
// If set to true, the task will report that it is not in the floating
|
// state regardless of it's stack affiliation. As the floating state drives
|
// production of content insets this can be used to preserve them across
|
// stack moves and we in fact do so when moving from full screen to pinned.
|
private boolean mPreserveNonFloatingState = false;
|
|
private Dimmer mDimmer = new Dimmer(this);
|
private final Rect mTmpDimBoundsRect = new Rect();
|
|
/** @see #setCanAffectSystemUiFlags */
|
private boolean mCanAffectSystemUiFlags = true;
|
|
// TODO: remove after unification
|
TaskRecord mTaskRecord;
|
|
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode,
|
boolean supportsPictureInPicture, TaskDescription taskDescription,
|
TaskRecord taskRecord) {
|
super(service);
|
mTaskId = taskId;
|
mStack = stack;
|
mUserId = userId;
|
mResizeMode = resizeMode;
|
mSupportsPictureInPicture = supportsPictureInPicture;
|
mTaskRecord = taskRecord;
|
if (mTaskRecord != null) {
|
// This can be null when we call createTaskInStack in WindowTestUtils. Remove this after
|
// unification.
|
mTaskRecord.registerConfigurationChangeListener(this);
|
}
|
setBounds(getRequestedOverrideBounds());
|
mTaskDescription = taskDescription;
|
|
// Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
|
setOrientation(SCREEN_ORIENTATION_UNSET);
|
}
|
|
@Override
|
DisplayContent getDisplayContent() {
|
return mStack != null ? mStack.getDisplayContent() : null;
|
}
|
|
private int getAdjustedAddPosition(int suggestedPosition) {
|
final int size = mChildren.size();
|
if (suggestedPosition >= size) {
|
return Math.min(size, suggestedPosition);
|
}
|
|
for (int pos = 0; pos < size && pos < suggestedPosition; ++pos) {
|
// TODO: Confirm that this is the behavior we want long term.
|
if (mChildren.get(pos).removed) {
|
// suggestedPosition assumes removed tokens are actually gone.
|
++suggestedPosition;
|
}
|
}
|
return Math.min(size, suggestedPosition);
|
}
|
|
@Override
|
void addChild(AppWindowToken wtoken, int position) {
|
position = getAdjustedAddPosition(position);
|
super.addChild(wtoken, position);
|
mDeferRemoval = false;
|
}
|
|
@Override
|
void positionChildAt(int position, AppWindowToken child, boolean includingParents) {
|
position = getAdjustedAddPosition(position);
|
super.positionChildAt(position, child, includingParents);
|
mDeferRemoval = false;
|
}
|
|
private boolean hasWindowsAlive() {
|
for (int i = mChildren.size() - 1; i >= 0; i--) {
|
if (mChildren.get(i).hasWindowsAlive()) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
@VisibleForTesting
|
boolean shouldDeferRemoval() {
|
// TODO: This should probably return false if mChildren.isEmpty() regardless if the stack
|
// is animating...
|
return hasWindowsAlive() && mStack.isSelfOrChildAnimating();
|
}
|
|
@Override
|
void removeIfPossible() {
|
if (shouldDeferRemoval()) {
|
if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId);
|
mDeferRemoval = true;
|
return;
|
}
|
removeImmediately();
|
}
|
|
@Override
|
void removeImmediately() {
|
if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
|
EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeTask");
|
mDeferRemoval = false;
|
if (mTaskRecord != null) {
|
mTaskRecord.unregisterConfigurationChangeListener(this);
|
}
|
|
super.removeImmediately();
|
}
|
|
void reparent(TaskStack stack, int position, boolean moveParents) {
|
if (stack == mStack) {
|
throw new IllegalArgumentException(
|
"task=" + this + " already child of stack=" + mStack);
|
}
|
if (stack == null) {
|
throw new IllegalArgumentException("reparent: could not find stack.");
|
}
|
if (DEBUG_STACK) Slog.i(TAG, "reParentTask: removing taskId=" + mTaskId
|
+ " from stack=" + mStack);
|
EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "reParentTask");
|
final DisplayContent prevDisplayContent = getDisplayContent();
|
|
// If we are moving from the fullscreen stack to the pinned stack
|
// then we want to preserve our insets so that there will not
|
// be a jump in the area covered by system decorations. We rely
|
// on the pinned animation to later unset this value.
|
if (stack.inPinnedWindowingMode()) {
|
mPreserveNonFloatingState = true;
|
} else {
|
mPreserveNonFloatingState = false;
|
}
|
|
getParent().removeChild(this);
|
stack.addTask(this, position, showForAllUsers(), moveParents);
|
|
// Relayout display(s).
|
final DisplayContent displayContent = stack.getDisplayContent();
|
displayContent.setLayoutNeeded();
|
if (prevDisplayContent != displayContent) {
|
onDisplayChanged(displayContent);
|
prevDisplayContent.setLayoutNeeded();
|
}
|
getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
|
}
|
|
/** @see ActivityTaskManagerService#positionTaskInStack(int, int, int). */
|
void positionAt(int position) {
|
mStack.positionChildAt(position, this, false /* includingParents */);
|
}
|
|
@Override
|
void onParentChanged() {
|
super.onParentChanged();
|
|
// Update task bounds if needed.
|
adjustBoundsForDisplayChangeIfNeeded(getDisplayContent());
|
|
if (getWindowConfiguration().windowsAreScaleable()) {
|
// We force windows out of SCALING_MODE_FREEZE so that we can continue to animate them
|
// while a resize is pending.
|
forceWindowsScaleable(true /* force */);
|
} else {
|
forceWindowsScaleable(false /* force */);
|
}
|
}
|
|
@Override
|
void removeChild(AppWindowToken token) {
|
if (!mChildren.contains(token)) {
|
Slog.e(TAG, "removeChild: token=" + this + " not found.");
|
return;
|
}
|
|
super.removeChild(token);
|
|
if (mChildren.isEmpty()) {
|
EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeAppToken: last token");
|
if (mDeferRemoval) {
|
removeIfPossible();
|
}
|
}
|
}
|
|
void setSendingToBottom(boolean toBottom) {
|
for (int appTokenNdx = 0; appTokenNdx < mChildren.size(); appTokenNdx++) {
|
mChildren.get(appTokenNdx).sendingToBottom = toBottom;
|
}
|
}
|
|
public int setBounds(Rect bounds, boolean forceResize) {
|
final int boundsChanged = setBounds(bounds);
|
|
if (forceResize && (boundsChanged & BOUNDS_CHANGE_SIZE) != BOUNDS_CHANGE_SIZE) {
|
onResize();
|
return BOUNDS_CHANGE_SIZE | boundsChanged;
|
}
|
|
return boundsChanged;
|
}
|
|
/** Set the task bounds. Passing in null sets the bounds to fullscreen. */
|
@Override
|
public int setBounds(Rect bounds) {
|
int rotation = Surface.ROTATION_0;
|
final DisplayContent displayContent = mStack.getDisplayContent();
|
if (displayContent != null) {
|
rotation = displayContent.getDisplayInfo().rotation;
|
} else if (bounds == null) {
|
return super.setBounds(bounds);
|
}
|
|
final int boundsChange = super.setBounds(bounds);
|
|
mRotation = rotation;
|
|
updateSurfacePosition();
|
return boundsChange;
|
}
|
|
@Override
|
public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
|
ConfigurationContainer requestingContainer) {
|
if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) {
|
return true;
|
}
|
|
// No one in higher hierarchy handles this request, let's adjust our bounds to fulfill
|
// it if possible.
|
// TODO: Move to TaskRecord after unification is done.
|
if (mTaskRecord != null && mTaskRecord.getParent() != null) {
|
mTaskRecord.onConfigurationChanged(mTaskRecord.getParent().getConfiguration());
|
return true;
|
}
|
return false;
|
}
|
|
void resize(boolean relayout, boolean forced) {
|
if (setBounds(getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE && relayout) {
|
getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
|
}
|
}
|
|
@Override
|
void onDisplayChanged(DisplayContent dc) {
|
adjustBoundsForDisplayChangeIfNeeded(dc);
|
super.onDisplayChanged(dc);
|
final int displayId = (dc != null) ? dc.getDisplayId() : Display.INVALID_DISPLAY;
|
mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged(
|
mTaskId, displayId);
|
}
|
|
/**
|
* Sets bounds that override where the task is displayed. Used during transient operations
|
* like animation / interaction.
|
*/
|
void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) {
|
if (overrideDisplayedBounds != null) {
|
mOverrideDisplayedBounds.set(overrideDisplayedBounds);
|
} else {
|
mOverrideDisplayedBounds.setEmpty();
|
}
|
updateSurfacePosition();
|
}
|
|
/**
|
* Gets the bounds that override where the task is displayed. See
|
* {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
|
*/
|
Rect getOverrideDisplayedBounds() {
|
return mOverrideDisplayedBounds;
|
}
|
|
void setResizeable(int resizeMode) {
|
mResizeMode = resizeMode;
|
}
|
|
boolean isResizeable() {
|
return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture
|
|| mWmService.mForceResizableTasks;
|
}
|
|
/**
|
* Tests if the orientation should be preserved upon user interactive resizig operations.
|
|
* @return true if orientation should not get changed upon resizing operation.
|
*/
|
boolean preserveOrientationOnResize() {
|
return mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY
|
|| mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY
|
|| mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
|
}
|
|
boolean cropWindowsToStackBounds() {
|
return isResizeable();
|
}
|
|
/**
|
* Prepares the task bounds to be frozen with the current size. See
|
* {@link AppWindowToken#freezeBounds}.
|
*/
|
void prepareFreezingBounds() {
|
mPreparedFrozenBounds.set(getBounds());
|
mPreparedFrozenMergedConfig.setTo(getConfiguration());
|
}
|
|
@Override
|
public void onConfigurationChanged(Configuration config) {
|
if (mChildren.size() > 0) {
|
AppWindowToken app = mChildren.get(mChildren.size() - 1);
|
if (app.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
|
Configuration newConfig;;
|
int width = config.windowConfiguration.getBounds().width();
|
int height = config.windowConfiguration.getBounds().height();
|
mTmpRect4.set(config.windowConfiguration.getBounds());
|
switch (app.getOrientation()) {
|
case SCREEN_ORIENTATION_LANDSCAPE :
|
if (width < height) {
|
newConfig = new Configuration();
|
newConfig.setTo(config);
|
getBounds(mTmpRect4);
|
newConfig.windowConfiguration.setBounds(mTmpRect4);
|
newConfig.orientation = getConfiguration().orientation;
|
super.onConfigurationChanged(newConfig);
|
return;
|
}
|
break;
|
case SCREEN_ORIENTATION_PORTRAIT :
|
if (width > height) {
|
newConfig = new Configuration();
|
newConfig.setTo(config);
|
getBounds(mTmpRect4);
|
newConfig.windowConfiguration.setBounds(mTmpRect4);
|
newConfig.orientation = getConfiguration().orientation;
|
super.onConfigurationChanged(newConfig);
|
return;
|
}
|
break;
|
default :
|
break;
|
}
|
}
|
}
|
super.onConfigurationChanged(config);
|
}
|
|
/**
|
* Align the task to the adjusted bounds.
|
*
|
* @param adjustedBounds Adjusted bounds to which the task should be aligned.
|
* @param tempInsetBounds Insets bounds for the task.
|
* @param alignBottom True if the task's bottom should be aligned to the adjusted
|
* bounds's bottom; false if the task's top should be aligned
|
* the adjusted bounds's top.
|
*/
|
void alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) {
|
if (!isResizeable() || EMPTY.equals(getRequestedOverrideConfiguration())) {
|
return;
|
}
|
|
getBounds(mTmpRect2);
|
if (alignBottom) {
|
int offsetY = adjustedBounds.bottom - mTmpRect2.bottom;
|
mTmpRect2.offset(0, offsetY);
|
} else {
|
mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
|
}
|
if (tempInsetBounds == null || tempInsetBounds.isEmpty()) {
|
setOverrideDisplayedBounds(null);
|
setBounds(mTmpRect2);
|
} else {
|
setOverrideDisplayedBounds(mTmpRect2);
|
setBounds(tempInsetBounds);
|
}
|
}
|
|
@Override
|
public Rect getDisplayedBounds() {
|
if (mOverrideDisplayedBounds.isEmpty()) {
|
return super.getDisplayedBounds();
|
} else {
|
return mOverrideDisplayedBounds;
|
}
|
}
|
|
/**
|
* Calculate the maximum visible area of this task. If the task has only one app,
|
* the result will be visible frame of that app. If the task has more than one apps,
|
* we search from top down if the next app got different visible area.
|
*
|
* This effort is to handle the case where some task (eg. GMail composer) might pop up
|
* a dialog that's different in size from the activity below, in which case we should
|
* be dimming the entire task area behind the dialog.
|
*
|
* @param out Rect containing the max visible bounds.
|
* @return true if the task has some visible app windows; false otherwise.
|
*/
|
private boolean getMaxVisibleBounds(Rect out) {
|
boolean foundTop = false;
|
for (int i = mChildren.size() - 1; i >= 0; i--) {
|
final AppWindowToken token = mChildren.get(i);
|
// skip hidden (or about to hide) apps
|
if (token.mIsExiting || token.isClientHidden() || token.hiddenRequested) {
|
continue;
|
}
|
final WindowState win = token.findMainWindow();
|
if (win == null) {
|
continue;
|
}
|
if (!foundTop) {
|
foundTop = true;
|
out.setEmpty();
|
}
|
|
win.getMaxVisibleBounds(out);
|
}
|
return foundTop;
|
}
|
|
/** Bounds of the task to be used for dimming, as well as touch related tests. */
|
public void getDimBounds(Rect out) {
|
final DisplayContent displayContent = mStack.getDisplayContent();
|
// It doesn't matter if we in particular are part of the resize, since we couldn't have
|
// a DimLayer anyway if we weren't visible.
|
final boolean dockedResizing = displayContent != null
|
&& displayContent.mDividerControllerLocked.isResizing();
|
if (inFreeformWindowingMode() && getMaxVisibleBounds(out)) {
|
return;
|
}
|
|
if (!matchParentBounds()) {
|
// When minimizing the docked stack when going home, we don't adjust the task bounds
|
// so we need to intersect the task bounds with the stack bounds here.
|
//
|
// If we are Docked Resizing with snap points, the task bounds could be smaller than the
|
// stack bounds and so we don't even want to use them. Even if the app should not be
|
// resized the Dim should keep up with the divider.
|
if (dockedResizing) {
|
mStack.getBounds(out);
|
} else {
|
mStack.getBounds(mTmpRect);
|
mTmpRect.intersect(getBounds());
|
out.set(mTmpRect);
|
}
|
} else {
|
out.set(getBounds());
|
}
|
return;
|
}
|
|
void setDragResizing(boolean dragResizing, int dragResizeMode) {
|
if (mDragResizing != dragResizing) {
|
// No need to check if the mode is allowed if it's leaving dragResize
|
if (dragResizing && !DragResizeMode.isModeAllowedForStack(mStack, dragResizeMode)) {
|
throw new IllegalArgumentException("Drag resize mode not allow for stack stackId="
|
+ mStack.mStackId + " dragResizeMode=" + dragResizeMode);
|
}
|
mDragResizing = dragResizing;
|
mDragResizeMode = dragResizeMode;
|
resetDragResizingChangeReported();
|
}
|
}
|
|
boolean isDragResizing() {
|
return mDragResizing;
|
}
|
|
int getDragResizeMode() {
|
return mDragResizeMode;
|
}
|
|
/**
|
* Puts this task into docked drag resizing mode. See {@link DragResizeMode}.
|
*
|
* @param resizing Whether to put the task into drag resize mode.
|
*/
|
public void setTaskDockedResizing(boolean resizing) {
|
setDragResizing(resizing, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
|
}
|
|
private void adjustBoundsForDisplayChangeIfNeeded(final DisplayContent displayContent) {
|
if (displayContent == null) {
|
return;
|
}
|
if (matchParentBounds()) {
|
// TODO: Yeah...not sure if this works with WindowConfiguration, but shouldn't be a
|
// problem once we move mBounds into WindowConfiguration.
|
setBounds(null);
|
return;
|
}
|
final int displayId = displayContent.getDisplayId();
|
final int newRotation = displayContent.getDisplayInfo().rotation;
|
if (displayId != mLastRotationDisplayId) {
|
// This task is on a display that it wasn't on. There is no point to keep the relative
|
// position if display rotations for old and new displays are different. Just keep these
|
// values.
|
mLastRotationDisplayId = displayId;
|
mRotation = newRotation;
|
return;
|
}
|
|
if (mRotation == newRotation) {
|
// Rotation didn't change. We don't need to adjust the bounds to keep the relative
|
// position.
|
return;
|
}
|
|
// Device rotation changed.
|
// - We don't want the task to move around on the screen when this happens, so update the
|
// task bounds so it stays in the same place.
|
// - Rotate the bounds and notify activity manager if the task can be resized independently
|
// from its stack. The stack will take care of task rotation for the other case.
|
mTmpRect2.set(getBounds());
|
|
if (!getWindowConfiguration().canResizeTask()) {
|
setBounds(mTmpRect2);
|
return;
|
}
|
|
displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
|
if (setBounds(mTmpRect2) != BOUNDS_CHANGE_NONE) {
|
if (mTaskRecord != null) {
|
mTaskRecord.requestResize(getBounds(), RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
|
}
|
}
|
}
|
|
/** Cancels any running app transitions associated with the task. */
|
void cancelTaskWindowTransition() {
|
for (int i = mChildren.size() - 1; i >= 0; --i) {
|
mChildren.get(i).cancelAnimation();
|
}
|
}
|
|
boolean showForAllUsers() {
|
final int tokensCount = mChildren.size();
|
return (tokensCount != 0) && mChildren.get(tokensCount - 1).mShowForAllUsers;
|
}
|
|
/**
|
* When we are in a floating stack (Freeform, Pinned, ...) we calculate
|
* insets differently. However if we are animating to the fullscreen stack
|
* we need to begin calculating insets as if we were fullscreen, otherwise
|
* we will have a jump at the end.
|
*/
|
boolean isFloating() {
|
return getWindowConfiguration().tasksAreFloating()
|
&& !mStack.isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState;
|
}
|
|
@Override
|
public SurfaceControl getAnimationLeashParent() {
|
// Currently, only the recents animation will create animation leashes for tasks. In this
|
// case, reparent the task to the home animation layer while it is being animated to allow
|
// the home activity to reorder the app windows relative to its own.
|
return getAppAnimationLayer(ANIMATION_LAYER_HOME);
|
}
|
|
@Override
|
SurfaceControl.Builder makeSurface() {
|
return super.makeSurface().setMetadata(METADATA_TASK_ID, mTaskId);
|
}
|
|
boolean isTaskAnimating() {
|
final RecentsAnimationController recentsAnim = mWmService.getRecentsAnimationController();
|
if (recentsAnim != null) {
|
if (recentsAnim.isAnimatingTask(this)) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
WindowState getTopVisibleAppMainWindow() {
|
final AppWindowToken token = getTopVisibleAppToken();
|
return token != null ? token.findMainWindow() : null;
|
}
|
|
AppWindowToken getTopFullscreenAppToken() {
|
for (int i = mChildren.size() - 1; i >= 0; i--) {
|
final AppWindowToken token = mChildren.get(i);
|
final WindowState win = token.findMainWindow();
|
if (win != null && win.mAttrs.isFullscreen()) {
|
return token;
|
}
|
}
|
return null;
|
}
|
|
AppWindowToken getTopVisibleAppToken() {
|
for (int i = mChildren.size() - 1; i >= 0; i--) {
|
final AppWindowToken token = mChildren.get(i);
|
// skip hidden (or about to hide) apps
|
if (!token.mIsExiting && !token.isClientHidden() && !token.hiddenRequested) {
|
return token;
|
}
|
}
|
return null;
|
}
|
|
void positionChildAtTop(AppWindowToken aToken) {
|
positionChildAt(aToken, POSITION_TOP);
|
}
|
|
void positionChildAt(AppWindowToken aToken, int position) {
|
if (aToken == null) {
|
Slog.w(TAG_WM,
|
"Attempted to position of non-existing app");
|
return;
|
}
|
|
positionChildAt(position, aToken, false /* includeParents */);
|
}
|
|
void forceWindowsScaleable(boolean force) {
|
mWmService.openSurfaceTransaction();
|
try {
|
for (int i = mChildren.size() - 1; i >= 0; i--) {
|
mChildren.get(i).forceWindowsScaleableInTransaction(force);
|
}
|
} finally {
|
mWmService.closeSurfaceTransaction("forceWindowsScaleable");
|
}
|
}
|
|
void setTaskDescription(TaskDescription taskDescription) {
|
mTaskDescription = taskDescription;
|
}
|
|
void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
|
mTaskRecord.onSnapshotChanged(snapshot);
|
}
|
|
TaskDescription getTaskDescription() {
|
return mTaskDescription;
|
}
|
|
@Override
|
boolean fillsParent() {
|
return matchParentBounds() || !getWindowConfiguration().canResizeTask();
|
}
|
|
@Override
|
void forAllTasks(Consumer<Task> callback) {
|
callback.accept(this);
|
}
|
|
/**
|
* @param canAffectSystemUiFlags If false, all windows in this task can not affect SystemUI
|
* flags. See {@link WindowState#canAffectSystemUiFlags()}.
|
*/
|
void setCanAffectSystemUiFlags(boolean canAffectSystemUiFlags) {
|
mCanAffectSystemUiFlags = canAffectSystemUiFlags;
|
}
|
|
/**
|
* @see #setCanAffectSystemUiFlags
|
*/
|
boolean canAffectSystemUiFlags() {
|
return mCanAffectSystemUiFlags;
|
}
|
|
void dontAnimateDimExit() {
|
mDimmer.dontAnimateExit();
|
}
|
|
@Override
|
public String toString() {
|
return "{taskId=" + mTaskId + " appTokens=" + mChildren + " mdr=" + mDeferRemoval + "}";
|
}
|
|
String getName() {
|
return toShortString();
|
}
|
|
void clearPreserveNonFloatingState() {
|
mPreserveNonFloatingState = false;
|
}
|
|
@Override
|
Dimmer getDimmer() {
|
return mDimmer;
|
}
|
|
@Override
|
void prepareSurfaces() {
|
mDimmer.resetDimStates();
|
super.prepareSurfaces();
|
getDimBounds(mTmpDimBoundsRect);
|
|
// Bounds need to be relative, as the dim layer is a child.
|
mTmpDimBoundsRect.offsetTo(0, 0);
|
if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
|
scheduleAnimation();
|
}
|
}
|
|
@CallSuper
|
@Override
|
public void writeToProto(ProtoOutputStream proto, long fieldId,
|
@WindowTraceLogLevel int logLevel) {
|
if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
|
return;
|
}
|
|
final long token = proto.start(fieldId);
|
super.writeToProto(proto, WINDOW_CONTAINER, logLevel);
|
proto.write(ID, mTaskId);
|
for (int i = mChildren.size() - 1; i >= 0; i--) {
|
final AppWindowToken appWindowToken = mChildren.get(i);
|
appWindowToken.writeToProto(proto, APP_WINDOW_TOKENS, logLevel);
|
}
|
proto.write(FILLS_PARENT, matchParentBounds());
|
getBounds().writeToProto(proto, BOUNDS);
|
mOverrideDisplayedBounds.writeToProto(proto, DISPLAYED_BOUNDS);
|
proto.write(DEFER_REMOVAL, mDeferRemoval);
|
proto.write(SURFACE_WIDTH, mSurfaceControl.getWidth());
|
proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
|
proto.end(token);
|
}
|
|
@Override
|
public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
|
super.dump(pw, prefix, dumpAll);
|
final String doublePrefix = prefix + " ";
|
|
pw.println(prefix + "taskId=" + mTaskId);
|
pw.println(doublePrefix + "mBounds=" + getBounds().toShortString());
|
pw.println(doublePrefix + "mdr=" + mDeferRemoval);
|
pw.println(doublePrefix + "appTokens=" + mChildren);
|
pw.println(doublePrefix + "mDisplayedBounds=" + mOverrideDisplayedBounds.toShortString());
|
|
final String triplePrefix = doublePrefix + " ";
|
final String quadruplePrefix = triplePrefix + " ";
|
|
for (int i = mChildren.size() - 1; i >= 0; i--) {
|
final AppWindowToken wtoken = mChildren.get(i);
|
pw.println(triplePrefix + "Activity #" + i + " " + wtoken);
|
wtoken.dump(pw, quadruplePrefix, dumpAll);
|
}
|
}
|
|
String toShortString() {
|
return "Task=" + mTaskId;
|
}
|
}
|