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

Commit f390f770 authored by Alan Viverette's avatar Alan Viverette
Browse files

Postpone creating new drawables for DrawableContainerState.

When creating a DrawableContainerState from a constant state, calls to
ConstantState.newDrawable() are postponed and made as they are needed.

Bug: 9280861
Change-Id: I03c93a43ee00aca3ff618d66d7f507f1722538d1
parent a6954f7e
Loading
Loading
Loading
Loading
+224 −101
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.SystemClock;
import android.util.SparseArray;

/**
 * A helper class that contains several {@link Drawable}s and selects which one to use.
@@ -316,7 +317,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
        }

        if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
            Drawable d = mDrawableContainerState.mDrawables[idx];
            final Drawable d = mDrawableContainerState.getChild(idx);
            mCurrDrawable = d;
            mCurIndex = idx;
            if (d != null) {
@@ -432,6 +433,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
     */
    public abstract static class DrawableContainerState extends ConstantState {
        final DrawableContainer mOwner;
        final Resources mRes;

        SparseArray<ConstantStateFuture> mDrawableFutures;

        int mChangingConfigurations;
        int mChildrenChangingConfigurations;
@@ -439,80 +443,92 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
        Drawable[] mDrawables;
        int mNumChildren;

        boolean     mVariablePadding = false;
        Rect        mConstantPadding = null;
        boolean mVariablePadding;
        boolean mPaddingChecked;
        Rect mConstantPadding;

        boolean     mConstantSize = false;
        boolean     mComputedConstantSize = false;
        boolean mConstantSize;
        boolean mComputedConstantSize;
        int mConstantWidth;
        int mConstantHeight;
        int mConstantMinimumWidth;
        int mConstantMinimumHeight;

        boolean mCheckedOpacity;
        int mOpacity;

        boolean     mHaveStateful = false;
        boolean mCheckedStateful;
        boolean mStateful;

        boolean mCheckedConstantState;
        boolean mCanConstantState;

        boolean     mPaddingChecked = false;
        
        boolean mDither = DEFAULT_DITHER;

        boolean mMutated;
        int mLayoutDirection;

        int mEnterFadeDuration;
        int mExitFadeDuration;

        DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
                Resources res) {
            mOwner = owner;
            mRes = res;

            if (orig != null) {
                mChangingConfigurations = orig.mChangingConfigurations;
                mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;

                final Drawable[] origDr = orig.mDrawables;
                mCheckedConstantState = true;
                mCanConstantState = true;

                mVariablePadding = orig.mVariablePadding;
                mConstantSize = orig.mConstantSize;
                mDither = orig.mDither;
                mMutated = orig.mMutated;
                mLayoutDirection = orig.mLayoutDirection;
                mEnterFadeDuration = orig.mEnterFadeDuration;
                mExitFadeDuration = orig.mExitFadeDuration;

                // Cloning the following values may require creating futures.
                mConstantPadding = orig.getConstantPadding();
                mPaddingChecked = true;

                mConstantWidth = orig.getConstantWidth();
                mConstantHeight = orig.getConstantHeight();
                mConstantMinimumWidth = orig.getConstantMinimumWidth();
                mConstantMinimumHeight = orig.getConstantMinimumHeight();
                mComputedConstantSize = true;

                mOpacity = orig.getOpacity();
                mCheckedOpacity = true;

                mStateful = orig.isStateful();
                mCheckedStateful = true;

                // Postpone cloning children and futures until we're absolutely
                // sure that we're done computing values for the original state.
                final Drawable[] origDr = orig.mDrawables;
                mDrawables = new Drawable[origDr.length];
                mNumChildren = orig.mNumChildren;

                final int N = mNumChildren;
                for (int i=0; i<N; i++) {
                    if (res != null) {
                        mDrawables[i] = origDr[i].getConstantState().newDrawable(res);
                final SparseArray<ConstantStateFuture> origDf = orig.mDrawableFutures;
                if (origDf != null) {
                    mDrawableFutures = origDf.clone();
                } else {
                        mDrawables[i] = origDr[i].getConstantState().newDrawable();
                    }
                    mDrawables[i].setCallback(owner);
                    mDrawables[i].setLayoutDirection(origDr[i].getLayoutDirection());
                    mDrawableFutures = new SparseArray<ConstantStateFuture>(mNumChildren);
                }

                mCheckedConstantState = mCanConstantState = true;
                mVariablePadding = orig.mVariablePadding;
                if (orig.mConstantPadding != null) {
                    mConstantPadding = new Rect(orig.mConstantPadding);
                final int N = mNumChildren;
                for (int i = 0; i < N; i++) {
                    if (origDr[i] != null) {
                        mDrawableFutures.put(i, new ConstantStateFuture(origDr[i]));
                    }
                }
                mConstantSize = orig.mConstantSize;
                mComputedConstantSize = orig.mComputedConstantSize;
                mConstantWidth = orig.mConstantWidth;
                mConstantHeight = orig.mConstantHeight;
                mConstantMinimumWidth = orig.mConstantMinimumWidth;
                mConstantMinimumHeight = orig.mConstantMinimumHeight;
                
                mOpacity = orig.mOpacity;
                mHaveStateful = orig.mHaveStateful;
                mStateful = orig.mStateful;
                
                mDither = orig.mDither;

                mEnterFadeDuration = orig.mEnterFadeDuration;
                mExitFadeDuration = orig.mExitFadeDuration;

            } else {
                mDrawables = new Drawable[10];
                mNumChildren = 0;
                mCheckedConstantState = mCanConstantState = false;
            }
        }

@@ -534,7 +550,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
            mDrawables[pos] = dr;
            mNumChildren++;
            mChildrenChangingConfigurations |= dr.getChangingConfigurations();
            mHaveStateful = false;
            mCheckedStateful = false;
            mCheckedOpacity = false;

            mConstantPadding = null;
            mPaddingChecked = false;
@@ -547,6 +564,18 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
            return mDrawables.length;
        }

        private final void createAllFutures() {
            if (mDrawableFutures != null) {
                final int futureCount = mDrawableFutures.size();
                for (int keyIndex = 0; keyIndex < futureCount; keyIndex++) {
                    final int index = mDrawableFutures.keyAt(keyIndex);
                    mDrawables[index] = mDrawableFutures.valueAt(keyIndex).get(this);
                }

                mDrawableFutures = null;
            }
        }

        public final int getChildCount() {
            return mNumChildren;
        }
@@ -555,24 +584,64 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
         * @deprecated Use {@link #getChild} instead.
         */
        public final Drawable[] getChildren() {
            // Create all futures for backwards compatibility.
            createAllFutures();

            return mDrawables;
        }

        public final Drawable getChild(int index) {
            return mDrawables[index];
            final Drawable result = mDrawables[index];
            if (result != null) {
                return result;
            }

            // Prepare future drawable if necessary.
            if (mDrawableFutures != null) {
                final int keyIndex = mDrawableFutures.indexOfKey(index);
                if (keyIndex >= 0) {
                    final Drawable prepared = mDrawableFutures.valueAt(keyIndex).get(this);
                    mDrawables[index] = prepared;
                    mDrawableFutures.removeAt(keyIndex);
                    return prepared;
                }
            }

            return null;
        }

        final void setLayoutDirection(int layoutDirection) {
            // No need to call createAllFutures, since future drawables will
            // change layout direction when they are prepared.
            final int N = mNumChildren;
            final Drawable[] drawables = mDrawables;
            for (int i = 0; i < N; i++) {
                if (drawables[i] != null) {
                    drawables[i].setLayoutDirection(layoutDirection);
                }
            }

            mLayoutDirection = layoutDirection;
        }

        final void mutate() {
            final int N = getChildCount();
            // No need to call createAllFutures, since future drawables will
            // mutate when they are prepared.
            final int N = mNumChildren;
            final Drawable[] drawables = mDrawables;
            for (int i = 0; i < N; i++) {
                if (drawables[i] != null) drawables[i].mutate();
                if (drawables[i] != null) {
                    drawables[i].mutate();
                }
            }

        /** A boolean value indicating whether to use the maximum padding value of 
          * all frames in the set (false), or to use the padding value of the frame 
          * being shown (true). Default value is false. 
            mMutated = true;
        }

        /**
         * A boolean value indicating whether to use the maximum padding value
         * of all frames in the set (false), or to use the padding value of the
         * frame being shown (true). Default value is false.
         */
        public final void setVariablePadding(boolean variable) {
            mVariablePadding = variable;
@@ -582,13 +651,16 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
            if (mVariablePadding) {
                return null;
            }
            if (mConstantPadding != null || mPaddingChecked) {

            if ((mConstantPadding != null) || mPaddingChecked) {
                return mConstantPadding;
            }

            createAllFutures();

            Rect r = null;
            final Rect t = new Rect();
            final int N = getChildCount();
            final int N = mNumChildren;
            final Drawable[] drawables = mDrawables;
            for (int i = 0; i < N; i++) {
                if (drawables[i].getPadding(t)) {
@@ -599,6 +671,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
                    if (t.bottom > r.bottom) r.bottom = t.bottom;
                }
            }

            mPaddingChecked = true;
            return (mConstantPadding = r);
        }
@@ -646,12 +719,14 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
        protected void computeConstantSize() {
            mComputedConstantSize = true;

            final int N = getChildCount();
            createAllFutures();

            final int N = mNumChildren;
            final Drawable[] drawables = mDrawables;
            mConstantWidth = mConstantHeight = -1;
            mConstantMinimumWidth = mConstantMinimumHeight = 0;
            for (int i = 0; i < N; i++) {
                Drawable dr = drawables[i];
                final Drawable dr = drawables[i];
                int s = dr.getIntrinsicWidth();
                if (s > mConstantWidth) mConstantWidth = s;
                s = dr.getIntrinsicHeight();
@@ -680,33 +755,45 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
        }

        public final int getOpacity() {
            final int N = getChildCount();
            if (mCheckedOpacity) {
                return mOpacity;
            }

            createAllFutures();

            mCheckedOpacity = true;

            final int N = mNumChildren;
            final Drawable[] drawables = mDrawables;
            int op = N > 0 ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT;
            int op = (N > 0) ? drawables[0].getOpacity() : PixelFormat.TRANSPARENT;
            for (int i = 1; i < N; i++) {
                op = Drawable.resolveOpacity(op, drawables[i].getOpacity());
            }

            mOpacity = op;
            return op;
        }

        public final boolean isStateful() {
            if (mHaveStateful) {
            if (mCheckedStateful) {
                return mStateful;
            }

            boolean stateful = false;
            final int N = getChildCount();
            createAllFutures();

            mCheckedStateful = true;

            final int N = mNumChildren;
            final Drawable[] drawables = mDrawables;
            for (int i = 0; i < N; i++) {
                if (mDrawables[i].isStateful()) {
                    stateful = true;
                    break;
                if (drawables[i].isStateful()) {
                    mStateful = true;
                    return true;
                }
            }

            mStateful = stateful;
            mHaveStateful = true;
            return stateful;
            mStateful = false;
            return false;
        }

        public void growArray(int oldSize, int newSize) {
@@ -716,24 +803,60 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
        }

        public synchronized boolean canConstantState() {
            if (!mCheckedConstantState) {
                mCanConstantState = true;
            if (mCheckedConstantState) {
                return mCanConstantState;
            }

            createAllFutures();

            mCheckedConstantState = true;

            final int N = mNumChildren;
            final Drawable[] drawables = mDrawables;
            for (int i = 0; i < N; i++) {
                    if (mDrawables[i].getConstantState() == null) {
                if (drawables[i].getConstantState() == null) {
                    mCanConstantState = false;
                        break;
                    return false;
                }
            }
                mCheckedConstantState = true;

            mCanConstantState = true;
            return true;
        }

            return mCanConstantState;
        /**
         * Class capable of cloning a Drawable from another Drawable's
         * ConstantState.
         */
        private static class ConstantStateFuture {
            private final ConstantState mConstantState;

            private ConstantStateFuture(Drawable source) {
                mConstantState = source.getConstantState();
            }

            /**
             * Obtains and prepares the Drawable represented by this future.
             *
             * @param state the container into which this future will be placed
             * @return a prepared Drawable
             */
            public Drawable get(DrawableContainerState state) {
                final Drawable result = (state.mRes == null) ?
                        mConstantState.newDrawable() : mConstantState.newDrawable(state.mRes);
                result.setLayoutDirection(state.mLayoutDirection);
                result.setCallback(state.mOwner);

                if (state.mMutated) {
                    result.mutate();
                }

                return result;
            }
        }
    }

    protected void setConstantState(DrawableContainerState state)
    {
    protected void setConstantState(DrawableContainerState state) {
        mDrawableContainerState = state;
    }
}
+4 −4
Original line number Diff line number Diff line
@@ -264,11 +264,11 @@ public class StateListDrawable extends DrawableContainer {
    /** @hide */
    @Override
    public void setLayoutDirection(int layoutDirection) {
        final int numStates = getStateCount();
        for (int i = 0; i < numStates; i++) {
            getStateDrawable(i).setLayoutDirection(layoutDirection);
        }
        super.setLayoutDirection(layoutDirection);

        // Let the container handle setting its own layout direction. Otherwise,
        // we're accessing potentially unused states.
        mStateListState.setLayoutDirection(layoutDirection);
    }

    static final class StateListState extends DrawableContainerState {