Optimizations for current wallpaper preview

Cache bitmaps more aggressively in BitmapCachingAsset, and
CurrentWallpaperAsset.
Also make WallpaperConnection and WallpaperSurfaceCallback not depend
on Activity

Bug: 184111918
Test: manual
Change-Id: I077f98e9a65759e32fd1896c67fe241ee8f68d1a
diff --git a/src/com/android/wallpaper/asset/BitmapCachingAsset.java b/src/com/android/wallpaper/asset/BitmapCachingAsset.java
index 62222f4..7674d4c 100644
--- a/src/com/android/wallpaper/asset/BitmapCachingAsset.java
+++ b/src/com/android/wallpaper/asset/BitmapCachingAsset.java
@@ -37,27 +37,37 @@
 public class BitmapCachingAsset extends Asset {
 
     private static class CacheKey {
-        final Asset asset;
-        final int width;
-        final int height;
+        final Asset mAsset;
+        final int mWidth;
+        final int mHeight;
+        final boolean mRtl;
+        final Rect mRect;
 
         CacheKey(Asset asset, int width, int height) {
-            this.asset = asset;
-            this.width = width;
-            this.height = height;
+            this(asset, width, height, false, null);
+        }
+
+        CacheKey(Asset asset, int width, int height, boolean rtl, Rect rect) {
+            mAsset = asset;
+            mWidth = width;
+            mHeight = height;
+            mRtl = rtl;
+            mRect = rect;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(asset, width, height);
+            return Objects.hash(mAsset, mWidth, mHeight);
         }
 
         @Override
         public boolean equals(Object obj) {
             return obj instanceof CacheKey
-                    && ((CacheKey)obj).asset == this.asset
-                    && ((CacheKey)obj).width == this.width
-                    && ((CacheKey)obj).height == this.height;
+                    && (Objects.equals(this.mAsset, ((CacheKey) obj).mAsset))
+                    && ((CacheKey) obj).mWidth == this.mWidth
+                    && ((CacheKey) obj).mHeight == this.mHeight
+                    && ((CacheKey) obj).mRtl == this.mRtl
+                    && (Objects.equals(this.mRect, ((CacheKey) obj).mRect));
         }
     }
 
@@ -72,7 +82,8 @@
     private final Asset mOriginalAsset;
 
     public BitmapCachingAsset(Context context, Asset originalAsset) {
-        mOriginalAsset = originalAsset;
+        mOriginalAsset = originalAsset instanceof BitmapCachingAsset
+                ? ((BitmapCachingAsset) originalAsset).mOriginalAsset : originalAsset;
         mIsLowRam = ActivityManagerCompat.isLowRamDevice(
                 (ActivityManager) context.getApplicationContext().getSystemService(
                         Context.ACTIVITY_SERVICE));
@@ -102,8 +113,26 @@
     @Override
     public void decodeBitmapRegion(Rect rect, int targetWidth, int targetHeight,
             boolean shouldAdjustForRtl, BitmapReceiver receiver) {
-        mOriginalAsset.decodeBitmapRegion(rect, targetWidth, targetHeight, shouldAdjustForRtl,
-                receiver);
+        // Skip the cache in low ram devices
+        if (mIsLowRam) {
+            mOriginalAsset.decodeBitmapRegion(rect, targetWidth, targetHeight, shouldAdjustForRtl,
+                    receiver);
+            return;
+        }
+        CacheKey key = new CacheKey(mOriginalAsset, targetWidth, targetHeight, shouldAdjustForRtl,
+                rect);
+        Bitmap cached = sCache.get(key);
+        if (cached != null) {
+            receiver.onBitmapDecoded(cached);
+        } else {
+            mOriginalAsset.decodeBitmapRegion(rect, targetWidth, targetHeight, shouldAdjustForRtl,
+                    bitmap -> {
+                        if (bitmap != null) {
+                            sCache.put(key, bitmap);
+                        }
+                        receiver.onBitmapDecoded(bitmap);
+                    });
+        }
     }
 
     @Override
diff --git a/src/com/android/wallpaper/asset/CurrentWallpaperAssetV16.java b/src/com/android/wallpaper/asset/CurrentWallpaperAssetV16.java
index 86b88a5..530c3a7 100755
--- a/src/com/android/wallpaper/asset/CurrentWallpaperAssetV16.java
+++ b/src/com/android/wallpaper/asset/CurrentWallpaperAssetV16.java
@@ -33,6 +33,7 @@
     private static final boolean FILTER_SCALED_BITMAP = true;
 
     private Context mApplicationContext;
+    private Drawable mCurrentWallpaperDrawable;
 
     public CurrentWallpaperAssetV16(Context context) {
         mApplicationContext = context.getApplicationContext();
@@ -62,17 +63,19 @@
         task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
-    private Drawable getCurrentWallpaperDrawable() {
-        WallpaperManager wallpaperManager = WallpaperManager.getInstance(mApplicationContext);
-        Drawable drawable;
-        try {
-            drawable = wallpaperManager.getDrawable();
-        } catch (java.lang.SecurityException e) {
-            // Work around Samsung bug where SecurityException is thrown if device is still using its
-            // default wallpaper.
-            drawable = wallpaperManager.getBuiltInDrawable();
+    private synchronized Drawable getCurrentWallpaperDrawable() {
+        if (mCurrentWallpaperDrawable != null) {
+            return mCurrentWallpaperDrawable;
         }
-        return drawable;
+        WallpaperManager wallpaperManager = WallpaperManager.getInstance(mApplicationContext);
+        try {
+            mCurrentWallpaperDrawable = wallpaperManager.getDrawable();
+        } catch (java.lang.SecurityException e) {
+            // Work around Samsung bug where SecurityException is thrown if device is still using
+            // its default wallpaper.
+            mCurrentWallpaperDrawable = wallpaperManager.getBuiltInDrawable();
+        }
+        return mCurrentWallpaperDrawable;
     }
 
     /**
diff --git a/src/com/android/wallpaper/module/DefaultCurrentWallpaperInfoFactory.java b/src/com/android/wallpaper/module/DefaultCurrentWallpaperInfoFactory.java
index dbe3722..9e2787f 100755
--- a/src/com/android/wallpaper/module/DefaultCurrentWallpaperInfoFactory.java
+++ b/src/com/android/wallpaper/module/DefaultCurrentWallpaperInfoFactory.java
@@ -51,7 +51,8 @@
     @Override
     public synchronized void createCurrentWallpaperInfos(final WallpaperInfoCallback callback,
                                                          boolean forceRefresh) {
-        if (!forceRefresh && mHomeWallpaper != null) {
+        if (!forceRefresh && mHomeWallpaper != null
+                && mPresentationMode != WallpaperPreferences.PRESENTATION_MODE_ROTATING) {
             callback.onWallpaperInfoCreated(mHomeWallpaper, mLockWallpaper, mPresentationMode);
             return;
         }
diff --git a/src/com/android/wallpaper/util/WallpaperConnection.java b/src/com/android/wallpaper/util/WallpaperConnection.java
index a31cfad..b161db7 100644
--- a/src/com/android/wallpaper/util/WallpaperConnection.java
+++ b/src/com/android/wallpaper/util/WallpaperConnection.java
@@ -20,7 +20,6 @@
 import static android.graphics.Matrix.MSKEW_X;
 import static android.graphics.Matrix.MSKEW_Y;
 
-import android.app.Activity;
 import android.app.WallpaperColors;
 import android.content.ComponentName;
 import android.content.Context;
@@ -35,10 +34,10 @@
 import android.service.wallpaper.IWallpaperConnection;
 import android.service.wallpaper.IWallpaperEngine;
 import android.service.wallpaper.IWallpaperService;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.SurfaceControl;
 import android.view.SurfaceView;
-import android.view.View;
 import android.view.WindowManager.LayoutParams;
 
 import androidx.annotation.Nullable;
@@ -61,7 +60,7 @@
     }
 
     private static final String TAG = "WallpaperConnection";
-    private final Activity mActivity;
+    private final Context mContext;
     private final Intent mIntent;
     private final WallpaperConnectionListener mListener;
     private final SurfaceView mContainerView;
@@ -75,27 +74,27 @@
 
     /**
      * @param intent used to bind the wallpaper service
-     * @param activity Activity that owns the window where the wallpaper is rendered
+     * @param context Context used to start and bind the live wallpaper service
      * @param listener if provided, it'll be notified of connection/disconnection events
      * @param containerView SurfaceView that will display the wallpaper
      */
-    public WallpaperConnection(Intent intent, Activity activity,
+    public WallpaperConnection(Intent intent, Context context,
             @Nullable WallpaperConnectionListener listener, SurfaceView containerView) {
-        this(intent, activity, listener, containerView, null);
+        this(intent, context, listener, containerView, null);
     }
 
     /**
      * @param intent used to bind the wallpaper service
-     * @param activity Activity that owns the window where the wallpaper is rendered
+     * @param context Context used to start and bind the live wallpaper service
      * @param listener if provided, it'll be notified of connection/disconnection events
      * @param containerView SurfaceView that will display the wallpaper
      * @param secondaryContainerView optional SurfaceView that will display a second, mirrored
      *                               version of the wallpaper
      */
-    public WallpaperConnection(Intent intent, Activity activity,
+    public WallpaperConnection(Intent intent, Context context,
             @Nullable WallpaperConnectionListener listener, SurfaceView containerView,
             @Nullable SurfaceView secondaryContainerView) {
-        mActivity = activity;
+        mContext = context.getApplicationContext();
         mIntent = intent;
         mListener = listener;
         mContainerView = containerView;
@@ -110,7 +109,7 @@
             if (mConnected) {
                 return true;
             }
-            if (!mActivity.bindService(mIntent, this,
+            if (!mContext.bindService(mIntent, this,
                     Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT)) {
                 return false;
             }
@@ -140,7 +139,7 @@
                 mEngine = null;
             }
             try {
-                mActivity.unbindService(this);
+                mContext.unbindService(this);
             } catch (IllegalArgumentException e) {
                 Log.i(TAG, "Can't unbind wallpaper service. "
                         + "It might have crashed, just ignoring.");
@@ -158,10 +157,9 @@
     public void onServiceConnected(ComponentName name, IBinder service) {
         mService = IWallpaperService.Stub.asInterface(service);
         try {
-            View root = mActivity.getWindow().getDecorView();
-            int displayId = root.getDisplay().getDisplayId();
+            int displayId = mContainerView.getDisplay().getDisplayId();
 
-            mService.attach(this, root.getWindowToken(),
+            mService.attach(this, mContainerView.getWindowToken(),
                     LayoutParams.TYPE_APPLICATION_MEDIA,
                     true, mContainerView.getWidth(), mContainerView.getHeight(),
                     new Rect(0, 0, 0, 0), displayId);
@@ -229,7 +227,7 @@
 
     @Override
     public void onWallpaperColorsChanged(WallpaperColors colors, int displayId) {
-        mActivity.runOnUiThread(() -> {
+        mContainerView.post(() -> {
             if (mListener != null) {
                 mListener.onWallpaperColorsChanged(colors, displayId);
             }
@@ -246,7 +244,7 @@
             reparentWallpaperSurface(mSecondContainerView);
         }
 
-        mActivity.runOnUiThread(() -> {
+        mContainerView.post(() -> {
             if (mListener != null) {
                 mListener.onEngineShown();
             }
@@ -299,9 +297,10 @@
         Matrix m = new Matrix();
         float[] values = new float[9];
         Rect surfacePosition = parentSurface.getHolder().getSurfaceFrame();
-        View decorView = mActivity.getWindow().getDecorView();
-        m.postScale(((float) surfacePosition.width()) / decorView.getWidth(),
-                ((float) surfacePosition.height()) / decorView.getHeight());
+        DisplayMetrics metrics = DisplayMetricsRetriever.getInstance().getDisplayMetrics(
+                mContainerView.getResources(), mContainerView.getDisplay());
+        m.postScale(((float) surfacePosition.width()) / metrics.widthPixels,
+                ((float) surfacePosition.height()) / metrics.heightPixels);
         m.getValues(values);
         return values;
     }
diff --git a/src/com/android/wallpaper/util/WallpaperSurfaceCallback.java b/src/com/android/wallpaper/util/WallpaperSurfaceCallback.java
index 7984f21..9855b21 100644
--- a/src/com/android/wallpaper/util/WallpaperSurfaceCallback.java
+++ b/src/com/android/wallpaper/util/WallpaperSurfaceCallback.java
@@ -65,8 +65,8 @@
     private PackageStatusNotifier mPackageStatusNotifier;
 
     public WallpaperSurfaceCallback(Context context, View containerView,
-            SurfaceView wallpaperSurface, @Nullable  SurfaceListener listener) {
-        mContext = context;
+            SurfaceView wallpaperSurface, @Nullable SurfaceListener listener) {
+        mContext = context.getApplicationContext();
         mContainerView = containerView;
         mWallpaperSurface = wallpaperSurface;
         mListener = listener;
@@ -152,7 +152,7 @@
         if (forceClean) {
             releaseHost();
             mHost = new SurfaceControlViewHost(mContext,
-                    mContext.getDisplay(), mWallpaperSurface.getHostToken());
+                    mContainerView.getDisplay(), mWallpaperSurface.getHostToken());
         }
         mHost.setView(mHomeImageWallpaper, mHomeImageWallpaper.getWidth(),
                 mHomeImageWallpaper.getHeight());