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