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 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