Merge "Fix ActivityLeak through text reference" into main
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 3bdaca9..e287bd9 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -622,7 +622,7 @@
             sBuilder = null;
         }
 
-        if (reflowed == null) {
+        if (b == null) {
             b = StaticLayout.Builder.obtain(text, where, where + after, getPaint(), getWidth());
         }
 
@@ -641,7 +641,7 @@
                 .setAddLastLineLineSpacing(!islast)
                 .setIncludePad(false);
 
-        reflowed = b.regenerate(true /* trackpadding */, reflowed);
+        reflowed = b.buildPartialStaticLayoutForDynamicLayout(true /* trackpadding */, reflowed);
         int n = reflowed.getLineCount();
         // If the new layout has a blank line at the end, but it is not
         // the very end of the buffer, then we already have a line that
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index f843900..3d1895c 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -437,13 +437,25 @@
             return result;
         }
 
-        /* package */ @NonNull StaticLayout regenerate(boolean trackpadding, StaticLayout recycle) {
+        /**
+         * DO NOT USE THIS METHOD OTHER THAN DynamicLayout.
+         *
+         * This class generates a very weird StaticLayout only for getting a result of line break.
+         * Since DynamicLayout keeps StaticLayout reference in the static context for object
+         * recycling but keeping text reference in static context will end up with leaking Context
+         * due to TextWatcher via TextView.
+         *
+         * So, this is a dirty work around that creating StaticLayout without passing text reference
+         * to the super constructor, but calculating the text layout by calling generate function
+         * directly.
+         */
+        /* package */ @NonNull StaticLayout buildPartialStaticLayoutForDynamicLayout(
+                boolean trackpadding, StaticLayout recycle) {
             if (recycle == null) {
-                return new StaticLayout(this, trackpadding, COLUMNS_ELLIPSIZE);
-            } else {
-                recycle.generate(this, mIncludePad, trackpadding);
-                return recycle;
+                recycle = new StaticLayout();
             }
+            recycle.generate(this, mIncludePad, trackpadding);
+            return recycle;
         }
 
         private CharSequence mText;
@@ -474,6 +486,37 @@
     }
 
     /**
+     * DO NOT USE THIS CONSTRUCTOR OTHER THAN FOR DYNAMIC LAYOUT.
+     * See Builder#buildPartialStaticLayoutForDynamicLayout for the reason of this constructor.
+     */
+    private StaticLayout() {
+        super(
+                null,  // text
+                null,  // paint
+                0,  // width
+                null, // alignment
+                null, // textDir
+                1, // spacing multiplier
+                0, // spacing amount
+                false, // include font padding
+                false, // fallback line spacing
+                0,  // ellipsized width
+                null, // ellipsize
+                1,  // maxLines
+                BREAK_STRATEGY_SIMPLE,
+                HYPHENATION_FREQUENCY_NONE,
+                null,  // leftIndents
+                null,  // rightIndents
+                JUSTIFICATION_MODE_NONE,
+                null  // lineBreakConfig
+        );
+
+        mColumns = COLUMNS_ELLIPSIZE;
+        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2);
+        mLines  = ArrayUtils.newUnpaddedIntArray(2 * mColumns);
+    }
+
+    /**
      * @deprecated Use {@link Builder} instead.
      */
     @Deprecated