Home | History | Annotate | Download | only in app
      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