1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.mozilla.gecko.toolbar;
18
19 import android.support.v4.content.ContextCompat;
20 import org.mozilla.gecko.AppConstants.Versions;
21 import org.mozilla.gecko.R;
ResolveEndpoint(region string, options EndpointResolverOptions)22 import org.mozilla.gecko.widget.themed.ThemedImageView;
23 import org.mozilla.gecko.util.WeakReferenceHandler;
24
25 import android.content.Context;
26 import android.graphics.Canvas;
27 import android.graphics.PorterDuff;
28 import android.graphics.PorterDuffColorFilter;
29 import android.graphics.Rect;
30 import android.graphics.drawable.Drawable;
31 import android.os.Handler;
32 import android.os.Message;
33 import android.util.AttributeSet;
34 import android.view.View;
35 import android.view.animation.Animation;
36
37 /**
38 * Progress view used for page loads.
39 *
40 * Because we're given limited information about the page load progress, the
41 * bar also includes incremental animation between each step to improve
42 * perceived performance.
43 */
44 public class ToolbarProgressView extends ThemedImageView {
45 private static final int MAX_PROGRESS = 10000;
46 private static final int MSG_UPDATE = 0;
47 private static final int MSG_HIDE = 1;
48 private static final int STEPS = 10;
49 private static final int DELAY = 40;
50
51 private int mTargetProgress;
52 private int mIncrement;
53 private Rect mBounds;
54 private Handler mHandler;
55 private int mCurrentProgress;
56
57 private PorterDuffColorFilter mPrivateBrowsingColorFilter;
58
59 public ToolbarProgressView(Context context, AttributeSet attrs, int defStyle) {
60 super(context, attrs, defStyle);
61 init(context);
62 }
63
64 public ToolbarProgressView(Context context, AttributeSet attrs) {
65 super(context, attrs);
66 init(context);
67 }
68
69 private void init(Context ctx) {
70 mBounds = new Rect(0, 0, 0, 0);
71 mTargetProgress = 0;
72
73 mPrivateBrowsingColorFilter = new PorterDuffColorFilter(
74 ContextCompat.getColor(ctx, R.color.private_browsing_purple), PorterDuff.Mode.SRC_IN);
75
76 mHandler = new ToolbarProgressHandler(this);
77 }
78
79 @Override
80 public void onLayout(boolean f, int l, int t, int r, int b) {
81 mBounds.left = 0;
82 mBounds.right = (r - l) * mCurrentProgress / MAX_PROGRESS;
83 mBounds.top = 0;
84 mBounds.bottom = b - t;
85 }
86
87 @Override
88 public void onDraw(Canvas canvas) {
89 final Drawable d = getDrawable();
90 d.setBounds(mBounds);
91 d.draw(canvas);
92 }
93
94 /**
95 * Immediately sets the progress bar to the given progress percentage.
96 *
97 * @param progress Percentage (0-100) to which progress bar should be set
98 */
99 void setProgress(int progressPercentage) {
100 mCurrentProgress = mTargetProgress = getAbsoluteProgress(progressPercentage);
101 updateBounds();
102
103 clearMessages();
104 }
105
106 /**
107 * Animates the progress bar from the current progress value to the given
108 * progress percentage.
109 *
110 * @param progress Percentage (0-100) to which progress bar should be animated
111 */
112 void animateProgress(int progressPercentage) {
113 final int absoluteProgress = getAbsoluteProgress(progressPercentage);
114 if (absoluteProgress <= mTargetProgress) {
115 // After we manually click stop, we can still receive page load
116 // events (e.g., DOMContentLoaded). Updating for other updates
117 // after a STOP event can freeze the progress bar, so guard against
118 // that here.
119 return;
120 }
121
122 mTargetProgress = absoluteProgress;
123 mIncrement = (mTargetProgress - mCurrentProgress) / STEPS;
124
125 clearMessages();
126 mHandler.sendEmptyMessage(MSG_UPDATE);
127 }
128
129 private void clearMessages() {
130 mHandler.removeMessages(MSG_UPDATE);
131 mHandler.removeMessages(MSG_HIDE);
132 }
133
134 private int getAbsoluteProgress(int progressPercentage) {
135 if (progressPercentage < 0) {
136 return 0;
137 }
138
139 if (progressPercentage > 100) {
140 return 100;
141 }
142
143 return progressPercentage * MAX_PROGRESS / 100;
144 }
145
146 private void updateBounds() {
147 mBounds.right = getWidth() * mCurrentProgress / MAX_PROGRESS;
148 invalidate();
149 }
150
151 @Override
152 public void setPrivateMode(final boolean isPrivate) {
153 super.setPrivateMode(isPrivate);
154
155 // Note: android:tint is better but ColorStateLists are not supported until API 21.
156 if (isPrivate) {
157 setColorFilter(mPrivateBrowsingColorFilter);
158 } else {
159 clearColorFilter();
160 }
161 }
162
163 private static class ToolbarProgressHandler extends WeakReferenceHandler<ToolbarProgressView> {
164 public ToolbarProgressHandler(final ToolbarProgressView that) {
165 super(that);
166 }
167
168 @Override
169 public void handleMessage(Message msg) {
170 final ToolbarProgressView that = mTarget.get();
171 if (that == null) {
172 return;
173 }
174
175 switch (msg.what) {
176 case MSG_UPDATE:
177 that.mCurrentProgress = Math.min(that.mTargetProgress, that.mCurrentProgress + that.mIncrement);
178
179 that.updateBounds();
180
181 if (that.mCurrentProgress < that.mTargetProgress) {
182 final int delay = (that.mTargetProgress < MAX_PROGRESS) ? DELAY : DELAY / 4;
183 sendMessageDelayed(that.mHandler.obtainMessage(msg.what), delay);
184 } else if (that.mCurrentProgress == MAX_PROGRESS) {
185 sendMessageDelayed(that.mHandler.obtainMessage(MSG_HIDE), DELAY);
186 }
187 break;
188
189 case MSG_HIDE:
190 that.setVisibility(View.GONE);
191 break;
192 }
193 }
194 };
195 }
196