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 { 39 void onScrubbingStart(); 40 41 void onScrubbingMove(int time); 42 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 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 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 */ 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 */ 139 public int getBarHeight() { 140 return mTimeBounds.height() + mVPaddingInPx; 141 } 142 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 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 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 167 private int getScrubberTime() { 168 return (int) ((long) (mScrubberLeft + mScrubber.getWidth() / 2 - mProgressBar.left) 169 * mTotalTime / mProgressBar.width()); 170 } 171 172 @Override 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 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 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 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 262 public void setSeekable(boolean canSeek) { 263 mShowScrubber = canSeek; 264 } 265 266 } 267