1 /*
2  * Copyright (C) 2012 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.view.MotionEvent;
24 
25 import com.android.gallery3d.R;
26 
27 /**
28  * The trim time bar view, which includes the current and total time, the progress
29  * bar, and the scrubbers for current time, start and end time for trimming.
30  */
31 public class TrimTimeBar extends TimeBar {
32 
33     public static final int SCRUBBER_NONE = 0;
34     public static final int SCRUBBER_START = 1;
35     public static final int SCRUBBER_CURRENT = 2;
36     public static final int SCRUBBER_END = 3;
37 
38     private int mPressedThumb = SCRUBBER_NONE;
39 
40     // On touch event, the setting order is Scrubber Position -> Time ->
41     // PlayedBar. At the setTimes(), activity can update the Time directly, then
42     // PlayedBar will be updated too.
43     private int mTrimStartScrubberLeft;
44     private int mTrimEndScrubberLeft;
45 
46     private int mTrimStartScrubberTop;
47     private int mTrimEndScrubberTop;
48 
49     private int mTrimStartTime;
50     private int mTrimEndTime;
51 
52     private final Bitmap mTrimStartScrubber;
53     private final Bitmap mTrimEndScrubber;
TrimTimeBar(Context context, Listener listener)54     public TrimTimeBar(Context context, Listener listener) {
55         super(context, listener);
56 
57         mTrimStartTime = 0;
58         mTrimEndTime = 0;
59         mTrimStartScrubberLeft = 0;
60         mTrimEndScrubberLeft = 0;
61         mTrimStartScrubberTop = 0;
62         mTrimEndScrubberTop = 0;
63 
64         mTrimStartScrubber = BitmapFactory.decodeResource(getResources(),
65                 R.drawable.text_select_handle_left);
66         mTrimEndScrubber = BitmapFactory.decodeResource(getResources(),
67                 R.drawable.text_select_handle_right);
68         // Increase the size of this trimTimeBar, but minimize the scrubber
69         // touch padding since we have 3 scrubbers now.
70         mScrubberPadding = 0;
71         mVPaddingInPx = mVPaddingInPx * 3 / 2;
72     }
73 
getBarPosFromTime(int time)74     private int getBarPosFromTime(int time) {
75         return mProgressBar.left +
76                 (int) ((mProgressBar.width() * (long) time) / mTotalTime);
77     }
78 
trimStartScrubberTipOffset()79     private int trimStartScrubberTipOffset() {
80         return mTrimStartScrubber.getWidth() * 3 / 4;
81     }
82 
trimEndScrubberTipOffset()83     private int trimEndScrubberTipOffset() {
84         return mTrimEndScrubber.getWidth() / 4;
85     }
86 
87     // Based on all the time info (current, total, trimStart, trimEnd), we
88     // decide the playedBar size.
updatePlayedBarAndScrubberFromTime()89     private void updatePlayedBarAndScrubberFromTime() {
90         // According to the Time, update the Played Bar
91         mPlayedBar.set(mProgressBar);
92         if (mTotalTime > 0) {
93             // set playedBar according to the trim time.
94             mPlayedBar.left = getBarPosFromTime(mTrimStartTime);
95             mPlayedBar.right = getBarPosFromTime(mCurrentTime);
96             if (!mScrubbing) {
97                 mScrubberLeft = mPlayedBar.right - mScrubber.getWidth() / 2;
98                 mTrimStartScrubberLeft = mPlayedBar.left - trimStartScrubberTipOffset();
99                 mTrimEndScrubberLeft = getBarPosFromTime(mTrimEndTime)
100                         - trimEndScrubberTipOffset();
101             }
102         } else {
103             // If the video is not prepared, just show the scrubber at the end
104             // of progressBar
105             mPlayedBar.right = mProgressBar.left;
106             mScrubberLeft = mProgressBar.left - mScrubber.getWidth() / 2;
107             mTrimStartScrubberLeft = mProgressBar.left - trimStartScrubberTipOffset();
108             mTrimEndScrubberLeft = mProgressBar.right - trimEndScrubberTipOffset();
109         }
110     }
111 
initTrimTimeIfNeeded()112     private void initTrimTimeIfNeeded() {
113         if (mTotalTime > 0 && mTrimEndTime == 0) {
114             mTrimEndTime = mTotalTime;
115         }
116     }
117 
update()118     private void update() {
119         initTrimTimeIfNeeded();
120         updatePlayedBarAndScrubberFromTime();
121         invalidate();
122     }
123 
124     @Override
setTime(int currentTime, int totalTime, int trimStartTime, int trimEndTime)125     public void setTime(int currentTime, int totalTime,
126             int trimStartTime, int trimEndTime) {
127         if (mCurrentTime == currentTime && mTotalTime == totalTime
128                 && mTrimStartTime == trimStartTime && mTrimEndTime == trimEndTime) {
129             return;
130         }
131         mCurrentTime = currentTime;
132         mTotalTime = totalTime;
133         mTrimStartTime = trimStartTime;
134         mTrimEndTime = trimEndTime;
135         update();
136     }
137 
whichScrubber(float x, float y)138     private int whichScrubber(float x, float y) {
139         if (inScrubber(x, y, mTrimStartScrubberLeft, mTrimStartScrubberTop, mTrimStartScrubber)) {
140             return SCRUBBER_START;
141         } else if (inScrubber(x, y, mTrimEndScrubberLeft, mTrimEndScrubberTop, mTrimEndScrubber)) {
142             return SCRUBBER_END;
143         } else if (inScrubber(x, y, mScrubberLeft, mScrubberTop, mScrubber)) {
144             return SCRUBBER_CURRENT;
145         }
146         return SCRUBBER_NONE;
147     }
148 
inScrubber(float x, float y, int startX, int startY, Bitmap scrubber)149     private boolean inScrubber(float x, float y, int startX, int startY, Bitmap scrubber) {
150         int scrubberRight = startX + scrubber.getWidth();
151         int scrubberBottom = startY + scrubber.getHeight();
152         return startX < x && x < scrubberRight && startY < y && y < scrubberBottom;
153     }
154 
clampScrubber(int scrubberLeft, int offset, int lowerBound, int upperBound)155     private int clampScrubber(int scrubberLeft, int offset, int lowerBound, int upperBound) {
156         int max = upperBound - offset;
157         int min = lowerBound - offset;
158         return Math.min(max, Math.max(min, scrubberLeft));
159     }
160 
getScrubberTime(int scrubberLeft, int offset)161     private int getScrubberTime(int scrubberLeft, int offset) {
162         return (int) ((long) (scrubberLeft + offset - mProgressBar.left)
163                 * mTotalTime / mProgressBar.width());
164     }
165 
166     @Override
onLayout(boolean changed, int l, int t, int r, int b)167     protected void onLayout(boolean changed, int l, int t, int r, int b) {
168         int w = r - l;
169         int h = b - t;
170         if (!mShowTimes && !mShowScrubber) {
171             mProgressBar.set(0, 0, w, h);
172         } else {
173             int margin = mScrubber.getWidth() / 3;
174             if (mShowTimes) {
175                 margin += mTimeBounds.width();
176             }
177             int progressY = h / 4;
178             int scrubberY = progressY - mScrubber.getHeight() / 2 + 1;
179             mScrubberTop = scrubberY;
180             mTrimStartScrubberTop = progressY;
181             mTrimEndScrubberTop = progressY;
182             mProgressBar.set(
183                     getPaddingLeft() + margin, progressY,
184                     w - getPaddingRight() - margin, progressY + 4);
185         }
186         update();
187     }
188 
189     @Override
onDraw(Canvas canvas)190     protected void onDraw(Canvas canvas) {
191         // draw progress bars
192         canvas.drawRect(mProgressBar, mProgressPaint);
193         canvas.drawRect(mPlayedBar, mPlayedPaint);
194 
195         if (mShowTimes) {
196             canvas.drawText(
197                     stringForTime(mCurrentTime),
198                             mTimeBounds.width() / 2 + getPaddingLeft(),
199                             mTimeBounds.height() / 2 +  mTrimStartScrubberTop,
200                     mTimeTextPaint);
201             canvas.drawText(
202                     stringForTime(mTotalTime),
203                             getWidth() - getPaddingRight() - mTimeBounds.width() / 2,
204                             mTimeBounds.height() / 2 +  mTrimStartScrubberTop,
205                     mTimeTextPaint);
206         }
207 
208         // draw extra scrubbers
209         if (mShowScrubber) {
210             canvas.drawBitmap(mScrubber, mScrubberLeft, mScrubberTop, null);
211             canvas.drawBitmap(mTrimStartScrubber, mTrimStartScrubberLeft,
212                     mTrimStartScrubberTop, null);
213             canvas.drawBitmap(mTrimEndScrubber, mTrimEndScrubberLeft,
214                     mTrimEndScrubberTop, null);
215         }
216     }
217 
updateTimeFromPos()218     private void updateTimeFromPos() {
219         mCurrentTime = getScrubberTime(mScrubberLeft, mScrubber.getWidth() / 2);
220         mTrimStartTime = getScrubberTime(mTrimStartScrubberLeft, trimStartScrubberTipOffset());
221         mTrimEndTime = getScrubberTime(mTrimEndScrubberLeft, trimEndScrubberTipOffset());
222     }
223 
224     @Override
onTouchEvent(MotionEvent event)225     public boolean onTouchEvent(MotionEvent event) {
226         if (mShowScrubber) {
227             int x = (int) event.getX();
228             int y = (int) event.getY();
229 
230             switch (event.getAction()) {
231                 case MotionEvent.ACTION_DOWN:
232                     mPressedThumb = whichScrubber(x, y);
233                     switch (mPressedThumb) {
234                         case SCRUBBER_NONE:
235                             break;
236                         case SCRUBBER_CURRENT:
237                             mScrubbing = true;
238                             mScrubberCorrection = x - mScrubberLeft;
239                             break;
240                         case SCRUBBER_START:
241                             mScrubbing = true;
242                             mScrubberCorrection = x - mTrimStartScrubberLeft;
243                             break;
244                         case SCRUBBER_END:
245                             mScrubbing = true;
246                             mScrubberCorrection = x - mTrimEndScrubberLeft;
247                             break;
248                     }
249                     if (mScrubbing == true) {
250                         mListener.onScrubbingStart();
251                         return true;
252                     }
253                     break;
254                 case MotionEvent.ACTION_MOVE:
255                     if (mScrubbing) {
256                         int seekToTime = -1;
257                         int lowerBound = mTrimStartScrubberLeft + trimStartScrubberTipOffset();
258                         int upperBound = mTrimEndScrubberLeft + trimEndScrubberTipOffset();
259                         switch (mPressedThumb) {
260                             case SCRUBBER_CURRENT:
261                                 mScrubberLeft = x - mScrubberCorrection;
262                                 mScrubberLeft =
263                                         clampScrubber(mScrubberLeft,
264                                                 mScrubber.getWidth() / 2,
265                                                 lowerBound, upperBound);
266                                 seekToTime = getScrubberTime(mScrubberLeft,
267                                         mScrubber.getWidth() / 2);
268                                 break;
269                             case SCRUBBER_START:
270                                 mTrimStartScrubberLeft = x - mScrubberCorrection;
271                                 // Limit start <= end
272                                 if (mTrimStartScrubberLeft > mTrimEndScrubberLeft) {
273                                     mTrimStartScrubberLeft = mTrimEndScrubberLeft;
274                                 }
275                                 lowerBound = mProgressBar.left;
276                                 mTrimStartScrubberLeft =
277                                         clampScrubber(mTrimStartScrubberLeft,
278                                                 trimStartScrubberTipOffset(),
279                                                 lowerBound, upperBound);
280                                 seekToTime = getScrubberTime(mTrimStartScrubberLeft,
281                                         trimStartScrubberTipOffset());
282                                 break;
283                             case SCRUBBER_END:
284                                 mTrimEndScrubberLeft = x - mScrubberCorrection;
285                                 upperBound = mProgressBar.right;
286                                 mTrimEndScrubberLeft =
287                                         clampScrubber(mTrimEndScrubberLeft,
288                                                 trimEndScrubberTipOffset(),
289                                                 lowerBound, upperBound);
290                                 seekToTime = getScrubberTime(mTrimEndScrubberLeft,
291                                         trimEndScrubberTipOffset());
292                                 break;
293                         }
294                         updateTimeFromPos();
295                         updatePlayedBarAndScrubberFromTime();
296                         if (seekToTime != -1) {
297                             mListener.onScrubbingMove(seekToTime);
298                         }
299                         invalidate();
300                         return true;
301                     }
302                     break;
303                 case MotionEvent.ACTION_CANCEL:
304                 case MotionEvent.ACTION_UP:
305                     if (mScrubbing) {
306                         int seekToTime = 0;
307                         switch (mPressedThumb) {
308                             case SCRUBBER_CURRENT:
309                                 seekToTime = getScrubberTime(mScrubberLeft,
310                                         mScrubber.getWidth() / 2);
311                                 break;
312                             case SCRUBBER_START:
313                                 seekToTime = getScrubberTime(mTrimStartScrubberLeft,
314                                         trimStartScrubberTipOffset());
315                                 mScrubberLeft = mTrimStartScrubberLeft +
316                                         trimStartScrubberTipOffset() - mScrubber.getWidth() / 2;
317                                 break;
318                             case SCRUBBER_END:
319                                 seekToTime = getScrubberTime(mTrimEndScrubberLeft,
320                                         trimEndScrubberTipOffset());
321                                 mScrubberLeft = mTrimEndScrubberLeft +
322                                         trimEndScrubberTipOffset() - mScrubber.getWidth() / 2;
323                                 break;
324                         }
325                         updateTimeFromPos();
326                         mListener.onScrubbingEnd(seekToTime,
327                                 getScrubberTime(mTrimStartScrubberLeft,
328                                         trimStartScrubberTipOffset()),
329                                 getScrubberTime(mTrimEndScrubberLeft, trimEndScrubberTipOffset()));
330                         mScrubbing = false;
331                         mPressedThumb = SCRUBBER_NONE;
332                         return true;
333                     }
334                     break;
335             }
336         }
337         return false;
338     }
339 }
340