Merge "DisplayCutout: Cache inflations from resources"
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 2723030..f5b7068 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -23,6 +23,8 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
 import android.content.res.Resources;
 import android.graphics.Matrix;
 import android.graphics.Path;
@@ -38,6 +40,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Objects;
@@ -73,6 +76,17 @@
     public static final DisplayCutout NO_CUTOUT = new DisplayCutout(ZERO_RECT, EMPTY_REGION,
             new Size(0, 0));
 
+
+    private static final Object CACHE_LOCK = new Object();
+    @GuardedBy("CACHE_LOCK")
+    private static String sCachedSpec;
+    @GuardedBy("CACHE_LOCK")
+    private static int sCachedDisplayWidth;
+    @GuardedBy("CACHE_LOCK")
+    private static float sCachedDensity;
+    @GuardedBy("CACHE_LOCK")
+    private static DisplayCutout sCachedCutout;
+
     private final Rect mSafeInsets;
     private final Region mBounds;
     private final Size mFrameSize;
@@ -350,10 +364,26 @@
      * @hide
      */
     public static DisplayCutout fromResources(Resources res, int displayWidth) {
-        String spec = res.getString(R.string.config_mainBuiltInDisplayCutout);
+        return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
+                displayWidth, res.getDisplayMetrics().density);
+    }
+
+    /**
+     * Creates an instance according to the supplied {@link android.util.PathParser.PathData} spec.
+     *
+     * @hide
+     */
+    @VisibleForTesting(visibility = PRIVATE)
+    public static DisplayCutout fromSpec(String spec, int displayWidth, float density) {
         if (TextUtils.isEmpty(spec)) {
             return null;
         }
+        synchronized (CACHE_LOCK) {
+            if (spec.equals(sCachedSpec) && sCachedDisplayWidth == displayWidth
+                    && sCachedDensity == density) {
+                return sCachedCutout;
+            }
+        }
         spec = spec.trim();
         final boolean inDp = spec.endsWith(DP_MARKER);
         if (inDp) {
@@ -370,12 +400,19 @@
 
         final Matrix m = new Matrix();
         if (inDp) {
-            final float dpToPx = res.getDisplayMetrics().density;
-            m.postScale(dpToPx, dpToPx);
+            m.postScale(density, density);
         }
         m.postTranslate(displayWidth / 2f, 0);
         p.transform(m);
-        return fromBounds(p);
+
+        final DisplayCutout result = fromBounds(p);
+        synchronized (CACHE_LOCK) {
+            sCachedSpec = spec;
+            sCachedDisplayWidth = displayWidth;
+            sCachedDensity = density;
+            sCachedCutout = result;
+        }
+        return result;
     }
 
     /**
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index ee4bc34..d807353 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -18,10 +18,14 @@
 
 import static android.view.DisplayCutout.NO_CUTOUT;
 import static android.view.DisplayCutout.fromBoundingRect;
+import static android.view.DisplayCutout.fromSpec;
 
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 import android.graphics.Rect;
@@ -271,6 +275,30 @@
     }
 
     @Test
+    public void fromSpec_caches() {
+        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 1f);
+        assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 1f), sameInstance(cached));
+    }
+
+    @Test
+    public void fromSpec_wontCacheIfSpecChanges() {
+        DisplayCutout cached = fromSpec("L1,0 L1000,1000 L0,1 z", 200, 1f);
+        assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 1f), not(sameInstance(cached)));
+    }
+
+    @Test
+    public void fromSpec_wontCacheIfScreenWidthChanges() {
+        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 2000, 1f);
+        assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 1f), not(sameInstance(cached)));
+    }
+
+    @Test
+    public void fromSpec_wontCacheIfDensityChanges() {
+        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 2f);
+        assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 1f), not(sameInstance(cached)));
+    }
+
+    @Test
     public void parcel_unparcel_nocutout() {
         Parcel p = Parcel.obtain();