Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a95ffcd6 authored by Naomi Musgrave's avatar Naomi Musgrave
Browse files

Offset the wallpaper to center in smaller display

If we have more than one physical display, offset the wallpaper
in the smaller displays so that the center corresponds to the
center of the image in the largest display.
This behavior is controlled by a config flag to avoid
affecting the behavior of existing foldable devices.

Bug: 195120817
Test: manual (visually verified with different images)
Test: atest WmTests:WallpaperControllerTests
Change-Id: I57bfeb5ab82f265c991f3be02313abb4289d42dd
parent 56fe9580
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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>
+4 −0
Original line number Diff line number Diff line
@@ -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" />

+4 −0
Original line number Diff line number Diff line
@@ -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)
+9 −5
Original line number Diff line number Diff line
@@ -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));
    }

    /**
+120 −11
Original line number Diff line number Diff line
@@ -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)  {
Loading