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

Commit 79b2781a authored by George Mount's avatar George Mount
Browse files

Fixed OOM due to visibility transitions.

Have maximum size for Visibility transition bitmaps.

Also changed Visibility to avoid transitioning out child views
while transitioning out their parents.

Also moved view-to-bitmap code to common utility.

Bug 17469198

Change-Id: I05f069a267539b479da1b39cdad2cc9270c5c381
parent 7ca1161e
Loading
Loading
Loading
Loading
+5 −13
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.os.Parcelable;
import android.transition.TransitionUtils;
import android.view.View;

import java.util.List;
@@ -141,21 +142,12 @@ public abstract class SharedElementCallback {
     */
    public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix,
            RectF screenBounds) {
        int bitmapWidth = Math.round(screenBounds.width());
        int bitmapHeight = Math.round(screenBounds.height());
        Bitmap bitmap = null;
        if (bitmapWidth > 0 && bitmapHeight > 0) {
        if (mTempMatrix == null) {
                mTempMatrix = new Matrix();
            }
            mTempMatrix = new Matrix(viewToGlobalMatrix);
        } else {
            mTempMatrix.set(viewToGlobalMatrix);
            mTempMatrix.postTranslate(-screenBounds.left, -screenBounds.top);
            bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            canvas.concat(mTempMatrix);
            sharedElement.draw(canvas);
        }
        return bitmap;
        return TransitionUtils.createViewBitmap(sharedElement, mTempMatrix, screenBounds);
    }

    /**
+72 −0
Original line number Diff line number Diff line
@@ -19,7 +19,14 @@ package android.transition;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.TypeEvaluator;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

/**
 * Static utility methods for Transitions.
@@ -27,6 +34,7 @@ import android.graphics.Matrix;
 * @hide
 */
public class TransitionUtils {
    private static int MAX_IMAGE_SIZE = (1024 * 1024);

    static Animator mergeAnimators(Animator animator1, Animator animator2) {
        if (animator1 == null) {
@@ -67,6 +75,70 @@ public class TransitionUtils {
        return transitionSet;
    }

    /**
     * Creates a View using the bitmap copy of <code>view</code>. If <code>view</code> is large,
     * the copy will use a scaled bitmap of the given view.
     *
     * @param sceneRoot The ViewGroup in which the view copy will be displayed.
     * @param view The view to create a copy of.
     * @param parent The parent of view.
     */
    public static View copyViewImage(ViewGroup sceneRoot, View view, View parent) {
        Matrix matrix = new Matrix();
        matrix.setTranslate(-parent.getScrollX(), -parent.getScrollY());
        view.transformMatrixToGlobal(matrix);
        sceneRoot.transformMatrixToLocal(matrix);
        RectF bounds = new RectF(0, 0, view.getWidth(), view.getHeight());
        matrix.mapRect(bounds);
        int left = Math.round(bounds.left);
        int top = Math.round(bounds.top);
        int right = Math.round(bounds.right);
        int bottom = Math.round(bounds.bottom);

        ImageView copy = new ImageView(view.getContext());
        copy.setScaleType(ImageView.ScaleType.CENTER_CROP);
        Bitmap bitmap = createViewBitmap(view, matrix, bounds);
        if (bitmap != null) {
            copy.setImageBitmap(bitmap);
        }
        int widthSpec = View.MeasureSpec.makeMeasureSpec(right - left, View.MeasureSpec.EXACTLY);
        int heightSpec = View.MeasureSpec.makeMeasureSpec(bottom - top, View.MeasureSpec.EXACTLY);
        copy.measure(widthSpec, heightSpec);
        copy.layout(left, top, right, bottom);
        return copy;
    }

    /**
     * Creates a Bitmap of the given view, using the Matrix matrix to transform to the local
     * coordinates. <code>matrix</code> will be modified during the bitmap creation.
     *
     * <p>If the bitmap is large, it will be scaled uniformly down to at most 1MB size.</p>
     * @param view The view to create a bitmap for.
     * @param matrix The matrix converting the view local coordinates to the coordinates that
     *               the bitmap will be displayed in. <code>matrix</code> will be modified before
     *               returning.
     * @param bounds The bounds of the bitmap in the destination coordinate system (where the
     *               view should be presented. Typically, this is matrix.mapRect(viewBounds);
     * @return A bitmap of the given view or null if bounds has no width or height.
     */
    public static Bitmap createViewBitmap(View view, Matrix matrix, RectF bounds) {
        Bitmap bitmap = null;
        int bitmapWidth = Math.round(bounds.width());
        int bitmapHeight = Math.round(bounds.height());
        if (bitmapWidth > 0 && bitmapHeight > 0) {
            float scale = Math.min(1f, ((float)MAX_IMAGE_SIZE) / (bitmapWidth * bitmapHeight));
            bitmapWidth *= scale;
            bitmapHeight *= scale;
            matrix.postTranslate(-bounds.left, -bounds.top);
            matrix.postScale(scale, scale);
            bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            canvas.concat(matrix);
            view.draw(canvas);
        }
        return bitmap;
    }

    public static class MatrixEvaluator implements TypeEvaluator<Matrix> {

        float[] mTempStartValues = new float[9];
+8 −31
Original line number Diff line number Diff line
@@ -224,11 +224,10 @@ public abstract class Visibility extends Transition {
                    }
                }
            }
        }
        if (startValues == null) {
        } else if (startValues == null && visInfo.endVisibility == View.VISIBLE) {
            visInfo.fadeIn = true;
            visInfo.visibilityChange = true;
        } else if (endValues == null) {
        } else if (endValues == null && visInfo.startVisibility == View.VISIBLE) {
            visInfo.fadeIn = false;
            visInfo.visibilityChange = true;
        }
@@ -370,16 +369,14 @@ public abstract class Visibility extends Transition {
                    overlayView = startView;
                } else if (startView.getParent() instanceof View) {
                    View startParent = (View) startView.getParent();
                    VisibilityInfo parentVisibilityInfo = null;
                    TransitionValues startParentValues = getTransitionValues(startParent, true);
                    TransitionValues endParentValues = getMatchedTransitionValues(startParent,
                            true);
                    if (endParentValues != null) {
                        TransitionValues startParentValues = getTransitionValues(startParent, true);
                        parentVisibilityInfo =
                    VisibilityInfo parentVisibilityInfo =
                            getVisibilityChangeInfo(startParentValues, endParentValues);
                    }
                    if (parentVisibilityInfo == null || !parentVisibilityInfo.visibilityChange) {
                        overlayView = copyViewImage(startView);
                    if (!parentVisibilityInfo.visibilityChange) {
                        overlayView = TransitionUtils.copyViewImage(sceneRoot, startView,
                                startParent);
                    } else if (startParent.getParent() == null) {
                        int id = startParent.getId();
                        if (id != View.NO_ID && sceneRoot.findViewById(id) != null
@@ -485,26 +482,6 @@ public abstract class Visibility extends Transition {
        return null;
    }

    private View copyViewImage(View view) {
        int width = view.getWidth();
        int height = view.getHeight();
        if (width <= 0 || height <= 0) {
            return null;
        }
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        view.draw(canvas);
        final BitmapDrawable drawable = new BitmapDrawable(bitmap);

        View overlayView = new View(view.getContext());
        overlayView.setBackground(drawable);
        int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
        int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
        overlayView.measure(widthSpec, heightSpec);
        overlayView.layout(0, 0, width, height);
        return overlayView;
    }

    @Override
    boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) {
        VisibilityInfo changeInfo = getVisibilityChangeInfo(oldValues, newValues);