MovieControllerOverlay.java revision 7b83fb8e3a8978b33a6b9bfc56d85fe2c1a9cf06
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 17package com.android.gallery3d.app; 18 19import com.android.gallery3d.common.Utils; 20import com.android.gallery3d.R; 21 22import android.content.Context; 23import android.os.Handler; 24import android.view.Gravity; 25import android.view.KeyEvent; 26import android.view.LayoutInflater; 27import android.view.MotionEvent; 28import android.view.View; 29import android.view.View.OnClickListener; 30import android.view.animation.Animation; 31import android.view.animation.Animation.AnimationListener; 32import android.view.animation.AnimationUtils; 33import android.widget.FrameLayout; 34import android.widget.ImageButton; 35import android.widget.ImageView; 36import android.widget.ImageView.ScaleType; 37import android.widget.LinearLayout; 38import android.widget.ProgressBar; 39import android.widget.RelativeLayout; 40import android.widget.TextView; 41 42/** 43 * The playback controller for the Movie Player. 44 */ 45public 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 setTimes(int currentTime, int totalTime) { 180 timeBar.setTime(currentTime, totalTime); 181 } 182 183 public void hide() { 184 boolean wasHidden = hidden; 185 hidden = true; 186 playPauseReplayView.setVisibility(View.INVISIBLE); 187 loadingView.setVisibility(View.INVISIBLE); 188 background.setVisibility(View.INVISIBLE); 189 timeBar.setVisibility(View.INVISIBLE); 190 setVisibility(View.INVISIBLE); 191 setFocusable(true); 192 requestFocus(); 193 if (listener != null && wasHidden != hidden) { 194 listener.onHidden(); 195 } 196 } 197 198 private void showMainView(View view) { 199 mainView = view; 200 errorView.setVisibility(mainView == errorView ? View.VISIBLE : View.INVISIBLE); 201 loadingView.setVisibility(mainView == loadingView ? View.VISIBLE : View.INVISIBLE); 202 playPauseReplayView.setVisibility( 203 mainView == playPauseReplayView ? View.VISIBLE : View.INVISIBLE); 204 show(); 205 } 206 207 public void show() { 208 boolean wasHidden = hidden; 209 hidden = false; 210 updateViews(); 211 setVisibility(View.VISIBLE); 212 setFocusable(false); 213 if (listener != null && wasHidden != hidden) { 214 listener.onShown(); 215 } 216 maybeStartHiding(); 217 } 218 219 private void maybeStartHiding() { 220 cancelHiding(); 221 if (state == State.PLAYING) { 222 handler.postDelayed(startHidingRunnable, 2500); 223 } 224 } 225 226 private void startHiding() { 227 startHideAnimation(timeBar); 228 startHideAnimation(playPauseReplayView); 229 } 230 231 private void startHideAnimation(View view) { 232 if (view.getVisibility() == View.VISIBLE) { 233 view.startAnimation(hideAnimation); 234 } 235 } 236 237 private void cancelHiding() { 238 handler.removeCallbacks(startHidingRunnable); 239 background.setAnimation(null); 240 timeBar.setAnimation(null); 241 playPauseReplayView.setAnimation(null); 242 } 243 244 public void onAnimationStart(Animation animation) { 245 // Do nothing. 246 } 247 248 public void onAnimationRepeat(Animation animation) { 249 // Do nothing. 250 } 251 252 public void onAnimationEnd(Animation animation) { 253 hide(); 254 } 255 256 public void onClick(View view) { 257 if (listener != null) { 258 if (view == playPauseReplayView) { 259 if (state == State.ENDED) { 260 if (canReplay) { 261 listener.onReplay(); 262 } 263 } else if (state == State.PAUSED || state == State.PLAYING) { 264 listener.onPlayPause(); 265 } 266 } 267 } 268 } 269 270 @Override 271 public boolean onKeyDown(int keyCode, KeyEvent event) { 272 if (hidden) { 273 show(); 274 } 275 return super.onKeyDown(keyCode, event); 276 } 277 278 @Override 279 public boolean onTouchEvent(MotionEvent event) { 280 if (super.onTouchEvent(event)) { 281 return true; 282 } 283 284 if (hidden) { 285 show(); 286 return true; 287 } 288 switch (event.getAction()) { 289 case MotionEvent.ACTION_DOWN: 290 cancelHiding(); 291 if (state == State.PLAYING || state == State.PAUSED) { 292 listener.onPlayPause(); 293 } 294 break; 295 case MotionEvent.ACTION_UP: 296 maybeStartHiding(); 297 break; 298 } 299 return true; 300 } 301 302 @Override 303 protected void onLayout(boolean changed, int l, int t, int r, int b) { 304 int bw; 305 int bh; 306 int y; 307 int h = b - t; 308 int w = r - l; 309 boolean error = errorView.getVisibility() == View.VISIBLE; 310 311 bw = timeBar.getBarHeight(); 312 bh = bw; 313 y = b - bh; 314 315 background.layout(l, y, r, b); 316 317 timeBar.layout(l, b - timeBar.getPreferredHeight(), r, b); 318 // Needed, otherwise the framework will not re-layout in case only the padding is changed 319 timeBar.requestLayout(); 320 321 // play pause / next / previous buttons 322 int cx = l + w / 2; // center x 323 int playbackButtonsCenterline = t + h / 2; 324 bw = playPauseReplayView.getMeasuredWidth(); 325 bh = playPauseReplayView.getMeasuredHeight(); 326 playPauseReplayView.layout( 327 cx - bw / 2, playbackButtonsCenterline - bh / 2, cx + bw / 2, 328 playbackButtonsCenterline + bh / 2); 329 330 // Space available on each side of the error message for the next and previous buttons 331 int errorMessagePadding = (int) (w * ERROR_MESSAGE_RELATIVE_PADDING); 332 333 if (mainView != null) { 334 layoutCenteredView(mainView, l, t, r, b); 335 } 336 } 337 338 private void layoutCenteredView(View view, int l, int t, int r, int b) { 339 int cw = view.getMeasuredWidth(); 340 int ch = view.getMeasuredHeight(); 341 int cl = (r - l - cw) / 2; 342 int ct = (b - t - ch) / 2; 343 view.layout(cl, ct, cl + cw, ct + ch); 344 } 345 346 @Override 347 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 348 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 349 measureChildren(widthMeasureSpec, heightMeasureSpec); 350 } 351 352 private void updateViews() { 353 if (hidden) { 354 return; 355 } 356 background.setVisibility(View.VISIBLE); 357 timeBar.setVisibility(View.VISIBLE); 358 playPauseReplayView.setImageResource( 359 state == State.PAUSED ? R.drawable.ic_vidcontrol_play : 360 state == State.PLAYING ? R.drawable.ic_vidcontrol_pause : 361 R.drawable.ic_vidcontrol_reload); 362 playPauseReplayView.setVisibility( 363 (state != State.LOADING && state != State.ERROR && 364 !(state == State.ENDED && !canReplay)) 365 ? View.VISIBLE : View.GONE); 366 requestLayout(); 367 } 368 369 // TimeBar listener 370 371 public void onScrubbingStart() { 372 cancelHiding(); 373 listener.onSeekStart(); 374 } 375 376 public void onScrubbingMove(int time) { 377 cancelHiding(); 378 listener.onSeekMove(time); 379 } 380 381 public void onScrubbingEnd(int time) { 382 maybeStartHiding(); 383 listener.onSeekEnd(time); 384 } 385 386} 387