Use the largest display size when calculating wallpaper crop size

Make sure we always use the same display size when calculating cropping for
static wallpapers if there's more than one display, specifically use the
largest screen

(Note: there's still some discrepancies between preview and actual
wallpaper that we need to sort out in a follow-up CL)

Bug: 195120817
Test: manual
Change-Id: I4ccdc55bcb28e417f9fd11b082dca92cc9688055
diff --git a/src/com/android/wallpaper/module/BaseWallpaperInjector.java b/src/com/android/wallpaper/module/BaseWallpaperInjector.java
index ea429a6..75b59fe 100755
--- a/src/com/android/wallpaper/module/BaseWallpaperInjector.java
+++ b/src/com/android/wallpaper/module/BaseWallpaperInjector.java
@@ -21,6 +21,7 @@
 import com.android.wallpaper.network.Requester;
 import com.android.wallpaper.network.WallpaperRequester;
 import com.android.wallpaper.picker.individual.IndividualPickerFragment;
+import com.android.wallpaper.util.DisplayUtils;
 
 /**
  * Base implementation of Injector.
@@ -44,6 +45,7 @@
     private LiveWallpaperInfoFactory mLiveWallpaperInfoFactory;
     private DrawableLayerResolver mDrawableLayerResolver;
     private CustomizationSections mCustomizationSections;
+    private DisplayUtils mDisplayUtils;
 
     @Override
     public synchronized BitmapCropper getBitmapCropper() {
@@ -195,4 +197,12 @@
         }
         return mCustomizationSections;
     }
+
+    @Override
+    public DisplayUtils getDisplayUtils(Context context) {
+        if (mDisplayUtils == null) {
+            mDisplayUtils = new DisplayUtils(context.getApplicationContext());
+        }
+        return mDisplayUtils;
+    }
 }
diff --git a/src/com/android/wallpaper/module/DefaultWallpaperPersister.java b/src/com/android/wallpaper/module/DefaultWallpaperPersister.java
index 9148b9f..dc87467 100755
--- a/src/com/android/wallpaper/module/DefaultWallpaperPersister.java
+++ b/src/com/android/wallpaper/module/DefaultWallpaperPersister.java
@@ -48,6 +48,7 @@
 import com.android.wallpaper.model.WallpaperInfo;
 import com.android.wallpaper.module.BitmapCropper.Callback;
 import com.android.wallpaper.util.BitmapTransformer;
+import com.android.wallpaper.util.DisplayUtils;
 import com.android.wallpaper.util.ScreenSizeCalculator;
 import com.android.wallpaper.util.WallpaperCropUtils;
 
@@ -73,6 +74,7 @@
     private final WallpaperManagerCompat mWallpaperManagerCompat;
     private final WallpaperPreferences mWallpaperPreferences;
     private final WallpaperChangedNotifier mWallpaperChangedNotifier;
+    private final DisplayUtils mDisplayUtils;
 
     private WallpaperInfo mWallpaperInfoInPreview;
 
@@ -86,6 +88,7 @@
         mWallpaperManagerCompat = injector.getWallpaperManagerCompat(context);
         mWallpaperPreferences = injector.getPreferences(context);
         mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
+        mDisplayUtils = injector.getDisplayUtils(context);
     }
 
     @Override
@@ -421,13 +424,11 @@
     private int cropAndSetWallpaperBitmapInRotationStatic(Bitmap wallpaperBitmap) {
         // Calculate crop and scale of the wallpaper to match the default one used in preview
         Point wallpaperSize = new Point(wallpaperBitmap.getWidth(), wallpaperBitmap.getHeight());
-        WindowManager windowManager =
-                (WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE);
         Resources resources = mAppContext.getResources();
+        Display croppingDisplay = mDisplayUtils.getWallpaperDisplay();
         Point defaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize(
-                resources, windowManager.getDefaultDisplay());
-        Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(
-                windowManager.getDefaultDisplay());
+                resources, croppingDisplay);
+        Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(croppingDisplay);
 
         // Determine minimum zoom to fit maximum visible area of wallpaper on crop surface.
         float minWallpaperZoom =
diff --git a/src/com/android/wallpaper/module/Injector.java b/src/com/android/wallpaper/module/Injector.java
index 919d1ba..2ec21fd 100755
--- a/src/com/android/wallpaper/module/Injector.java
+++ b/src/com/android/wallpaper/module/Injector.java
@@ -28,6 +28,7 @@
 import com.android.wallpaper.network.Requester;
 import com.android.wallpaper.picker.PreviewFragment.PreviewMode;
 import com.android.wallpaper.picker.individual.IndividualPickerFragment;
+import com.android.wallpaper.util.DisplayUtils;
 
 /**
  * Interface for a provider of "injected dependencies." (NOTE: The term "injector" is somewhat of a
@@ -92,4 +93,9 @@
     String getDownloadableIntentAction();
 
     CustomizationSections getCustomizationSections();
+
+    /**
+     * @return the singleton instance of {@link DisplayUtils}
+     */
+    DisplayUtils getDisplayUtils(Context context);
 }
diff --git a/src/com/android/wallpaper/picker/ImagePreviewFragment.java b/src/com/android/wallpaper/picker/ImagePreviewFragment.java
index ae3d63c..87c3101 100755
--- a/src/com/android/wallpaper/picker/ImagePreviewFragment.java
+++ b/src/com/android/wallpaper/picker/ImagePreviewFragment.java
@@ -62,6 +62,7 @@
 import com.android.wallpaper.asset.CurrentWallpaperAssetVN;
 import com.android.wallpaper.model.SetWallpaperViewModel;
 import com.android.wallpaper.module.BitmapCropper;
+import com.android.wallpaper.module.Injector;
 import com.android.wallpaper.module.InjectorProvider;
 import com.android.wallpaper.module.WallpaperPersister.Destination;
 import com.android.wallpaper.util.FullScreenAnimation;
@@ -101,9 +102,18 @@
 
     private final AtomicInteger mImageScaleChangeCounter = new AtomicInteger(0);
     private final AtomicInteger mRecalculateColorCounter = new AtomicInteger(0);
+    private final Injector mInjector = InjectorProvider.getInjector();
 
     private SubsamplingScaleImageView mFullResImageView;
     private Asset mWallpaperAsset;
+    /**
+     * Size of the screen considered for cropping the wallpaper (typically the same as
+     * {@link #mScreenSize} but it could be different on multi-display)
+     */
+    private Point mWallpaperScreenSize;
+    /**
+     * The size of the current screen
+     */
     private Point mScreenSize;
     private Point mRawWallpaperSize; // Native size of wallpaper image.
     private ImageView mLowResImageView;
@@ -137,8 +147,12 @@
         View view = super.onCreateView(inflater, container, savedInstanceState);
 
         Activity activity = requireActivity();
-        mScreenSize = ScreenSizeCalculator.getInstance().getScreenSize(
+        ScreenSizeCalculator screenSizeCalculator = ScreenSizeCalculator.getInstance();
+        mScreenSize = screenSizeCalculator.getScreenSize(
                 activity.getWindowManager().getDefaultDisplay());
+        // "Wallpaper screen" size will be the size of the largest screen available
+        mWallpaperScreenSize = screenSizeCalculator.getScreenSize(
+                mInjector.getDisplayUtils(activity).getWallpaperDisplay());
 
         mContainer = view.findViewById(R.id.container);
         mTouchForwardingLayout = mContainer.findViewById(R.id.touch_forwarding_layout);
@@ -350,7 +364,7 @@
             return;
         }
 
-        BitmapCropper bitmapCropper = InjectorProvider.getInjector().getBitmapCropper();
+        BitmapCropper bitmapCropper = mInjector.getBitmapCropper();
         bitmapCropper.cropAndScaleBitmap(mWallpaperAsset, mFullResImageView.getScale(),
                 calculateCropRect(context), /* adjustForRtl= */ false,
                 new BitmapCropper.Callback() {
@@ -536,13 +550,21 @@
                     mRawWallpaperSize = dimensions;
                     initFullResView();
                 });
-                // Scale the mWallpaperSurface based on system zoom's scale so that the wallpaper is
+
+                // Calculate the size of mWallpaperSurface based on system zoom's scale and
+                // on the larger screen size (if more than one) so that the wallpaper is
                 // rendered in a larger surface than what preview shows, simulating the behavior of
-                // the actual wallpaper surface.
+                // the actual wallpaper surface and so we can crop it to a size that fits in all
+                // screens.
                 float scale = WallpaperCropUtils.getSystemWallpaperMaximumScale(context);
                 int origWidth = mWallpaperSurface.getWidth();
-                int width = (int) (origWidth * scale);
                 int origHeight = mWallpaperSurface.getHeight();
+
+                if (!mScreenSize.equals(mWallpaperScreenSize)) {
+                    float previewToScreenScale = (float) origWidth / mScreenSize.x;
+                    origWidth = (int) (mWallpaperScreenSize.x * previewToScreenScale);
+                }
+                int width = (int) (origWidth * scale);
                 int height = (int) (origHeight * scale);
                 int left = (origWidth - width) / 2;
                 int top = (origHeight - height) / 2;
diff --git a/src/com/android/wallpaper/util/DisplayUtils.kt b/src/com/android/wallpaper/util/DisplayUtils.kt
new file mode 100644
index 0000000..9f7c644
--- /dev/null
+++ b/src/com/android/wallpaper/util/DisplayUtils.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 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.wallpaper.util
+
+import android.content.Context
+import android.graphics.Point
+import android.hardware.display.DisplayManager
+import android.util.Log
+import android.view.Display
+
+/**
+ * Utility class to provide methods to find and obtain information about displays via
+ * {@link DisplayManager}
+ */
+class DisplayUtils(context: Context) {
+    companion object {
+        private const val TAG = "DisplayUtils"
+    }
+
+    private val internalDisplays: List<Display>
+
+    init {
+        val appContext = context.applicationContext
+        val dm = appContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+        val allDisplays: Array<out Display> = dm.displays
+        if (allDisplays.isEmpty()) {
+            Log.e(TAG, "No displays found on context $appContext")
+            throw RuntimeException("No displays found!")
+        }
+        internalDisplays = allDisplays.filter { it.type == Display.TYPE_INTERNAL }
+    }
+
+    /**
+     * Returns the {@link Display} to be used to calculate wallpaper size and cropping.
+     */
+    fun getWallpaperDisplay(): Display {
+        return internalDisplays.maxWithOrNull { a, b -> getRealSize(a) - getRealSize(b) }
+                ?: internalDisplays[0]
+    }
+
+    private fun getRealSize(display: Display): Int {
+        val p = Point()
+        display.getRealSize(p)
+        return p.x * p.y
+    }
+}
diff --git a/src/com/android/wallpaper/util/ScreenSizeCalculator.java b/src/com/android/wallpaper/util/ScreenSizeCalculator.java
index d206e55..64ca88d 100755
--- a/src/com/android/wallpaper/util/ScreenSizeCalculator.java
+++ b/src/com/android/wallpaper/util/ScreenSizeCalculator.java
@@ -88,7 +88,7 @@
             mPortraitScreenSize = new Point();
         }
         writeDisplaySizeToPoint(display, mPortraitScreenSize);
-        return mPortraitScreenSize;
+        return new Point(mPortraitScreenSize);
     }
 
     private Point getLandscapeScreenSize(Display display) {
@@ -96,7 +96,7 @@
             mLandscapeScreenSize = new Point();
         }
         writeDisplaySizeToPoint(display, mLandscapeScreenSize);
-        return mLandscapeScreenSize;
+        return new Point(mLandscapeScreenSize);
     }
 
     /**
diff --git a/tests/src/com/android/wallpaper/testing/TestInjector.java b/tests/src/com/android/wallpaper/testing/TestInjector.java
index 42cb7f5..61d6d36 100644
--- a/tests/src/com/android/wallpaper/testing/TestInjector.java
+++ b/tests/src/com/android/wallpaper/testing/TestInjector.java
@@ -49,6 +49,7 @@
 import com.android.wallpaper.network.Requester;
 import com.android.wallpaper.picker.ImagePreviewFragment;
 import com.android.wallpaper.picker.individual.IndividualPickerFragment;
+import com.android.wallpaper.util.DisplayUtils;
 
 /**
  * Test implementation of the dependency injector.
@@ -266,4 +267,9 @@
     public CustomizationSections getCustomizationSections() {
         return null;
     }
+
+    @Override
+    public DisplayUtils getDisplayUtils(Context context) {
+        return null;
+    }
 }