Home | History | Annotate | Download | only in dvr
      1 /*
      2  * Copyright (C) 2015 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;
     18 
     19 import android.annotation.TargetApi;
     20 import android.app.Activity;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.media.tv.TvInputManager;
     24 import android.os.Build;
     25 import android.os.Bundle;
     26 import android.support.annotation.MainThread;
     27 import android.support.annotation.Nullable;
     28 import android.support.v4.app.ActivityOptionsCompat;
     29 import android.text.TextUtils;
     30 import android.widget.ImageView;
     31 import android.widget.Toast;
     32 
     33 import com.android.tv.MainActivity;
     34 import com.android.tv.R;
     35 import com.android.tv.TvApplication;
     36 import com.android.tv.common.SoftPreconditions;
     37 import com.android.tv.data.Channel;
     38 import com.android.tv.data.Program;
     39 import com.android.tv.dvr.ui.DvrDetailsActivity;
     40 import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment;
     41 import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrAlreadyRecordedDialogFragment;
     42 import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrAlreadyScheduledDialogFragment;
     43 import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrChannelRecordDurationOptionDialogFragment;
     44 import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrChannelWatchConflictDialogFragment;
     45 import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrInsufficientSpaceErrorDialogFragment;
     46 import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrMissingStorageErrorDialogFragment;
     47 import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrProgramConflictDialogFragment;
     48 import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrScheduleDialogFragment;
     49 import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrSmallSizedStorageErrorDialogFragment;
     50 import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrStopRecordingDialogFragment;
     51 import com.android.tv.dvr.ui.DvrSchedulesActivity;
     52 import com.android.tv.dvr.ui.DvrSeriesDeletionActivity;
     53 import com.android.tv.dvr.ui.DvrSeriesScheduledDialogActivity;
     54 import com.android.tv.dvr.ui.DvrSeriesSettingsActivity;
     55 import com.android.tv.dvr.ui.DvrStopRecordingFragment;
     56 import com.android.tv.dvr.ui.DvrStopSeriesRecordingDialogFragment;
     57 import com.android.tv.dvr.ui.DvrStopSeriesRecordingFragment;
     58 import com.android.tv.dvr.ui.HalfSizedDialogFragment;
     59 import com.android.tv.dvr.ui.list.DvrSchedulesFragment;
     60 import com.android.tv.dvr.ui.list.DvrSeriesSchedulesFragment;
     61 import com.android.tv.util.Utils;
     62 
     63 import java.util.Collections;
     64 import java.util.List;
     65 
     66 /**
     67  * A helper class for DVR UI.
     68  */
     69 @MainThread
     70 @TargetApi(Build.VERSION_CODES.N)
     71 public class DvrUiHelper {
     72     /**
     73      * Handles the action to create the new schedule. It returns {@code true} if the schedule is
     74      * added and there's no additional UI, otherwise {@code false}.
     75      */
     76     public static boolean handleCreateSchedule(MainActivity activity, Program program) {
     77         if (program == null) {
     78             return false;
     79         }
     80         DvrManager dvrManager = TvApplication.getSingletons(activity).getDvrManager();
     81         if (!program.isEpisodic()) {
     82             // One time recording.
     83             dvrManager.addSchedule(program);
     84             if (!dvrManager.getConflictingSchedules(program).isEmpty()) {
     85                 DvrUiHelper.showScheduleConflictDialog(activity, program);
     86                 return false;
     87             }
     88         } else {
     89             SeriesRecording seriesRecording = dvrManager.getSeriesRecording(program);
     90             if (seriesRecording == null || seriesRecording.isStopped()) {
     91                 DvrUiHelper.showScheduleDialog(activity, program);
     92                 return false;
     93             } else {
     94                 // Show recorded program rather than the schedule.
     95                 RecordedProgram recordedProgram = dvrManager.getRecordedProgram(program.getTitle(),
     96                         program.getSeasonNumber(), program.getEpisodeNumber());
     97                 if (recordedProgram != null) {
     98                     DvrUiHelper.showAlreadyRecordedDialog(activity, program);
     99                     return false;
    100                 }
    101                 ScheduledRecording duplicate = dvrManager.getScheduledRecording(program.getTitle(),
    102                         program.getSeasonNumber(), program.getEpisodeNumber());
    103                 if (duplicate != null
    104                         && (duplicate.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED
    105                         || duplicate.getState()
    106                         == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) {
    107                     DvrUiHelper.showAlreadyScheduleDialog(activity, program);
    108                     return false;
    109                 }
    110                 // Just add the schedule.
    111                 dvrManager.addSchedule(program);
    112             }
    113         }
    114         return true;
    115 
    116     }
    117 
    118     /**
    119      * Checks if the storage status is good for recording and shows error messages if needed.
    120      *
    121      * @return true if the storage status is fine to be recorded for {@code inputId}.
    122      */
    123     public static boolean checkStorageStatusAndShowErrorMessage(Activity activity, String inputId) {
    124         if (!Utils.isBundledInput(inputId)) {
    125             return true;
    126         }
    127         DvrStorageStatusManager dvrStorageStatusManager =
    128                 TvApplication.getSingletons(activity).getDvrStorageStatusManager();
    129         int status = dvrStorageStatusManager.getDvrStorageStatus();
    130         if (status == DvrStorageStatusManager.STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL) {
    131             showDvrSmallSizedStorageErrorDialog(activity);
    132             return false;
    133         } else if (status == DvrStorageStatusManager.STORAGE_STATUS_MISSING) {
    134             showDvrMissingStorageErrorDialog(activity, inputId);
    135             return false;
    136         } else if (status == DvrStorageStatusManager.STORAGE_STATUS_FREE_SPACE_INSUFFICIENT) {
    137             // TODO: handle insufficient storage case.
    138             return true;
    139         } else {
    140             return true;
    141         }
    142     }
    143 
    144     /**
    145      * Shows the schedule dialog.
    146      */
    147     public static void showScheduleDialog(MainActivity activity, Program program) {
    148         if (SoftPreconditions.checkNotNull(program) == null) {
    149             return;
    150         }
    151         Bundle args = new Bundle();
    152         args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program);
    153         showDialogFragment(activity, new DvrScheduleDialogFragment(), args, true, true);
    154     }
    155 
    156     /**
    157      * Shows the recording duration options dialog.
    158      */
    159     public static void showChannelRecordDurationOptions(MainActivity activity, Channel channel) {
    160         if (SoftPreconditions.checkNotNull(channel) == null) {
    161             return;
    162         }
    163         Bundle args = new Bundle();
    164         args.putLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID, channel.getId());
    165         showDialogFragment(activity, new DvrChannelRecordDurationOptionDialogFragment(), args);
    166     }
    167 
    168     /**
    169      * Shows the dialog which says that the new schedule conflicts with others.
    170      */
    171     public static void showScheduleConflictDialog(MainActivity activity, Program program) {
    172         if (program == null) {
    173             return;
    174         }
    175         Bundle args = new Bundle();
    176         args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program);
    177         showDialogFragment(activity, new DvrProgramConflictDialogFragment(), args, false, true);
    178     }
    179 
    180     /**
    181      * Shows the conflict dialog for the channel watching.
    182      */
    183     public static void showChannelWatchConflictDialog(MainActivity activity, Channel channel) {
    184         if (channel == null) {
    185             return;
    186         }
    187         Bundle args = new Bundle();
    188         args.putLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID, channel.getId());
    189         showDialogFragment(activity, new DvrChannelWatchConflictDialogFragment(), args);
    190     }
    191 
    192     /**
    193      * Shows DVR insufficient space error dialog.
    194      */
    195     public static void showDvrInsufficientSpaceErrorDialog(MainActivity activity) {
    196         showDialogFragment(activity, new DvrInsufficientSpaceErrorDialogFragment(), null);
    197         Utils.clearRecordingFailedReason(activity,
    198                 TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE);
    199     }
    200 
    201     /**
    202      * Shows DVR missing storage error dialog.
    203      */
    204     private static void showDvrMissingStorageErrorDialog(Activity activity, String inputId) {
    205         SoftPreconditions.checkArgument(!TextUtils.isEmpty(inputId));
    206         Bundle args = new Bundle();
    207         args.putString(DvrHalfSizedDialogFragment.KEY_INPUT_ID, inputId);
    208         showDialogFragment(activity, new DvrMissingStorageErrorDialogFragment(), args);
    209     }
    210 
    211     /**
    212      * Shows DVR small sized storage error dialog.
    213      */
    214     public static void showDvrSmallSizedStorageErrorDialog(Activity activity) {
    215         showDialogFragment(activity, new DvrSmallSizedStorageErrorDialogFragment(), null);
    216     }
    217 
    218     /**
    219      * Shows stop recording dialog.
    220      */
    221     public static void showStopRecordingDialog(Activity activity, long channelId, int reason,
    222             HalfSizedDialogFragment.OnActionClickListener listener) {
    223         Bundle args = new Bundle();
    224         args.putLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID, channelId);
    225         args.putInt(DvrStopRecordingFragment.KEY_REASON, reason);
    226         DvrHalfSizedDialogFragment fragment = new DvrStopRecordingDialogFragment();
    227         fragment.setOnActionClickListener(listener);
    228         showDialogFragment(activity, fragment, args);
    229     }
    230 
    231     /**
    232      * Shows "already scheduled" dialog.
    233      */
    234     public static void showAlreadyScheduleDialog(MainActivity activity, Program program) {
    235         if (program == null) {
    236             return;
    237         }
    238         Bundle args = new Bundle();
    239         args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program);
    240         showDialogFragment(activity, new DvrAlreadyScheduledDialogFragment(), args, false, true);
    241     }
    242 
    243     /**
    244      * Shows "already recorded" dialog.
    245      */
    246     public static void showAlreadyRecordedDialog(MainActivity activity, Program program) {
    247         if (program == null) {
    248             return;
    249         }
    250         Bundle args = new Bundle();
    251         args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program);
    252         showDialogFragment(activity, new DvrAlreadyRecordedDialogFragment(), args, false, true);
    253     }
    254 
    255     private static void showDialogFragment(Activity activity,
    256             DvrHalfSizedDialogFragment dialogFragment, Bundle args) {
    257         showDialogFragment(activity, dialogFragment, args, false, false);
    258     }
    259 
    260     private static void showDialogFragment(Activity activity,
    261             DvrHalfSizedDialogFragment dialogFragment, Bundle args, boolean keepSidePanelHistory,
    262             boolean keepProgramGuide) {
    263         dialogFragment.setArguments(args);
    264         if (activity instanceof MainActivity) {
    265             ((MainActivity) activity).getOverlayManager()
    266                     .showDialogFragment(DvrHalfSizedDialogFragment.DIALOG_TAG, dialogFragment,
    267                             keepSidePanelHistory, keepProgramGuide);
    268         } else {
    269             dialogFragment.show(activity.getFragmentManager(),
    270                     DvrHalfSizedDialogFragment.DIALOG_TAG);
    271         }
    272     }
    273 
    274     /**
    275      * Checks whether channel watch conflict dialog is open or not.
    276      */
    277     public static boolean isChannelWatchConflictDialogShown(MainActivity activity) {
    278         return activity.getOverlayManager().getCurrentDialog() instanceof
    279                 DvrChannelWatchConflictDialogFragment;
    280     }
    281 
    282     private static ScheduledRecording getEarliestScheduledRecording(List<ScheduledRecording>
    283             recordings) {
    284         ScheduledRecording earlistScheduledRecording = null;
    285         if (!recordings.isEmpty()) {
    286             Collections.sort(recordings,
    287                     ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR);
    288             earlistScheduledRecording = recordings.get(0);
    289         }
    290         return earlistScheduledRecording;
    291     }
    292 
    293     /**
    294      * Shows the schedules activity to resolve the tune conflict.
    295      */
    296     public static void startSchedulesActivityForTuneConflict(Context context, Channel channel) {
    297         if (channel == null) {
    298             return;
    299         }
    300         List<ScheduledRecording> conflicts = TvApplication.getSingletons(context).getDvrManager()
    301                 .getConflictingSchedulesForTune(channel.getId());
    302         startSchedulesActivity(context, getEarliestScheduledRecording(conflicts));
    303     }
    304 
    305     /**
    306      * Shows the schedules activity to resolve the one time recording conflict.
    307      */
    308     public static void startSchedulesActivityForOneTimeRecordingConflict(Context context,
    309             List<ScheduledRecording> conflicts) {
    310         startSchedulesActivity(context, getEarliestScheduledRecording(conflicts));
    311     }
    312 
    313     /**
    314      * Shows the schedules activity with full schedule.
    315      */
    316     public static void startSchedulesActivity(Context context, ScheduledRecording
    317             focusedScheduledRecording) {
    318         Intent intent = new Intent(context, DvrSchedulesActivity.class);
    319         intent.putExtra(DvrSchedulesActivity.KEY_SCHEDULES_TYPE,
    320                 DvrSchedulesActivity.TYPE_FULL_SCHEDULE);
    321         if (focusedScheduledRecording != null) {
    322             intent.putExtra(DvrSchedulesFragment.SCHEDULES_KEY_SCHEDULED_RECORDING,
    323                     focusedScheduledRecording);
    324         }
    325         context.startActivity(intent);
    326     }
    327 
    328     /**
    329      * Shows the schedules activity for series recording.
    330      */
    331     public static void startSchedulesActivityForSeries(Context context,
    332             SeriesRecording seriesRecording) {
    333         Intent intent = new Intent(context, DvrSchedulesActivity.class);
    334         intent.putExtra(DvrSchedulesActivity.KEY_SCHEDULES_TYPE,
    335                 DvrSchedulesActivity.TYPE_SERIES_SCHEDULE);
    336         intent.putExtra(DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_RECORDING,
    337                 seriesRecording);
    338         context.startActivity(intent);
    339     }
    340 
    341     /**
    342      * Shows the series settings activity.
    343      *
    344      * @param channelIds Channel ID list which has programs belonging to the series.
    345      */
    346     public static void startSeriesSettingsActivity(Context context, long seriesRecordingId,
    347             @Nullable long[] channelIds, boolean removeEmptySeriesSchedule,
    348             boolean isWindowTranslucent, boolean showViewScheduleOptionInDialog) {
    349         Intent intent = new Intent(context, DvrSeriesSettingsActivity.class);
    350         intent.putExtra(DvrSeriesSettingsActivity.SERIES_RECORDING_ID, seriesRecordingId);
    351         intent.putExtra(DvrSeriesSettingsActivity.CHANNEL_ID_LIST, channelIds);
    352         intent.putExtra(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING,
    353                 removeEmptySeriesSchedule);
    354         intent.putExtra(DvrSeriesSettingsActivity.IS_WINDOW_TRANSLUCENT, isWindowTranslucent);
    355         intent.putExtra(DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG,
    356                 showViewScheduleOptionInDialog);
    357         context.startActivity(intent);
    358     }
    359 
    360     /**
    361      * Shows "series recording scheduled" dialog activity.
    362      */
    363     public static void StartSeriesScheduledDialogActivity(Context context,
    364             SeriesRecording seriesRecording, boolean showViewScheduleOptionInDialog) {
    365         if (seriesRecording == null) {
    366             return;
    367         }
    368         Intent intent = new Intent(context, DvrSeriesScheduledDialogActivity.class);
    369         intent.putExtra(DvrSeriesScheduledDialogActivity.SERIES_RECORDING_ID,
    370                 seriesRecording.getId());
    371         intent.putExtra(DvrSeriesScheduledDialogActivity.SHOW_VIEW_SCHEDULE_OPTION,
    372                 showViewScheduleOptionInDialog);
    373         context.startActivity(intent);
    374     }
    375 
    376     /**
    377      * Shows the details activity for the DVR items. The type of DVR items may be
    378      * {@link ScheduledRecording}, {@link RecordedProgram}, or {@link SeriesRecording}.
    379      */
    380     public static void startDetailsActivity(Activity activity, Object dvrItem,
    381             @Nullable ImageView imageView, boolean hideViewSchedule) {
    382         if (dvrItem == null) {
    383             return;
    384         }
    385         Intent intent = new Intent(activity, DvrDetailsActivity.class);
    386         long recordingId;
    387         int viewType;
    388         if (dvrItem instanceof ScheduledRecording) {
    389             ScheduledRecording schedule = (ScheduledRecording) dvrItem;
    390             recordingId = schedule.getId();
    391             if (schedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED) {
    392                 viewType = DvrDetailsActivity.SCHEDULED_RECORDING_VIEW;
    393             } else if (schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
    394                 viewType = DvrDetailsActivity.CURRENT_RECORDING_VIEW;
    395             } else {
    396                 return;
    397             }
    398         } else if (dvrItem instanceof RecordedProgram) {
    399             recordingId = ((RecordedProgram) dvrItem).getId();
    400             viewType = DvrDetailsActivity.RECORDED_PROGRAM_VIEW;
    401         } else if (dvrItem instanceof SeriesRecording) {
    402             recordingId = ((SeriesRecording) dvrItem).getId();
    403             viewType = DvrDetailsActivity.SERIES_RECORDING_VIEW;
    404         } else {
    405             return;
    406         }
    407         intent.putExtra(DvrDetailsActivity.RECORDING_ID, recordingId);
    408         intent.putExtra(DvrDetailsActivity.DETAILS_VIEW_TYPE, viewType);
    409         intent.putExtra(DvrDetailsActivity.HIDE_VIEW_SCHEDULE, hideViewSchedule);
    410         Bundle bundle = null;
    411         if (imageView != null) {
    412             bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, imageView,
    413                     DvrDetailsActivity.SHARED_ELEMENT_NAME).toBundle();
    414         }
    415         activity.startActivity(intent, bundle);
    416     }
    417 
    418     /**
    419      * Shows the cancel all dialog for series schedules list.
    420      */
    421     public static void showCancelAllSeriesRecordingDialog(DvrSchedulesActivity activity,
    422             SeriesRecording seriesRecording) {
    423         DvrStopSeriesRecordingDialogFragment dvrStopSeriesRecordingDialogFragment =
    424                 new DvrStopSeriesRecordingDialogFragment();
    425         Bundle arguments = new Bundle();
    426         arguments.putParcelable(DvrStopSeriesRecordingFragment.KEY_SERIES_RECORDING,
    427                 seriesRecording);
    428         dvrStopSeriesRecordingDialogFragment.setArguments(arguments);
    429         dvrStopSeriesRecordingDialogFragment.show(activity.getFragmentManager(),
    430                 DvrStopSeriesRecordingDialogFragment.DIALOG_TAG);
    431     }
    432 
    433     /**
    434      * Shows the series deletion activity.
    435      */
    436     public static void startSeriesDeletionActivity(Context context, long seriesRecordingId) {
    437         Intent intent = new Intent(context, DvrSeriesDeletionActivity.class);
    438         intent.putExtra(DvrSeriesDeletionActivity.SERIES_RECORDING_ID, seriesRecordingId);
    439         context.startActivity(intent);
    440     }
    441 
    442     public static void showAddScheduleToast(Context context,
    443             String title, long startTimeMs, long endTimeMs) {
    444         String msg = (startTimeMs > System.currentTimeMillis()) ?
    445             context.getString(R.string.dvr_msg_program_scheduled, title)
    446             : context.getString(R.string.dvr_msg_current_program_scheduled, title,
    447                     Utils.toTimeString(endTimeMs, false));
    448         Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
    449     }
    450 }
    451