Move size compat logic to TaskFragment
This CL just moves the logic, and does not introduce any new logic. Test: atest WmTests Bug: 189384393 Change-Id: I645e3e90ab072f80e0bdf240165eab394e4b2625
This commit is contained in:
@@ -1273,11 +1273,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
|
||||
private void computeConfigurationAfterMultiWindowModeChange() {
|
||||
final Configuration newConfig = new Configuration();
|
||||
newConfig.setTo(task.getRequestedOverrideConfiguration());
|
||||
final TaskFragment taskFrag = getTaskFragment();
|
||||
newConfig.setTo(taskFrag.getRequestedOverrideConfiguration());
|
||||
Rect outBounds = newConfig.windowConfiguration.getBounds();
|
||||
final Configuration parentConfig = task.getParent().getConfiguration();
|
||||
task.adjustForMinimalTaskDimensions(outBounds, outBounds, parentConfig);
|
||||
task.computeConfigResourceOverrides(newConfig, parentConfig);
|
||||
// TODO(b/189384393): which parent does this Activity really needed?
|
||||
// I guess we should either call getRootTask or getDisplayArea for this case.
|
||||
final Configuration parentConfig = taskFrag.getParent().getConfiguration();
|
||||
taskFrag.adjustForMinimalTaskDimensions(outBounds, outBounds, parentConfig);
|
||||
taskFrag.computeConfigResourceOverrides(newConfig, parentConfig);
|
||||
}
|
||||
|
||||
Task getTask() {
|
||||
@@ -7064,7 +7067,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
// If the activity has requested override bounds, the configuration needs to be
|
||||
// computed accordingly.
|
||||
if (!matchParentBounds()) {
|
||||
task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
|
||||
getTaskFragment().computeConfigResourceOverrides(resolvedConfig,
|
||||
newParentConfiguration);
|
||||
}
|
||||
// If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
|
||||
// are already calculated in resolveFixedOrientationConfiguration.
|
||||
@@ -7179,7 +7183,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
|
||||
// Since bounds has changed, the configuration needs to be computed accordingly.
|
||||
task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
|
||||
getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7364,7 +7368,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
|
||||
// Calculate app bounds using fixed orientation bounds because they will be needed later
|
||||
// for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
|
||||
task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
|
||||
getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
|
||||
newParentConfig);
|
||||
mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds);
|
||||
}
|
||||
|
||||
@@ -7394,7 +7399,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
|
||||
// Compute the configuration based on the resolved bounds. If aspect ratio doesn't
|
||||
// restrict, the bounds should be the requested override bounds.
|
||||
task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
|
||||
getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
|
||||
getFixedRotationTransformDisplayInfo());
|
||||
}
|
||||
}
|
||||
@@ -7461,10 +7466,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
// Use resolvedBounds to compute other override configurations such as appBounds. The bounds
|
||||
// are calculated in compat container space. The actual position on screen will be applied
|
||||
// later, so the calculation is simpler that doesn't need to involve offset from parent.
|
||||
task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
|
||||
getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
|
||||
mCompatDisplayInsets);
|
||||
// Use current screen layout as source because the size of app is independent to parent.
|
||||
resolvedConfig.screenLayout = Task.computeScreenLayoutOverride(
|
||||
resolvedConfig.screenLayout = TaskFragment.computeScreenLayoutOverride(
|
||||
getConfiguration().screenLayout, resolvedConfig.screenWidthDp,
|
||||
resolvedConfig.screenHeightDp);
|
||||
|
||||
@@ -8784,7 +8789,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
outAppBounds.offset(insets.left, insets.top);
|
||||
} else if (rotation != ROTATION_UNDEFINED) {
|
||||
// Ensure the app bounds won't overlap with insets.
|
||||
Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]);
|
||||
TaskFragment.intersectWithInsetsIfFits(outAppBounds, outBounds,
|
||||
mNonDecorInsets[rotation]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_
|
||||
import static com.android.server.wm.ConfigurationContainerProto.OVERRIDE_CONFIGURATION;
|
||||
|
||||
import android.annotation.CallSuper;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Point;
|
||||
@@ -111,6 +112,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
|
||||
* This method should be used for getting settings applied in each particular level of the
|
||||
* hierarchy.
|
||||
*/
|
||||
@NonNull
|
||||
public Configuration getConfiguration() {
|
||||
return mFullConfiguration;
|
||||
}
|
||||
@@ -170,11 +172,13 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
|
||||
}
|
||||
|
||||
/** Returns requested override configuration applied to this configuration container. */
|
||||
@NonNull
|
||||
public Configuration getRequestedOverrideConfiguration() {
|
||||
return mRequestedOverrideConfiguration;
|
||||
}
|
||||
|
||||
/** Returns the resolved override configuration. */
|
||||
@NonNull
|
||||
Configuration getResolvedOverrideConfiguration() {
|
||||
return mResolvedOverrideConfiguration;
|
||||
}
|
||||
@@ -203,6 +207,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
|
||||
* Get merged override configuration from the top of the hierarchy down to this particular
|
||||
* instance. This should be reported to client as override config.
|
||||
*/
|
||||
@NonNull
|
||||
public Configuration getMergedOverrideConfiguration() {
|
||||
return mMergedOverrideConfiguration;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
|
||||
import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
|
||||
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
@@ -50,9 +49,6 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
|
||||
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
|
||||
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
|
||||
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
||||
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
||||
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
|
||||
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
||||
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
@@ -156,7 +152,6 @@ import android.app.IActivityController;
|
||||
import android.app.PictureInPictureParams;
|
||||
import android.app.RemoteAction;
|
||||
import android.app.TaskInfo;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -291,7 +286,6 @@ class Task extends TaskFragment {
|
||||
// code.
|
||||
static final int PERSIST_TASK_VERSION = 1;
|
||||
|
||||
static final int INVALID_MIN_SIZE = -1;
|
||||
private float mShadowRadius = 0;
|
||||
|
||||
/**
|
||||
@@ -417,21 +411,12 @@ class Task extends TaskFragment {
|
||||
String mCallingPackage;
|
||||
String mCallingFeatureId;
|
||||
|
||||
private final Rect mTmpStableBounds = new Rect();
|
||||
private final Rect mTmpNonDecorBounds = new Rect();
|
||||
private final Rect mTmpBounds = new Rect();
|
||||
private final Rect mTmpInsets = new Rect();
|
||||
private final Rect mTmpFullBounds = new Rect();
|
||||
private static final Rect sTmpBounds = new Rect();
|
||||
|
||||
// Last non-fullscreen bounds the task was launched in or resized to.
|
||||
// The information is persisted and used to determine the appropriate root task to launch the
|
||||
// task into on restore.
|
||||
Rect mLastNonFullscreenBounds = null;
|
||||
// Minimal width and height of this task when it's resizeable. -1 means it should use the
|
||||
// default minimal width/height.
|
||||
int mMinWidth;
|
||||
int mMinHeight;
|
||||
|
||||
// The surface transition of the target when recents animation is finished.
|
||||
// This is originally introduced to carry out the current surface control position and window
|
||||
@@ -573,119 +558,6 @@ class Task extends TaskFragment {
|
||||
}
|
||||
|
||||
private static final ResetTargetTaskHelper sResetTargetTaskHelper = new ResetTargetTaskHelper();
|
||||
private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
|
||||
new EnsureVisibleActivitiesConfigHelper();
|
||||
private class EnsureVisibleActivitiesConfigHelper {
|
||||
private boolean mUpdateConfig;
|
||||
private boolean mPreserveWindow;
|
||||
private boolean mBehindFullscreen;
|
||||
|
||||
void reset(boolean preserveWindow) {
|
||||
mPreserveWindow = preserveWindow;
|
||||
mUpdateConfig = false;
|
||||
mBehindFullscreen = false;
|
||||
}
|
||||
|
||||
void process(ActivityRecord start, boolean preserveWindow) {
|
||||
if (start == null || !start.mVisibleRequested) {
|
||||
return;
|
||||
}
|
||||
reset(preserveWindow);
|
||||
|
||||
final PooledFunction f = PooledLambda.obtainFunction(
|
||||
EnsureVisibleActivitiesConfigHelper::processActivity, this,
|
||||
PooledLambda.__(ActivityRecord.class));
|
||||
forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/);
|
||||
f.recycle();
|
||||
|
||||
if (mUpdateConfig) {
|
||||
// Ensure the resumed state of the focus activity if we updated the configuration of
|
||||
// any activity.
|
||||
mRootWindowContainer.resumeFocusedTasksTopActivities();
|
||||
}
|
||||
}
|
||||
|
||||
boolean processActivity(ActivityRecord r) {
|
||||
mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
|
||||
mBehindFullscreen |= r.occludesParent();
|
||||
return mBehindFullscreen;
|
||||
}
|
||||
}
|
||||
|
||||
private final CheckBehindFullscreenActivityHelper mCheckBehindFullscreenActivityHelper =
|
||||
new CheckBehindFullscreenActivityHelper();
|
||||
private class CheckBehindFullscreenActivityHelper {
|
||||
private boolean mAboveTop;
|
||||
private boolean mBehindFullscreenActivity;
|
||||
private ActivityRecord mToCheck;
|
||||
private Consumer<ActivityRecord> mHandleBehindFullscreenActivity;
|
||||
private boolean mHandlingOccluded;
|
||||
|
||||
private void reset(ActivityRecord toCheck,
|
||||
Consumer<ActivityRecord> handleBehindFullscreenActivity) {
|
||||
mToCheck = toCheck;
|
||||
mHandleBehindFullscreenActivity = handleBehindFullscreenActivity;
|
||||
mAboveTop = true;
|
||||
mBehindFullscreenActivity = false;
|
||||
|
||||
if (!shouldBeVisible(null)) {
|
||||
// The root task is not visible, so no activity in it should be displaying a
|
||||
// starting window. Mark all activities below top and behind fullscreen.
|
||||
mAboveTop = false;
|
||||
mBehindFullscreenActivity = true;
|
||||
}
|
||||
|
||||
mHandlingOccluded = mToCheck == null && mHandleBehindFullscreenActivity != null;
|
||||
}
|
||||
|
||||
boolean process(ActivityRecord toCheck,
|
||||
Consumer<ActivityRecord> handleBehindFullscreenActivity) {
|
||||
reset(toCheck, handleBehindFullscreenActivity);
|
||||
|
||||
if (!mHandlingOccluded && mBehindFullscreenActivity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final ActivityRecord topActivity = topRunningActivity();
|
||||
final PooledFunction f = PooledLambda.obtainFunction(
|
||||
CheckBehindFullscreenActivityHelper::processActivity, this,
|
||||
PooledLambda.__(ActivityRecord.class), topActivity);
|
||||
forAllActivities(f);
|
||||
f.recycle();
|
||||
|
||||
return mBehindFullscreenActivity;
|
||||
}
|
||||
|
||||
/** Returns {@code true} to stop the outer loop and indicate the result is computed. */
|
||||
private boolean processActivity(ActivityRecord r, ActivityRecord topActivity) {
|
||||
if (mAboveTop) {
|
||||
if (r == topActivity) {
|
||||
if (r == mToCheck) {
|
||||
// It is the top activity in a visible root task.
|
||||
mBehindFullscreenActivity = false;
|
||||
return true;
|
||||
}
|
||||
mAboveTop = false;
|
||||
}
|
||||
mBehindFullscreenActivity |= r.occludesParent();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mHandlingOccluded) {
|
||||
// Iterating through all occluded activities.
|
||||
if (mBehindFullscreenActivity) {
|
||||
mHandleBehindFullscreenActivity.accept(r);
|
||||
}
|
||||
} else if (r == mToCheck) {
|
||||
return true;
|
||||
} else if (mBehindFullscreenActivity) {
|
||||
// It is occluded before {@param toCheck} is found.
|
||||
return true;
|
||||
}
|
||||
mBehindFullscreenActivity |= r.occludesParent();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private final FindRootHelper mFindRootHelper = new FindRootHelper();
|
||||
private class FindRootHelper {
|
||||
@@ -1595,15 +1467,6 @@ class Task extends TaskFragment {
|
||||
updateEffectiveIntent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getActivityType() {
|
||||
final int applicationType = super.getActivityType();
|
||||
if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
|
||||
return applicationType;
|
||||
}
|
||||
return getTopChild().getActivityType();
|
||||
}
|
||||
|
||||
@Override
|
||||
void addChild(WindowContainer child, int index) {
|
||||
// If this task had any child before we added this one.
|
||||
@@ -1906,32 +1769,6 @@ class Task extends TaskFragment {
|
||||
&& supportsMultiWindowInDisplayArea(tda);
|
||||
}
|
||||
|
||||
boolean supportsMultiWindow() {
|
||||
return supportsMultiWindowInDisplayArea(getDisplayArea());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this task supports multi-window if it is in the given
|
||||
* {@link TaskDisplayArea}.
|
||||
*/
|
||||
boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
|
||||
if (!mAtmService.mSupportsMultiWindow) {
|
||||
return false;
|
||||
}
|
||||
if (tda == null) {
|
||||
Slog.w(TAG_TASKS, "Can't find TaskDisplayArea to determine support for multi"
|
||||
+ " window. Task id=" + mTaskId + " attached=" + isAttached());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isResizeable() && !tda.supportsNonResizableMultiWindow()) {
|
||||
// Not support non-resizable in multi window.
|
||||
return false;
|
||||
}
|
||||
|
||||
return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this task can be launched on the specified display.
|
||||
*
|
||||
@@ -2067,60 +1904,6 @@ class Task extends TaskFragment {
|
||||
}
|
||||
}
|
||||
|
||||
void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
|
||||
@NonNull Configuration parentConfig) {
|
||||
int minWidth = mMinWidth;
|
||||
int minHeight = mMinHeight;
|
||||
// If the task has no requested minimal size, we'd like to enforce a minimal size
|
||||
// so that the user can not render the task too small to manipulate. We don't need
|
||||
// to do this for the root pinned task as the bounds are controlled by the system.
|
||||
if (!inPinnedWindowingMode()) {
|
||||
final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
|
||||
final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
|
||||
final int defaultMinSize = (int) (defaultMinSizeDp * density);
|
||||
|
||||
if (minWidth == INVALID_MIN_SIZE) {
|
||||
minWidth = defaultMinSize;
|
||||
}
|
||||
if (minHeight == INVALID_MIN_SIZE) {
|
||||
minHeight = defaultMinSize;
|
||||
}
|
||||
}
|
||||
if (bounds.isEmpty()) {
|
||||
// If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
|
||||
// do, we can just skip.
|
||||
final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
|
||||
if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) {
|
||||
return;
|
||||
}
|
||||
bounds.set(parentBounds);
|
||||
}
|
||||
final boolean adjustWidth = minWidth > bounds.width();
|
||||
final boolean adjustHeight = minHeight > bounds.height();
|
||||
if (!(adjustWidth || adjustHeight)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (adjustWidth) {
|
||||
if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
|
||||
bounds.left = bounds.right - minWidth;
|
||||
} else {
|
||||
// Either left bounds match, or neither match, or the previous bounds were
|
||||
// fullscreen and we default to keeping left.
|
||||
bounds.right = bounds.left + minWidth;
|
||||
}
|
||||
}
|
||||
if (adjustHeight) {
|
||||
if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
|
||||
bounds.top = bounds.bottom - minHeight;
|
||||
} else {
|
||||
// Either top bounds match, or neither match, or the previous bounds were
|
||||
// fullscreen and we default to keeping top.
|
||||
bounds.bottom = bounds.top + minHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setLastNonFullscreenBounds(Rect bounds) {
|
||||
if (mLastNonFullscreenBounds == null) {
|
||||
mLastNonFullscreenBounds = new Rect(bounds);
|
||||
@@ -2421,400 +2204,6 @@ class Task extends TaskFragment {
|
||||
mTaskSupervisor.mLaunchParamsPersister.saveTask(this, display);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust bounds to stay within root task bounds.
|
||||
*
|
||||
* Since bounds might be outside of root task bounds, this method tries to move the bounds in
|
||||
* a way that keep them unchanged, but be contained within the root task bounds.
|
||||
*
|
||||
* @param bounds Bounds to be adjusted.
|
||||
* @param rootTaskBounds Bounds within which the other bounds should remain.
|
||||
* @param overlapPxX The amount of px required to be visible in the X dimension.
|
||||
* @param overlapPxY The amount of px required to be visible in the Y dimension.
|
||||
*/
|
||||
private static void fitWithinBounds(Rect bounds, Rect rootTaskBounds, int overlapPxX,
|
||||
int overlapPxY) {
|
||||
if (rootTaskBounds == null || rootTaskBounds.isEmpty() || rootTaskBounds.contains(bounds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For each side of the parent (eg. left), check if the opposing side of the window (eg.
|
||||
// right) is at least overlap pixels away. If less, offset the window by that difference.
|
||||
int horizontalDiff = 0;
|
||||
// If window is smaller than overlap, use it's smallest dimension instead
|
||||
int overlapLR = Math.min(overlapPxX, bounds.width());
|
||||
if (bounds.right < (rootTaskBounds.left + overlapLR)) {
|
||||
horizontalDiff = overlapLR - (bounds.right - rootTaskBounds.left);
|
||||
} else if (bounds.left > (rootTaskBounds.right - overlapLR)) {
|
||||
horizontalDiff = -(overlapLR - (rootTaskBounds.right - bounds.left));
|
||||
}
|
||||
int verticalDiff = 0;
|
||||
int overlapTB = Math.min(overlapPxY, bounds.width());
|
||||
if (bounds.bottom < (rootTaskBounds.top + overlapTB)) {
|
||||
verticalDiff = overlapTB - (bounds.bottom - rootTaskBounds.top);
|
||||
} else if (bounds.top > (rootTaskBounds.bottom - overlapTB)) {
|
||||
verticalDiff = -(overlapTB - (rootTaskBounds.bottom - bounds.top));
|
||||
}
|
||||
bounds.offset(horizontalDiff, verticalDiff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than
|
||||
* intersectBounds on a side, then the respective side will not be intersected.
|
||||
*
|
||||
* The assumption is that if inOutBounds is initially larger than intersectBounds, then the
|
||||
* inset on that side is no-longer applicable. This scenario happens when a task's minimal
|
||||
* bounds are larger than the provided parent/display bounds.
|
||||
*
|
||||
* @param inOutBounds the bounds to intersect.
|
||||
* @param intersectBounds the bounds to intersect with.
|
||||
* @param intersectInsets insets to apply to intersectBounds before intersecting.
|
||||
*/
|
||||
static void intersectWithInsetsIfFits(
|
||||
Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) {
|
||||
if (inOutBounds.right <= intersectBounds.right) {
|
||||
inOutBounds.right =
|
||||
Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right);
|
||||
}
|
||||
if (inOutBounds.bottom <= intersectBounds.bottom) {
|
||||
inOutBounds.bottom =
|
||||
Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom);
|
||||
}
|
||||
if (inOutBounds.left >= intersectBounds.left) {
|
||||
inOutBounds.left =
|
||||
Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left);
|
||||
}
|
||||
if (inOutBounds.top >= intersectBounds.top) {
|
||||
inOutBounds.top =
|
||||
Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets bounds with non-decor and stable insets applied respectively.
|
||||
*
|
||||
* If bounds overhangs the display, those edges will not get insets. See
|
||||
* {@link #intersectWithInsetsIfFits}
|
||||
*
|
||||
* @param outNonDecorBounds where to place bounds with non-decor insets applied.
|
||||
* @param outStableBounds where to place bounds with stable insets applied.
|
||||
* @param bounds the bounds to inset.
|
||||
*/
|
||||
void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
|
||||
DisplayInfo displayInfo) {
|
||||
outNonDecorBounds.set(bounds);
|
||||
outStableBounds.set(bounds);
|
||||
final Task rootTask = getRootTask();
|
||||
if (rootTask == null || rootTask.mDisplayContent == null) {
|
||||
return;
|
||||
}
|
||||
mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
|
||||
|
||||
final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
|
||||
policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
|
||||
displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
|
||||
intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
|
||||
|
||||
policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
|
||||
intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the app bounds related configuration can be computed by
|
||||
* {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
|
||||
* ActivityRecord.CompatDisplayInsets)}.
|
||||
*/
|
||||
private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
|
||||
final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
|
||||
if (appBounds != null) {
|
||||
appBounds.setEmpty();
|
||||
}
|
||||
inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
|
||||
inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
|
||||
}
|
||||
|
||||
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
|
||||
@NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
|
||||
if (overrideDisplayInfo != null) {
|
||||
// Make sure the screen related configs can be computed by the provided display info.
|
||||
inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
|
||||
invalidateAppBoundsConfig(inOutConfig);
|
||||
}
|
||||
computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
|
||||
null /* compatInsets */);
|
||||
}
|
||||
|
||||
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
|
||||
@NonNull Configuration parentConfig) {
|
||||
computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
|
||||
null /* compatInsets */);
|
||||
}
|
||||
|
||||
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
|
||||
@NonNull Configuration parentConfig,
|
||||
@Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
|
||||
if (compatInsets != null) {
|
||||
// Make sure the app bounds can be computed by the compat insets.
|
||||
invalidateAppBoundsConfig(inOutConfig);
|
||||
}
|
||||
computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
|
||||
compatInsets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates configuration values used by the client to get resources. This should be run
|
||||
* using app-facing bounds (bounds unmodified by animations or transient interactions).
|
||||
*
|
||||
* This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely
|
||||
* configuring an "inherit-bounds" window which means that all configuration settings would
|
||||
* just be inherited from the parent configuration.
|
||||
**/
|
||||
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
|
||||
@NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo,
|
||||
@Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
|
||||
int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
|
||||
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
|
||||
windowingMode = parentConfig.windowConfiguration.getWindowingMode();
|
||||
}
|
||||
|
||||
float density = inOutConfig.densityDpi;
|
||||
if (density == Configuration.DENSITY_DPI_UNDEFINED) {
|
||||
density = parentConfig.densityDpi;
|
||||
}
|
||||
density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
|
||||
|
||||
// The bounds may have been overridden at this level. If the parent cannot cover these
|
||||
// bounds, the configuration is still computed according to the override bounds.
|
||||
final boolean insideParentBounds;
|
||||
|
||||
final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
|
||||
final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds();
|
||||
if (resolvedBounds == null || resolvedBounds.isEmpty()) {
|
||||
mTmpFullBounds.set(parentBounds);
|
||||
insideParentBounds = true;
|
||||
} else {
|
||||
mTmpFullBounds.set(resolvedBounds);
|
||||
insideParentBounds = parentBounds.contains(resolvedBounds);
|
||||
}
|
||||
|
||||
// Non-null compatibility insets means the activity prefers to keep its original size, so
|
||||
// out bounds doesn't need to be restricted by the parent or current display
|
||||
final boolean customContainerPolicy = compatInsets != null;
|
||||
|
||||
Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
|
||||
if (outAppBounds == null || outAppBounds.isEmpty()) {
|
||||
// App-bounds hasn't been overridden, so calculate a value for it.
|
||||
inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds);
|
||||
outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
|
||||
|
||||
if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
|
||||
final Rect containingAppBounds;
|
||||
if (insideParentBounds) {
|
||||
containingAppBounds = parentConfig.windowConfiguration.getAppBounds();
|
||||
} else {
|
||||
// Restrict appBounds to display non-decor rather than parent because the
|
||||
// override bounds are beyond the parent. Otherwise, it won't match the
|
||||
// overridden bounds.
|
||||
final TaskDisplayArea displayArea = getDisplayArea();
|
||||
containingAppBounds = displayArea != null
|
||||
? displayArea.getWindowConfiguration().getAppBounds() : null;
|
||||
}
|
||||
if (containingAppBounds != null && !containingAppBounds.isEmpty()) {
|
||||
outAppBounds.intersect(containingAppBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
|
||||
|| inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
|
||||
if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) {
|
||||
mTmpNonDecorBounds.set(mTmpFullBounds);
|
||||
mTmpStableBounds.set(mTmpFullBounds);
|
||||
} else if (!customContainerPolicy
|
||||
&& (overrideDisplayInfo != null || getDisplayContent() != null)) {
|
||||
final DisplayInfo di = overrideDisplayInfo != null
|
||||
? overrideDisplayInfo
|
||||
: getDisplayContent().getDisplayInfo();
|
||||
|
||||
// For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
|
||||
// area, i.e. the screen area without the system bars.
|
||||
// The non decor inset are areas that could never be removed in Honeycomb. See
|
||||
// {@link WindowManagerPolicy#getNonDecorInsetsLw}.
|
||||
calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di);
|
||||
} else {
|
||||
// Apply the given non-decor and stable insets to calculate the corresponding bounds
|
||||
// for screen size of configuration.
|
||||
int rotation = inOutConfig.windowConfiguration.getRotation();
|
||||
if (rotation == ROTATION_UNDEFINED) {
|
||||
rotation = parentConfig.windowConfiguration.getRotation();
|
||||
}
|
||||
if (rotation != ROTATION_UNDEFINED && customContainerPolicy) {
|
||||
mTmpNonDecorBounds.set(mTmpFullBounds);
|
||||
mTmpStableBounds.set(mTmpFullBounds);
|
||||
compatInsets.getBoundsByRotation(mTmpBounds, rotation);
|
||||
intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds,
|
||||
compatInsets.mNonDecorInsets[rotation]);
|
||||
intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
|
||||
compatInsets.mStableInsets[rotation]);
|
||||
outAppBounds.set(mTmpNonDecorBounds);
|
||||
} else {
|
||||
// Set to app bounds because it excludes decor insets.
|
||||
mTmpNonDecorBounds.set(outAppBounds);
|
||||
mTmpStableBounds.set(outAppBounds);
|
||||
}
|
||||
}
|
||||
|
||||
if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
|
||||
final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
|
||||
inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy)
|
||||
? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
|
||||
: overrideScreenWidthDp;
|
||||
}
|
||||
if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
|
||||
final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
|
||||
inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy)
|
||||
? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
|
||||
: overrideScreenHeightDp;
|
||||
}
|
||||
|
||||
if (inOutConfig.smallestScreenWidthDp
|
||||
== Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
|
||||
if (WindowConfiguration.isFloating(windowingMode)) {
|
||||
// For floating tasks, calculate the smallest width from the bounds of the task
|
||||
inOutConfig.smallestScreenWidthDp = (int) (
|
||||
Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
|
||||
}
|
||||
// otherwise, it will just inherit
|
||||
}
|
||||
}
|
||||
|
||||
if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
|
||||
inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
|
||||
? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
|
||||
}
|
||||
if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
|
||||
// For calculating screen layout, we need to use the non-decor inset screen area for the
|
||||
// calculation for compatibility reasons, i.e. screen area without system bars that
|
||||
// could never go away in Honeycomb.
|
||||
int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
|
||||
int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
|
||||
// Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
|
||||
// undefined so it can't be used.
|
||||
if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
|
||||
compatScreenWidthDp = inOutConfig.screenWidthDp;
|
||||
}
|
||||
if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
|
||||
compatScreenHeightDp = inOutConfig.screenHeightDp;
|
||||
}
|
||||
// Reducing the screen layout starting from its parent config.
|
||||
inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout,
|
||||
compatScreenWidthDp, compatScreenHeightDp);
|
||||
}
|
||||
}
|
||||
|
||||
/** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */
|
||||
static int computeScreenLayoutOverride(int sourceScreenLayout, int screenWidthDp,
|
||||
int screenHeightDp) {
|
||||
sourceScreenLayout = sourceScreenLayout
|
||||
& (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
|
||||
final int longSize = Math.max(screenWidthDp, screenHeightDp);
|
||||
final int shortSize = Math.min(screenWidthDp, screenHeightDp);
|
||||
return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
void resolveOverrideConfiguration(Configuration newParentConfig) {
|
||||
mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
|
||||
super.resolveOverrideConfiguration(newParentConfig);
|
||||
|
||||
int windowingMode =
|
||||
getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
|
||||
final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
|
||||
|
||||
// Resolve override windowing mode to fullscreen for home task (even on freeform
|
||||
// display), or split-screen if in split-screen mode.
|
||||
if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
|
||||
windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
|
||||
? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
|
||||
getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
|
||||
}
|
||||
|
||||
// Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
|
||||
// pinned windowing mode.
|
||||
if (!supportsMultiWindow()) {
|
||||
final int candidateWindowingMode =
|
||||
windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
|
||||
if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
|
||||
&& candidateWindowingMode != WINDOWING_MODE_PINNED) {
|
||||
getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
|
||||
WINDOWING_MODE_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
if (isLeafTask()) {
|
||||
resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */);
|
||||
}
|
||||
computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
|
||||
}
|
||||
|
||||
private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig,
|
||||
Rect previousBounds) {
|
||||
|
||||
int windowingMode =
|
||||
getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
|
||||
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
|
||||
windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
|
||||
}
|
||||
// Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
|
||||
// mode that may cause the bounds to be miscalculated, e.g. letterboxed.
|
||||
getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
|
||||
Rect outOverrideBounds =
|
||||
getResolvedOverrideConfiguration().windowConfiguration.getBounds();
|
||||
|
||||
if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
|
||||
// Use empty bounds to indicate "fill parent".
|
||||
outOverrideBounds.setEmpty();
|
||||
// The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
|
||||
// the parent or display is smaller than the size, the content may be cropped.
|
||||
return;
|
||||
}
|
||||
|
||||
adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
|
||||
if (windowingMode == WINDOWING_MODE_FREEFORM) {
|
||||
computeFreeformBounds(outOverrideBounds, newParentConfig);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
|
||||
private void computeFreeformBounds(@NonNull Rect outBounds,
|
||||
@NonNull Configuration newParentConfig) {
|
||||
// by policy, make sure the window remains within parent somewhere
|
||||
final float density =
|
||||
((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
|
||||
final Rect parentBounds =
|
||||
new Rect(newParentConfig.windowConfiguration.getBounds());
|
||||
final DisplayContent display = getDisplayContent();
|
||||
if (display != null) {
|
||||
// If a freeform window moves below system bar, there is no way to move it again
|
||||
// by touch. Because its caption is covered by system bar. So we exclude them
|
||||
// from root task bounds. and then caption will be shown inside stable area.
|
||||
final Rect stableBounds = new Rect();
|
||||
display.getStableRect(stableBounds);
|
||||
parentBounds.intersect(stableBounds);
|
||||
}
|
||||
|
||||
fitWithinBounds(outBounds, parentBounds,
|
||||
(int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
|
||||
(int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
|
||||
|
||||
// Prevent to overlap caption with stable insets.
|
||||
final int offsetTop = parentBounds.top - outBounds.top;
|
||||
if (offsetTop > 0) {
|
||||
outBounds.offset(0, offsetTop);
|
||||
}
|
||||
}
|
||||
|
||||
Rect updateOverrideConfigurationFromLaunchBounds() {
|
||||
// If the task is controlled by another organized task, do not set override
|
||||
// configurations and let its parent (organized task) to control it;
|
||||
@@ -2873,12 +2262,9 @@ class Task extends TaskFragment {
|
||||
return getRootTask().mTaskId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Task getRootTask() {
|
||||
final WindowContainer parent = getParent();
|
||||
if (parent == null) return this;
|
||||
|
||||
final Task parentTask = parent.asTask();
|
||||
return parentTask == null ? this : parentTask.getRootTask();
|
||||
return getRootTaskFragment().asTask();
|
||||
}
|
||||
|
||||
/** @return the first organized task. */
|
||||
@@ -3449,18 +2835,6 @@ class Task extends TaskFragment {
|
||||
mForceShowForAllUsers = forceShowForAllUsers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAttached() {
|
||||
final TaskDisplayArea taskDisplayArea = getDisplayArea();
|
||||
return taskDisplayArea != null && !taskDisplayArea.isRemoved();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
TaskDisplayArea getDisplayArea() {
|
||||
return (TaskDisplayArea) super.getDisplayArea();
|
||||
}
|
||||
|
||||
/**
|
||||
* When we are in a floating root task (Freeform, Pinned, ...) we calculate
|
||||
* insets differently. However if we are animating to the fullscreen root task
|
||||
@@ -5400,25 +4774,6 @@ class Task extends TaskFragment {
|
||||
}
|
||||
}
|
||||
|
||||
/** @see ActivityRecord#cancelInitializing() */
|
||||
void cancelInitializingActivities() {
|
||||
// We don't want to clear starting window for activities that aren't behind fullscreen
|
||||
// activities as we need to display their starting window until they are done initializing.
|
||||
checkBehindFullscreenActivity(null /* toCheck */, ActivityRecord::cancelInitializing);
|
||||
}
|
||||
|
||||
/**
|
||||
* If an activity {@param toCheck} is given, this method returns {@code true} if the activity
|
||||
* is occluded by any fullscreen activity. If there is no {@param toCheck} and the handling
|
||||
* function {@param handleBehindFullscreenActivity} is given, this method will pass all occluded
|
||||
* activities to the function.
|
||||
*/
|
||||
boolean checkBehindFullscreenActivity(ActivityRecord toCheck,
|
||||
Consumer<ActivityRecord> handleBehindFullscreenActivity) {
|
||||
return mCheckBehindFullscreenActivityHelper.process(
|
||||
toCheck, handleBehindFullscreenActivity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the top activity in the root task is resumed.
|
||||
*
|
||||
@@ -6197,13 +5552,6 @@ class Task extends TaskFragment {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures all visible activities at or below the input activity have the right configuration.
|
||||
*/
|
||||
void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
|
||||
mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
|
||||
}
|
||||
|
||||
// TODO: Can only be called from special methods in ActivityTaskSupervisor.
|
||||
// Need to consolidate those calls points into this resize method so anyone can call directly.
|
||||
void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
|
||||
|
||||
@@ -16,11 +16,20 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
|
||||
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
|
||||
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
||||
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
||||
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
|
||||
import static android.view.Display.INVALID_DISPLAY;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
|
||||
@@ -44,19 +53,26 @@ import static com.android.server.wm.Task.ActivityState.RESUMED;
|
||||
import static com.android.server.wm.Task.ActivityState.STOPPING;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.ResultInfo;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.app.servertransaction.ActivityResultItem;
|
||||
import android.app.servertransaction.ClientTransaction;
|
||||
import android.app.servertransaction.NewIntentItem;
|
||||
import android.app.servertransaction.PauseActivityItem;
|
||||
import android.app.servertransaction.ResumeActivityItem;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.os.RemoteException;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Slog;
|
||||
import android.view.DisplayInfo;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.internal.util.function.pooled.PooledFunction;
|
||||
import com.android.internal.util.function.pooled.PooledLambda;
|
||||
import com.android.internal.util.function.pooled.PooledPredicate;
|
||||
|
||||
@@ -97,10 +113,33 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
/** Set to false to disable the preview that is shown while a new activity is being started. */
|
||||
static final boolean SHOW_APP_STARTING_PREVIEW = true;
|
||||
|
||||
/**
|
||||
* Indicate that the minimal width/height should use the default value.
|
||||
*
|
||||
* @see #mMinWidth
|
||||
* @see #mMinHeight
|
||||
*/
|
||||
static final int INVALID_MIN_SIZE = -1;
|
||||
|
||||
final ActivityTaskManagerService mAtmService;
|
||||
final ActivityTaskSupervisor mTaskSupervisor;
|
||||
final RootWindowContainer mRootWindowContainer;
|
||||
|
||||
// TODO(b/189384393): this is not set in TaskFragment so far. It should be passed from the
|
||||
// parent task when adding to the hierarchy
|
||||
/**
|
||||
* Minimal width of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it
|
||||
* should use the default minimal width.
|
||||
*/
|
||||
int mMinWidth;
|
||||
// TODO(b/189384393): this is not set in TaskFragment so far. It should be passed from the
|
||||
// parent task when adding to the hierarchy
|
||||
/**
|
||||
* Minimal height of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it
|
||||
* should use the default minimal height.
|
||||
*/
|
||||
int mMinHeight;
|
||||
|
||||
// The TaskFragment that adjacent to this one.
|
||||
private TaskFragment mAdjacentTaskFragment;
|
||||
|
||||
@@ -128,8 +167,53 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
@Nullable
|
||||
private ActivityRecord mResumedActivity = null;
|
||||
|
||||
private final Rect mTmpInsets = new Rect();
|
||||
private final Rect mTmpBounds = new Rect();
|
||||
private final Rect mTmpFullBounds = new Rect();
|
||||
private final Rect mTmpStableBounds = new Rect();
|
||||
private final Rect mTmpNonDecorBounds = new Rect();
|
||||
|
||||
private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
|
||||
new EnsureActivitiesVisibleHelper(this);
|
||||
private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
|
||||
new EnsureVisibleActivitiesConfigHelper();
|
||||
private class EnsureVisibleActivitiesConfigHelper {
|
||||
private boolean mUpdateConfig;
|
||||
private boolean mPreserveWindow;
|
||||
private boolean mBehindFullscreen;
|
||||
|
||||
void reset(boolean preserveWindow) {
|
||||
mPreserveWindow = preserveWindow;
|
||||
mUpdateConfig = false;
|
||||
mBehindFullscreen = false;
|
||||
}
|
||||
|
||||
void process(ActivityRecord start, boolean preserveWindow) {
|
||||
if (start == null || !start.mVisibleRequested) {
|
||||
return;
|
||||
}
|
||||
reset(preserveWindow);
|
||||
|
||||
final PooledFunction f = PooledLambda.obtainFunction(
|
||||
EnsureVisibleActivitiesConfigHelper::processActivity, this,
|
||||
PooledLambda.__(ActivityRecord.class));
|
||||
forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/);
|
||||
f.recycle();
|
||||
|
||||
if (mUpdateConfig) {
|
||||
// TODO(b/189384393): rename to resumeFocusedTaskFragmentsTopActivities
|
||||
// Ensure the resumed state of the focus activity if we updated the configuration of
|
||||
// any activity.
|
||||
mRootWindowContainer.resumeFocusedTasksTopActivities();
|
||||
}
|
||||
}
|
||||
|
||||
boolean processActivity(ActivityRecord r) {
|
||||
mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
|
||||
mBehindFullscreen |= r.occludesParent();
|
||||
return mBehindFullscreen;
|
||||
}
|
||||
}
|
||||
|
||||
TaskFragment(ActivityTaskManagerService atmService) {
|
||||
super(atmService.mWindowManager);
|
||||
@@ -197,9 +281,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
return (TaskDisplayArea) super.getDisplayArea();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAttached() {
|
||||
final TaskDisplayArea taskDisplayArea = getDisplayArea();
|
||||
return taskDisplayArea != null && !taskDisplayArea.isRemoved();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root {@link TaskFragment}, which is usually also a {@link Task}.
|
||||
*/
|
||||
@NonNull
|
||||
TaskFragment getRootTaskFragment() {
|
||||
final WindowContainer parent = getParent();
|
||||
if (parent == null) return this;
|
||||
@@ -1297,4 +1388,500 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
boolean shouldSleepActivities() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
void resolveOverrideConfiguration(Configuration newParentConfig) {
|
||||
mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
|
||||
super.resolveOverrideConfiguration(newParentConfig);
|
||||
|
||||
int windowingMode =
|
||||
getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
|
||||
final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
|
||||
|
||||
// Resolve override windowing mode to fullscreen for home task (even on freeform
|
||||
// display), or split-screen if in split-screen mode.
|
||||
if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
|
||||
windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
|
||||
? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
|
||||
getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
|
||||
}
|
||||
|
||||
// Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
|
||||
// pinned windowing mode.
|
||||
if (!supportsMultiWindow()) {
|
||||
final int candidateWindowingMode =
|
||||
windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
|
||||
if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
|
||||
&& candidateWindowingMode != WINDOWING_MODE_PINNED) {
|
||||
getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
|
||||
WINDOWING_MODE_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
if (isLeafTaskFragment()) {
|
||||
resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */);
|
||||
}
|
||||
computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
|
||||
}
|
||||
|
||||
boolean supportsMultiWindow() {
|
||||
return supportsMultiWindowInDisplayArea(getDisplayArea());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this task supports multi-window if it is in the given
|
||||
* {@link TaskDisplayArea}.
|
||||
*/
|
||||
boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
|
||||
if (!mAtmService.mSupportsMultiWindow) {
|
||||
return false;
|
||||
}
|
||||
final Task task = getTask();
|
||||
if (task == null) {
|
||||
return false;
|
||||
}
|
||||
if (tda == null) {
|
||||
Slog.w(TAG, "Can't find TaskDisplayArea to determine support for multi"
|
||||
+ " window. Task id=" + getTaskId() + " attached=" + isAttached());
|
||||
return false;
|
||||
}
|
||||
if (!getTask().isResizeable() && !tda.supportsNonResizableMultiWindow()) {
|
||||
// Not support non-resizable in multi window.
|
||||
return false;
|
||||
}
|
||||
|
||||
return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight);
|
||||
}
|
||||
|
||||
private int getTaskId() {
|
||||
return getTask().mTaskId;
|
||||
}
|
||||
|
||||
private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig,
|
||||
Rect previousBounds) {
|
||||
|
||||
int windowingMode =
|
||||
getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
|
||||
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
|
||||
windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
|
||||
}
|
||||
// Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
|
||||
// mode that may cause the bounds to be miscalculated, e.g. letterboxed.
|
||||
getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
|
||||
Rect outOverrideBounds = getResolvedOverrideConfiguration().windowConfiguration.getBounds();
|
||||
|
||||
if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
|
||||
// Use empty bounds to indicate "fill parent".
|
||||
outOverrideBounds.setEmpty();
|
||||
// The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
|
||||
// the parent or display is smaller than the size, the content may be cropped.
|
||||
return;
|
||||
}
|
||||
|
||||
adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
|
||||
if (windowingMode == WINDOWING_MODE_FREEFORM) {
|
||||
computeFreeformBounds(outOverrideBounds, newParentConfig);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
|
||||
private void computeFreeformBounds(@NonNull Rect outBounds,
|
||||
@NonNull Configuration newParentConfig) {
|
||||
// by policy, make sure the window remains within parent somewhere
|
||||
final float density =
|
||||
((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
|
||||
final Rect parentBounds =
|
||||
new Rect(newParentConfig.windowConfiguration.getBounds());
|
||||
final DisplayContent display = getDisplayContent();
|
||||
if (display != null) {
|
||||
// If a freeform window moves below system bar, there is no way to move it again
|
||||
// by touch. Because its caption is covered by system bar. So we exclude them
|
||||
// from root task bounds. and then caption will be shown inside stable area.
|
||||
final Rect stableBounds = new Rect();
|
||||
display.getStableRect(stableBounds);
|
||||
parentBounds.intersect(stableBounds);
|
||||
}
|
||||
|
||||
fitWithinBounds(outBounds, parentBounds,
|
||||
(int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
|
||||
(int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
|
||||
|
||||
// Prevent to overlap caption with stable insets.
|
||||
final int offsetTop = parentBounds.top - outBounds.top;
|
||||
if (offsetTop > 0) {
|
||||
outBounds.offset(0, offsetTop);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts bounds to stay within root task bounds.
|
||||
*
|
||||
* Since bounds might be outside of root task bounds, this method tries to move the bounds in
|
||||
* a way that keep them unchanged, but be contained within the root task bounds.
|
||||
*
|
||||
* @param bounds Bounds to be adjusted.
|
||||
* @param rootTaskBounds Bounds within which the other bounds should remain.
|
||||
* @param overlapPxX The amount of px required to be visible in the X dimension.
|
||||
* @param overlapPxY The amount of px required to be visible in the Y dimension.
|
||||
*/
|
||||
private static void fitWithinBounds(Rect bounds, Rect rootTaskBounds, int overlapPxX,
|
||||
int overlapPxY) {
|
||||
if (rootTaskBounds == null || rootTaskBounds.isEmpty() || rootTaskBounds.contains(bounds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For each side of the parent (eg. left), check if the opposing side of the window (eg.
|
||||
// right) is at least overlap pixels away. If less, offset the window by that difference.
|
||||
int horizontalDiff = 0;
|
||||
// If window is smaller than overlap, use it's smallest dimension instead
|
||||
int overlapLR = Math.min(overlapPxX, bounds.width());
|
||||
if (bounds.right < (rootTaskBounds.left + overlapLR)) {
|
||||
horizontalDiff = overlapLR - (bounds.right - rootTaskBounds.left);
|
||||
} else if (bounds.left > (rootTaskBounds.right - overlapLR)) {
|
||||
horizontalDiff = -(overlapLR - (rootTaskBounds.right - bounds.left));
|
||||
}
|
||||
int verticalDiff = 0;
|
||||
int overlapTB = Math.min(overlapPxY, bounds.width());
|
||||
if (bounds.bottom < (rootTaskBounds.top + overlapTB)) {
|
||||
verticalDiff = overlapTB - (bounds.bottom - rootTaskBounds.top);
|
||||
} else if (bounds.top > (rootTaskBounds.bottom - overlapTB)) {
|
||||
verticalDiff = -(overlapTB - (rootTaskBounds.bottom - bounds.top));
|
||||
}
|
||||
bounds.offset(horizontalDiff, verticalDiff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures all visible activities at or below the input activity have the right configuration.
|
||||
*/
|
||||
void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
|
||||
mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
|
||||
}
|
||||
|
||||
void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
|
||||
@NonNull Configuration parentConfig) {
|
||||
int minWidth = mMinWidth;
|
||||
int minHeight = mMinHeight;
|
||||
// If the task has no requested minimal size, we'd like to enforce a minimal size
|
||||
// so that the user can not render the task fragment too small to manipulate. We don't need
|
||||
// to do this for the root pinned task as the bounds are controlled by the system.
|
||||
if (!inPinnedWindowingMode()) {
|
||||
final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
|
||||
final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
|
||||
final int defaultMinSize = (int) (defaultMinSizeDp * density);
|
||||
|
||||
if (minWidth == INVALID_MIN_SIZE) {
|
||||
minWidth = defaultMinSize;
|
||||
}
|
||||
if (minHeight == INVALID_MIN_SIZE) {
|
||||
minHeight = defaultMinSize;
|
||||
}
|
||||
}
|
||||
if (bounds.isEmpty()) {
|
||||
// If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
|
||||
// do, we can just skip.
|
||||
final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
|
||||
if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) {
|
||||
return;
|
||||
}
|
||||
bounds.set(parentBounds);
|
||||
}
|
||||
final boolean adjustWidth = minWidth > bounds.width();
|
||||
final boolean adjustHeight = minHeight > bounds.height();
|
||||
if (!(adjustWidth || adjustHeight)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (adjustWidth) {
|
||||
if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
|
||||
bounds.left = bounds.right - minWidth;
|
||||
} else {
|
||||
// Either left bounds match, or neither match, or the previous bounds were
|
||||
// fullscreen and we default to keeping left.
|
||||
bounds.right = bounds.left + minWidth;
|
||||
}
|
||||
}
|
||||
if (adjustHeight) {
|
||||
if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
|
||||
bounds.top = bounds.bottom - minHeight;
|
||||
} else {
|
||||
// Either top bounds match, or neither match, or the previous bounds were
|
||||
// fullscreen and we default to keeping top.
|
||||
bounds.bottom = bounds.top + minHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
|
||||
@NonNull Configuration parentConfig) {
|
||||
computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
|
||||
null /* compatInsets */);
|
||||
}
|
||||
|
||||
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
|
||||
@NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
|
||||
if (overrideDisplayInfo != null) {
|
||||
// Make sure the screen related configs can be computed by the provided display info.
|
||||
inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
|
||||
invalidateAppBoundsConfig(inOutConfig);
|
||||
}
|
||||
computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
|
||||
null /* compatInsets */);
|
||||
}
|
||||
|
||||
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
|
||||
@NonNull Configuration parentConfig,
|
||||
@Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
|
||||
if (compatInsets != null) {
|
||||
// Make sure the app bounds can be computed by the compat insets.
|
||||
invalidateAppBoundsConfig(inOutConfig);
|
||||
}
|
||||
computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
|
||||
compatInsets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the app bounds related configuration can be computed by
|
||||
* {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
|
||||
* ActivityRecord.CompatDisplayInsets)}.
|
||||
*/
|
||||
private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
|
||||
final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
|
||||
if (appBounds != null) {
|
||||
appBounds.setEmpty();
|
||||
}
|
||||
inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
|
||||
inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates configuration values used by the client to get resources. This should be run
|
||||
* using app-facing bounds (bounds unmodified by animations or transient interactions).
|
||||
*
|
||||
* This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely
|
||||
* configuring an "inherit-bounds" window which means that all configuration settings would
|
||||
* just be inherited from the parent configuration.
|
||||
**/
|
||||
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
|
||||
@NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo,
|
||||
@Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
|
||||
int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
|
||||
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
|
||||
windowingMode = parentConfig.windowConfiguration.getWindowingMode();
|
||||
}
|
||||
|
||||
float density = inOutConfig.densityDpi;
|
||||
if (density == Configuration.DENSITY_DPI_UNDEFINED) {
|
||||
density = parentConfig.densityDpi;
|
||||
}
|
||||
density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
|
||||
|
||||
// The bounds may have been overridden at this level. If the parent cannot cover these
|
||||
// bounds, the configuration is still computed according to the override bounds.
|
||||
final boolean insideParentBounds;
|
||||
|
||||
final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
|
||||
final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds();
|
||||
if (resolvedBounds == null || resolvedBounds.isEmpty()) {
|
||||
mTmpFullBounds.set(parentBounds);
|
||||
insideParentBounds = true;
|
||||
} else {
|
||||
mTmpFullBounds.set(resolvedBounds);
|
||||
insideParentBounds = parentBounds.contains(resolvedBounds);
|
||||
}
|
||||
|
||||
// Non-null compatibility insets means the activity prefers to keep its original size, so
|
||||
// out bounds doesn't need to be restricted by the parent or current display
|
||||
final boolean customContainerPolicy = compatInsets != null;
|
||||
|
||||
Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
|
||||
if (outAppBounds == null || outAppBounds.isEmpty()) {
|
||||
// App-bounds hasn't been overridden, so calculate a value for it.
|
||||
inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds);
|
||||
outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
|
||||
|
||||
if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
|
||||
final Rect containingAppBounds;
|
||||
if (insideParentBounds) {
|
||||
containingAppBounds = parentConfig.windowConfiguration.getAppBounds();
|
||||
} else {
|
||||
// Restrict appBounds to display non-decor rather than parent because the
|
||||
// override bounds are beyond the parent. Otherwise, it won't match the
|
||||
// overridden bounds.
|
||||
final TaskDisplayArea displayArea = getDisplayArea();
|
||||
containingAppBounds = displayArea != null
|
||||
? displayArea.getWindowConfiguration().getAppBounds() : null;
|
||||
}
|
||||
if (containingAppBounds != null && !containingAppBounds.isEmpty()) {
|
||||
outAppBounds.intersect(containingAppBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
|
||||
|| inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
|
||||
if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) {
|
||||
mTmpNonDecorBounds.set(mTmpFullBounds);
|
||||
mTmpStableBounds.set(mTmpFullBounds);
|
||||
} else if (!customContainerPolicy
|
||||
&& (overrideDisplayInfo != null || getDisplayContent() != null)) {
|
||||
final DisplayInfo di = overrideDisplayInfo != null
|
||||
? overrideDisplayInfo
|
||||
: getDisplayContent().getDisplayInfo();
|
||||
|
||||
// For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
|
||||
// area, i.e. the screen area without the system bars.
|
||||
// The non decor inset are areas that could never be removed in Honeycomb. See
|
||||
// {@link WindowManagerPolicy#getNonDecorInsetsLw}.
|
||||
calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di);
|
||||
} else {
|
||||
// Apply the given non-decor and stable insets to calculate the corresponding bounds
|
||||
// for screen size of configuration.
|
||||
int rotation = inOutConfig.windowConfiguration.getRotation();
|
||||
if (rotation == ROTATION_UNDEFINED) {
|
||||
rotation = parentConfig.windowConfiguration.getRotation();
|
||||
}
|
||||
if (rotation != ROTATION_UNDEFINED && customContainerPolicy) {
|
||||
mTmpNonDecorBounds.set(mTmpFullBounds);
|
||||
mTmpStableBounds.set(mTmpFullBounds);
|
||||
compatInsets.getBoundsByRotation(mTmpBounds, rotation);
|
||||
intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds,
|
||||
compatInsets.mNonDecorInsets[rotation]);
|
||||
intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
|
||||
compatInsets.mStableInsets[rotation]);
|
||||
outAppBounds.set(mTmpNonDecorBounds);
|
||||
} else {
|
||||
// Set to app bounds because it excludes decor insets.
|
||||
mTmpNonDecorBounds.set(outAppBounds);
|
||||
mTmpStableBounds.set(outAppBounds);
|
||||
}
|
||||
}
|
||||
|
||||
if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
|
||||
final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
|
||||
inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy)
|
||||
? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
|
||||
: overrideScreenWidthDp;
|
||||
}
|
||||
if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
|
||||
final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
|
||||
inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy)
|
||||
? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
|
||||
: overrideScreenHeightDp;
|
||||
}
|
||||
|
||||
if (inOutConfig.smallestScreenWidthDp
|
||||
== Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
|
||||
if (WindowConfiguration.isFloating(windowingMode)) {
|
||||
// For floating tasks, calculate the smallest width from the bounds of the task
|
||||
inOutConfig.smallestScreenWidthDp = (int) (
|
||||
Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
|
||||
}
|
||||
// otherwise, it will just inherit
|
||||
}
|
||||
}
|
||||
|
||||
if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
|
||||
inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
|
||||
? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
|
||||
}
|
||||
if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
|
||||
// For calculating screen layout, we need to use the non-decor inset screen area for the
|
||||
// calculation for compatibility reasons, i.e. screen area without system bars that
|
||||
// could never go away in Honeycomb.
|
||||
int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
|
||||
int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
|
||||
// Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
|
||||
// undefined so it can't be used.
|
||||
if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
|
||||
compatScreenWidthDp = inOutConfig.screenWidthDp;
|
||||
}
|
||||
if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
|
||||
compatScreenHeightDp = inOutConfig.screenHeightDp;
|
||||
}
|
||||
// Reducing the screen layout starting from its parent config.
|
||||
inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout,
|
||||
compatScreenWidthDp, compatScreenHeightDp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets bounds with non-decor and stable insets applied respectively.
|
||||
*
|
||||
* If bounds overhangs the display, those edges will not get insets. See
|
||||
* {@link #intersectWithInsetsIfFits}
|
||||
*
|
||||
* @param outNonDecorBounds where to place bounds with non-decor insets applied.
|
||||
* @param outStableBounds where to place bounds with stable insets applied.
|
||||
* @param bounds the bounds to inset.
|
||||
*/
|
||||
void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
|
||||
DisplayInfo displayInfo) {
|
||||
outNonDecorBounds.set(bounds);
|
||||
outStableBounds.set(bounds);
|
||||
final Task rootTask = getRootTaskFragment().asTask();
|
||||
if (rootTask == null || rootTask.mDisplayContent == null) {
|
||||
return;
|
||||
}
|
||||
mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
|
||||
|
||||
final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
|
||||
policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
|
||||
displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
|
||||
intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
|
||||
|
||||
policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
|
||||
intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than
|
||||
* intersectBounds on a side, then the respective side will not be intersected.
|
||||
*
|
||||
* The assumption is that if inOutBounds is initially larger than intersectBounds, then the
|
||||
* inset on that side is no-longer applicable. This scenario happens when a task's minimal
|
||||
* bounds are larger than the provided parent/display bounds.
|
||||
*
|
||||
* @param inOutBounds the bounds to intersect.
|
||||
* @param intersectBounds the bounds to intersect with.
|
||||
* @param intersectInsets insets to apply to intersectBounds before intersecting.
|
||||
*/
|
||||
static void intersectWithInsetsIfFits(
|
||||
Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) {
|
||||
if (inOutBounds.right <= intersectBounds.right) {
|
||||
inOutBounds.right =
|
||||
Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right);
|
||||
}
|
||||
if (inOutBounds.bottom <= intersectBounds.bottom) {
|
||||
inOutBounds.bottom =
|
||||
Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom);
|
||||
}
|
||||
if (inOutBounds.left >= intersectBounds.left) {
|
||||
inOutBounds.left =
|
||||
Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left);
|
||||
}
|
||||
if (inOutBounds.top >= intersectBounds.top) {
|
||||
inOutBounds.top =
|
||||
Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top);
|
||||
}
|
||||
}
|
||||
|
||||
/** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */
|
||||
static int computeScreenLayoutOverride(int sourceScreenLayout, int screenWidthDp,
|
||||
int screenHeightDp) {
|
||||
sourceScreenLayout = sourceScreenLayout
|
||||
& (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
|
||||
final int longSize = Math.max(screenWidthDp, screenHeightDp);
|
||||
final int shortSize = Math.min(screenWidthDp, screenHeightDp);
|
||||
return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getActivityType() {
|
||||
final int applicationType = super.getActivityType();
|
||||
if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
|
||||
return applicationType;
|
||||
}
|
||||
return getTopChild().getActivityType();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user