193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein/*
293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Copyright (C) 2013 The Android Open Source Project
393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Licensed under the Apache License, Version 2.0 (the "License");
593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * you may not use this file except in compliance with the License.
693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * You may obtain a copy of the License at
793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *      http://www.apache.org/licenses/LICENSE-2.0
993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
1093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Unless required by applicable law or agreed to in writing, software
1193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * distributed under the License is distributed on an "AS IS" BASIS,
1293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * See the License for the specific language governing permissions and
1493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * limitations under the License.
1593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein */
1693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinpackage com.android.bitmap.drawable;
1893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.animation.Animator;
2093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.animation.AnimatorListenerAdapter;
2193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.animation.ValueAnimator;
2293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.animation.ValueAnimator.AnimatorUpdateListener;
2393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.content.res.Resources;
2493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.Canvas;
25df3da61c8f2f54604376d9761649bdba54aa858bMark Weiimport android.graphics.Color;
2693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.ColorFilter;
2793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.Rect;
2893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.drawable.Drawable;
2993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.os.Handler;
3093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.util.Log;
3193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.view.animation.LinearInterpolator;
3293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
3393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.BitmapCache;
3493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.DecodeAggregator;
3593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.DecodeTask;
369c6ac19d4a3d39b7c2992060957920118ff56a65Mark Weiimport com.android.bitmap.R;
3740662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzsteinimport com.android.bitmap.RequestKey;
3893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.ReusableBitmap;
3993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.util.Trace;
4093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
4193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein/**
4293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * This class encapsulates all functionality needed to display a single image bitmap,
4393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * including request creation/cancelling, data unbinding and re-binding, and fancy animations
4493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * to draw upon state changes.
4593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <p>
4693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * The actual bitmap decode work is handled by {@link DecodeTask}.
4793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein */
482e4d0863dba53435372ec96538f2ef3e1c3675bfMark Weipublic class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
492e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    Runnable, Parallaxable, DecodeAggregator.Callback {
50cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei
51b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei    public static final int LOAD_STATE_UNINITIALIZED = 0;
52b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei    public static final int LOAD_STATE_NOT_YET_LOADED = 1;
53b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei    public static final int LOAD_STATE_LOADING = 2;
54b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei    public static final int LOAD_STATE_LOADED = 3;
55b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei    public static final int LOAD_STATE_FAILED = 4;
56b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei
57b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei    public static final boolean DEBUG = false;
58c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei    private static final String TAG = ExtendedBitmapDrawable.class.getSimpleName();
59b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei
605d6521e290594fe0851086b0c27413e9709e437fMark Wei    private final Resources mResources;
615030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    private final ExtendedOptions mOpts;
625030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
632e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    // Parallax.
645030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    private float mParallaxFraction = 1f / 2;
6593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
665030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    // State changes.
672e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    private int mLoadState = LOAD_STATE_UNINITIALIZED;
682e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    private Placeholder mPlaceholder;
692e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    private Progress mProgress;
7093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private int mProgressDelayMs;
7193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private final Handler mHandler = new Handler();
7293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
7393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public ExtendedBitmapDrawable(final Resources res, final BitmapCache cache,
7409f46006437e7de33afdb51192bf0bdc08e97040Mark Wei            final boolean limitDensity, ExtendedOptions opts) {
752e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        super(res, cache, limitDensity);
765d6521e290594fe0851086b0c27413e9709e437fMark Wei        mResources = res;
7709f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        if (opts == null) {
7809f46006437e7de33afdb51192bf0bdc08e97040Mark Wei            opts = new ExtendedOptions(0);
7909f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        }
805030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        mOpts = opts;
8193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
825d6521e290594fe0851086b0c27413e9709e437fMark Wei        onOptsChanged();
835d6521e290594fe0851086b0c27413e9709e437fMark Wei    }
845d6521e290594fe0851086b0c27413e9709e437fMark Wei
855d6521e290594fe0851086b0c27413e9709e437fMark Wei    /**
865d6521e290594fe0851086b0c27413e9709e437fMark Wei     * Called after a field is changed in an {@link ExtendedOptions}, if that field requests this
875d6521e290594fe0851086b0c27413e9709e437fMark Wei     * method to be called.
885d6521e290594fe0851086b0c27413e9709e437fMark Wei     */
895d6521e290594fe0851086b0c27413e9709e437fMark Wei    public void onOptsChanged() {
905d6521e290594fe0851086b0c27413e9709e437fMark Wei        mOpts.validate();
915d6521e290594fe0851086b0c27413e9709e437fMark Wei
922e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        // Placeholder and progress.
935d6521e290594fe0851086b0c27413e9709e437fMark Wei        if ((mOpts.features & ExtendedOptions.FEATURE_STATE_CHANGES) != 0) {
945d6521e290594fe0851086b0c27413e9709e437fMark Wei            final int fadeOutDurationMs = mResources.getInteger(R.integer.bitmap_fade_animation_duration);
955d6521e290594fe0851086b0c27413e9709e437fMark Wei            mProgressDelayMs = mResources.getInteger(R.integer.bitmap_progress_animation_delay);
965030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
9788e3100ef41cf50e4f40bbaab661df41e176dae2Mark Wei            // Placeholder is not optional because backgroundColor is part of it.
9888e3100ef41cf50e4f40bbaab661df41e176dae2Mark Wei            Drawable placeholder = null;
995d6521e290594fe0851086b0c27413e9709e437fMark Wei            int placeholderWidth = mResources.getDimensionPixelSize(R.dimen.placeholder_size);
1005d6521e290594fe0851086b0c27413e9709e437fMark Wei            int placeholderHeight = mResources.getDimensionPixelSize(R.dimen.placeholder_size);
1015d6521e290594fe0851086b0c27413e9709e437fMark Wei            if (mOpts.placeholder != null) {
1025d6521e290594fe0851086b0c27413e9709e437fMark Wei                ConstantState constantState = mOpts.placeholder.getConstantState();
10388e3100ef41cf50e4f40bbaab661df41e176dae2Mark Wei                if (constantState != null) {
1045d6521e290594fe0851086b0c27413e9709e437fMark Wei                    placeholder = constantState.newDrawable(mResources);
105c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                } else {
106c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                    placeholder = mOpts.placeholder;
107c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                }
108b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei
109c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                Rect bounds = mOpts.placeholder.getBounds();
110c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                if (bounds.width() != 0) {
111c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                    placeholderWidth = bounds.width();
112c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                } else if (placeholder.getIntrinsicWidth() != -1) {
113c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                    placeholderWidth = placeholder.getIntrinsicWidth();
114c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                }
115c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                if (bounds.height() != 0) {
116c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                    placeholderHeight = bounds.height();
117c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                } else if (placeholder.getIntrinsicHeight() != -1) {
118c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei                    placeholderHeight = placeholder.getIntrinsicHeight();
11988e3100ef41cf50e4f40bbaab661df41e176dae2Mark Wei                }
12088e3100ef41cf50e4f40bbaab661df41e176dae2Mark Wei            }
121b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei
1225d6521e290594fe0851086b0c27413e9709e437fMark Wei            mPlaceholder = new Placeholder(placeholder, mResources, placeholderWidth, placeholderHeight,
1235d6521e290594fe0851086b0c27413e9709e437fMark Wei                    fadeOutDurationMs, mOpts);
1245030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            mPlaceholder.setCallback(this);
1254d404fe4edb0b5074ed9d3e90dd7aa942f252692Mark Wei            mPlaceholder.setBounds(getBounds());
1265030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
1275030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            // Progress bar is optional.
1285d6521e290594fe0851086b0c27413e9709e437fMark Wei            if (mOpts.progressBar != null) {
1295d6521e290594fe0851086b0c27413e9709e437fMark Wei                int progressBarSize = mResources.getDimensionPixelSize(R.dimen.progress_bar_size);
1305d6521e290594fe0851086b0c27413e9709e437fMark Wei                mProgress = new Progress(mOpts.progressBar.getConstantState().newDrawable(mResources), mResources,
1315d6521e290594fe0851086b0c27413e9709e437fMark Wei                        progressBarSize, progressBarSize, fadeOutDurationMs, mOpts);
1325030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                mProgress.setCallback(this);
1334d404fe4edb0b5074ed9d3e90dd7aa942f252692Mark Wei                mProgress.setBounds(getBounds());
1345d6521e290594fe0851086b0c27413e9709e437fMark Wei            } else {
1355d6521e290594fe0851086b0c27413e9709e437fMark Wei                mProgress = null;
1365030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            }
1375030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        }
1385d6521e290594fe0851086b0c27413e9709e437fMark Wei
1395d6521e290594fe0851086b0c27413e9709e437fMark Wei        setLoadState(mLoadState);
14093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
14193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1422e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    @Override
1432e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    public void setParallaxFraction(float fraction) {
1442e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        mParallaxFraction = fraction;
1452e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        invalidateSelf();
14693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
14793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1485030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    /**
1495030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     * Get the ExtendedOptions used to instantiate this ExtendedBitmapDrawable. Any changes made to
1505030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     * the parameters inside the options will take effect immediately.
1515030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     */
1525030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    public ExtendedOptions getExtendedOptions() {
1535030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        return mOpts;
15493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
15593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1562e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    /**
1572e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei     * This sets the drawable to the failed state, which remove all animations from the placeholder.
1582e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei     * This is different from unbinding to the uninitialized state, where we expect animations.
1592e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei     */
16093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void showStaticPlaceholder() {
16193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        setLoadState(LOAD_STATE_FAILED);
16293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
16393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
16489e59f00d67791754e44e65413baa95f94056df4Mark Wei    /**
16589e59f00d67791754e44e65413baa95f94056df4Mark Wei     * Directly sets the decode width and height. The given height should already have had the
16689e59f00d67791754e44e65413baa95f94056df4Mark Wei     * parallaxSpeedMultiplier applied to it.
16789e59f00d67791754e44e65413baa95f94056df4Mark Wei     */
16889e59f00d67791754e44e65413baa95f94056df4Mark Wei    public void setExactDecodeDimensions(int width, int height) {
16989e59f00d67791754e44e65413baa95f94056df4Mark Wei        super.setDecodeDimensions(width, height);
17089e59f00d67791754e44e65413baa95f94056df4Mark Wei    }
17189e59f00d67791754e44e65413baa95f94056df4Mark Wei
17289e59f00d67791754e44e65413baa95f94056df4Mark Wei    /**
17389e59f00d67791754e44e65413baa95f94056df4Mark Wei     * {@inheritDoc}
17489e59f00d67791754e44e65413baa95f94056df4Mark Wei     *
17589e59f00d67791754e44e65413baa95f94056df4Mark Wei     * The given height should not have had the parallaxSpeedMultiplier applied to it.
17689e59f00d67791754e44e65413baa95f94056df4Mark Wei     */
1772e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    @Override
17889e59f00d67791754e44e65413baa95f94056df4Mark Wei    public void setDecodeDimensions(int width, int height) {
17989e59f00d67791754e44e65413baa95f94056df4Mark Wei        super.setDecodeDimensions(width, (int) (height * mOpts.parallaxSpeedMultiplier));
18089e59f00d67791754e44e65413baa95f94056df4Mark Wei    }
18193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
18289e59f00d67791754e44e65413baa95f94056df4Mark Wei    @Override
18389e59f00d67791754e44e65413baa95f94056df4Mark Wei    protected void setImage(final RequestKey key) {
1845030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        if (mCurrKey != null && getDecodeAggregator() != null) {
1855030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            getDecodeAggregator().forget(mCurrKey);
18693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
18793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
18893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mHandler.removeCallbacks(this);
18993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        // start from a clean slate on every bind
19093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        // this allows the initial transition to be specially instantaneous, so e.g. a cache hit
19193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        // doesn't unnecessarily trigger a fade-in
19293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        setLoadState(LOAD_STATE_UNINITIALIZED);
19393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1942e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        super.setImage(key);
195b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei
196b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei        if (key == null) {
197b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei            showStaticPlaceholder();
198b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei        }
1992e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
2002e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
2012e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    @Override
2022e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    protected void setBitmap(ReusableBitmap bmp) {
203c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei        if (bmp != null) {
204c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei            setLoadState(LOAD_STATE_LOADED);
205c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei        } else {
206c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei            onDecodeFailed();
207c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei        }
2082e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
2092e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        super.setBitmap(bmp);
2102e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
2112e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
2122e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    @Override
21341af50eb1ac488572b066629c3954b23c21dfa76Mark Wei    protected void loadFileDescriptorFactory() {
2142e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        boolean executeStateChange = shouldExecuteStateChange();
21519a41ec7aaace2ab0b117d0baaeb544c5667b240Mark Wei        if (mCurrKey == null || mDecodeWidth == 0 || mDecodeHeight == 0) {
21619a41ec7aaace2ab0b117d0baaeb544c5667b240Mark Wei          return;
21719a41ec7aaace2ab0b117d0baaeb544c5667b240Mark Wei        }
21819a41ec7aaace2ab0b117d0baaeb544c5667b240Mark Wei
2192e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        if (executeStateChange) {
22019a41ec7aaace2ab0b117d0baaeb544c5667b240Mark Wei            setLoadState(LOAD_STATE_NOT_YET_LOADED);
22193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
2222e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
22341af50eb1ac488572b066629c3954b23c21dfa76Mark Wei        super.loadFileDescriptorFactory();
2242e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
2252e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
226c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei    @Override
227c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei    protected void onDecodeFailed() {
228c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei        super.onDecodeFailed();
229c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei
230c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei        setLoadState(LOAD_STATE_FAILED);
231c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei    }
232c5644927c0e7e121049b063046296ee8a59a4b37Mark Wei
2332e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    protected boolean shouldExecuteStateChange() {
2342e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        // TODO: AttachmentDrawable should override this method to match prev and curr request keys.
2352e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        return /* opts.stateChanges */ true;
23693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
23793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
23893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
2392e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    public float getDrawVerticalCenter() {
2402e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        return mParallaxFraction;
2412e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
2422e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
2432e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    @Override
24489e59f00d67791754e44e65413baa95f94056df4Mark Wei    protected final float getDrawVerticalOffsetMultiplier() {
2455030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        return mOpts.parallaxSpeedMultiplier;
2462e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    }
2472e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
2482e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    @Override
2493a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung    protected float getDecodeHorizontalCenter() {
2503a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung        return mOpts.decodeHorizontalCenter;
2513a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung    }
2523a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung
2533a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung    @Override
2542e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    protected float getDecodeVerticalCenter() {
25589e59f00d67791754e44e65413baa95f94056df4Mark Wei        return mOpts.decodeVerticalCenter;
25693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
25793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
2585030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    private DecodeAggregator getDecodeAggregator() {
2595030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        return mOpts.decodeAggregator;
2605030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    }
2615030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
2625030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    /**
2635030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     * Instead of overriding this method, subclasses should override {@link #onDraw(Canvas)}.
2645030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     *
2655030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     * The reason for this is that we need the placeholder and progress bar to be drawn over our
2665030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     * content. Those two drawables fade out, giving the impression that our content is fading in.
2675f42121579221299e02f6d4627725814ed3d0fbfMark Wei     *
2685f42121579221299e02f6d4627725814ed3d0fbfMark Wei     * Only override this method for custom drawings on top of all the drawable layers.
2695030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     */
27093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
2715f42121579221299e02f6d4627725814ed3d0fbfMark Wei    public void draw(final Canvas canvas) {
27293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final Rect bounds = getBounds();
27393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (bounds.isEmpty()) {
27493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return;
27593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
27693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
2775030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        onDraw(canvas);
27893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
27993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        // Draw the two possible overlay layers in reverse-priority order.
28093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        // (each layer will no-op the draw when appropriate)
28193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        // This ordering means cross-fade transitions are just fade-outs of each layer.
28209f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        if (mProgress != null) onDrawPlaceholderOrProgress(canvas, mProgress);
28309f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        if (mPlaceholder != null) onDrawPlaceholderOrProgress(canvas, mPlaceholder);
2845030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    }
2855030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
2865030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    /**
2875030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     * Overriding this method to add your own custom drawing.
2885030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     */
2895030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    protected void onDraw(final Canvas canvas) {
2905030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        super.draw(canvas);
29193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
29293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
29309f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    /**
29409f46006437e7de33afdb51192bf0bdc08e97040Mark Wei     * Overriding this method to add your own custom placeholder or progress drawing.
29509f46006437e7de33afdb51192bf0bdc08e97040Mark Wei     */
29609f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    protected void onDrawPlaceholderOrProgress(final Canvas canvas, final TileDrawable drawable) {
29709f46006437e7de33afdb51192bf0bdc08e97040Mark Wei        drawable.draw(canvas);
29809f46006437e7de33afdb51192bf0bdc08e97040Mark Wei    }
29909f46006437e7de33afdb51192bf0bdc08e97040Mark Wei
30093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
30193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void setAlpha(int alpha) {
30293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final int old = mPaint.getAlpha();
3032e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        super.setAlpha(alpha);
3045030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        if (mPlaceholder != null) mPlaceholder.setAlpha(alpha);
3055030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        if (mProgress != null) mProgress.setAlpha(alpha);
30693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (alpha != old) {
30793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            invalidateSelf();
30893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
30993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
31093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
31193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
31293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void setColorFilter(ColorFilter cf) {
3132e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        super.setColorFilter(cf);
3145030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        if (mPlaceholder != null) mPlaceholder.setColorFilter(cf);
3155030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        if (mProgress != null) mProgress.setColorFilter(cf);
31693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        invalidateSelf();
31793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
31893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
31993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
32093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    protected void onBoundsChange(Rect bounds) {
32193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        super.onBoundsChange(bounds);
3225030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        if (mPlaceholder != null) mPlaceholder.setBounds(bounds);
3235030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        if (mProgress != null) mProgress.setBounds(bounds);
32493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
32593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
32693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
32740662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein    public void onDecodeBegin(final RequestKey key) {
3285030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        if (getDecodeAggregator() != null) {
3295030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            getDecodeAggregator().expect(key, this);
33093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        } else {
33193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            onBecomeFirstExpected(key);
33293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
3332e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        super.onDecodeBegin(key);
33493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
33593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
33693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
33740662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein    public void onBecomeFirstExpected(final RequestKey key) {
33893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (!key.equals(mCurrKey)) {
33993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return;
34093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
34193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        // normally, we'd transition to the LOADING state now, but we want to delay that a bit
34293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        // to minimize excess occurrences of the rotating spinner
34393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mHandler.postDelayed(this, mProgressDelayMs);
34493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
34593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
34693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
34793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void run() {
34893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (mLoadState == LOAD_STATE_NOT_YET_LOADED) {
34993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            setLoadState(LOAD_STATE_LOADING);
35093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
35193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
35293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
35393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
35440662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein    public void onDecodeComplete(final RequestKey key, final ReusableBitmap result) {
3555030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        if (getDecodeAggregator() != null) {
3565030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            getDecodeAggregator().execute(key, new Runnable() {
35793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                @Override
35893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                public void run() {
3592e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                    ExtendedBitmapDrawable.super.onDecodeComplete(key, result);
36093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
36193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
36293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                @Override
36393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                public String toString() {
36493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    return "DONE";
36593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
36693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            });
36793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        } else {
3682e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            super.onDecodeComplete(key, result);
36993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
37093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
37193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
37293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
37340662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein    public void onDecodeCancel(final RequestKey key) {
3745030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        if (getDecodeAggregator() != null) {
3755030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            getDecodeAggregator().forget(key);
37693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
3772e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei        super.onDecodeCancel(key);
37893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
37993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
3802e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei    /**
381b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei     * Get the load state of this drawable. Return one of the LOAD_STATE constants.
382b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei     */
383b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei    public int getLoadState() {
384b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei        return mLoadState;
385b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei    }
386b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei
387b6ec2afe9710112214d79b36b2233fef6a52845aMark Wei    /**
3882e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei     * Each attachment gets its own placeholder and progress indicator, to be shown, hidden,
3892e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei     * and animated based on Drawable#setVisible() changes, which are in turn driven by
3902e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei     * setLoadState().
3912e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei     */
39293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private void setLoadState(int loadState) {
39393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (DEBUG) {
39493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Log.v(TAG, String.format("IN setLoadState. old=%s new=%s key=%s this=%s",
39593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    mLoadState, loadState, mCurrKey, this));
39693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
39793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
39893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.beginSection("set load state");
39993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        switch (loadState) {
40093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // This state differs from LOADED in that the subsequent state transition away from
40193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // UNINITIALIZED will not have a fancy transition. This allows list item binds to
40293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // cached data to take immediate effect without unnecessary whizzery.
40393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            case LOAD_STATE_UNINITIALIZED:
4045030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                if (mPlaceholder != null) mPlaceholder.reset();
4055030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                if (mProgress != null) mProgress.reset();
40693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                break;
40793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            case LOAD_STATE_NOT_YET_LOADED:
4085030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                if (mPlaceholder != null) {
4095030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                    mPlaceholder.setPulseEnabled(true);
4105030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                    mPlaceholder.setVisible(true);
4115030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                }
4125030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                if (mProgress != null) mProgress.setVisible(false);
41393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                break;
41493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            case LOAD_STATE_LOADING:
4155030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                if (mProgress == null) {
4165030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                    // Stay in same visual state as LOAD_STATE_NOT_YET_LOADED.
4175030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                    break;
4185030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                }
4195030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                if (mPlaceholder != null) mPlaceholder.setVisible(false);
4205030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                if (mProgress != null) mProgress.setVisible(true);
42193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                break;
42293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            case LOAD_STATE_LOADED:
4235030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                if (mPlaceholder != null) mPlaceholder.setVisible(false);
4245030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                if (mProgress != null) mProgress.setVisible(false);
42593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                break;
42693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            case LOAD_STATE_FAILED:
4275030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                if (mPlaceholder != null) {
4285030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                    mPlaceholder.setPulseEnabled(false);
4295030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                    mPlaceholder.setVisible(true);
4305030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                }
4315030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                if (mProgress != null) mProgress.setVisible(false);
43293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                break;
43393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
43493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.endSection();
43593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
43693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mLoadState = loadState;
43793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        boolean placeholderVisible = mPlaceholder != null && mPlaceholder.isVisible();
43893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        boolean progressVisible = mProgress != null && mProgress.isVisible();
43993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
44093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (DEBUG) {
44193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Log.v(TAG, String.format("OUT stateful setLoadState. new=%s placeholder=%s progress=%s",
44293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    loadState, placeholderVisible, progressVisible));
44393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
44493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
44593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
44693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static class Placeholder extends TileDrawable {
44793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
44893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        private final ValueAnimator mPulseAnimator;
44993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        private boolean mPulseEnabled = true;
45093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        private float mPulseAlphaFraction = 1f;
45193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
4525030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        public Placeholder(Drawable placeholder, Resources res, int placeholderWidth,
4535030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                int placeholderHeight, int fadeOutDurationMs, ExtendedOptions opts) {
4545030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            super(placeholder, placeholderWidth, placeholderHeight, fadeOutDurationMs, opts);
4555030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
456c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei            if (opts.placeholderAnimationDuration == -1) {
457c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                mPulseAnimator = null;
458c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei            } else {
459c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                final long pulseDuration;
460c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                if (opts.placeholderAnimationDuration == 0) {
461c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                    pulseDuration = res.getInteger(R.integer.bitmap_placeholder_animation_duration);
462c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                } else {
463c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                    pulseDuration = opts.placeholderAnimationDuration;
46493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
465c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                mPulseAnimator = ValueAnimator.ofInt(55, 255).setDuration(pulseDuration);
466c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                mPulseAnimator.setRepeatCount(ValueAnimator.INFINITE);
467c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                mPulseAnimator.setRepeatMode(ValueAnimator.REVERSE);
468c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                mPulseAnimator.addUpdateListener(new AnimatorUpdateListener() {
469c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                    @Override
470c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                    public void onAnimationUpdate(ValueAnimator animation) {
471c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                        mPulseAlphaFraction = ((Integer) animation.getAnimatedValue()) / 255f;
472c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                        setInnerAlpha(getCurrentAlpha());
473c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                    }
474c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                });
475c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei            }
47693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
47793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                @Override
47893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                public void onAnimationEnd(Animator animation) {
47993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    stopPulsing();
48093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
48193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            });
48293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
48393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
48493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        @Override
48593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        public void setInnerAlpha(final int alpha) {
48693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            super.setInnerAlpha((int) (alpha * mPulseAlphaFraction));
48793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
48893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
48993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        public void setPulseEnabled(boolean enabled) {
49093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mPulseEnabled = enabled;
49193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (!mPulseEnabled) {
49293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                stopPulsing();
493c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei            } else {
494c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                startPulsing();
49593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
49693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
49793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
49893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        private void stopPulsing() {
49993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (mPulseAnimator != null) {
50093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                mPulseAnimator.cancel();
50193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                mPulseAlphaFraction = 1f;
50293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                setInnerAlpha(getCurrentAlpha());
50393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
50493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
50593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
506c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei        private void startPulsing() {
507c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei            if (mPulseAnimator != null && !mPulseAnimator.isStarted()) {
508c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                mPulseAnimator.start();
509c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei            }
510c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei        }
511c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei
51293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        @Override
51393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        public boolean setVisible(boolean visible) {
51493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final boolean changed = super.setVisible(visible);
51593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (changed) {
51693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (isVisible()) {
51793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    // start
518c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                    if (mPulseAnimator != null && mPulseEnabled && !mPulseAnimator.isStarted()) {
51993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        mPulseAnimator.start();
52093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
52193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                } else {
52293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    // can't cancel the pulsing yet-- wait for the fade-out animation to end
52393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    // one exception: if alpha is already zero, there is no fade-out, so stop now
52493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    if (getCurrentAlpha() == 0) {
52593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        stopPulsing();
52693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
52793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
52893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
52993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return changed;
53093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
53193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
53293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
53393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
53493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static class Progress extends TileDrawable {
53593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
53693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        private final ValueAnimator mRotateAnimator;
53793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
53893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        public Progress(Drawable progress, Resources res,
53993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                int progressBarWidth, int progressBarHeight, int fadeOutDurationMs,
5405030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                ExtendedOptions opts) {
5415030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            super(progress, progressBarWidth, progressBarHeight, fadeOutDurationMs, opts);
54293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
54393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mRotateAnimator = ValueAnimator.ofInt(0, 10000)
54493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    .setDuration(res.getInteger(R.integer.bitmap_progress_animation_duration));
54593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mRotateAnimator.setInterpolator(new LinearInterpolator());
54693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mRotateAnimator.setRepeatCount(ValueAnimator.INFINITE);
54793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mRotateAnimator.addUpdateListener(new AnimatorUpdateListener() {
54893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                @Override
54993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                public void onAnimationUpdate(ValueAnimator animation) {
55093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    setLevel((Integer) animation.getAnimatedValue());
55193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
55293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            });
55393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
55493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                @Override
55593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                public void onAnimationEnd(Animator animation) {
55693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    if (mRotateAnimator != null) {
55793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        mRotateAnimator.cancel();
55893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
55993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
56093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            });
56193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
56293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
56393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        @Override
56493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        public boolean setVisible(boolean visible) {
56593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final boolean changed = super.setVisible(visible);
56693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (changed) {
56793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (isVisible()) {
56893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    if (mRotateAnimator != null) {
56993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        mRotateAnimator.start();
57093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
57193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                } else {
57293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    // can't cancel the rotate yet-- wait for the fade-out animation to end
57393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    // one exception: if alpha is already zero, there is no fade-out, so stop now
57493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    if (getCurrentAlpha() == 0 && mRotateAnimator != null) {
57593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        mRotateAnimator.cancel();
57693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
57793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
57893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
57993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return changed;
58093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
5815030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    }
5825030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
5835030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    /**
5845030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     * This class contains the features a client can specify, and arguments to those features.
5855030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     * Clients can later retrieve the ExtendedOptions from an ExtendedBitmapDrawable and change the
5865030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     * parameters, which will be reflected immediately.
5875030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     */
5885030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    public static class ExtendedOptions {
5895030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
5905030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        /**
5915030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Summary:
5925030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * This feature enables you to draw decoded bitmap in order on the screen, to give the
5935030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * visual effect of a single decode thread.
5945030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
5955030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
5965030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Explanation:
5975030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Since DecodeTasks are asynchronous, multiple tasks may finish decoding at different
5985030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * times. To have a smooth user experience, provide a shared {@link DecodeAggregator} to all
5995030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * the ExtendedBitmapDrawables, and the decode aggregator will hold finished decodes so they
6005030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * come back in order.
6015030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
6025030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
6035030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Pros:
6045030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Visual consistency. Images are not popping up randomly all over the place.
6055030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
6065030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
6075030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Cons:
6085030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Artificial delay. Images are not drawn as soon as they are decoded. They must wait
6095030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * for their turn.
6105030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
6115030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
6125030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Requirements:
6135030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Set {@link #decodeAggregator} to a shared {@link DecodeAggregator}.
6145030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         */
6155030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        public static final int FEATURE_ORDERED_DISPLAY = 1;
6165030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
6175030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        /**
6185030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Summary:
6195030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * This feature enables the image to move in parallax as the user scrolls, to give visual
6205030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * flair to your images.
6215030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
6225030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
6235030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Explanation:
6245030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * When the user scrolls D pixels in the vertical direction, this ExtendedBitmapDrawable
6255030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * shifts its Bitmap f(D) pixels in the vertical direction before drawing to the screen.
6265030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Depending on the function f, the parallax effect can give varying interesting results.
6275030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
6285030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
6295030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Pros:
6305030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Visual pop and playfulness. Feeling of movement. Pleasantly surprise your users.
6315030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
6325030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
6335030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Cons:
6345030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Some users report motion sickness with certain speed multiplier values. Decode height
6355030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * must be greater than visual bounds to account for the parallax. This uses more memory and
6365030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * decoding time.
6375030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
6385030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
6395030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Requirements:
6405030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Set {@link #parallaxSpeedMultiplier} to the ratio between the decoded height and the
64110dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei         * visual bound height. Call {@link ExtendedBitmapDrawable#setDecodeDimensions(int, int)}
64210dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei         * with the height multiplied by {@link #parallaxSpeedMultiplier}.
64310dddd8a24a80d1d539997d8eaa9763c62bd02adMark Wei         * Call {@link ExtendedBitmapDrawable#setParallaxFraction(float)} when the user scrolls.
6445030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         */
6455030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        public static final int FEATURE_PARALLAX = 1 << 1;
6465030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
6475030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        /**
6485030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Summary:
6495030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * This feature enables fading in between multiple decode states, to give smooth transitions
6505030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * to and from the placeholder, progress bars, and decoded image.
6515030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
6525030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
6535030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Explanation:
6545030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * The states are: {@link ExtendedBitmapDrawable#LOAD_STATE_UNINITIALIZED},
6555030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * {@link ExtendedBitmapDrawable#LOAD_STATE_NOT_YET_LOADED},
6565030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * {@link ExtendedBitmapDrawable#LOAD_STATE_LOADING},
6575030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * {@link ExtendedBitmapDrawable#LOAD_STATE_LOADED}, and
6585030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * {@link ExtendedBitmapDrawable#LOAD_STATE_FAILED}. These states affect whether the
6595030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * placeholder and/or the progress bar is showing and animating. We first show the
6605030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * pulsating placeholder when an image begins decoding. After 2 seconds, we fade in a
6615030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * spinning progress bar. When the decode completes, we fade in the image.
6625030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
6635030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
6645030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Pros:
6655030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Smooth, beautiful transitions avoid perceived jank. Progress indicator informs users that
6665030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * work is being done and the app is not stalled.
6675030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
6685030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
6695030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Cons:
6705030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Very fast decodes' short decode time would be eclipsed by the animation duration. Static
6715030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * placeholder could be accomplished by {@link BasicBitmapDrawable} without the added
6725030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * complexity of states.
6735030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
6745030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
6755030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Requirements:
6765030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Set {@link #backgroundColor} to the color used for the background of the placeholder and
6775030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * progress bar. Use the alternative constructor to populate {@link #placeholder} and
678c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei         * {@link #progressBar}. Optionally set {@link #placeholderAnimationDuration}.
6795030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         */
6805030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        public static final int FEATURE_STATE_CHANGES = 1 << 2;
6815030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
6825030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        /**
6835030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Non-changeable bit field describing the features you want the
6845030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * {@link ExtendedBitmapDrawable} to support.
6855030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
6865030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <p/>
6875030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Example:
6885030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * <code>
6895030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * opts.features = FEATURE_ORDERED_DISPLAY | FEATURE_PARALLAX | FEATURE_STATE_CHANGES;
6905030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * </code>
6915030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         */
6925030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        public final int features;
6935030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
6945030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        /**
69589e59f00d67791754e44e65413baa95f94056df4Mark Wei         * Optional field for general decoding.
69689e59f00d67791754e44e65413baa95f94056df4Mark Wei         *
69789e59f00d67791754e44e65413baa95f94056df4Mark Wei         * This field determines which section of the source image to decode from. A value of 0
6983a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * indicates a preference for the far left of the source, while a value of 1 indicates a
6993a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * preference for the far right of the source. A value of .5 will result in the center
7003a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * of the source being decoded.
7013a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         */
7023a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung        public float decodeHorizontalCenter = 1f / 2;
7033a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung
7043a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung        /**
7053a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * Optional field for general decoding.
7063a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         *
7073a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * This field determines which section of the source image to decode from. A value of 0
70889e59f00d67791754e44e65413baa95f94056df4Mark Wei         * indicates a preference for the very top of the source, while a value of 1 indicates a
70989e59f00d67791754e44e65413baa95f94056df4Mark Wei         * preference for the very bottom of the source. A value of .5 will result in the center
71089e59f00d67791754e44e65413baa95f94056df4Mark Wei         * of the source being decoded.
71189e59f00d67791754e44e65413baa95f94056df4Mark Wei         *
71289e59f00d67791754e44e65413baa95f94056df4Mark Wei         * This should not be confused with {@link #setParallaxFraction(float)}. This field
71389e59f00d67791754e44e65413baa95f94056df4Mark Wei         * determines the general section for decode. The parallax fraction then determines the
71489e59f00d67791754e44e65413baa95f94056df4Mark Wei         * slice from within that section for display.
71589e59f00d67791754e44e65413baa95f94056df4Mark Wei         */
716a8b1e1f5cad36086e89c052007473609c379ccbdOleksandr Kyreiev        public float decodeVerticalCenter = 1f / 2;
71789e59f00d67791754e44e65413baa95f94056df4Mark Wei
71889e59f00d67791754e44e65413baa95f94056df4Mark Wei        /**
7195030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Required field if {@link #FEATURE_ORDERED_DISPLAY} is supported.
7205030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         */
7215030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        public DecodeAggregator decodeAggregator = null;
7225030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
7235030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        /**
7245030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Required field if {@link #FEATURE_PARALLAX} is supported.
7255030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
7265030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * A value of 1.5f gives a subtle parallax, and is a good value to
7275030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * start with. 2.0f gives a more obvious parallax, arguably exaggerated. Some users report
7285030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * motion sickness with 2.0f. A value of 1.0f is synonymous with no parallax. Be careful not
7295030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * to set too high a value, since we will start cropping the widths if the image's height is
7305030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * not sufficient.
7315030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         */
7325030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        public float parallaxSpeedMultiplier = 1;
7335030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
7345030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        /**
735df3da61c8f2f54604376d9761649bdba54aa858bMark Wei         * Optional field if {@link #FEATURE_STATE_CHANGES} is supported. Must be an opaque color.
7365030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
7375030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * See {@link android.graphics.Color}.
7385030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         */
7395030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        public int backgroundColor = 0;
7405030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
7415030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        /**
7425d6521e290594fe0851086b0c27413e9709e437fMark Wei         * Optional field if {@link #FEATURE_STATE_CHANGES} is supported.
7435d6521e290594fe0851086b0c27413e9709e437fMark Wei         *
7445d6521e290594fe0851086b0c27413e9709e437fMark Wei         * If you modify this field you must call
7455d6521e290594fe0851086b0c27413e9709e437fMark Wei         * {@link ExtendedBitmapDrawable#onOptsChanged(Resources, ExtendedOptions)} on the
7465d6521e290594fe0851086b0c27413e9709e437fMark Wei         * appropriate ExtendedBitmapDrawable.
7475030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         */
7485d6521e290594fe0851086b0c27413e9709e437fMark Wei        public Drawable placeholder;
7495030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
7505030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        /**
751c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei         * Optional field if {@link #FEATURE_STATE_CHANGES} is supported.
752c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei         *
753c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei         * Special value 0 means default animation duration. Special value -1 means disable the
754c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei         * animation (placeholder will be at maximum alpha always). Any value > 0 defines the
755c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei         * duration in milliseconds.
756c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei         */
757c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei        public int placeholderAnimationDuration = 0;
758c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei
759c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei        /**
7605d6521e290594fe0851086b0c27413e9709e437fMark Wei         * Optional field if {@link #FEATURE_STATE_CHANGES} is supported.
7615d6521e290594fe0851086b0c27413e9709e437fMark Wei         *
7625d6521e290594fe0851086b0c27413e9709e437fMark Wei         * If you modify this field you must call
7635d6521e290594fe0851086b0c27413e9709e437fMark Wei         * {@link ExtendedBitmapDrawable#onOptsChanged(Resources, ExtendedOptions)} on the
7645d6521e290594fe0851086b0c27413e9709e437fMark Wei         * appropriate ExtendedBitmapDrawable.
7655030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         */
7665d6521e290594fe0851086b0c27413e9709e437fMark Wei        public Drawable progressBar;
7675030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
7685030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        /**
7695030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Use this constructor when all the feature parameters are changeable.
7705030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         */
7715030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        public ExtendedOptions(final int features) {
7725030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            this(features, null, null);
7735030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        }
7745030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei
7755030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        /**
7765030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Use this constructor when you have to specify non-changeable feature parameters.
7775030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         */
7785030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        public ExtendedOptions(final int features, final Drawable placeholder,
7795030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                final Drawable progressBar) {
7805030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            this.features = features;
7815030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            this.placeholder = placeholder;
7825030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            this.progressBar = progressBar;
7835030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        }
78493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
7855030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        /**
7865030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * Validate this ExtendedOptions instance to make sure that all the required fields are set
7875030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * for the requested features.
7885030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         *
7895030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         * This will throw an IllegalStateException if validation fails.
7905030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei         */
7915030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        private void validate()
7925030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                throws IllegalStateException {
7933a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung            if (decodeHorizontalCenter < 0 || decodeHorizontalCenter > 1) {
7943a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung                throw new IllegalStateException(
7953a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung                        "ExtendedOptions: decodeHorizontalCenter must be within 0 and 1, " +
7963a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung                                "inclusive");
7973a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung            }
79889e59f00d67791754e44e65413baa95f94056df4Mark Wei            if (decodeVerticalCenter < 0 || decodeVerticalCenter > 1) {
79989e59f00d67791754e44e65413baa95f94056df4Mark Wei                throw new IllegalStateException(
80089e59f00d67791754e44e65413baa95f94056df4Mark Wei                        "ExtendedOptions: decodeVerticalCenter must be within 0 and 1, inclusive");
80189e59f00d67791754e44e65413baa95f94056df4Mark Wei            }
8025030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            if ((features & FEATURE_ORDERED_DISPLAY) != 0 && decodeAggregator == null) {
8035030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                throw new IllegalStateException(
8045030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                        "ExtendedOptions: To support FEATURE_ORDERED_DISPLAY, "
8055030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                                + "decodeAggregator must be set.");
8065030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            }
8074a1464aad9c626bd63821697b215a16ee21db824Mark Wei            if ((features & FEATURE_PARALLAX) != 0 && parallaxSpeedMultiplier <= 1) {
8085030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                throw new IllegalStateException(
8095030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei                        "ExtendedOptions: To support FEATURE_PARALLAX, "
8104a1464aad9c626bd63821697b215a16ee21db824Mark Wei                                + "parallaxSpeedMultiplier must be greater than 1.");
8115030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            }
812c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei            if ((features & FEATURE_STATE_CHANGES) != 0) {
813c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                if (backgroundColor == 0
814c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                        && placeholder == null) {
815c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                    throw new IllegalStateException(
816c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                            "ExtendedOptions: To support FEATURE_STATE_CHANGES, "
817c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                                    + "either backgroundColor or placeholder must be set.");
818c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                }
819c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                if (placeholderAnimationDuration < -1) {
820c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                    throw new IllegalStateException(
821c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                            "ExtendedOptions: To support FEATURE_STATE_CHANGES, "
822c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                                    + "placeholderAnimationDuration must be set correctly.");
823c4ba226c78b8478de6ac8e293d7f9bc64cba36ecMark Wei                }
824df3da61c8f2f54604376d9761649bdba54aa858bMark Wei                if (backgroundColor != 0 && Color.alpha(backgroundColor) != 255) {
825df3da61c8f2f54604376d9761649bdba54aa858bMark Wei                    throw new IllegalStateException(
826df3da61c8f2f54604376d9761649bdba54aa858bMark Wei                            "ExtendedOptions: To support FEATURE_STATE_CHANGES, "
827df3da61c8f2f54604376d9761649bdba54aa858bMark Wei                                    + "backgroundColor must be set to an opaque color.");
828df3da61c8f2f54604376d9761649bdba54aa858bMark Wei                }
8295030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei            }
8305030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei        }
83193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
83293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein}
833