Merge "Add app bounds to configuration." into oc-dev

am: 7efe56b779

Change-Id: If871d90a72f675a93503e883135ebee77f12d971
This commit is contained in:
Bryce Lee
2017-04-03 17:41:40 +00:00
committed by android-build-merger
9 changed files with 280 additions and 24 deletions

View File

@@ -16,6 +16,11 @@
package android.content.res;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.DisplayInfo;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -293,6 +298,16 @@ public final class Configuration implements Parcelable, Comparable<Configuration
*/
public int screenLayout;
/**
* @hide
* {@link android.graphics.Rect} defining app bounds. The dimensions override usages of
* {@link DisplayInfo#appHeight} and {@link DisplayInfo#appWidth} and mirrors these values at
* the display level. Lower levels can override these values to provide custom bounds to enforce
* features such as a max aspect ratio.
* TODO(b/36812336): Move appBounds out of {@link Configuration}.
*/
public Rect appBounds;
/** @hide */
static public int resetScreenLayout(int curLayout) {
return (curLayout&~(SCREENLAYOUT_LONG_MASK | SCREENLAYOUT_SIZE_MASK
@@ -882,6 +897,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
compatScreenWidthDp = o.compatScreenWidthDp;
compatScreenHeightDp = o.compatScreenHeightDp;
compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp;
setAppBounds(o.appBounds);
assetsSeq = o.assetsSeq;
seq = o.seq;
}
@@ -1032,6 +1048,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration
case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
default: sb.append("/"); sb.append(navigationHidden); break;
}
if (appBounds != null) {
sb.append(" appBounds="); sb.append(appBounds);
}
if (assetsSeq != 0) {
sb.append(" as.").append(assetsSeq);
}
@@ -1066,6 +1085,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
densityDpi = DENSITY_DPI_UNDEFINED;
assetsSeq = ASSETS_SEQ_UNDEFINED;
appBounds = null;
seq = 0;
}
@@ -1253,6 +1273,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp;
}
if (delta.appBounds != null && !delta.appBounds.equals(appBounds)) {
changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
setAppBounds(delta.appBounds);
}
if (delta.assetsSeq != ASSETS_SEQ_UNDEFINED) {
changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
assetsSeq = delta.assetsSeq;
@@ -1399,6 +1423,13 @@ public final class Configuration implements Parcelable, Comparable<Configuration
changed |= ActivityInfo.CONFIG_ASSETS_PATHS;
}
// Make sure that one of the values is not null and that they are not equal.
if ((compareUndefined || delta.appBounds != null)
&& appBounds != delta.appBounds
&& (appBounds == null || !appBounds.equals(delta.appBounds))) {
changed |= ActivityInfo.CONFIG_SCREEN_SIZE;
}
return changed;
}
@@ -1494,6 +1525,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
dest.writeInt(compatScreenWidthDp);
dest.writeInt(compatScreenHeightDp);
dest.writeInt(compatSmallestScreenWidthDp);
dest.writeValue(appBounds);
dest.writeInt(assetsSeq);
dest.writeInt(seq);
}
@@ -1529,6 +1561,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
compatScreenWidthDp = source.readInt();
compatScreenHeightDp = source.readInt();
compatSmallestScreenWidthDp = source.readInt();
appBounds = (Rect) source.readValue(null);
assetsSeq = source.readInt();
seq = source.readInt();
}
@@ -1703,6 +1736,33 @@ public final class Configuration implements Parcelable, Comparable<Configuration
setLocales(loc == null ? LocaleList.getEmptyLocaleList() : new LocaleList(loc));
}
/**
* @hide
*
* Helper method for setting the app bounds.
*/
public void setAppBounds(Rect rect) {
if (rect == null) {
appBounds = null;
return;
}
setAppBounds(rect.left, rect.top, rect.right, rect.bottom);
}
/**
* @hide
*
* Helper method for setting the app bounds.
*/
public void setAppBounds(int left, int top, int right, int bottom) {
if (appBounds == null) {
appBounds = new Rect();
}
appBounds.set(left, top, right, bottom);
}
/**
* @hide
*
@@ -2212,6 +2272,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
private static final String XML_ATTR_SCREEN_HEIGHT = "height";
private static final String XML_ATTR_SMALLEST_WIDTH = "sw";
private static final String XML_ATTR_DENSITY = "density";
private static final String XML_ATTR_APP_BOUNDS = "app_bounds";
/**
* Reads the attributes corresponding to Configuration member fields from the Xml parser.
@@ -2261,6 +2322,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration
SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
configOut.densityDpi = XmlUtils.readIntAttribute(parser, XML_ATTR_DENSITY,
DENSITY_DPI_UNDEFINED);
configOut.appBounds =
Rect.unflattenFromString(XmlUtils.readStringAttribute(parser, XML_ATTR_APP_BOUNDS));
// For persistence, we don't care about assetsSeq, so do not read it out.
}
@@ -2332,6 +2395,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration
XmlUtils.writeIntAttribute(xml, XML_ATTR_DENSITY, config.densityDpi);
}
if (config.appBounds != null) {
XmlUtils.writeStringAttribute(xml, XML_ATTR_APP_BOUNDS,
config.appBounds.flattenToString());
}
// For persistence, we do not care about assetsSeq, so do not write it out.
}
}

View File

@@ -562,12 +562,10 @@ public final class DisplayInfo implements Parcelable {
outMetrics.xdpi = outMetrics.noncompatXdpi = physicalXDpi;
outMetrics.ydpi = outMetrics.noncompatYdpi = physicalYDpi;
width = (configuration != null
&& configuration.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED)
? (int)((configuration.screenWidthDp * outMetrics.density) + 0.5f) : width;
height = (configuration != null
&& configuration.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED)
? (int)((configuration.screenHeightDp * outMetrics.density) + 0.5f) : height;
width = configuration != null && configuration.appBounds != null
? configuration.appBounds.width() : width;
height = configuration != null && configuration.appBounds != null
? configuration.appBounds.height() : height;
outMetrics.noncompatWidthPixels = outMetrics.widthPixels = width;
outMetrics.noncompatHeightPixels = outMetrics.heightPixels = height;

View File

@@ -20,6 +20,7 @@ import android.annotation.CheckResult;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -170,6 +171,10 @@ public final class Rect implements Parcelable {
* or null if the string is not of that form.
*/
public static Rect unflattenFromString(String str) {
if (TextUtils.isEmpty(str)) {
return null;
}
Matcher matcher = UnflattenHelper.getMatcher(str);
if (!matcher.matches()) {
return null;
@@ -179,7 +184,7 @@ public final class Rect implements Parcelable {
Integer.parseInt(matcher.group(3)),
Integer.parseInt(matcher.group(4)));
}
/**
* Print short representation to given writer.
* @hide

View File

@@ -2122,23 +2122,31 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
return true;
}
/** Computes the override configuration for this activity */
/**
* Computes the bounds to fit the Activity within the bounds of the {@link Configuration}.
*/
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private void computeBounds(Rect outBounds) {
outBounds.setEmpty();
final float maxAspectRatio = info.maxAspectRatio;
final ActivityStack stack = getStack();
if ((task != null && !task.mFullscreen) || maxAspectRatio == 0 || stack == null) {
if (task == null || stack == null || !task.mFullscreen || maxAspectRatio == 0) {
// We don't set override configuration if that activity task isn't fullscreen. I.e. the
// activity is in multi-window mode. Or, there isn't a max aspect ratio specified for
// the activity.
// the activity. This is indicated by an empty {@link outBounds}.
return;
}
stack.getDisplaySize(mTmpPoint);
int maxActivityWidth = mTmpPoint.x;
int maxActivityHeight = mTmpPoint.y;
if (mTmpPoint.x < mTmpPoint.y) {
// We must base this on the parent configuration, because we set our override
// configuration's appBounds based on the result of this method. If we used our own
// configuration, it would be influenced by past invocations.
final Configuration configuration = getParent().getConfiguration();
final int containingAppWidth = configuration.appBounds.width();
final int containingAppHeight = configuration.appBounds.height();
int maxActivityWidth = containingAppWidth;
int maxActivityHeight = containingAppHeight;
if (containingAppWidth < containingAppHeight) {
// Width is the shorter side, so we use that to figure-out what the max. height should
// be given the aspect ratio.
maxActivityHeight = (int) ((maxActivityWidth * maxAspectRatio) + 0.5f);
@@ -2148,8 +2156,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
maxActivityWidth = (int) ((maxActivityHeight * maxAspectRatio) + 0.5f);
}
if (mTmpPoint.x <= maxActivityWidth && mTmpPoint.y <= maxActivityHeight) {
if (containingAppWidth <= maxActivityWidth && containingAppHeight <= maxActivityHeight) {
// The display matches or is less than the activity aspect ratio, so nothing else to do.
// Return the existing bounds. If this method is running for the first time,
// {@link mBounds} will be empty (representing no override). If the method has run
// before, then effect of {@link mBounds} will already have been applied to the
// value returned from {@link getConfiguration}. Refer to
// {@link TaskRecord#computeOverrideConfiguration}.
outBounds.set(mBounds);
return;
}

View File

@@ -2044,6 +2044,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta
config.unset();
final Configuration parentConfig = getParent().getConfiguration();
final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
if (mStack != null) {
@@ -2052,11 +2053,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta
mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density,
config, parentConfig);
} else {
// No stack, give some default values
config.smallestScreenWidthDp =
mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
config.screenWidthDp = config.screenHeightDp = config.smallestScreenWidthDp;
Slog.wtf(TAG, "Expected stack when calculating override config");
throw new IllegalArgumentException("Expected stack when calculating override config");
}
config.orientation = (config.screenWidthDp <= config.screenHeightDp)

View File

@@ -1134,6 +1134,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
config.screenHeightDp =
(int)(mService.mPolicy.getConfigDisplayHeight(dw, dh, displayInfo.rotation,
config.uiMode, mDisplayId) / mDisplayMetrics.density);
mService.mPolicy.getNonDecorInsetsLw(displayInfo.rotation, dw, dh, mTmpRect);
final int leftInset = mTmpRect.left;
final int topInset = mTmpRect.top;
// appBounds at the root level should mirror the app screen size.
config.setAppBounds(leftInset /*left*/, topInset /*top*/, leftInset + displayInfo.appWidth /*right*/,
topInset + displayInfo.appHeight /*bottom*/);
final boolean rotated = (displayInfo.rotation == Surface.ROTATION_90
|| displayInfo.rotation == Surface.ROTATION_270);

View File

@@ -225,14 +225,25 @@ public class DockedStackDividerController implements DimLayerUser {
mService.mPolicy.getStableInsetsLw(rotation, dw, dh, mTmpRect);
config.unset();
config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
final int displayId = mDisplayContent.getDisplayId();
final int appWidth = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation,
baseConfig.uiMode, displayId);
final int appHeight = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation,
baseConfig.uiMode, displayId);
mService.mPolicy.getNonDecorInsetsLw(rotation, dw, dh, mTmpRect);
final int leftInset = mTmpRect.left;
final int topInset = mTmpRect.top;
config.setAppBounds(leftInset /*left*/, topInset /*top*/, leftInset + appWidth /*right*/,
topInset + appHeight /*bottom*/);
config.screenWidthDp = (int)
(mService.mPolicy.getConfigDisplayWidth(dw, dh, rotation, baseConfig.uiMode,
mDisplayContent.getDisplayId()) /
mDisplayContent.getDisplayMetrics().density);
displayId) / mDisplayContent.getDisplayMetrics().density);
config.screenHeightDp = (int)
(mService.mPolicy.getConfigDisplayHeight(dw, dh, rotation, baseConfig.uiMode,
mDisplayContent.getDisplayId()) /
mDisplayContent.getDisplayMetrics().density);
displayId) / mDisplayContent.getDisplayMetrics().density);
final Context rotationContext = mService.mContext.createConfigurationContext(config);
mSnapAlgorithmForRotation[rotation] = new DividerSnapAlgorithm(
rotationContext.getResources(), dw, dh, getContentWidth(),

View File

@@ -270,6 +270,12 @@ public class StackWindowController
int width;
int height;
final Rect parentAppBounds = parentConfig.appBounds;
config.setAppBounds(!bounds.isEmpty() ? bounds : null);
boolean intersectParentBounds = false;
if (StackId.tasksAreFloating(mStackId)) {
// Floating tasks should not be resized to the screen's bounds.
@@ -280,6 +286,7 @@ public class StackWindowController
// the fullscreen stack, without intersecting it with the display bounds
stableBounds.inset(mTmpStableInsets);
nonDecorBounds.inset(mTmpNonDecorInsets);
intersectParentBounds = true;
}
width = (int) (stableBounds.width() / density);
height = (int) (stableBounds.height() / density);
@@ -299,6 +306,11 @@ public class StackWindowController
parentConfig.screenWidthDp);
height = Math.min((int) (stableBounds.height() / density),
parentConfig.screenHeightDp);
intersectParentBounds = true;
}
if (intersectParentBounds && config.appBounds != null) {
config.appBounds.intersect(parentAppBounds);
}
config.screenWidthDp = width;

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2017 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 android.app.ActivityManager;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Debug;
import android.view.DisplayInfo;
import org.junit.Test;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Test class to exercise logic related to {@link android.content.res.Configuration#appBounds}.
*
* Build/Install/Run:
* bit FrameworksServicesTests:com.android.server.wm.AppBoundsTests
*/
@SmallTest
@Presubmit
@org.junit.runner.RunWith(AndroidJUnit4.class)
public class AppBoundsTests extends WindowTestsBase {
private Rect mParentBounds;
@Override
public void setUp() throws Exception {
super.setUp();
mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
}
/**
* Ensures the configuration app bounds at the root level match the app dimensions.
*/
@Test
public void testRootConfigurationBounds() throws Exception {
final DisplayInfo info = sDisplayContent.getDisplayInfo();
info.appWidth = 1024;
info.appHeight = 768;
final Configuration config = sWm.computeNewConfiguration(sDisplayContent.getDisplayId());
// The bounds should always be positioned in the top left.
assertEquals(config.appBounds.left, 0);
assertEquals(config.appBounds.top, 0);
// The bounds should equal the defined app width and height
assertEquals(config.appBounds.width(), info.appWidth);
assertEquals(config.appBounds.height(), info.appHeight);
}
/**
* Ensures that bounds are clipped to their parent.
*/
@Test
public void testBoundsClipping() throws Exception {
final Rect shiftedBounds = new Rect(mParentBounds);
shiftedBounds.offset(10, 10);
final Rect expectedBounds = new Rect(mParentBounds);
expectedBounds.intersect(shiftedBounds);
testStackBoundsConfiguration(null /*stackId*/, mParentBounds, shiftedBounds,
expectedBounds);
}
/**
* Ensures that empty bounds are not propagated to the configuration.
*/
@Test
public void testEmptyBounds() throws Exception {
final Rect emptyBounds = new Rect();
testStackBoundsConfiguration(null /*stackId*/, mParentBounds, emptyBounds,
null /*ExpectedBounds*/);
}
/**
* Ensures that bounds on freeform stacks are not clipped.
*/
@Test
public void testFreeFormBounds() throws Exception {
final Rect freeFormBounds = new Rect(mParentBounds);
freeFormBounds.offset(10, 10);
testStackBoundsConfiguration(ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID,
mParentBounds, freeFormBounds, freeFormBounds);
}
/**
* Ensures that fully contained bounds are not clipped.
*/
@Test
public void testContainedBounds() throws Exception {
final Rect insetBounds = new Rect(mParentBounds);
insetBounds.inset(5, 5, 5, 5);
testStackBoundsConfiguration(null /*stackId*/, mParentBounds, insetBounds, insetBounds);
}
/**
* Ensures that full screen free form bounds are clipped
*/
@Test
public void testFullScreenFreeFormBounds() throws Exception {
final Rect fullScreenBounds = new Rect(0, 0, sDisplayInfo.logicalWidth,
sDisplayInfo.logicalHeight);
testStackBoundsConfiguration(null /*stackId*/, mParentBounds, fullScreenBounds,
mParentBounds);
}
private void testStackBoundsConfiguration(Integer stackId, Rect parentBounds, Rect bounds,
Rect expectedConfigBounds) {
final StackWindowController stackController = stackId != null ?
createStackControllerOnStackOnDisplay(stackId, sDisplayContent)
: createStackControllerOnDisplay(sDisplayContent);
final Configuration parentConfig = sDisplayContent.getConfiguration();
parentConfig.setAppBounds(parentBounds);
final Configuration config = new Configuration();
stackController.adjustConfigurationForBounds(bounds, null /*insetBounds*/,
new Rect() /*nonDecorBounds*/, new Rect() /*stableBounds*/, false /*overrideWidth*/,
false /*overrideHeight*/, sDisplayInfo.logicalDensityDpi, config, parentConfig);
// Assert that both expected and actual are null or are equal to each other
assertTrue((expectedConfigBounds == null && config.appBounds == null)
|| expectedConfigBounds.equals(config.appBounds));
}
}