Bug 1366672 - part2: Add new custom widgets draft
authorJulian_Chu <walkingice0204@gmail.com>
Sat, 05 Aug 2017 13:53:44 +0800
changeset 651216 54aa1e6375eaa543bfea0feb683edc2d37bffabe
parent 651215 3099cca59120578acb895b3b7c65865a91722131
child 651217 ca32af3cfed39bb9ae826f8958ededd3f4003aba
push id75642
push userbmo:walkingice0204@gmail.com
push dateWed, 23 Aug 2017 11:27:33 +0000
bugs1366672
milestone57.0a1
Bug 1366672 - part2: Add new custom widgets * DrawableWrapper - a class similiar to DrawableWrapper in Android v23. * ShiftDrawable - provide shif-animation for its child drawable * AnimatedProgressBar - ProgressBar.setProgress(int, boolean) involved since API v24. This is an implementation for animation. MozReview-Commit-ID: HjLAXXQdZKO
mobile/android/app/src/main/res/values/attrs.xml
mobile/android/base/java/org/mozilla/gecko/drawable/DrawableWrapper.java
mobile/android/base/java/org/mozilla/gecko/drawable/ShiftDrawable.java
mobile/android/base/java/org/mozilla/gecko/widget/AnimatedProgressBar.java
mobile/android/base/moz.build
--- a/mobile/android/app/src/main/res/values/attrs.xml
+++ b/mobile/android/app/src/main/res/values/attrs.xml
@@ -176,10 +176,16 @@
         <attr name="drawableTintList" format="color" />
     </declare-styleable>
 
     <declare-styleable name="NavButton">
         <attr name="borderColor" format="color" />
         <attr name="borderColorPrivate" format="color" />
     </declare-styleable>
 
+    <declare-styleable name="AnimatedProgressBar">
+        <attr name="wrapShiftDrawable" format="boolean" />
+        <attr name="shiftDuration" format="reference" />
+        <attr name="shiftInterpolator" format="reference" />
+    </declare-styleable>
+
 </resources>
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/drawable/DrawableWrapper.java
@@ -0,0 +1,178 @@
+package org.mozilla.gecko.drawable;
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * DrawableWrapper was added since API Level 23. But in v7 support library, it has annotation
+ * "@RestrictTo(LIBRARY_GROUP)". Hence we should not extends it, so we create this wrapper for now.
+ * Once we start to support API 23, or v7-support-library allows us to extends its DrawableWrapper,
+ * then this file can be removed.
+ */
+
+public class DrawableWrapper extends Drawable {
+
+    private final Drawable mWrapped;
+
+    public DrawableWrapper(@NonNull Drawable drawable) {
+        mWrapped = drawable;
+    }
+
+    public Drawable getWrappedDrawable() {
+        return mWrapped;
+    }
+
+    @Override
+    public void draw(@NonNull Canvas canvas) {
+        mWrapped.draw(canvas);
+    }
+
+    @Override
+    public int getChangingConfigurations() {
+        return mWrapped.getChangingConfigurations();
+    }
+
+    @Override
+    public Drawable.ConstantState getConstantState() {
+        return mWrapped.getConstantState();
+    }
+
+    @Override
+    public Drawable getCurrent() {
+        return mWrapped.getCurrent();
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mWrapped.getIntrinsicHeight();
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mWrapped.getIntrinsicWidth();
+    }
+
+    @Override
+    public int getMinimumHeight() {
+        return mWrapped.getMinimumHeight();
+    }
+
+    @Override
+    public int getMinimumWidth() {
+        return mWrapped.getMinimumWidth();
+    }
+
+    @Override
+    public int getOpacity() {
+        return mWrapped.getOpacity();
+    }
+
+    @Override
+    public boolean getPadding(Rect padding) {
+        return mWrapped.getPadding(padding);
+    }
+
+    @Override
+    public int[] getState() {
+        return mWrapped.getState();
+    }
+
+    @Override
+    public Region getTransparentRegion() {
+        return mWrapped.getTransparentRegion();
+    }
+
+    @Override
+    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+            throws XmlPullParserException, IOException {
+        mWrapped.inflate(r, parser, attrs);
+    }
+
+    @Override
+    public boolean isStateful() {
+        return mWrapped.isStateful();
+    }
+
+    @Override
+    public void jumpToCurrentState() {
+        mWrapped.jumpToCurrentState();
+    }
+
+    @Override
+    public Drawable mutate() {
+        return mWrapped.mutate();
+    }
+
+    @Override
+    public void setAlpha(@IntRange(from = 0, to = 255) int i) {
+        mWrapped.setAlpha(i);
+    }
+
+    @Override
+    public void scheduleSelf(Runnable what, long when) {
+        mWrapped.scheduleSelf(what, when);
+    }
+
+    @Override
+    public void setChangingConfigurations(int configs) {
+        mWrapped.setChangingConfigurations(configs);
+    }
+
+    @Override
+    public void setColorFilter(@Nullable ColorFilter colorFilter) {
+        mWrapped.setColorFilter(colorFilter);
+    }
+
+    @Override
+    public void setColorFilter(int color, PorterDuff.Mode mode) {
+        mWrapped.setColorFilter(color, mode);
+    }
+
+    @Override
+    public void setFilterBitmap(boolean filter) {
+        mWrapped.setFilterBitmap(filter);
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        return mWrapped.setVisible(visible, restart);
+    }
+
+    @Override
+    public void unscheduleSelf(Runnable what) {
+        mWrapped.unscheduleSelf(what);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        mWrapped.setBounds(bounds);
+    }
+
+    @Override
+    protected boolean onLevelChange(int level) {
+        return mWrapped.setLevel(level);
+    }
+
+    @Override
+    protected boolean onStateChange(int[] state) {
+        return mWrapped.setState(state);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/drawable/ShiftDrawable.java
@@ -0,0 +1,156 @@
+package org.mozilla.gecko.drawable;
+
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+/**
+ * A drawable to keep shifting its wrapped drawable.
+ * Assume the wrapped drawable value is "00000010", this class will keep drawing in this way
+ * <p>
+ * 00000010 -> 00000001 -> 10000000 -> 01000000 -> 00100000 -> ...
+ * <p>
+ * This drawable will keep drawing until be invisible.
+ */
+public class ShiftDrawable extends DrawableWrapper {
+
+    /**
+     * An animator to trigger redraw and update offset-of-shifting
+     */
+    private final ValueAnimator mAnimator = ValueAnimator.ofFloat(0f, 1f);
+
+    /**
+     * Visible rectangle, wrapped-drawable is resized and draw in this rectangle
+     */
+    private final Rect mVisibleRect = new Rect();
+
+    /**
+     * Canvas will clip itself by this Path. Used to draw rounded head.
+     */
+    private final Path mPath = new Path();
+
+    // align to ScaleDrawable implementation
+    private static final int MAX_LEVEL = 10000;
+
+    private static final int DEFAULT_DURATION = 1000;
+
+    public ShiftDrawable(@NonNull Drawable d) {
+        this(d, DEFAULT_DURATION);
+    }
+
+    public ShiftDrawable(@NonNull Drawable d, int duration) {
+        this(d, duration, new LinearInterpolator());
+    }
+
+    public ShiftDrawable(@NonNull Drawable d, int duration, @Nullable Interpolator interpolator) {
+        super(d);
+        mAnimator.setDuration(duration);
+        mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+        mAnimator.setInterpolator((interpolator == null) ? new LinearInterpolator() : interpolator);
+        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                if (isVisible()) {
+                    invalidateSelf();
+                }
+            }
+        });
+        mAnimator.start();
+    }
+
+    public Animator getAnimator() {
+        return mAnimator;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * override to enable / disable animator as well.
+     */
+    @Override
+    public boolean setVisible(final boolean visible, final boolean restart) {
+        final boolean result = super.setVisible(visible, restart);
+        if (isVisible()) {
+            mAnimator.start();
+        } else {
+            mAnimator.end();
+        }
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+        updateBounds();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected boolean onLevelChange(int level) {
+        final boolean result = super.onLevelChange(level);
+        updateBounds();
+        return result;
+    }
+
+    @Override
+    public void draw(@NonNull Canvas canvas) {
+        final Drawable wrapped = getWrappedDrawable();
+        final float fraction = mAnimator.getAnimatedFraction();
+        final int width = mVisibleRect.width();
+        final int offset = (int) (width * fraction);
+
+        final int stack = canvas.save();
+
+        // To apply mPath, then we have rounded-head
+        canvas.clipPath(mPath);
+
+        // To draw left-half part of Drawable, shift from right to left
+        canvas.save();
+        canvas.translate(-offset, 0);
+        wrapped.draw(canvas);
+        canvas.restore();
+
+        // Then to draw right-half part of Drawable
+        canvas.save();
+        canvas.translate(width - offset, 0);
+        wrapped.draw(canvas);
+        canvas.restore();
+
+        canvas.restoreToCount(stack);
+    }
+
+    private void updateBounds() {
+        final Rect b = getBounds();
+        final int width = (int) ((float) b.width() * getLevel() / MAX_LEVEL);
+        mVisibleRect.set(b.left, b.top, b.left + width, b.height());
+
+        // to create path to help drawing rounded head. mPath is enclosed by mVisibleRect
+        final float radius = b.height() / 2;
+        mPath.reset();
+
+        // The added rectangle width is smaller than mVisibleRect, due to semi-circular.
+        mPath.addRect(mVisibleRect.left,
+                mVisibleRect.top, mVisibleRect.right - radius,
+                mVisibleRect.height(),
+                Path.Direction.CCW);
+        // To add semi-circular
+        mPath.addCircle(mVisibleRect.right - radius, radius, radius, Path.Direction.CCW);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/widget/AnimatedProgressBar.java
@@ -0,0 +1,341 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.widget;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Handler;
+import android.support.annotation.InterpolatorRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+import org.mozilla.gecko.DynamicToolbar;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.drawable.ShiftDrawable;
+import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
+import org.mozilla.gecko.widget.themed.ThemedProgressBar;
+
+/**
+ * A progressbar with some animations on changing progress.
+ * When changing progress of this bar, it does not change value directly. Instead, it use
+ * {@link Animator} to change value progressively. Moreover, change visibility to View.GONE will
+ * cause closing animation.
+ */
+public class AnimatedProgressBar extends ThemedProgressBar {
+
+    /**
+     * Animation duration of progress changing.
+     */
+    private final static int PROGRESS_DURATION = 200;
+
+    /**
+     * Delay before applying closing animation when progress reach max value.
+     */
+    private final static int CLOSING_DELAY = 300;
+
+    /**
+     * Animation duration for closing
+     */
+    private final static int CLOSING_DURATION = 300;
+
+    private ValueAnimator mPrimaryAnimator;
+    private final ValueAnimator mClosingAnimator = ValueAnimator.ofFloat(0f, 1f);
+
+    /**
+     * For closing animation. To indicate how many visible region should be clipped.
+     */
+    private float mClipRatio = 0f;
+    private final Rect mRect = new Rect();
+
+    /**
+     * To store the final expected progress to reach, it does matter in animation.
+     */
+    private int mExpectedProgress = 0;
+
+    /**
+     * setProgress() might be invoked in constructor. Add to flag to avoid null checking for animators.
+     */
+    private boolean mInitialized = false;
+
+    private boolean mIsRtl = false;
+
+    private DynamicToolbar mDynamicToolbar;
+    private EndingRunner mEndingRunner = new EndingRunner();
+
+    private final ValueAnimator.AnimatorUpdateListener mListener =
+            new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    setProgressImmediately((int) mPrimaryAnimator.getAnimatedValue());
+                }
+            };
+
+    public AnimatedProgressBar(@NonNull Context context) {
+        super(context, null);
+        init(context, null);
+    }
+
+    public AnimatedProgressBar(@NonNull Context context,
+                               @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        init(context, attrs);
+    }
+
+    public AnimatedProgressBar(@NonNull Context context,
+                               @Nullable AttributeSet attrs,
+                               int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context, attrs);
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public AnimatedProgressBar(Context context,
+                               AttributeSet attrs,
+                               int defStyleAttr,
+                               int defStyleRes) {
+        super(context, attrs, defStyleAttr);
+        init(context, attrs);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized void setMax(int max) {
+        super.setMax(max);
+        mPrimaryAnimator = createAnimator(getMax(), mListener);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Instead of set progress directly, this method triggers an animator to change progress.
+     */
+    @Override
+    public void setProgress(int nextProgress) {
+        nextProgress = Math.min(nextProgress, getMax());
+        nextProgress = Math.max(0, nextProgress);
+        mExpectedProgress = nextProgress;
+        if (!mInitialized) {
+            setProgressImmediately(mExpectedProgress);
+            return;
+        }
+
+        // if regress, jump to the expected value without any animation
+        if (mExpectedProgress < getProgress()) {
+            cancelAnimations();
+            setProgressImmediately(mExpectedProgress);
+            return;
+        }
+
+        // Animation is not needed for reloading a completed page
+        if ((mExpectedProgress == 0) && (getProgress() == getMax())) {
+            cancelAnimations();
+            setProgressImmediately(0);
+            return;
+        }
+
+        cancelAnimations();
+        mPrimaryAnimator.setIntValues(getProgress(), nextProgress);
+        mPrimaryAnimator.start();
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        if (mClipRatio == 0) {
+            super.onDraw(canvas);
+        } else {
+            canvas.getClipBounds(mRect);
+            final float clipWidth = mRect.width() * mClipRatio;
+            canvas.save();
+            if (mIsRtl) {
+                canvas.clipRect(mRect.left, mRect.top, mRect.right - clipWidth, mRect.bottom);
+            } else {
+                canvas.clipRect(mRect.left + clipWidth, mRect.top, mRect.right, mRect.bottom);
+            }
+            super.onDraw(canvas);
+            canvas.restore();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Instead of change visibility directly, this method also applies the closing animation if
+     * progress reaches max value.
+     */
+    @Override
+    public void setVisibility(int value) {
+        // nothing changed
+        if (getVisibility() == value) {
+            return;
+        }
+
+        if (value == GONE) {
+            if (mExpectedProgress == getMax()) {
+                setProgressImmediately(mExpectedProgress);
+                animateClosing();
+            } else {
+                setVisibilityImmediately(value);
+            }
+        } else {
+            final Handler handler = getHandler();
+            // if this view is detached from window, the handler would be null
+            if (handler != null) {
+                handler.removeCallbacks(mEndingRunner);
+            }
+
+            if (mClosingAnimator != null) {
+                mClipRatio = 0;
+                mClosingAnimator.cancel();
+            }
+            setVisibilityImmediately(value);
+        }
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mIsRtl = (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL);
+    }
+
+    public void setDynamicToolbar(@Nullable DynamicToolbar toolbar) {
+        mDynamicToolbar = toolbar;
+    }
+
+    public void pinDynamicToolbar() {
+        if (mDynamicToolbar == null) {
+            return;
+        }
+        if (mDynamicToolbar.isEnabled()) {
+            mDynamicToolbar.setPinned(true, DynamicToolbarAnimator.PinReason.PAGE_LOADING);
+            mDynamicToolbar.setVisible(true, DynamicToolbar.VisibilityTransition.ANIMATE);
+        }
+    }
+
+    public void unpinDynamicToolbar() {
+        if (mDynamicToolbar == null) {
+            return;
+        }
+        if (mDynamicToolbar.isEnabled()) {
+            mDynamicToolbar.setPinned(false, DynamicToolbarAnimator.PinReason.PAGE_LOADING);
+        }
+    }
+
+    private void cancelAnimations() {
+        if (mPrimaryAnimator != null) {
+            mPrimaryAnimator.cancel();
+        }
+        if (mClosingAnimator != null) {
+            mClosingAnimator.cancel();
+        }
+
+        mClipRatio = 0;
+    }
+
+    private void init(@NonNull Context context, @Nullable AttributeSet attrs) {
+        mInitialized = true;
+
+        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AnimatedProgressBar);
+        final int duration = a.getInteger(R.styleable.AnimatedProgressBar_shiftDuration, 1000);
+        final boolean wrap = a.getBoolean(R.styleable.AnimatedProgressBar_wrapShiftDrawable, false);
+        @InterpolatorRes final int itplId = a.getResourceId(R.styleable.AnimatedProgressBar_shiftInterpolator, 0);
+        a.recycle();
+
+        setProgressDrawable(buildDrawable(getProgressDrawable(), wrap, duration, itplId));
+
+        mPrimaryAnimator = createAnimator(getMax(), mListener);
+
+        mClosingAnimator.setDuration(CLOSING_DURATION);
+        mClosingAnimator.setInterpolator(new LinearInterpolator());
+        mClosingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                mClipRatio = (float) valueAnimator.getAnimatedValue();
+                invalidate();
+            }
+        });
+        mClosingAnimator.addListener(new Animator.AnimatorListener() {
+            @Override
+            public void onAnimationStart(Animator animator) {
+                mClipRatio = 0f;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                setVisibilityImmediately(GONE);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animator) {
+                mClipRatio = 0f;
+            }
+
+            @Override
+            public void onAnimationRepeat(Animator animator) {
+            }
+        });
+    }
+
+    private void setVisibilityImmediately(int value) {
+        super.setVisibility(value);
+    }
+
+    private void animateClosing() {
+        mClosingAnimator.cancel();
+        final Handler handler = getHandler();
+        // if this view is detached from window, the handler would be null
+        if (handler != null) {
+            handler.removeCallbacks(mEndingRunner);
+            handler.postDelayed(mEndingRunner, CLOSING_DELAY);
+        }
+    }
+
+    private void setProgressImmediately(int progress) {
+        super.setProgress(progress);
+    }
+
+    private Drawable buildDrawable(@NonNull Drawable original,
+                                   boolean isWrap,
+                                   int duration,
+                                   @InterpolatorRes int itplId) {
+        if (isWrap) {
+            final Interpolator interpolator = (itplId > 0)
+                    ? AnimationUtils.loadInterpolator(getContext(), itplId)
+                    : null;
+            return new ShiftDrawable(original, duration, interpolator);
+        } else {
+            return original;
+        }
+    }
+
+    private static ValueAnimator createAnimator(int max, ValueAnimator.AnimatorUpdateListener listener) {
+        ValueAnimator animator = ValueAnimator.ofInt(0, max);
+        animator.setInterpolator(new LinearInterpolator());
+        animator.setDuration(PROGRESS_DURATION);
+        animator.addUpdateListener(listener);
+        return animator;
+    }
+
+    private class EndingRunner implements Runnable {
+        @Override
+        public void run() {
+            mClosingAnimator.start();
+        }
+    }
+}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -613,16 +613,18 @@ gbjar.sources += ['java/org/mozilla/geck
     'dlc/DownloadAction.java',
     'dlc/DownloadContentService.java',
     'dlc/DownloadContentTelemetry.java',
     'dlc/StudyAction.java',
     'dlc/SyncAction.java',
     'dlc/VerifyAction.java',
     'DoorHangerPopup.java',
     'DownloadsIntegration.java',
+    'drawable/DrawableWrapper.java',
+    'drawable/ShiftDrawable.java',
     'DynamicToolbar.java',
     'EditBookmarkDialog.java',
     'Experiments.java',
     'feeds/action/CheckForUpdatesAction.java',
     'feeds/action/EnrollSubscriptionsAction.java',
     'feeds/action/FeedAction.java',
     'feeds/action/SetupAlarmsAction.java',
     'feeds/action/SubscribeToFeedAction.java',
@@ -957,16 +959,17 @@ gbjar.sources += ['java/org/mozilla/geck
     'webapps/WebAppActivity.java',
     'webapps/WebAppIndexer.java',
     'webapps/WebApps.java',
     'widget/ActionModePresenter.java',
     'widget/ActivityChooserModel.java',
     'widget/AllCapsTextView.java',
     'widget/AnchoredPopup.java',
     'widget/AnimatedHeightLayout.java',
+    'widget/AnimatedProgressBar.java',
     'widget/BasicColorPicker.java',
     'widget/CheckableLinearLayout.java',
     'widget/ClickableWhenDisabledEditText.java',
     'widget/ContentSecurityDoorHanger.java',
     'widget/CropImageView.java',
     'widget/DateTimePicker.java',
     'widget/DefaultDoorHanger.java',
     'widget/DefaultItemAnimatorBase.java',