summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java4
-rw-r--r--services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java14
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java131
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java19
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java121
8 files changed, 281 insertions, 24 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e7cae76c60fc..0f66586881c4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5066,6 +5066,10 @@
<!-- If true, the wallpaper will scale regardless of the value of shouldZoomOutWallpaper() -->
<bool name="config_alwaysScaleWallpaper">false</bool>
+ <!-- Set to true to offset the wallpaper when using multiple displays so that it's centered
+ at the same position as in the largest display.-->
+ <bool name="config_offsetWallpaperToCenterOfLargestDisplay">false</bool>
+
<!-- Package name that will receive an explicit manifest broadcast for
android.os.action.POWER_SAVE_MODE_CHANGED. -->
<string name="config_powerSaveModeChangedListenerPackage" translatable="false"></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index aa0bae656da6..3f81169005a4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4373,6 +4373,10 @@
<!-- The max scale for the wallpaper when it's zoomed in -->
<java-symbol type="dimen" name="config_wallpaperMaxScale"/>
+ <!-- Set to true to offset the wallpaper when using multiple displays so that it's centered
+ at the same position than in the largest display. -->
+ <java-symbol type="bool" name="config_offsetWallpaperToCenterOfLargestDisplay" />
+
<!-- Set to true to enable the user switcher on the keyguard. -->
<java-symbol type="bool" name="config_keyguardUserSwitcher" />
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6ee018606d71..39904f94f47f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1038,6 +1038,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mCurrentUniqueDisplayId = display.getUniqueId();
mOffTokenAcquirer = mRootWindowContainer.mDisplayOffTokenAcquirer;
mWallpaperController = new WallpaperController(mWmService, this);
+ mWallpaperController.resetLargestDisplay(display);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
mSystemGestureExclusionLimit = mWmService.mConstants.mSystemGestureExclusionLimitDp
@@ -3199,6 +3200,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mWmService.mAccessibilityController.onDisplayRemoved(mDisplayId);
mRootWindowContainer.mTaskSupervisor
.getKeyguardController().onDisplayRemoved(mDisplayId);
+ mWallpaperController.resetLargestDisplay(mDisplay);
} finally {
mDisplayReady = false;
}
@@ -5769,6 +5771,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
updateRecording();
}
}
+ // Notify wallpaper controller of any size changes.
+ mWallpaperController.resetLargestDisplay(mDisplay);
// Dispatch pending Configuration to WindowContext if the associated display changes to
// un-suspended state from suspended.
if (isSuspendedState(lastDisplayState)
diff --git a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
index 3f6fb622481f..e3a2065838d1 100644
--- a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
+++ b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
@@ -22,6 +22,8 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayInfo;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
/**
@@ -52,19 +54,21 @@ public class PossibleDisplayInfoMapper {
/**
- * Returns, for the given displayId, a set of display infos. Set contains each supported device
- * state.
+ * Returns, for the given displayId, a list of unique display infos. List contains each
+ * supported device state.
+ * <p>List contents are guaranteed to be unique, but returned as a list rather than a set to
+ * minimize copies needed to make an iteraable data structure.
*/
- public Set<DisplayInfo> getPossibleDisplayInfos(int displayId) {
+ public List<DisplayInfo> getPossibleDisplayInfos(int displayId) {
// Update display infos before returning, since any cached values would have been removed
// in response to any display event. This model avoids re-computing the cache for every
// display change event (which occurs extremely frequently in the normal usage of the
// device).
updatePossibleDisplayInfos(displayId);
if (!mDisplayInfos.contains(displayId)) {
- return new ArraySet<>();
+ return new ArrayList<>();
}
- return Set.copyOf(mDisplayInfos.get(displayId));
+ return List.copyOf(mDisplayInfos.get(displayId));
}
/**
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6245005606d7..d652b8e0ab32 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.app.WallpaperManager.COMMAND_FREEZE;
import static android.app.WallpaperManager.COMMAND_UNFREEZE;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -35,7 +36,9 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.WALLPAPER_DRAW_PENDING_TIMEOUT;
+import android.annotation.Nullable;
import android.graphics.Bitmap;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Debug;
@@ -45,6 +48,8 @@ import android.os.SystemClock;
import android.util.ArraySet;
import android.util.MathUtils;
import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.animation.Animation;
@@ -56,6 +61,7 @@ import com.android.internal.util.ToBooleanFunction;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -113,8 +119,12 @@ class WallpaperController {
*/
private WindowState mTmpTopWallpaper;
+ @Nullable private Point mLargestDisplaySize = null;
+
private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult();
+ private boolean mShouldOffsetWallpaperCenter;
+
private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> {
if ((w.mAttrs.type == TYPE_WALLPAPER)) {
if (mFindResults.topWallpaper == null || mFindResults.resetTopWallpaper) {
@@ -241,6 +251,38 @@ class WallpaperController {
mDisplayContent = displayContent;
mMaxWallpaperScale = service.mContext.getResources()
.getFloat(com.android.internal.R.dimen.config_wallpaperMaxScale);
+ mShouldOffsetWallpaperCenter = service.mContext.getResources()
+ .getBoolean(
+ com.android.internal.R.bool.config_offsetWallpaperToCenterOfLargestDisplay);
+ }
+
+ void resetLargestDisplay(Display display) {
+ if (display != null && display.getType() == Display.TYPE_INTERNAL) {
+ mLargestDisplaySize = null;
+ }
+ }
+
+ @VisibleForTesting void setShouldOffsetWallpaperCenter(boolean shouldOffset) {
+ mShouldOffsetWallpaperCenter = shouldOffset;
+ }
+
+ @Nullable private Point findLargestDisplaySize() {
+ if (!mShouldOffsetWallpaperCenter) {
+ return null;
+ }
+ Point largestDisplaySize = new Point();
+ List<DisplayInfo> possibleDisplayInfo =
+ mService.getPossibleDisplayInfoLocked(DEFAULT_DISPLAY);
+ for (int i = 0; i < possibleDisplayInfo.size(); i++) {
+ DisplayInfo displayInfo = possibleDisplayInfo.get(i);
+ if (displayInfo.type == Display.TYPE_INTERNAL
+ && Math.max(displayInfo.logicalWidth, displayInfo.logicalHeight)
+ > Math.max(largestDisplaySize.x, largestDisplaySize.y)) {
+ largestDisplaySize.set(displayInfo.logicalWidth,
+ displayInfo.logicalHeight);
+ }
+ }
+ return largestDisplaySize;
}
WindowState getWallpaperTarget() {
@@ -327,24 +369,44 @@ class WallpaperController {
}
boolean updateWallpaperOffset(WindowState wallpaperWin, boolean sync) {
- final Rect bounds = wallpaperWin.getLastReportedBounds();
- final int dw = bounds.width();
- final int dh = bounds.height();
-
- int xOffset = 0;
- int yOffset = 0;
+ // Size of the display the wallpaper is rendered on.
+ final Rect lastWallpaperBounds = wallpaperWin.getLastReportedBounds();
+ // Full size of the wallpaper (usually larger than bounds above to parallax scroll when
+ // swiping through Launcher pages).
+ final Rect wallpaperFrame = wallpaperWin.getFrame();
+
+ int newXOffset = 0;
+ int newYOffset = 0;
boolean rawChanged = false;
// Set the default wallpaper x-offset to either edge of the screen (depending on RTL), to
// match the behavior of most Launchers
float defaultWallpaperX = wallpaperWin.isRtl() ? 1f : 0f;
+ // "Wallpaper X" is the previous x-offset of the wallpaper (in a 0 to 1 scale).
+ // The 0 to 1 scale is because the "length" varies depending on how many home screens you
+ // have, so 0 is the left of the first home screen, and 1 is the right of the last one (for
+ // LTR, and the opposite for RTL).
float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : defaultWallpaperX;
+ // "Wallpaper X step size" is how much of that 0-1 is one "page" of the home screen
+ // when scrolling.
float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
- int availw = wallpaperWin.getFrame().right - wallpaperWin.getFrame().left - dw;
+ // Difference between width of wallpaper image, and the last size of the wallpaper.
+ // This is the horizontal surplus from the prior configuration.
+ int availw = wallpaperFrame.width() - lastWallpaperBounds.width();
+
+ int displayOffset = getDisplayWidthOffset(availw, lastWallpaperBounds,
+ wallpaperWin.isRtl());
+ availw -= displayOffset;
int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0;
if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
+ // if device is LTR, then offset wallpaper to the left (the wallpaper is drawn
+ // always starting from the left of the screen).
offset += mLastWallpaperDisplayOffsetX;
+ } else if (!wallpaperWin.isRtl()) {
+ // In RTL the offset is calculated so that the wallpaper ends up right aligned (see
+ // offset above).
+ offset -= displayOffset;
}
- xOffset = offset;
+ newXOffset = offset;
if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) {
wallpaperWin.mWallpaperX = wpx;
@@ -354,12 +416,13 @@ class WallpaperController {
float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f;
float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
- int availh = wallpaperWin.getFrame().bottom - wallpaperWin.getFrame().top - dh;
+ int availh = wallpaperWin.getFrame().bottom - wallpaperWin.getFrame().top
+ - lastWallpaperBounds.height();
offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0;
if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetY;
}
- yOffset = offset;
+ newYOffset = offset;
if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) {
wallpaperWin.mWallpaperY = wpy;
@@ -372,7 +435,7 @@ class WallpaperController {
rawChanged = true;
}
- boolean changed = wallpaperWin.setWallpaperOffset(xOffset, yOffset,
+ boolean changed = wallpaperWin.setWallpaperOffset(newXOffset, newYOffset,
wallpaperWin.mShouldScaleWallpaper
? zoomOutToScale(wallpaperWin.mWallpaperZoomOut) : 1);
@@ -419,6 +482,52 @@ class WallpaperController {
return changed;
}
+ /**
+ * Get an extra offset if needed ({@link #mShouldOffsetWallpaperCenter} = true, typically on
+ * multiple display devices) so that the wallpaper in a smaller display ends up centered at the
+ * same position as in the largest display of the device.
+ *
+ * Note that the wallpaper has already been cropped when set by the user, so these calculations
+ * apply to the image size for the display the wallpaper was set for.
+ *
+ * @param availWidth width available for the wallpaper offset in the current display
+ * @param displayFrame size of the "display" (parent frame)
+ * @param isRtl whether we're in an RTL configuration
+ * @return an offset to apply to the width, or 0 if the current configuration doesn't require
+ * any adjustment (either @link #mShouldOffsetWallpaperCenter} is false or we're on the largest
+ * display).
+ */
+ private int getDisplayWidthOffset(int availWidth, Rect displayFrame, boolean isRtl) {
+ if (!mShouldOffsetWallpaperCenter) {
+ return 0;
+ }
+ if (mLargestDisplaySize == null) {
+ mLargestDisplaySize = findLargestDisplaySize();
+ }
+ if (mLargestDisplaySize == null) {
+ return 0;
+ }
+ // Page width is the width of a Launcher "page", for pagination when swiping right.
+ int pageWidth = displayFrame.width();
+ // Only need offset if the current size is different from the largest display, and we're
+ // in a portrait configuration
+ if (mLargestDisplaySize.x != pageWidth && displayFrame.width() < displayFrame.height()) {
+ // The wallpaper will be scaled to fit the height of the wallpaper, so if the height
+ // of the displays are different, we need to account for that scaling when calculating
+ // the offset to the center
+ float sizeRatio = (float) displayFrame.height() / mLargestDisplaySize.y;
+ // Scale the width of the largest display to match the scale of the wallpaper size in
+ // the current display
+ int adjustedLargestWidth = Math.round(mLargestDisplaySize.x * sizeRatio);
+ // Finally, find the difference between the centers, taking into account that the
+ // size of the wallpaper frame could be smaller than the screen
+ return isRtl
+ ? adjustedLargestWidth - (adjustedLargestWidth + pageWidth) / 2
+ : Math.min(adjustedLargestWidth - pageWidth, availWidth) / 2;
+ }
+ return 0;
+ }
+
void setWindowWallpaperPosition(
WindowState window, float x, float y, float xStep, float yStep) {
if (window.mWallpaperX != x || window.mWallpaperY != y) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8025cb296b32..b8a1c96807cd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8916,13 +8916,19 @@ public class WindowManagerService extends IWindowManager.Stub
}
// Retrieve the DisplayInfo across all possible display layouts.
- return List.copyOf(mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId));
+ return mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
+ List<DisplayInfo> getPossibleDisplayInfoLocked(int displayId) {
+ // Retrieve the DisplayInfo for all possible rotations across all possible display
+ // layouts.
+ return mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId);
+ }
+
/**
* Returns {@code true} when the calling package is the recents component.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java b/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java
index 8b0a54050c3c..58b0e16eefe8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java
@@ -36,6 +36,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
@@ -88,7 +90,8 @@ public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
mPossibleDisplayInfo.add(mDefaultDisplayInfo);
mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
- Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
+ List<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(
+ DEFAULT_DISPLAY);
// An entry for rotation 0, for a display that can be in a single state.
assertThat(displayInfos.size()).isEqualTo(1);
assertPossibleDisplayInfoEntries(displayInfos, mDefaultDisplayInfo);
@@ -105,9 +108,10 @@ public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
mPossibleDisplayInfo.add(mSecondDisplayInfo);
mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
- Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
- Set<DisplayInfo> defaultDisplayInfos = new ArraySet<>();
- Set<DisplayInfo> secondDisplayInfos = new ArraySet<>();
+ List<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(
+ DEFAULT_DISPLAY);
+ List<DisplayInfo> defaultDisplayInfos = new ArrayList<>();
+ List<DisplayInfo> secondDisplayInfos = new ArrayList<>();
for (DisplayInfo di : displayInfos) {
if ((di.flags & FLAG_PRESENTATION) != 0) {
secondDisplayInfos.add(di);
@@ -137,12 +141,13 @@ public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
mPossibleDisplayInfo.add(mSecondDisplayInfo);
mDisplayInfoMapper.updatePossibleDisplayInfos(mSecondDisplayInfo.displayId);
- Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
+ List<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(
+ DEFAULT_DISPLAY);
// An entry for rotation 0, for the default display.
assertThat(displayInfos).hasSize(1);
assertPossibleDisplayInfoEntries(displayInfos, mDefaultDisplayInfo);
- Set<DisplayInfo> secondStateEntries =
+ List<DisplayInfo> secondStateEntries =
mDisplayInfoMapper.getPossibleDisplayInfos(mSecondDisplayInfo.displayId);
// An entry for rotation 0, for the second display.
assertThat(secondStateEntries).hasSize(1);
@@ -157,7 +162,7 @@ public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
outDisplayInfo.logicalHeight = logicalBounds.height();
}
- private static void assertPossibleDisplayInfoEntries(Set<DisplayInfo> displayInfos,
+ private static void assertPossibleDisplayInfoEntries(List<DisplayInfo> displayInfos,
DisplayInfo expectedDisplayInfo) {
for (DisplayInfo displayInfo : displayInfos) {
assertThat(displayInfo.displayId).isEqualTo(expectedDisplayInfo.displayId);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index fba4ff1f1c25..82887135c73d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -33,17 +33,21 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
@@ -61,9 +65,12 @@ import androidx.test.filters.SmallTest;
import com.android.server.wm.utils.WmDisplayCutout;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+
/**
* Tests for the {@link WallpaperController} class.
*
@@ -74,6 +81,18 @@ import org.junit.runner.RunWith;
@Presubmit
@RunWith(WindowTestRunner.class)
public class WallpaperControllerTests extends WindowTestsBase {
+ private static final int INITIAL_WIDTH = 600;
+ private static final int INITIAL_HEIGHT = 900;
+ private static final int SECOND_WIDTH = 300;
+
+ @Before
+ public void setup() {
+ Resources resources = mWm.mContext.getResources();
+ spyOn(resources);
+ doReturn(false).when(resources).getBoolean(
+ com.android.internal.R.bool.config_offsetWallpaperToCenterOfLargestDisplay);
+ }
+
@Test
public void testWallpaperScreenshot() {
WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class);
@@ -365,6 +384,108 @@ public class WallpaperControllerTests extends WindowTestsBase {
assertTrue(token.isVisible());
}
+ private static void prepareSmallerSecondDisplay(DisplayContent dc, int width, int height) {
+ spyOn(dc.mWmService);
+ DisplayInfo firstDisplay = dc.getDisplayInfo();
+ DisplayInfo secondDisplay = new DisplayInfo(firstDisplay);
+ // Second display is narrower than first display.
+ secondDisplay.logicalWidth = width;
+ secondDisplay.logicalHeight = height;
+ doReturn(List.of(firstDisplay, secondDisplay)).when(
+ dc.mWmService).getPossibleDisplayInfoLocked(anyInt());
+ }
+
+ private static void resizeDisplayAndWallpaper(DisplayContent dc, WindowState wallpaperWindow,
+ int width, int height) {
+ dc.setBounds(0, 0, width, height);
+ dc.updateOrientation();
+ dc.sendNewConfiguration();
+ spyOn(wallpaperWindow);
+ doReturn(new Rect(0, 0, width, height)).when(wallpaperWindow).getLastReportedBounds();
+ }
+
+ @Test
+ public void testUpdateWallpaperOffset_initial_shouldCenterDisabled() {
+ final DisplayContent dc = new TestDisplayContent.Builder(mAtm, INITIAL_WIDTH,
+ INITIAL_HEIGHT).build();
+ dc.mWallpaperController.setShouldOffsetWallpaperCenter(false);
+ prepareSmallerSecondDisplay(dc, SECOND_WIDTH, INITIAL_HEIGHT);
+ final WindowState wallpaperWindow = createWallpaperWindow(dc, INITIAL_WIDTH,
+ INITIAL_HEIGHT);
+
+ dc.mWallpaperController.updateWallpaperOffset(wallpaperWindow, false);
+
+ // Wallpaper centering is disabled, so no offset.
+ assertThat(wallpaperWindow.mXOffset).isEqualTo(0);
+ assertThat(wallpaperWindow.mYOffset).isEqualTo(0);
+ }
+
+ @Test
+ public void testUpdateWallpaperOffset_initial_shouldCenterEnabled() {
+ final DisplayContent dc = new TestDisplayContent.Builder(mAtm, INITIAL_WIDTH,
+ INITIAL_HEIGHT).build();
+ dc.mWallpaperController.setShouldOffsetWallpaperCenter(true);
+ prepareSmallerSecondDisplay(dc, SECOND_WIDTH, INITIAL_HEIGHT);
+ final WindowState wallpaperWindow = createWallpaperWindow(dc, INITIAL_WIDTH,
+ INITIAL_HEIGHT);
+
+ dc.mWallpaperController.updateWallpaperOffset(wallpaperWindow, false);
+
+ // Wallpaper matches first display, so has no offset.
+ assertThat(wallpaperWindow.mXOffset).isEqualTo(0);
+ assertThat(wallpaperWindow.mYOffset).isEqualTo(0);
+ }
+
+ @Test
+ public void testUpdateWallpaperOffset_resize_shouldCenterEnabled() {
+ final DisplayContent dc = new TestDisplayContent.Builder(mAtm, INITIAL_WIDTH,
+ INITIAL_HEIGHT).build();
+ dc.mWallpaperController.setShouldOffsetWallpaperCenter(true);
+ prepareSmallerSecondDisplay(dc, SECOND_WIDTH, INITIAL_HEIGHT);
+ final WindowState wallpaperWindow = createWallpaperWindow(dc, INITIAL_WIDTH,
+ INITIAL_HEIGHT);
+
+ dc.mWallpaperController.updateWallpaperOffset(wallpaperWindow, false);
+
+ // Resize display to match second display bounds.
+ resizeDisplayAndWallpaper(dc, wallpaperWindow, SECOND_WIDTH, INITIAL_HEIGHT);
+
+ dc.mWallpaperController.updateWallpaperOffset(wallpaperWindow, false);
+
+ // Wallpaper is 300 wider than second display.
+ assertThat(wallpaperWindow.mXOffset).isEqualTo(-Math.abs(INITIAL_WIDTH - SECOND_WIDTH) / 2);
+ assertThat(wallpaperWindow.mYOffset).isEqualTo(0);
+ }
+
+ @Test
+ public void testUpdateWallpaperOffset_resize_shouldCenterDisabled() {
+ final DisplayContent dc = new TestDisplayContent.Builder(mAtm, INITIAL_WIDTH,
+ INITIAL_HEIGHT).build();
+ dc.mWallpaperController.setShouldOffsetWallpaperCenter(false);
+ prepareSmallerSecondDisplay(dc, SECOND_WIDTH, INITIAL_HEIGHT);
+ final WindowState wallpaperWindow = createWallpaperWindow(dc, INITIAL_WIDTH,
+ INITIAL_HEIGHT);
+
+ dc.mWallpaperController.updateWallpaperOffset(wallpaperWindow, false);
+
+ // Resize display to match second display bounds.
+ resizeDisplayAndWallpaper(dc, wallpaperWindow, SECOND_WIDTH, INITIAL_HEIGHT);
+
+ dc.mWallpaperController.updateWallpaperOffset(wallpaperWindow, false);
+
+ // Wallpaper is 300 wider than second display, but offset disabled.
+ assertThat(wallpaperWindow.mXOffset).isEqualTo(0);
+ assertThat(wallpaperWindow.mYOffset).isEqualTo(0);
+ }
+
+ private WindowState createWallpaperWindow(DisplayContent dc, int width, int height) {
+ final WindowState wallpaperWindow = createWallpaperWindow(dc);
+ // Wallpaper is cropped to match first display.
+ wallpaperWindow.getWindowFrames().mParentFrame.set(new Rect(0, 0, width, height));
+ wallpaperWindow.getWindowFrames().mFrame.set(0, 0, width, height);
+ return wallpaperWindow;
+ }
+
private WindowState createWallpaperWindow(DisplayContent dc) {
final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
true /* explicit */, dc, true /* ownerCanManageAppTokens */);