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