Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2016 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.tv.dvr.ui;
     18 
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.graphics.Point;
     22 import android.hardware.display.DisplayManager;
     23 import android.media.tv.TvContentRating;
     24 import android.os.Bundle;
     25 import android.media.session.PlaybackState;
     26 import android.media.tv.TvInputManager;
     27 import android.media.tv.TvView;
     28 import android.support.v17.leanback.app.PlaybackOverlayFragment;
     29 import android.support.v17.leanback.widget.ArrayObjectAdapter;
     30 import android.support.v17.leanback.widget.ClassPresenterSelector;
     31 import android.support.v17.leanback.widget.HeaderItem;
     32 import android.support.v17.leanback.widget.ListRow;
     33 import android.support.v17.leanback.widget.ListRowPresenter;
     34 import android.support.v17.leanback.widget.PlaybackControlsRow;
     35 import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
     36 import android.support.v17.leanback.widget.SinglePresenterSelector;
     37 import android.view.Display;
     38 import android.view.View;
     39 import android.view.ViewGroup;
     40 import android.widget.Toast;
     41 import android.text.TextUtils;
     42 import android.util.Log;
     43 
     44 import com.android.tv.R;
     45 import com.android.tv.TvApplication;
     46 import com.android.tv.data.BaseProgram;
     47 import com.android.tv.dvr.RecordedProgram;
     48 import com.android.tv.dialog.PinDialogFragment;
     49 import com.android.tv.dvr.DvrDataManager;
     50 import com.android.tv.dvr.DvrPlayer;
     51 import com.android.tv.dvr.DvrPlaybackMediaSessionHelper;
     52 import com.android.tv.parental.ContentRatingsManager;
     53 import com.android.tv.util.Utils;
     54 
     55 public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment {
     56     // TODO: Handles audio focus. Deals with block and ratings.
     57     private static final String TAG = "DvrPlaybackOverlayFragment";
     58     private static final boolean DEBUG = false;
     59 
     60     private static final String MEDIA_SESSION_TAG = "com.android.tv.dvr.mediasession";
     61     private static final float DISPLAY_ASPECT_RATIO_EPSILON = 0.01f;
     62 
     63     // mProgram is only used to store program from intent. Don't use it elsewhere.
     64     private RecordedProgram mProgram;
     65     private DvrPlaybackMediaSessionHelper mMediaSessionHelper;
     66     private DvrPlaybackControlHelper mPlaybackControlHelper;
     67     private ArrayObjectAdapter mRowsAdapter;
     68     private SortedArrayAdapter<BaseProgram> mRelatedRecordingsRowAdapter;
     69     private DvrPlaybackCardPresenter mRelatedRecordingCardPresenter;
     70     private DvrDataManager mDvrDataManager;
     71     private ContentRatingsManager mContentRatingsManager;
     72     private TvView mTvView;
     73     private View mBlockScreenView;
     74     private ListRow mRelatedRecordingsRow;
     75     private int mExtraPaddingNoRelatedRow;
     76     private int mWindowWidth;
     77     private int mWindowHeight;
     78     private float mAppliedAspectRatio;
     79     private float mWindowAspectRatio;
     80     private boolean mPinChecked;
     81 
     82     @Override
     83     public void onCreate(Bundle savedInstanceState) {
     84         if (DEBUG) Log.d(TAG, "onCreate");
     85         super.onCreate(savedInstanceState);
     86         mExtraPaddingNoRelatedRow = getActivity().getResources()
     87                 .getDimensionPixelOffset(R.dimen.dvr_playback_fragment_extra_padding_top);
     88         mDvrDataManager = TvApplication.getSingletons(getActivity()).getDvrDataManager();
     89         mContentRatingsManager = TvApplication.getSingletons(getContext())
     90                 .getTvInputManagerHelper().getContentRatingsManager();
     91         mProgram = getProgramFromIntent(getActivity().getIntent());
     92         if (mProgram == null) {
     93             Toast.makeText(getActivity(), getString(R.string.dvr_program_not_found),
     94                     Toast.LENGTH_SHORT).show();
     95             getActivity().finish();
     96             return;
     97         }
     98         Point size = new Point();
     99         ((DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE))
    100                 .getDisplay(Display.DEFAULT_DISPLAY).getSize(size);
    101         mWindowWidth = size.x;
    102         mWindowHeight = size.y;
    103         mWindowAspectRatio = mAppliedAspectRatio = (float) mWindowWidth / mWindowHeight;
    104         setBackgroundType(PlaybackOverlayFragment.BG_LIGHT);
    105         setFadingEnabled(true);
    106     }
    107 
    108     @Override
    109     public void onActivityCreated(Bundle savedInstanceState) {
    110         super.onActivityCreated(savedInstanceState);
    111         mTvView = (TvView) getActivity().findViewById(R.id.dvr_tv_view);
    112         mBlockScreenView = getActivity().findViewById(R.id.block_screen);
    113         mMediaSessionHelper = new DvrPlaybackMediaSessionHelper(
    114                 getActivity(), MEDIA_SESSION_TAG, new DvrPlayer(mTvView), this);
    115         mPlaybackControlHelper = new DvrPlaybackControlHelper(getActivity(), this);
    116         setUpRows();
    117         preparePlayback(getActivity().getIntent());
    118         DvrPlayer dvrPlayer = mMediaSessionHelper.getDvrPlayer();
    119         dvrPlayer.setAspectRatioChangedListener(new DvrPlayer.AspectRatioChangedListener() {
    120             @Override
    121             public void onAspectRatioChanged(float videoAspectRatio) {
    122                 updateAspectRatio(videoAspectRatio);
    123             }
    124         });
    125         mPinChecked = getActivity().getIntent()
    126                 .getBooleanExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_PIN_CHECKED, false);
    127         dvrPlayer.setContentBlockedListener(new DvrPlayer.ContentBlockedListener() {
    128             @Override
    129             public void onContentBlocked(TvContentRating rating) {
    130                 if (mPinChecked) {
    131                     mTvView.unblockContent(rating);
    132                     return;
    133                 }
    134                 mBlockScreenView.setVisibility(View.VISIBLE);
    135                 getActivity().getMediaController().getTransportControls().pause();
    136                 new PinDialogFragment(PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_DVR,
    137                         new PinDialogFragment.ResultListener() {
    138                             @Override
    139                             public void done(boolean success) {
    140                                 if (success) {
    141                                     mPinChecked = true;
    142                                     mTvView.unblockContent(rating);
    143                                     mBlockScreenView.setVisibility(View.GONE);
    144                                     getActivity().getMediaController()
    145                                             .getTransportControls().play();
    146                                 }
    147                             }
    148                         }, mContentRatingsManager.getDisplayNameForRating(rating))
    149                         .show(getActivity().getFragmentManager(), PinDialogFragment.DIALOG_TAG);
    150                 }
    151             });
    152     }
    153 
    154     @Override
    155     public void onPause() {
    156         if (DEBUG) Log.d(TAG, "onPause");
    157         super.onPause();
    158         if (mMediaSessionHelper.getPlaybackState() == PlaybackState.STATE_FAST_FORWARDING
    159                 || mMediaSessionHelper.getPlaybackState() == PlaybackState.STATE_REWINDING) {
    160             getActivity().getMediaController().getTransportControls().pause();
    161         }
    162         if (mMediaSessionHelper.getPlaybackState() == PlaybackState.STATE_NONE) {
    163             getActivity().requestVisibleBehind(false);
    164         } else {
    165             getActivity().requestVisibleBehind(true);
    166         }
    167     }
    168 
    169     @Override
    170     public void onDestroy() {
    171         if (DEBUG) Log.d(TAG, "onDestroy");
    172         mPlaybackControlHelper.unregisterCallback();
    173         mMediaSessionHelper.release();
    174         mRelatedRecordingCardPresenter.unbindAllViewHolders();
    175         super.onDestroy();
    176     }
    177 
    178     /**
    179      * Passes the intent to the fragment.
    180      */
    181     public void onNewIntent(Intent intent) {
    182         mProgram = getProgramFromIntent(intent);
    183         if (mProgram == null) {
    184             Toast.makeText(getActivity(), getString(R.string.dvr_program_not_found),
    185                     Toast.LENGTH_SHORT).show();
    186             // Continue playing the original program
    187             return;
    188         }
    189         preparePlayback(intent);
    190     }
    191 
    192     /**
    193      * Should be called when windows' size is changed in order to notify DVR player
    194      * to update it's view width/height and position.
    195      */
    196     public void onWindowSizeChanged(final int windowWidth, final int windowHeight) {
    197         mWindowWidth = windowWidth;
    198         mWindowHeight = windowHeight;
    199         mWindowAspectRatio = (float) mWindowWidth / mWindowHeight;
    200         updateAspectRatio(mAppliedAspectRatio);
    201     }
    202 
    203     public RecordedProgram getNextEpisode(RecordedProgram program) {
    204         int position = mRelatedRecordingsRowAdapter.findInsertPosition(program);
    205         if (position == mRelatedRecordingsRowAdapter.size()) {
    206             return null;
    207         } else {
    208             return (RecordedProgram) mRelatedRecordingsRowAdapter.get(position);
    209         }
    210     }
    211 
    212     void onMediaControllerUpdated() {
    213         mRowsAdapter.notifyArrayItemRangeChanged(0, 1);
    214     }
    215 
    216     private void updateAspectRatio(float videoAspectRatio) {
    217         if (Math.abs(mAppliedAspectRatio - videoAspectRatio) < DISPLAY_ASPECT_RATIO_EPSILON) {
    218             // No need to change
    219             return;
    220         }
    221         if (videoAspectRatio < mWindowAspectRatio) {
    222             int newPadding = (mWindowWidth - Math.round(mWindowHeight * videoAspectRatio)) / 2;
    223             ((ViewGroup) mTvView.getParent()).setPadding(newPadding, 0, newPadding, 0);
    224         } else {
    225             int newPadding = (mWindowHeight - Math.round(mWindowWidth / videoAspectRatio)) / 2;
    226             ((ViewGroup) mTvView.getParent()).setPadding(0, newPadding, 0, newPadding);
    227         }
    228         mAppliedAspectRatio = videoAspectRatio;
    229     }
    230 
    231     private void preparePlayback(Intent intent) {
    232         mMediaSessionHelper.setupPlayback(mProgram, getSeekTimeFromIntent(intent));
    233         getActivity().getMediaController().getTransportControls().prepare();
    234         updateRelatedRecordingsRow();
    235     }
    236 
    237     private void updateRelatedRecordingsRow() {
    238         boolean wasEmpty = (mRelatedRecordingsRowAdapter.size() == 0);
    239         mRelatedRecordingsRowAdapter.clear();
    240         long programId = mProgram.getId();
    241         String seriesId = mProgram.getSeriesId();
    242         if (!TextUtils.isEmpty(seriesId)) {
    243             if (DEBUG) Log.d(TAG, "Update related recordings with:" + seriesId);
    244             for (RecordedProgram program : mDvrDataManager.getRecordedPrograms()) {
    245                 if (seriesId.equals(program.getSeriesId()) && programId != program.getId()) {
    246                     mRelatedRecordingsRowAdapter.add(program);
    247                 }
    248             }
    249         }
    250         View view = getView();
    251         if (mRelatedRecordingsRowAdapter.size() == 0) {
    252             mRowsAdapter.remove(mRelatedRecordingsRow);
    253             view.setPadding(view.getPaddingLeft(), mExtraPaddingNoRelatedRow,
    254                     view.getPaddingRight(), view.getPaddingBottom());
    255         } else if (wasEmpty){
    256             mRowsAdapter.add(mRelatedRecordingsRow);
    257             view.setPadding(view.getPaddingLeft(), 0,
    258                     view.getPaddingRight(), view.getPaddingBottom());
    259         }
    260     }
    261 
    262     private void setUpRows() {
    263         PlaybackControlsRowPresenter controlsRowPresenter =
    264                 mPlaybackControlHelper.createControlsRowAndPresenter();
    265 
    266         ClassPresenterSelector selector = new ClassPresenterSelector();
    267         selector.addClassPresenter(PlaybackControlsRow.class, controlsRowPresenter);
    268         selector.addClassPresenter(ListRow.class, new ListRowPresenter());
    269 
    270         mRowsAdapter = new ArrayObjectAdapter(selector);
    271         mRowsAdapter.add(mPlaybackControlHelper.getControlsRow());
    272         mRelatedRecordingsRow = getRelatedRecordingsRow();
    273         setAdapter(mRowsAdapter);
    274     }
    275 
    276     private ListRow getRelatedRecordingsRow() {
    277         mRelatedRecordingCardPresenter = new DvrPlaybackCardPresenter(getActivity());
    278         mRelatedRecordingsRowAdapter = new RelatedRecordingsAdapter(mRelatedRecordingCardPresenter);
    279         HeaderItem header = new HeaderItem(0,
    280                 getActivity().getString(R.string.dvr_playback_related_recordings));
    281         return new ListRow(header, mRelatedRecordingsRowAdapter);
    282     }
    283 
    284     private RecordedProgram getProgramFromIntent(Intent intent) {
    285         long programId = intent.getLongExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, -1);
    286         return mDvrDataManager.getRecordedProgram(programId);
    287     }
    288 
    289     private long getSeekTimeFromIntent(Intent intent) {
    290         return intent.getLongExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_SEEK_TIME,
    291                 TvInputManager.TIME_SHIFT_INVALID_TIME);
    292     }
    293 
    294     private class RelatedRecordingsAdapter extends SortedArrayAdapter<BaseProgram> {
    295         RelatedRecordingsAdapter(DvrPlaybackCardPresenter presenter) {
    296             super(new SinglePresenterSelector(presenter), BaseProgram.EPISODE_COMPARATOR);
    297         }
    298 
    299         @Override
    300         long getId(BaseProgram item) {
    301             return item.getId();
    302         }
    303     }
    304 }