blob: 1f5bfd96b1503801bc122270f91893f84005a28f [file] [log] [blame]
Chih-Chung Chang6645e872011-10-14 16:09:09 +08001/*
2 * Copyright (C) 2011 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
17package com.android.gallery3d.app;
18
Chih-Chung Chang6645e872011-10-14 16:09:09 +080019import android.content.Context;
20import android.graphics.Bitmap;
21import android.graphics.BitmapFactory;
22import android.graphics.Canvas;
23import android.graphics.Paint;
24import android.graphics.Rect;
25import android.util.DisplayMetrics;
26import android.view.MotionEvent;
27import android.view.View;
28
Owen Lin73a04ff2012-03-14 17:27:24 +080029import com.android.gallery3d.R;
30import com.android.gallery3d.common.Utils;
31
Chih-Chung Chang6645e872011-10-14 16:09:09 +080032/**
33 * The time bar view, which includes the current and total time, the progress bar,
34 * and the scrubber.
35 */
36public class TimeBar extends View {
37
38 public interface Listener {
39 void onScrubbingStart();
40 void onScrubbingMove(int time);
41 void onScrubbingEnd(int time);
42 }
43
44 // Padding around the scrubber to increase its touch target
45 private static final int SCRUBBER_PADDING_IN_DP = 10;
46
47 // The total padding, top plus bottom
48 private static final int V_PADDING_IN_DP = 30;
49
50 private static final int TEXT_SIZE_IN_DP = 14;
51
52 private final Listener listener;
53
54 // the bars we use for displaying the progress
55 private final Rect progressBar;
56 private final Rect playedBar;
57
58 private final Paint progressPaint;
59 private final Paint playedPaint;
60 private final Paint timeTextPaint;
61
62 private final Bitmap scrubber;
63 private final int scrubberPadding; // adds some touch tolerance around the scrubber
64
65 private int scrubberLeft;
66 private int scrubberTop;
67 private int scrubberCorrection;
68 private boolean scrubbing;
69 private boolean showTimes;
70 private boolean showScrubber;
71
72 private int totalTime;
73 private int currentTime;
74
75 private final Rect timeBounds;
76
77 private int vPaddingInPx;
78
79 public TimeBar(Context context, Listener listener) {
80 super(context);
81 this.listener = Utils.checkNotNull(listener);
82
83 showTimes = true;
84 showScrubber = true;
85
86 progressBar = new Rect();
87 playedBar = new Rect();
88
89 progressPaint = new Paint();
90 progressPaint.setColor(0xFF808080);
91 playedPaint = new Paint();
92 playedPaint.setColor(0xFFFFFFFF);
93
94 DisplayMetrics metrics = context.getResources().getDisplayMetrics();
95 float textSizeInPx = metrics.density * TEXT_SIZE_IN_DP;
96 timeTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
97 timeTextPaint.setColor(0xFFCECECE);
98 timeTextPaint.setTextSize(textSizeInPx);
99 timeTextPaint.setTextAlign(Paint.Align.CENTER);
100
101 timeBounds = new Rect();
102 timeTextPaint.getTextBounds("0:00:00", 0, 7, timeBounds);
103
104 scrubber = BitmapFactory.decodeResource(getResources(), R.drawable.scrubber_knob);
105 scrubberPadding = (int) (metrics.density * SCRUBBER_PADDING_IN_DP);
106
107 vPaddingInPx = (int) (metrics.density * V_PADDING_IN_DP);
108 }
109
110 private void update() {
111 playedBar.set(progressBar);
112
113 if (totalTime > 0) {
114 playedBar.right =
115 playedBar.left + (int) ((progressBar.width() * (long) currentTime) / totalTime);
116 } else {
117 playedBar.right = progressBar.left;
118 }
119
120 if (!scrubbing) {
121 scrubberLeft = playedBar.right - scrubber.getWidth() / 2;
122 }
123 invalidate();
124 }
125
126 /**
127 * @return the preferred height of this view, including invisible padding
128 */
129 public int getPreferredHeight() {
130 return timeBounds.height() + vPaddingInPx + scrubberPadding;
131 }
132
133 /**
134 * @return the height of the time bar, excluding invisible padding
135 */
136 public int getBarHeight() {
137 return timeBounds.height() + vPaddingInPx;
138 }
139
140 public void setTime(int currentTime, int totalTime) {
141 if (this.currentTime == currentTime && this.totalTime == totalTime) {
142 return;
143 }
144 this.currentTime = currentTime;
145 this.totalTime = totalTime;
146 update();
147 }
148
149 public void setShowTimes(boolean showTimes) {
150 this.showTimes = showTimes;
151 requestLayout();
152 }
153
154 public void resetTime() {
155 setTime(0, 0);
156 }
157
158 public void setShowScrubber(boolean showScrubber) {
159 this.showScrubber = showScrubber;
160 if (!showScrubber && scrubbing) {
161 listener.onScrubbingEnd(getScrubberTime());
162 scrubbing = false;
163 }
164 requestLayout();
165 }
166
167 private boolean inScrubber(float x, float y) {
168 int scrubberRight = scrubberLeft + scrubber.getWidth();
169 int scrubberBottom = scrubberTop + scrubber.getHeight();
170 return scrubberLeft - scrubberPadding < x && x < scrubberRight + scrubberPadding
171 && scrubberTop - scrubberPadding < y && y < scrubberBottom + scrubberPadding;
172 }
173
174 private void clampScrubber() {
175 int half = scrubber.getWidth() / 2;
176 int max = progressBar.right - half;
177 int min = progressBar.left - half;
178 scrubberLeft = Math.min(max, Math.max(min, scrubberLeft));
179 }
180
181 private int getScrubberTime() {
182 return (int) ((long) (scrubberLeft + scrubber.getWidth() / 2 - progressBar.left)
183 * totalTime / progressBar.width());
184 }
185
186 @Override
187 protected void onLayout(boolean changed, int l, int t, int r, int b) {
188 int w = r - l;
189 int h = b - t;
190 if (!showTimes && !showScrubber) {
191 progressBar.set(0, 0, w, h);
192 } else {
193 int margin = scrubber.getWidth() / 3;
194 if (showTimes) {
195 margin += timeBounds.width();
196 }
197 int progressY = (h + scrubberPadding) / 2;
198 scrubberTop = progressY - scrubber.getHeight() / 2 + 1;
199 progressBar.set(
200 getPaddingLeft() + margin, progressY,
201 w - getPaddingRight() - margin, progressY + 4);
202 }
203 update();
204 }
205
206 @Override
207 public void draw(Canvas canvas) {
208 super.draw(canvas);
209
210 // draw progress bars
211 canvas.drawRect(progressBar, progressPaint);
212 canvas.drawRect(playedBar, playedPaint);
213
214 // draw scrubber and timers
215 if (showScrubber) {
216 canvas.drawBitmap(scrubber, scrubberLeft, scrubberTop, null);
217 }
218 if (showTimes) {
219 canvas.drawText(
220 stringForTime(currentTime),
221 timeBounds.width() / 2 + getPaddingLeft(),
222 timeBounds.height() + vPaddingInPx / 2 + scrubberPadding + 1,
223 timeTextPaint);
224 canvas.drawText(
225 stringForTime(totalTime),
226 getWidth() - getPaddingRight() - timeBounds.width() / 2,
227 timeBounds.height() + vPaddingInPx / 2 + scrubberPadding + 1,
228 timeTextPaint);
229 }
230 }
231
232 @Override
233 public boolean onTouchEvent(MotionEvent event) {
234
235 if (showScrubber) {
236 int x = (int) event.getX();
237 int y = (int) event.getY();
238
239 switch (event.getAction()) {
240 case MotionEvent.ACTION_DOWN:
241 if (inScrubber(x, y)) {
242 scrubbing = true;
243 scrubberCorrection = x - scrubberLeft;
244 listener.onScrubbingStart();
245 return true;
246 }
247 break;
248 case MotionEvent.ACTION_MOVE:
249 if (scrubbing) {
250 scrubberLeft = x - scrubberCorrection;
251 clampScrubber();
252 currentTime = getScrubberTime();
253 listener.onScrubbingMove(currentTime);
254 invalidate();
255 return true;
256 }
257 break;
258 case MotionEvent.ACTION_UP:
259 if (scrubbing) {
260 listener.onScrubbingEnd(getScrubberTime());
261 scrubbing = false;
262 return true;
263 }
264 break;
265 }
266 }
267 return false;
268 }
269
270 private String stringForTime(long millis) {
271 int totalSeconds = (int) millis / 1000;
272 int seconds = totalSeconds % 60;
273 int minutes = (totalSeconds / 60) % 60;
274 int hours = totalSeconds / 3600;
275 if (hours > 0) {
276 return String.format("%d:%02d:%02d", hours, minutes, seconds).toString();
277 } else {
278 return String.format("%02d:%02d", minutes, seconds).toString();
279 }
280 }
281
282}