it.ndorigatti.android.view.MulticolorProgressBar.java Source code

Java tutorial

Introduction

Here is the source code for it.ndorigatti.android.view.MulticolorProgressBar.java

Source

package it.ndorigatti.android.view;
/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.util.Pools;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ProgressBar;

import java.util.ArrayList;

/**
 * <p>
 * Visual indicator of double progress in some operation.  Displays a bar to the user
 * representing how far the operation has progressed; the application can
 * change the amount of progress (modifying the length of the bar) as it moves
 * forward.  There is also a secondary progress displayable on a progress bar
 * which is useful for displaying alternative progress, such as download/upload in P2P applications.
 * The secondary progress is drawn on top of the primary whenever it is less than the primary progress.
 * If they are the same value, primary progress takes the precedence.
 * </p>
 * <p/> *
 * <p/>
 * *
 * <p>To add a progress bar to a layout file, you can use the {@code &lt;MultiColorProgressBar&gt;} element.
 * By default, the progress bar is coloured of blue/green colors. To change progress colors,
 * call the {@link #setProgressColor(int)} and {@link #setSecondaryProgressColor(int)}, like so:</p>
 * <p/>
 * <pre>
 * MulticolorProgressBar mcpb = ...
 *     mcpb.setProgressColor(Color.RED);
 *     mcpb.setSecondaryProgressColor(getResources().getColor(android.R.color.holo_blue_dark));
 *     ... </pre>
 *
 * <p>You can then increment the  progress with {@link #incrementProgressBy(int)} or
 * {@link #setProgress(int)}. By default, the progress bar is full when it reaches 100. If
 * necessary, you can adjust the maximum value (the value for a full bar) using the {@link
 * R.styleable#MulticolorProgressBar_mcp_max mcpb:max} attribute. Other attributes available are listed
 * below.</p>
 *
 *
 * <p><strong>XML attributes</b></strong>
 * <p>
 * See {@link R.styleable#MulticolorProgressBar MulticolorProgressBar Attributes},
 * {@link View View Attributes}
 * </p>
 *
 * @attr ref R.styleable#MulticolorProgressBar_mcp_max
 * @attr ref R.styleable#MulticolorProgressBar_mcp_maxHeight
 * @attr ref R.styleable#MulticolorProgressBar_mcp_maxWidth
 * @attr ref R.styleable#MulticolorProgressBar_mcp_minHeight
 * @attr ref R.styleable#MulticolorProgressBar_mcp_minWidth
 * @attr ref R.styleable#MulticolorProgressBar_mcp_progress
 * @attr ref R.styleable#MulticolorProgressBar_mcp_progressColor
 * @attr ref R.styleable#MulticolorProgressBar_mcp_straightProgressDrawable
 * @attr ref R.styleable#MulticolorProgressBar_mcp_secondaryProgress
 * @attr ref R.styleable#MulticolorProgressBar_mcp_secondaryProgressColor
 * @attr ref R.styleable#MulticolorProgressBar_mcp_reversedProgressDrawable
 */
public class MulticolorProgressBar extends View {
    private static final int MAX_LEVEL = 10000;
    private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>();
    int mMinWidth;
    int mMaxWidth;
    int mMinHeight;
    int mMaxHeight;
    int mProgressColor;
    int mSecondaryProgressColor;

    private int mProgress;
    private int mSecondaryProgress;
    private int mMax;
    private Drawable mProgressDrawable;
    private Drawable mCurrentDrawable;
    private Drawable mStraightDrawable;
    private Drawable mReversedDrawable;
    private boolean mNoInvalidate;
    private RefreshProgressRunnable mRefreshProgressRunnable;
    private long mUiThreadId;
    private boolean mAttached;
    private boolean mRefreshIsPosted;

    /**
     * Create a new progress bar with range 0...100 and initial progress of 0.
     *
     * @param context the application environment
     */
    public MulticolorProgressBar(Context context) {
        this(context, null);
    }

    public MulticolorProgressBar(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.multicolorProgressBarStyle);
    }

    public MulticolorProgressBar(Context context, AttributeSet attrs, int defStyle) {
        this(context, attrs, defStyle, R.style.MulticolorProgressBarStyle);
    }

    /**
     * @hide
     */
    public MulticolorProgressBar(Context context, AttributeSet attrs, int defStyle, int styleRes) {
        super(context, attrs, defStyle);
        mUiThreadId = Thread.currentThread().getId();
        initProgressBar();

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MulticolorProgressBar, defStyle, styleRes);

        mNoInvalidate = true;
        //straight (normal) order
        mStraightDrawable = a.getDrawable(R.styleable.MulticolorProgressBar_mcp_straightProgressDrawable);
        if (mStraightDrawable != null) {
            mStraightDrawable = tileify(mStraightDrawable, false);
            // Calling this method can set mMaxHeight, make sure the corresponding
            // XML attribute for mMaxHeight is read after calling this method
            setProgressDrawable(mStraightDrawable);
        }
        //save for later use
        mReversedDrawable = a.getDrawable(R.styleable.MulticolorProgressBar_mcp_reversedProgressDrawable);
        if (mReversedDrawable != null) {
            mReversedDrawable = tileify(mReversedDrawable, false);
        }

        mMinWidth = a.getDimensionPixelSize(R.styleable.MulticolorProgressBar_mcp_minWidth, mMinWidth);
        mMaxWidth = a.getDimensionPixelSize(R.styleable.MulticolorProgressBar_mcp_maxWidth, mMaxWidth);
        mMinHeight = a.getDimensionPixelSize(R.styleable.MulticolorProgressBar_mcp_minHeight, mMinHeight);
        mMaxHeight = a.getDimensionPixelSize(R.styleable.MulticolorProgressBar_mcp_maxHeight, mMaxHeight);

        // set colors and set drawable color filter
        mProgressColor = a.getColor(R.styleable.MulticolorProgressBar_mcp_progressColor, mProgressColor);
        mSecondaryProgressColor = a.getColor(R.styleable.MulticolorProgressBar_mcp_secondaryProgressColor,
                mSecondaryProgressColor);
        setDrawableColors(mProgressColor, mSecondaryProgressColor);
        //set max and progress
        setMax(a.getInt(R.styleable.MulticolorProgressBar_mcp_max, mMax));
        setProgress(a.getInt(R.styleable.MulticolorProgressBar_mcp_progress, mProgress));
        setSecondaryProgress(a.getInt(R.styleable.MulticolorProgressBar_mcp_secondaryProgress, mSecondaryProgress));
        mNoInvalidate = false;
        a.recycle();
    }

    /**
     * Set color for the two drawables
     *
     * @param firstColor
     * @param secondColor
     */
    private void setDrawableColors(int firstColor, int secondColor) {
        doUpdateProgressColor(android.R.id.progress, firstColor);
        doUpdateProgressColor(android.R.id.secondaryProgress, secondColor);
    }

    /**
     * Update the progress colors
     *
     * @param id
     * @param color
     */
    private void doUpdateProgressColor(int id, int color) {
        LayerDrawable layerDrawable = (LayerDrawable) mStraightDrawable;
        LayerDrawable reverseLayerDrawable = (LayerDrawable) mReversedDrawable;
        Drawable mOverlayProgressDrawable = layerDrawable.findDrawableByLayerId(id);
        mOverlayProgressDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
        mOverlayProgressDrawable = reverseLayerDrawable.findDrawableByLayerId(id);
        mOverlayProgressDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
    }

    /**
     * Converts a drawable to a tiled version of itself. It will recursively
     * traverse layer and state list drawables.
     */
    private Drawable tileify(Drawable drawable, boolean clip) {

        if (drawable instanceof LayerDrawable) {
            LayerDrawable background = (LayerDrawable) drawable;
            final int N = background.getNumberOfLayers();
            Drawable[] outDrawables = new Drawable[N];

            for (int i = 0; i < N; i++) {
                int id = background.getId(i);
                outDrawables[i] = tileify(background.getDrawable(i),
                        (id == android.R.id.progress || id == android.R.id.secondaryProgress));
            }

            LayerDrawable newBg = new LayerDrawable(outDrawables);
            for (int i = 0; i < N; i++) {
                newBg.setId(i, background.getId(i));
            }
            return newBg;
        }
        return drawable;
    }

    /**
     * <p>
     * Initialize the progress bar's default values:
     * </p>
     * <ul>
     * <li>progress = 0</li>
     * <li>max = 100</li>
     * <li>animation duration = 4000 ms</li>
     * <li>indeterminate = false</li>
     * <li>behavior = repeat</li>
     * </ul>
     */
    private void initProgressBar() {
        mMax = 100;
        mProgress = 0;
        mProgressColor = Color.GREEN;
        mSecondaryProgress = 0;
        mSecondaryProgressColor = Color.BLUE;
        mMinWidth = 24;
        mMaxWidth = 48;
        mMinHeight = 24;
        mMaxHeight = 48;
    }

    /**
     * <p>Get the drawable used to draw the progress bar in
     * progress mode.</p>
     *
     * @return a {@link android.graphics.drawable.Drawable} instance
     *
     * @see #setProgressDrawable(android.graphics.drawable.Drawable)
     */
    public Drawable getProgressDrawable() {
        return mProgressDrawable;
    }

    /**
     * <p>Define the drawable used to draw the progress bar in
     * progress mode.</p>
     *
     * @param d the new drawable
     *
     * @see #getProgressDrawable()
     */
    public void setProgressDrawable(Drawable d) {
        boolean needUpdate;
        if (mProgressDrawable != null && d != mProgressDrawable) {
            mProgressDrawable.setCallback(null);
            needUpdate = true;
        } else {
            needUpdate = false;
        }

        if (d != null) {
            d.setCallback(this);

            // Make sure the ProgressBar is always tall enough
            int drawableHeight = d.getMinimumHeight();
            if (mMaxHeight < drawableHeight) {
                mMaxHeight = drawableHeight;
                requestLayout();
            }
        }
        mProgressDrawable = d;
        mCurrentDrawable = d;
        postInvalidate();

        if (needUpdate) {
            updateDrawableBounds(getWidth(), getHeight());
            updateDrawableState();
            doRefreshProgress(android.R.id.progress, mProgress, false, false);
            doRefreshProgress(android.R.id.secondaryProgress, mSecondaryProgress, false, false);
        }
    }

    private synchronized void doRefreshProgress(int id, int progress, boolean fromUser, boolean callBackToApp) {
        float scale = mMax > 0 ? (float) progress / (float) mMax : 0;
        final Drawable d = mCurrentDrawable;
        if (d != null) {
            Drawable progressDrawable = null;
            if (d instanceof LayerDrawable) {
                progressDrawable = ((LayerDrawable) d).findDrawableByLayerId(id);
            }

            final int level = (int) (scale * MAX_LEVEL);
            (progressDrawable != null ? progressDrawable : d).setLevel(level);
        } else {
            invalidate();
        }
    }

    private synchronized void refreshProgress(int id, int progress, boolean fromUser) {
        if (mUiThreadId == Thread.currentThread().getId()) {
            doRefreshProgress(id, progress, fromUser, true);
        } else {
            if (mRefreshProgressRunnable == null) {
                mRefreshProgressRunnable = new RefreshProgressRunnable();
            }

            final RefreshData rd = RefreshData.obtain(id, progress, fromUser);
            mRefreshData.add(rd);
            if (mAttached && !mRefreshIsPosted) {
                post(mRefreshProgressRunnable);
                mRefreshIsPosted = true;
            }
        }
    }

    synchronized void setProgress(int progress, boolean fromUser) {
        if (progress < 0) {
            progress = 0;
        }

        if (progress > mMax) {
            progress = mMax;
        }
        //before updating progress, check if we need to switch the colors, by doing a double in memory check:
        boolean isOrderChanged = (isNormalColorOrder() ^ checkIsNormalColorOrder(progress, mSecondaryProgress));

        if (progress != mProgress) {
            mProgress = progress;
            if (isOrderChanged) {
                if (checkIsNormalColorOrder(mProgress, mSecondaryProgress)) {
                    setProgressDrawable(mStraightDrawable);
                } else {
                    // in this case the primary progress (the overlay) is greater than secondary, so we should switch to the inversed coloring and reverse the values
                    setProgressDrawable(mReversedDrawable);
                }
            } else {
                //if the progress order has changed, then change the progress drawable, otherwise refresh progress
                refreshProgress(android.R.id.progress, mProgress, fromUser);
            }
        }
    }

    /**
     * <p>Get the progress bar's current level of progress. Return 0 when the
     * progress bar is in indeterminate mode.</p>
     *
     * @return the current progress, between 0 and {@link #getMax()}
     *
     * @see #setProgress(int)
     * @see #setMax(int)
     * @see #getMax()
     */
    public synchronized int getProgress() {
        return mProgress;
    }

    /**
     * <p>Set the current progress to the specified value. Does not do anything
     * if the progress bar is in indeterminate mode.</p>
     *
     * @param progress the new progress, between 0 and {@link #getMax()}
     *
     * @see #getProgress()
     * @see #incrementProgressBy(int)
     */
    public synchronized void setProgress(int progress) {
        setProgress(progress, false);
    }

    /**
     * <p>Set the current progress color to the specified one.</p>
     *
     * @param progressColor the new color; must be a valid color.
     */
    public void setProgressColor(int progressColor) {
        if (progressColor != mProgressColor) {
            mProgressColor = progressColor;
            doUpdateProgressColor(android.R.id.progress, mProgressColor);
        }
    }

    /**
     * <p>Get the progress bar's current level of secondary progress. Return 0 when the
     * progress bar is in indeterminate mode.</p>
     *
     * @return the current secondary progress, between 0 and {@link #getMax()}
     *
     * @see #setSecondaryProgress(int)
     * @see #setMax(int)
     * @see #getMax()
     */
    public synchronized int getSecondaryProgress() {
        return mSecondaryProgress;
    }

    /**
     * <p>
     * Set the current secondary progress to the specified value. Does not do
     * anything if the progress bar is in indeterminate mode.
     * </p>
     *
     * @param secondaryProgress the new secondary progress, between 0 and {@link #getMax()}
     *
     * @see #getSecondaryProgress()
     * @see #incrementSecondaryProgressBy(int)
     */
    public synchronized void setSecondaryProgress(int secondaryProgress) {
        if (secondaryProgress < 0) {
            secondaryProgress = 0;
        }

        if (secondaryProgress > mMax) {
            secondaryProgress = mMax;
        }
        //before updating progress, check if we need to switch the colors, by doing a double in memory check:
        boolean isOrderChanged = (isNormalColorOrder() ^ checkIsNormalColorOrder(mProgress, secondaryProgress));

        if (secondaryProgress != mSecondaryProgress) {
            mSecondaryProgress = secondaryProgress;
            if (isOrderChanged) {
                if (checkIsNormalColorOrder(mProgress, mSecondaryProgress)) {
                    setProgressDrawable(mStraightDrawable);
                } else {
                    // in this case the primary progress (the overlay) is greater than secondary, so we should switch to the inversed coloring and reverse the values
                    setProgressDrawable(mReversedDrawable);
                }
            } else {
                refreshProgress(android.R.id.secondaryProgress, mSecondaryProgress, false);
            }
        }
    }

    /**
     * <p>Set the current secondary progress color to the specified one.</p>
     *
     * @param secondaryProgressColor
     */
    public void setSecondaryProgressColor(int secondaryProgressColor) {
        if (secondaryProgressColor != mSecondaryProgressColor) {
            mSecondaryProgressColor = secondaryProgressColor;
            doUpdateProgressColor(android.R.id.secondaryProgress, mSecondaryProgressColor);
        }
    }

    /**
     * <p>Return the upper limit of this progress bar's range.</p>
     *
     * @return a positive integer
     *
     * @see #setMax(int)
     * @see #getProgress()
     * @see #getSecondaryProgress()
     */
    public synchronized int getMax() {
        return mMax;
    }

    /**
     * <p>Set the range of the progress bar to 0...<tt>max</tt>.</p>
     *
     * @param max the upper range of this progress bar
     *
     * @see #getMax()
     * @see #setProgress(int)
     * @see #setSecondaryProgress(int)
     */
    public synchronized void setMax(int max) {
        if (max < 0) {
            max = 0;
        }
        if (max != mMax) {
            mMax = max;
            postInvalidate();

            if (mProgress > max) {
                mProgress = max;
            }
            refreshProgress(android.R.id.progress, mProgress, false);
            if (mSecondaryProgress > max) {
                mSecondaryProgress = max;
            }
            refreshProgress(android.R.id.secondaryProgress, mSecondaryProgress, false);
        }
    }

    /**
     * <p>Increase the progress bar's progress by the specified amount.</p>
     *
     * @param diff the amount by which the progress must be increased
     *
     * @see #setProgress(int)
     */
    public synchronized final void incrementProgressBy(int diff) {
        setProgress(mProgress + diff);
    }

    /**
     * <p>Increase the progress bar's secondary progress by the specified amount.</p>
     *
     * @param diff the amount by which the secondary progress must be increased
     *
     * @see #setSecondaryProgress(int)
     */
    public synchronized final void incrementSecondaryProgressBy(int diff) {
        setSecondaryProgress(mSecondaryProgress + diff);
    }

    private void updateDrawableBounds(int w, int h) {
        // onDraw will translate the canvas so we draw starting at 0,0.
        // Subtract out padding for the purposes of the calculations below.
        w -= getPaddingRight() + getPaddingLeft();
        h -= getPaddingTop() + getPaddingBottom();

        int right = w;
        int bottom = h;

        if (mProgressDrawable != null) {
            mProgressDrawable.setBounds(0, 0, right, bottom);
        }
    }

    private void updateDrawableState() {
        int[] state = getDrawableState();

        if (mProgressDrawable != null && mProgressDrawable.isStateful()) {
            mProgressDrawable.setState(state);
        }
    }

    @Override
    public void setVisibility(int v) {
        if (getVisibility() != v) {
            super.setVisibility(v);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        updateDrawableBounds(w, h);
    }

    @Override
    public void postInvalidate() {
        if (!mNoInvalidate) {
            super.postInvalidate();
        }
    }

    @Override
    protected synchronized void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Drawable d = mCurrentDrawable;
        if (d != null) {
            // Translate canvas so a indeterminate circular progress bar with padding
            // rotates properly in its animation
            canvas.save();
            canvas.translate(getPaddingLeft(), getPaddingTop());
            d.draw(canvas);
            canvas.restore();
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mRefreshData != null) {
            synchronized (this) {
                final int count = mRefreshData.size();
                for (int i = 0; i < count; i++) {
                    final RefreshData rd = mRefreshData.get(i);
                    doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
                    rd.recycle();
                }
                mRefreshData.clear();
            }
        }
        mAttached = true;
    }

    @Override
    protected void onDetachedFromWindow() {
        if (mRefreshProgressRunnable != null) {
            removeCallbacks(mRefreshProgressRunnable);
        }
        if (mRefreshProgressRunnable != null && mRefreshIsPosted) {
            removeCallbacks(mRefreshProgressRunnable);
        }
        // This should come after stopAnimation(), otherwise an invalidate message remains in the
        // queue, which can prevent the entire view hierarchy from being GC'ed during a rotation
        super.onDetachedFromWindow();
        mAttached = false;
    }

    @Override
    public Parcelable onSaveInstanceState() {
        // Force our ancestor class to save its state
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);

        ss.progress = mProgress;
        ss.secondaryProgress = mSecondaryProgress;
        ss.progressColor = mProgressColor;
        ss.secondaryProgressColor = mSecondaryProgressColor;
        return ss;
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());

        setProgress(ss.progress);
        setSecondaryProgress(ss.secondaryProgress);
        setProgressColor(ss.progressColor);
        setSecondaryProgressColor(ss.secondaryProgressColor);
    }

    @Override
    public void invalidateDrawable(Drawable dr) {
        super.invalidateDrawable(dr);
    }

    @Override
    protected boolean verifyDrawable(Drawable who) {
        return who == mProgressDrawable || super.verifyDrawable(who);
    }

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        updateDrawableState();
    }

    @Override
    public void jumpDrawablesToCurrentState() {
        super.jumpDrawablesToCurrentState();
        if (mProgressDrawable != null)
            mProgressDrawable.jumpToCurrentState();
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Drawable d = mCurrentDrawable;

        int dw = 0;
        int dh = 0;
        if (d != null) {
            dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth()));
            dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight()));
        }
        updateDrawableState();
        dw += getPaddingLeft() + getPaddingRight();
        dh += getPaddingTop() + getPaddingBottom();

        setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
                resolveSizeAndState(dh, heightMeasureSpec, 0));
    }

    /**
     * @param progress
     * @param secondaryProgress
     *
     * @return
     */
    private int compareNewProgress(int progress, int secondaryProgress) {
        return (progress - secondaryProgress);
    }

    /**
     * Returns whether if colors are shown as normal ordering or if they are drawn reversed.
     * It looks up at stored parameter, does not recalculate anything.
     */
    private boolean isNormalColorOrder() {
        return (compareNewProgress(mProgress, mSecondaryProgress) <= 0);
    }

    //Colored

    /**
     * This function calculates (in memory) the ordering of the two given parameter and returns the result as a boolean.
     *
     * @param progress
     * @param secondaryProgress
     *
     * @return
     */
    private boolean checkIsNormalColorOrder(int progress, int secondaryProgress) {
        return (compareNewProgress(progress, secondaryProgress) <= 0);
    }

    private static class RefreshData {
        private static final int POOL_MAX = 24;
        private static final Pools.SynchronizedPool<RefreshData> sPool = new Pools.SynchronizedPool<RefreshData>(
                POOL_MAX);

        public int id;
        public int progress;
        public boolean fromUser;

        public static RefreshData obtain(int id, int progress, boolean fromUser) {
            RefreshData rd = sPool.acquire();
            if (rd == null) {
                rd = new RefreshData();
            }
            rd.id = id;
            rd.progress = progress;
            rd.fromUser = fromUser;
            return rd;
        }

        public void recycle() {
            sPool.release(this);
        }
    }

    static class SavedState extends BaseSavedState {
        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
        int progress;
        int secondaryProgress;
        int progressColor;
        int secondaryProgressColor;

        /**
         * Constructor called from {@link ProgressBar#onSaveInstanceState()}
         */
        SavedState(Parcelable superState) {
            super(superState);
        }

        /**
         * Constructor called from {@link #CREATOR}
         */
        private SavedState(Parcel in) {
            super(in);
            progress = in.readInt();
            secondaryProgress = in.readInt();
            progressColor = in.readInt();
            secondaryProgressColor = in.readInt();
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(progress);
            out.writeInt(secondaryProgress);
            out.writeInt(progressColor);
            out.writeInt(secondaryProgressColor);
        }
    }

    private class RefreshProgressRunnable implements Runnable {
        public void run() {
            synchronized (MulticolorProgressBar.this) {
                final int count = mRefreshData.size();
                for (int i = 0; i < count; i++) {
                    final RefreshData rd = mRefreshData.get(i);
                    doRefreshProgress(rd.id, rd.progress, rd.fromUser, true);
                    rd.recycle();
                }
                mRefreshData.clear();
                mRefreshIsPosted = false;
            }
        }
    }
}