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