Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
      5  * in compliance with the License. You may obtain a copy of the License at
      6  *
      7  * http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the License
     10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
     11  * or implied. See the License for the specific language governing permissions and limitations under
     12  * the License.
     13  */
     14 package com.example.android.tvleanback.ui;
     15 
     16 import android.app.Activity;
     17 import android.content.Intent;
     18 import android.graphics.Bitmap;
     19 import android.media.MediaMetadata;
     20 import android.media.MediaPlayer;
     21 import android.media.session.MediaSession;
     22 import android.media.session.PlaybackState;
     23 import android.net.Uri;
     24 import android.os.Bundle;
     25 import android.util.DisplayMetrics;
     26 import android.util.Log;
     27 import android.widget.FrameLayout;
     28 import android.widget.VideoView;
     29 
     30 import com.bumptech.glide.Glide;
     31 import com.bumptech.glide.request.animation.GlideAnimation;
     32 import com.bumptech.glide.request.target.SimpleTarget;
     33 import com.example.android.tvleanback.R;
     34 import com.example.android.tvleanback.model.Movie;
     35 
     36 /**
     37  * PlaybackOverlayActivity for video playback that loads PlaybackOverlayFragment
     38  */
     39 public class PlaybackOverlayActivity extends Activity implements
     40         PlaybackOverlayFragment.OnPlayPauseClickedListener {
     41     private static final String TAG = "PlaybackOverlayActivity";
     42 
     43     private static final double MEDIA_HEIGHT = 0.95;
     44     private static final double MEDIA_WIDTH = 0.95;
     45     private static final double MEDIA_TOP_MARGIN = 0.025;
     46     private static final double MEDIA_RIGHT_MARGIN = 0.025;
     47     private static final double MEDIA_BOTTOM_MARGIN = 0.025;
     48     private static final double MEDIA_LEFT_MARGIN = 0.025;
     49     private VideoView mVideoView;
     50     private LeanbackPlaybackState mPlaybackState = LeanbackPlaybackState.IDLE;
     51     private MediaSession mSession;
     52 
     53     /**
     54      * Called when the activity is first created.
     55      */
     56     @Override
     57     public void onCreate(Bundle savedInstanceState) {
     58         super.onCreate(savedInstanceState);
     59         setContentView(R.layout.playback_controls);
     60         loadViews();
     61         //Example for handling resizing view for overscan
     62         //overScan();
     63 
     64         mSession = new MediaSession (this, "LeanbackSampleApp");
     65         mSession.setCallback(new MediaSessionCallback());
     66         mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
     67                 MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
     68 
     69         mSession.setActive(true);
     70 
     71     }
     72 
     73     @Override
     74     public void onDestroy() {
     75         super.onDestroy();
     76         mVideoView.suspend();
     77     }
     78 
     79     /**
     80      * Implementation of OnPlayPauseClickedListener
     81      */
     82     public void onFragmentPlayPause(Movie movie, int position, Boolean playPause) {
     83         mVideoView.setVideoPath(movie.getVideoUrl());
     84 
     85         if (position == 0 || mPlaybackState == LeanbackPlaybackState.IDLE) {
     86             setupCallbacks();
     87             mPlaybackState = LeanbackPlaybackState.IDLE;
     88         }
     89 
     90         if (playPause && mPlaybackState != LeanbackPlaybackState.PLAYING) {
     91             mPlaybackState = LeanbackPlaybackState.PLAYING;
     92             if (position > 0) {
     93                 mVideoView.seekTo(position);
     94                 mVideoView.start();
     95             }
     96         } else {
     97             mPlaybackState = LeanbackPlaybackState.PAUSED;
     98             mVideoView.pause();
     99         }
    100         updatePlaybackState(position);
    101         updateMetadata(movie);
    102     }
    103 
    104     /**
    105      * Implementation of OnPlayPauseClickedListener
    106      */
    107     public void onFragmentFfwRwd(Movie movie, int position) {
    108         mVideoView.setVideoPath(movie.getVideoUrl());
    109 
    110         Log.d(TAG, "seek current time: " + position);
    111         if (mPlaybackState == LeanbackPlaybackState.PLAYING) {
    112             if (position > 0) {
    113                 mVideoView.seekTo(position);
    114                 mVideoView.start();
    115             }
    116         }
    117     }
    118 
    119     private void updatePlaybackState(int position) {
    120         PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
    121                 .setActions(getAvailableActions());
    122         int state = PlaybackState.STATE_PLAYING;
    123         if (mPlaybackState == LeanbackPlaybackState.PAUSED) {
    124             state = PlaybackState.STATE_PAUSED;
    125         }
    126         stateBuilder.setState(state, position, 1.0f);
    127         mSession.setPlaybackState(stateBuilder.build());
    128     }
    129 
    130     private long getAvailableActions() {
    131         long actions = PlaybackState.ACTION_PLAY |
    132                 PlaybackState.ACTION_PLAY_FROM_MEDIA_ID |
    133                 PlaybackState.ACTION_PLAY_FROM_SEARCH;
    134 
    135         if (mPlaybackState == LeanbackPlaybackState.PLAYING) {
    136             actions |= PlaybackState.ACTION_PAUSE;
    137         }
    138 
    139         return actions;
    140     }
    141 
    142     private void updateMetadata(final Movie movie) {
    143         final MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
    144 
    145         String title = movie.getTitle().replace("_", " -");
    146 
    147         metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, title);
    148         metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE,
    149                 movie.getDescription());
    150         metadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI,
    151                 movie.getCardImageUrl());
    152 
    153         // And at minimum the title and artist for legacy support
    154         metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, title);
    155         metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, movie.getStudio());
    156 
    157         Glide.with(this)
    158             .load(Uri.parse(movie.getCardImageUrl()))
    159             .asBitmap()
    160             .into(new SimpleTarget<Bitmap>(500, 500) {
    161                 @Override
    162                 public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
    163                     metadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_ART, bitmap);
    164                     mSession.setMetadata(metadataBuilder.build());
    165                 }
    166             });
    167     }
    168 
    169     private void loadViews() {
    170         mVideoView = (VideoView) findViewById(R.id.videoView);
    171     }
    172 
    173     /**
    174      * Example for handling resizing content for overscan.  Typically you won't need to resize which
    175      * is why overScan(); is commented out.
    176      */
    177     private void overScan() {
    178         DisplayMetrics metrics = new DisplayMetrics();
    179         getWindowManager().getDefaultDisplay().getMetrics(metrics);
    180         int w = (int) (metrics.widthPixels * MEDIA_WIDTH);
    181         int h = (int) (metrics.heightPixels * MEDIA_HEIGHT);
    182         int marginLeft = (int) (metrics.widthPixels * MEDIA_LEFT_MARGIN);
    183         int marginTop = (int) (metrics.heightPixels * MEDIA_TOP_MARGIN);
    184         int marginRight = (int) (metrics.widthPixels * MEDIA_RIGHT_MARGIN);
    185         int marginBottom = (int) (metrics.heightPixels * MEDIA_BOTTOM_MARGIN);
    186         FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(w, h);
    187         lp.setMargins(marginLeft, marginTop, marginRight, marginBottom);
    188         mVideoView.setLayoutParams(lp);
    189     }
    190 
    191     private void setupCallbacks() {
    192 
    193         mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
    194 
    195             @Override
    196             public boolean onError(MediaPlayer mp, int what, int extra) {
    197                 String msg = "";
    198                 if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) {
    199                     msg = getString(R.string.video_error_media_load_timeout);
    200                 } else if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
    201                     msg = getString(R.string.video_error_server_inaccessible);
    202                 } else {
    203                     msg = getString(R.string.video_error_unknown_error);
    204                 }
    205                 mVideoView.stopPlayback();
    206                 mPlaybackState = LeanbackPlaybackState.IDLE;
    207                 return false;
    208             }
    209         });
    210 
    211 
    212         mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    213             @Override
    214             public void onPrepared(MediaPlayer mp) {
    215                 if (mPlaybackState == LeanbackPlaybackState.PLAYING) {
    216                     mVideoView.start();
    217                 }
    218             }
    219         });
    220 
    221 
    222         mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    223             @Override
    224             public void onCompletion(MediaPlayer mp) {
    225                 mPlaybackState = LeanbackPlaybackState.IDLE;
    226             }
    227         });
    228 
    229     }
    230 
    231     @Override
    232     public void onResume() {
    233         super.onResume();
    234     }
    235 
    236     @Override
    237     public void onPause() {
    238         super.onPause();
    239         if (mVideoView.isPlaying()) {
    240             if (!requestVisibleBehind(true)) {
    241                 // Try to play behind launcher, but if it fails, stop playback.
    242                 stopPlayback();
    243             }
    244         } else {
    245             requestVisibleBehind(false);
    246         }
    247     }
    248 
    249     @Override
    250     protected void onStop() {
    251         super.onStop();
    252         mSession.release();
    253     }
    254 
    255     @Override
    256     public void onVisibleBehindCanceled() {
    257         super.onVisibleBehindCanceled();
    258         stopPlayback();
    259     }
    260 
    261     private void stopPlayback() {
    262         if (mVideoView != null) {
    263             mVideoView.stopPlayback();
    264         }
    265     }
    266 
    267     @Override
    268     public boolean onSearchRequested() {
    269         startActivity(new Intent(this, SearchActivity.class));
    270         return true;
    271     }
    272 
    273     /*
    274      * List of various states that we can be in
    275      */
    276     public static enum LeanbackPlaybackState {
    277         PLAYING, PAUSED, BUFFERING, IDLE;
    278     }
    279 
    280     private class MediaSessionCallback extends MediaSession.Callback {
    281     }
    282 }
    283