michael@0: /* michael@0: * Copyright (C) 2010 The Android Open Source Project michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: package org.mozilla.gecko.toolbar; michael@0: michael@0: import android.content.Context; michael@0: import android.graphics.Canvas; michael@0: import android.graphics.Rect; michael@0: import android.graphics.drawable.Drawable; michael@0: import android.os.Build; michael@0: import android.os.Handler; michael@0: import android.os.Message; michael@0: import android.util.AttributeSet; michael@0: import android.widget.ImageView; michael@0: import android.view.View; michael@0: import android.view.animation.Animation; michael@0: michael@0: /** michael@0: * Progress view used for page loads. michael@0: * michael@0: * Because we're given limited information about the page load progress, the michael@0: * bar also includes incremental animation between each step to improve michael@0: * perceived performance. michael@0: */ michael@0: public class ToolbarProgressView extends ImageView { michael@0: private static final int MAX_PROGRESS = 10000; michael@0: private static final int MSG_UPDATE = 0; michael@0: private static final int MSG_HIDE = 1; michael@0: private static final int STEPS = 10; michael@0: private static final int DELAY = 40; michael@0: michael@0: private static final boolean PRE_HONEYCOMB = Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB; michael@0: michael@0: private int mTargetProgress; michael@0: private int mIncrement; michael@0: private Rect mBounds; michael@0: private Handler mHandler; michael@0: private int mCurrentProgress; michael@0: michael@0: public ToolbarProgressView(Context context, AttributeSet attrs, int defStyle) { michael@0: super(context, attrs, defStyle); michael@0: init(context); michael@0: } michael@0: michael@0: public ToolbarProgressView(Context context, AttributeSet attrs) { michael@0: super(context, attrs); michael@0: init(context); michael@0: } michael@0: michael@0: public ToolbarProgressView(Context context) { michael@0: super(context); michael@0: init(context); michael@0: } michael@0: michael@0: private void init(Context ctx) { michael@0: mBounds = new Rect(0,0,0,0); michael@0: mTargetProgress = 0; michael@0: michael@0: mHandler = new Handler() { michael@0: @Override michael@0: public void handleMessage(Message msg) { michael@0: switch (msg.what) { michael@0: case MSG_UPDATE: michael@0: mCurrentProgress = Math.min(mTargetProgress, mCurrentProgress + mIncrement); michael@0: michael@0: updateBounds(); michael@0: michael@0: if (mCurrentProgress < mTargetProgress) { michael@0: final int delay = (mTargetProgress < MAX_PROGRESS) ? DELAY : DELAY / 4; michael@0: sendMessageDelayed(mHandler.obtainMessage(msg.what), delay); michael@0: } else if (mCurrentProgress == MAX_PROGRESS) { michael@0: sendMessageDelayed(mHandler.obtainMessage(MSG_HIDE), DELAY); michael@0: } michael@0: break; michael@0: michael@0: case MSG_HIDE: michael@0: setVisibility(View.GONE); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: }; michael@0: } michael@0: michael@0: @Override michael@0: public void setVisibility(int visibility) { michael@0: // On GB/Froyo, setting the visibility to GONE/HIDDEN alone does not michael@0: // work with translations. Calling clearAnimation acts as a workaround. michael@0: if (PRE_HONEYCOMB && visibility != VISIBLE) { michael@0: clearAnimation(); michael@0: } michael@0: michael@0: super.setVisibility(visibility); michael@0: } michael@0: michael@0: @Override michael@0: public void setAnimation(Animation animation) { michael@0: // On GB/Froyo, setting the animation after hiding the view causes it michael@0: // to reappear. As a workaround, disallow setAnimation from being michael@0: // called if the view is not shown. michael@0: if (PRE_HONEYCOMB && isShown()) { michael@0: super.setAnimation(animation); michael@0: } michael@0: } michael@0: michael@0: @Override michael@0: public void onLayout(boolean f, int l, int t, int r, int b) { michael@0: mBounds.left = 0; michael@0: mBounds.right = (r - l) * mCurrentProgress / MAX_PROGRESS; michael@0: mBounds.top = 0; michael@0: mBounds.bottom = b - t; michael@0: } michael@0: michael@0: @Override michael@0: public void onDraw(Canvas canvas) { michael@0: final Drawable d = getDrawable(); michael@0: d.setBounds(mBounds); michael@0: d.draw(canvas); michael@0: } michael@0: michael@0: /** michael@0: * Immediately sets the progress bar to the given progress percentage. michael@0: * michael@0: * @param progress Percentage (0-100) to which progress bar should be set michael@0: */ michael@0: void setProgress(int progressPercentage) { michael@0: mCurrentProgress = mTargetProgress = getAbsoluteProgress(progressPercentage); michael@0: updateBounds(); michael@0: michael@0: clearMessages(); michael@0: } michael@0: michael@0: /** michael@0: * Animates the progress bar from the current progress value to the given michael@0: * progress percentage. michael@0: * michael@0: * @param progress Percentage (0-100) to which progress bar should be animated michael@0: */ michael@0: void animateProgress(int progressPercentage) { michael@0: final int absoluteProgress = getAbsoluteProgress(progressPercentage); michael@0: if (absoluteProgress <= mTargetProgress) { michael@0: // After we manually click stop, we can still receive page load michael@0: // events (e.g., DOMContentLoaded). Updating for other updates michael@0: // after a STOP event can freeze the progress bar, so guard against michael@0: // that here. michael@0: return; michael@0: } michael@0: michael@0: mTargetProgress = absoluteProgress; michael@0: mIncrement = (mTargetProgress - mCurrentProgress) / STEPS; michael@0: michael@0: clearMessages(); michael@0: mHandler.sendEmptyMessage(MSG_UPDATE); michael@0: } michael@0: michael@0: private void clearMessages() { michael@0: mHandler.removeMessages(MSG_UPDATE); michael@0: mHandler.removeMessages(MSG_HIDE); michael@0: } michael@0: michael@0: private int getAbsoluteProgress(int progressPercentage) { michael@0: if (progressPercentage < 0) { michael@0: return 0; michael@0: } michael@0: michael@0: if (progressPercentage > 100) { michael@0: return 100; michael@0: } michael@0: michael@0: return progressPercentage * MAX_PROGRESS / 100; michael@0: } michael@0: michael@0: private void updateBounds() { michael@0: mBounds.right = getWidth() * mCurrentProgress / MAX_PROGRESS; michael@0: invalidate(); michael@0: } michael@0: }