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 com.android.gallery3d.common.Utils; 20 import com.android.gallery3d.R; 21 22 import android.content.Context; 23 import android.graphics.Bitmap; 24 import android.graphics.BitmapFactory; 25 import android.graphics.Canvas; 26 import android.graphics.Paint; 27 import android.graphics.Rect; 28 import android.util.DisplayMetrics; 29 import android.view.MotionEvent; 30 import android.view.View; 31 32 /** 33 * The time bar view, which includes the current and total time, the progress bar, 34 * and the scrubber. 35 */ 36 public 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 } 283