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.Rect; 21 import android.os.Handler; 22 import android.view.Gravity; 23 import android.view.KeyEvent; 24 import android.view.MotionEvent; 25 import android.view.View; 26 import android.view.View.OnClickListener; 27 import android.view.animation.Animation; 28 import android.view.animation.Animation.AnimationListener; 29 import android.view.animation.AnimationUtils; 30 import android.widget.FrameLayout; 31 import android.widget.ImageView; 32 import android.widget.ImageView.ScaleType; 33 import android.widget.LinearLayout; 34 import android.widget.ProgressBar; 35 import android.widget.RelativeLayout; 36 import android.widget.TextView; 37 38 import com.android.gallery3d.R; 39 40 /** 41 * The playback controller for the Movie Player. 42 */ 43 public class MovieControllerOverlay extends FrameLayout implements 44 ControllerOverlay, 45 OnClickListener, 46 AnimationListener, 47 TimeBar.Listener { 48 49 private enum State { 50 PLAYING, 51 PAUSED, 52 ENDED, 53 ERROR, 54 LOADING 55 } 56 57 private static final float ERROR_MESSAGE_RELATIVE_PADDING = 1.0f / 6; 58 59 private Listener listener; 60 61 private final View background; 62 private final TimeBar timeBar; 63 64 private View mainView; 65 private final LinearLayout loadingView; 66 private final TextView errorView; 67 private final ImageView playPauseReplayView; 68 69 private final Handler handler; 70 private final Runnable startHidingRunnable; 71 private final Animation hideAnimation; 72 73 private State state; 74 75 private boolean hidden; 76 77 private boolean canReplay = true; 78 79 public MovieControllerOverlay(Context context) { 80 super(context); 81 82 state = State.LOADING; 83 84 LayoutParams wrapContent = 85 new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 86 LayoutParams matchParent = 87 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 88 89 background = new View(context); 90 background.setBackgroundColor(context.getResources().getColor(R.color.darker_transparent)); 91 addView(background, matchParent); 92 93 timeBar = new TimeBar(context, this); 94 addView(timeBar, wrapContent); 95 96 loadingView = new LinearLayout(context); 97 loadingView.setOrientation(LinearLayout.VERTICAL); 98 loadingView.setGravity(Gravity.CENTER_HORIZONTAL); 99 ProgressBar spinner = new ProgressBar(context); 100 spinner.setIndeterminate(true); 101 loadingView.addView(spinner, wrapContent); 102 TextView loadingText = createOverlayTextView(context); 103 loadingText.setText(R.string.loading_video); 104 loadingView.addView(loadingText, wrapContent); 105 addView(loadingView, wrapContent); 106 107 playPauseReplayView = new ImageView(context); 108 playPauseReplayView.setImageResource(R.drawable.ic_vidcontrol_play); 109 playPauseReplayView.setBackgroundResource(R.drawable.bg_vidcontrol); 110 playPauseReplayView.setScaleType(ScaleType.CENTER); 111 playPauseReplayView.setFocusable(true); 112 playPauseReplayView.setClickable(true); 113 playPauseReplayView.setOnClickListener(this); 114 addView(playPauseReplayView, wrapContent); 115 116 errorView = createOverlayTextView(context); 117 addView(errorView, matchParent); 118 119 handler = new Handler(); 120 startHidingRunnable = new Runnable() { 121 public void run() { 122 startHiding(); 123 } 124 }; 125 126 hideAnimation = AnimationUtils.loadAnimation(context, R.anim.player_out); 127 hideAnimation.setAnimationListener(this); 128 129 RelativeLayout.LayoutParams params = 130 new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 131 setLayoutParams(params); 132 hide(); 133 } 134 135 private TextView createOverlayTextView(Context context) { 136 TextView view = new TextView(context); 137 view.setGravity(Gravity.CENTER); 138 view.setTextColor(0xFFFFFFFF); 139 view.setPadding(0, 15, 0, 15); 140 return view; 141 } 142 143 public void setListener(Listener listener) { 144 this.listener = listener; 145 } 146 147 public void setCanReplay(boolean canReplay) { 148 this.canReplay = canReplay; 149 } 150 151 public View getView() { 152 return this; 153 } 154 155 public void showPlaying() { 156 state = State.PLAYING; 157 showMainView(playPauseReplayView); 158 } 159 160 public void showPaused() { 161 state = State.PAUSED; 162 showMainView(playPauseReplayView); 163 } 164 165 public void showEnded() { 166 state = State.ENDED; 167 showMainView(playPauseReplayView); 168 } 169 170 public void showLoading() { 171 state = State.LOADING; 172 showMainView(loadingView); 173 } 174 175 public void showErrorMessage(String message) { 176 state = State.ERROR; 177 int padding = (int) (getMeasuredWidth() * ERROR_MESSAGE_RELATIVE_PADDING); 178 errorView.setPadding(padding, errorView.getPaddingTop(), padding, errorView.getPaddingBottom()); 179 errorView.setText(message); 180 showMainView(errorView); 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(background); 232 startHideAnimation(timeBar); 233 startHideAnimation(playPauseReplayView); 234 } 235 236 private void startHideAnimation(View view) { 237 if (view.getVisibility() == View.VISIBLE) { 238 view.startAnimation(hideAnimation); 239 } 240 } 241 242 private void cancelHiding() { 243 handler.removeCallbacks(startHidingRunnable); 244 background.setAnimation(null); 245 timeBar.setAnimation(null); 246 playPauseReplayView.setAnimation(null); 247 } 248 249 public void onAnimationStart(Animation animation) { 250 // Do nothing. 251 } 252 253 public void onAnimationRepeat(Animation animation) { 254 // Do nothing. 255 } 256 257 public void onAnimationEnd(Animation animation) { 258 hide(); 259 } 260 261 public void onClick(View view) { 262 if (listener != null) { 263 if (view == playPauseReplayView) { 264 if (state == State.ENDED) { 265 if (canReplay) { 266 listener.onReplay(); 267 } 268 } else if (state == State.PAUSED || state == State.PLAYING) { 269 listener.onPlayPause(); 270 } 271 } 272 } 273 } 274 275 @Override 276 public boolean onKeyDown(int keyCode, KeyEvent event) { 277 if (hidden) { 278 show(); 279 } 280 return super.onKeyDown(keyCode, event); 281 } 282 283 @Override 284 public boolean onTouchEvent(MotionEvent event) { 285 if (super.onTouchEvent(event)) { 286 return true; 287 } 288 289 if (hidden) { 290 show(); 291 return true; 292 } 293 switch (event.getAction()) { 294 case MotionEvent.ACTION_DOWN: 295 cancelHiding(); 296 if (state == State.PLAYING || state == State.PAUSED) { 297 listener.onPlayPause(); 298 } 299 break; 300 case MotionEvent.ACTION_UP: 301 maybeStartHiding(); 302 break; 303 } 304 return true; 305 } 306 307 // The paddings of 4 sides which covered by system components. E.g. 308 // +-----------------+\ 309 // | Action Bar | insets.top 310 // +-----------------+/ 311 // | | 312 // | Content Area | insets.right = insets.left = 0 313 // | | 314 // +-----------------+\ 315 // | Navigation Bar | insets.bottom 316 // +-----------------+/ 317 // Please see View.fitSystemWindows() for more details. 318 private final Rect mWindowInsets = new Rect(); 319 320 @Override 321 protected boolean fitSystemWindows(Rect insets) { 322 // We don't set the paddings of this View, otherwise, 323 // the content will get cropped outside window 324 mWindowInsets.set(insets); 325 return true; 326 } 327 328 @Override 329 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 330 Rect insets = mWindowInsets; 331 int pl = insets.left; // the left paddings 332 int pr = insets.right; 333 int pt = insets.top; 334 int pb = insets.bottom; 335 336 int h = bottom - top; 337 int w = right - left; 338 boolean error = errorView.getVisibility() == View.VISIBLE; 339 340 int y = h - pb; 341 // Put both TimeBar and Background just above the bottom system component. 342 // But extend the background to the width of the screen, since we don't 343 // care if it will be covered by a system component and it looks better. 344 background.layout(0, y - timeBar.getBarHeight(), w, y); 345 timeBar.layout(pl, y - timeBar.getPreferredHeight(), w - pr, y); 346 347 // Needed, otherwise the framework will not re-layout in case only the padding is changed 348 timeBar.requestLayout(); 349 350 // Put the play/pause/next/ previous button in the center of the screen 351 layoutCenteredView(playPauseReplayView, 0, 0, w, h); 352 353 if (mainView != null) { 354 layoutCenteredView(mainView, 0, 0, w, h); 355 } 356 } 357 358 private void layoutCenteredView(View view, int l, int t, int r, int b) { 359 int cw = view.getMeasuredWidth(); 360 int ch = view.getMeasuredHeight(); 361 int cl = (r - l - cw) / 2; 362 int ct = (b - t - ch) / 2; 363 view.layout(cl, ct, cl + cw, ct + ch); 364 } 365 366 @Override 367 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 368 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 369 measureChildren(widthMeasureSpec, heightMeasureSpec); 370 } 371 372 private void updateViews() { 373 if (hidden) { 374 return; 375 } 376 background.setVisibility(View.VISIBLE); 377 timeBar.setVisibility(View.VISIBLE); 378 playPauseReplayView.setImageResource( 379 state == State.PAUSED ? R.drawable.ic_vidcontrol_play : 380 state == State.PLAYING ? R.drawable.ic_vidcontrol_pause : 381 R.drawable.ic_vidcontrol_reload); 382 playPauseReplayView.setVisibility( 383 (state != State.LOADING && state != State.ERROR && 384 !(state == State.ENDED && !canReplay)) 385 ? View.VISIBLE : View.GONE); 386 requestLayout(); 387 } 388 389 // TimeBar listener 390 391 public void onScrubbingStart() { 392 cancelHiding(); 393 listener.onSeekStart(); 394 } 395 396 public void onScrubbingMove(int time) { 397 cancelHiding(); 398 listener.onSeekMove(time); 399 } 400 401 public void onScrubbingEnd(int time) { 402 maybeStartHiding(); 403 listener.onSeekEnd(time); 404 } 405 } 406