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.os.Handler; 24 import android.view.Gravity; 25 import android.view.KeyEvent; 26 import android.view.LayoutInflater; 27 import android.view.MotionEvent; 28 import android.view.View; 29 import android.view.View.OnClickListener; 30 import android.view.animation.Animation; 31 import android.view.animation.Animation.AnimationListener; 32 import android.view.animation.AnimationUtils; 33 import android.widget.FrameLayout; 34 import android.widget.ImageButton; 35 import android.widget.ImageView; 36 import android.widget.ImageView.ScaleType; 37 import android.widget.LinearLayout; 38 import android.widget.ProgressBar; 39 import android.widget.RelativeLayout; 40 import android.widget.TextView; 41 42 /** 43 * The playback controller for the Movie Player. 44 */ 45 public class MovieControllerOverlay extends FrameLayout implements 46 ControllerOverlay, 47 OnClickListener, 48 AnimationListener, 49 TimeBar.Listener { 50 51 private enum State { 52 PLAYING, 53 PAUSED, 54 ENDED, 55 ERROR, 56 LOADING 57 } 58 59 private static final float ERROR_MESSAGE_RELATIVE_PADDING = 1.0f / 6; 60 61 private Listener listener; 62 63 private final View background; 64 private final TimeBar timeBar; 65 66 private View mainView; 67 private final LinearLayout loadingView; 68 private final TextView errorView; 69 private final ImageView playPauseReplayView; 70 71 private final Handler handler; 72 private final Runnable startHidingRunnable; 73 private final Animation hideAnimation; 74 75 private State state; 76 77 private boolean hidden; 78 79 private boolean canReplay = true; 80 81 public MovieControllerOverlay(Context context) { 82 super(context); 83 84 state = State.LOADING; 85 86 LayoutParams wrapContent = 87 new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 88 LayoutParams matchParent = 89 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 90 91 LayoutInflater inflater = LayoutInflater.from(context); 92 93 background = new View(context); 94 background.setBackgroundColor(context.getResources().getColor(R.color.darker_transparent)); 95 addView(background, matchParent); 96 97 timeBar = new TimeBar(context, this); 98 addView(timeBar, wrapContent); 99 100 loadingView = new LinearLayout(context); 101 loadingView.setOrientation(LinearLayout.VERTICAL); 102 loadingView.setGravity(Gravity.CENTER_HORIZONTAL); 103 ProgressBar spinner = new ProgressBar(context); 104 spinner.setIndeterminate(true); 105 loadingView.addView(spinner, wrapContent); 106 addView(loadingView, wrapContent); 107 108 playPauseReplayView = new ImageView(context); 109 playPauseReplayView.setImageResource(R.drawable.ic_vidcontrol_play); 110 playPauseReplayView.setBackgroundResource(R.drawable.bg_vidcontrol); 111 playPauseReplayView.setScaleType(ScaleType.CENTER); 112 playPauseReplayView.setFocusable(true); 113 playPauseReplayView.setClickable(true); 114 playPauseReplayView.setOnClickListener(this); 115 addView(playPauseReplayView, wrapContent); 116 117 errorView = new TextView(context); 118 errorView.setGravity(Gravity.CENTER); 119 errorView.setBackgroundColor(0xCC000000); 120 errorView.setTextColor(0xFFFFFFFF); 121 addView(errorView, matchParent); 122 123 handler = new Handler(); 124 startHidingRunnable = new Runnable() { 125 public void run() { 126 startHiding(); 127 } 128 }; 129 130 hideAnimation = AnimationUtils.loadAnimation(context, R.anim.player_out); 131 hideAnimation.setAnimationListener(this); 132 133 RelativeLayout.LayoutParams params = 134 new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 135 setLayoutParams(params); 136 hide(); 137 } 138 139 public void setListener(Listener listener) { 140 this.listener = listener; 141 } 142 143 public void setCanReplay(boolean canReplay) { 144 this.canReplay = canReplay; 145 } 146 147 public View getView() { 148 return this; 149 } 150 151 public void showPlaying() { 152 state = State.PLAYING; 153 showMainView(playPauseReplayView); 154 } 155 156 public void showPaused() { 157 state = State.PAUSED; 158 showMainView(playPauseReplayView); 159 } 160 161 public void showEnded() { 162 state = State.ENDED; 163 showMainView(playPauseReplayView); 164 } 165 166 public void showLoading() { 167 state = State.LOADING; 168 showMainView(loadingView); 169 } 170 171 public void showErrorMessage(String message) { 172 state = State.ERROR; 173 int padding = (int) (getMeasuredWidth() * ERROR_MESSAGE_RELATIVE_PADDING); 174 errorView.setPadding(padding, 10, padding, 10); 175 errorView.setText(message); 176 showMainView(errorView); 177 } 178 179 public void resetTime() { 180 timeBar.resetTime(); 181 } 182 183 public void setTimes(int currentTime, int totalTime) { 184 timeBar.setTime(currentTime, totalTime); 185 } 186 187 public void hide() { 188 boolean wasHidden = hidden; 189 hidden = true; 190 playPauseReplayView.setVisibility(View.INVISIBLE); 191 loadingView.setVisibility(View.INVISIBLE); 192 background.setVisibility(View.INVISIBLE); 193 timeBar.setVisibility(View.INVISIBLE); 194 setVisibility(View.INVISIBLE); 195 setFocusable(true); 196 requestFocus(); 197 if (listener != null && wasHidden != hidden) { 198 listener.onHidden(); 199 } 200 } 201 202 private void showMainView(View view) { 203 mainView = view; 204 errorView.setVisibility(mainView == errorView ? View.VISIBLE : View.INVISIBLE); 205 loadingView.setVisibility(mainView == loadingView ? View.VISIBLE : View.INVISIBLE); 206 playPauseReplayView.setVisibility( 207 mainView == playPauseReplayView ? View.VISIBLE : View.INVISIBLE); 208 show(); 209 } 210 211 public void show() { 212 boolean wasHidden = hidden; 213 hidden = false; 214 updateViews(); 215 setVisibility(View.VISIBLE); 216 setFocusable(false); 217 if (listener != null && wasHidden != hidden) { 218 listener.onShown(); 219 } 220 maybeStartHiding(); 221 } 222 223 private void maybeStartHiding() { 224 cancelHiding(); 225 if (state == State.PLAYING) { 226 handler.postDelayed(startHidingRunnable, 2500); 227 } 228 } 229 230 private void startHiding() { 231 startHideAnimation(timeBar); 232 startHideAnimation(playPauseReplayView); 233 } 234 235 private void startHideAnimation(View view) { 236 if (view.getVisibility() == View.VISIBLE) { 237 view.startAnimation(hideAnimation); 238 } 239 } 240 241 private void cancelHiding() { 242 handler.removeCallbacks(startHidingRunnable); 243 background.setAnimation(null); 244 timeBar.setAnimation(null); 245 playPauseReplayView.setAnimation(null); 246 } 247 248 public void onAnimationStart(Animation animation) { 249 // Do nothing. 250 } 251 252 public void onAnimationRepeat(Animation animation) { 253 // Do nothing. 254 } 255 256 public void onAnimationEnd(Animation animation) { 257 hide(); 258 } 259 260 public void onClick(View view) { 261 if (listener != null) { 262 if (view == playPauseReplayView) { 263 if (state == State.ENDED) { 264 if (canReplay) { 265 listener.onReplay(); 266 } 267 } else if (state == State.PAUSED || state == State.PLAYING) { 268 listener.onPlayPause(); 269 } 270 } 271 } 272 } 273 274 @Override 275 public boolean onKeyDown(int keyCode, KeyEvent event) { 276 if (hidden) { 277 show(); 278 } 279 return super.onKeyDown(keyCode, event); 280 } 281 282 @Override 283 public boolean onTouchEvent(MotionEvent event) { 284 if (super.onTouchEvent(event)) { 285 return true; 286 } 287 288 if (hidden) { 289 show(); 290 return true; 291 } 292 switch (event.getAction()) { 293 case MotionEvent.ACTION_DOWN: 294 cancelHiding(); 295 if (state == State.PLAYING || state == State.PAUSED) { 296 listener.onPlayPause(); 297 } 298 break; 299 case MotionEvent.ACTION_UP: 300 maybeStartHiding(); 301 break; 302 } 303 return true; 304 } 305 306 @Override 307 protected void onLayout(boolean changed, int l, int t, int r, int b) { 308 int bw; 309 int bh; 310 int y; 311 int h = b - t; 312 int w = r - l; 313 boolean error = errorView.getVisibility() == View.VISIBLE; 314 315 bw = timeBar.getBarHeight(); 316 bh = bw; 317 y = b - bh; 318 319 background.layout(l, y, r, b); 320 321 timeBar.layout(l, b - timeBar.getPreferredHeight(), r, b); 322 // Needed, otherwise the framework will not re-layout in case only the padding is changed 323 timeBar.requestLayout(); 324 325 // play pause / next / previous buttons 326 int cx = l + w / 2; // center x 327 int playbackButtonsCenterline = t + h / 2; 328 bw = playPauseReplayView.getMeasuredWidth(); 329 bh = playPauseReplayView.getMeasuredHeight(); 330 playPauseReplayView.layout( 331 cx - bw / 2, playbackButtonsCenterline - bh / 2, cx + bw / 2, 332 playbackButtonsCenterline + bh / 2); 333 334 // Space available on each side of the error message for the next and previous buttons 335 int errorMessagePadding = (int) (w * ERROR_MESSAGE_RELATIVE_PADDING); 336 337 if (mainView != null) { 338 layoutCenteredView(mainView, l, t, r, b); 339 } 340 } 341 342 private void layoutCenteredView(View view, int l, int t, int r, int b) { 343 int cw = view.getMeasuredWidth(); 344 int ch = view.getMeasuredHeight(); 345 int cl = (r - l - cw) / 2; 346 int ct = (b - t - ch) / 2; 347 view.layout(cl, ct, cl + cw, ct + ch); 348 } 349 350 @Override 351 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 352 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 353 measureChildren(widthMeasureSpec, heightMeasureSpec); 354 } 355 356 private void updateViews() { 357 if (hidden) { 358 return; 359 } 360 background.setVisibility(View.VISIBLE); 361 timeBar.setVisibility(View.VISIBLE); 362 playPauseReplayView.setImageResource( 363 state == State.PAUSED ? R.drawable.ic_vidcontrol_play : 364 state == State.PLAYING ? R.drawable.ic_vidcontrol_pause : 365 R.drawable.ic_vidcontrol_reload); 366 playPauseReplayView.setVisibility( 367 (state != State.LOADING && state != State.ERROR && 368 !(state == State.ENDED && !canReplay)) 369 ? View.VISIBLE : View.GONE); 370 requestLayout(); 371 } 372 373 // TimeBar listener 374 375 public void onScrubbingStart() { 376 cancelHiding(); 377 listener.onSeekStart(); 378 } 379 380 public void onScrubbingMove(int time) { 381 cancelHiding(); 382 listener.onSeekMove(time); 383 } 384 385 public void onScrubbingEnd(int time) { 386 maybeStartHiding(); 387 listener.onSeekEnd(time); 388 } 389 390 } 391