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; 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 74 private int getBarPosFromTime(int time) { 75 return mProgressBar.left + 76 (int) ((mProgressBar.width() * (long) time) / mTotalTime); 77 } 78 79 private int trimStartScrubberTipOffset() { 80 return mTrimStartScrubber.getWidth() * 3 / 4; 81 } 82 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. 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 112 private void initTrimTimeIfNeeded() { 113 if (mTotalTime > 0 && mTrimEndTime == 0) { 114 mTrimEndTime = mTotalTime; 115 } 116 } 117 118 private void update() { 119 initTrimTimeIfNeeded(); 120 updatePlayedBarAndScrubberFromTime(); 121 invalidate(); 122 } 123 124 @Override 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 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 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 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 161 private int getScrubberTime(int scrubberLeft, int offset) { 162 return (int) ((long) (scrubberLeft + offset - mProgressBar.left) 163 * mTotalTime / mProgressBar.width()); 164 } 165 166 @Override 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 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 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 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