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.graphics.drawable.Drawable;
     20 import android.media.tv.TvInputInfo;
     21 import android.os.Bundle;
     22 import android.support.annotation.NonNull;
     23 import android.support.annotation.Nullable;
     24 import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
     25 import android.support.v17.leanback.widget.GuidedAction;
     26 import android.util.Log;
     27 import android.view.LayoutInflater;
     28 import android.view.View;
     29 import android.view.ViewGroup;
     30 
     31 import com.android.tv.MainActivity;
     32 import com.android.tv.R;
     33 import com.android.tv.TvApplication;
     34 import com.android.tv.common.SoftPreconditions;
     35 import com.android.tv.data.Channel;
     36 import com.android.tv.data.Program;
     37 import com.android.tv.dvr.recorder.ConflictChecker;
     38 import com.android.tv.dvr.recorder.ConflictChecker.OnUpcomingConflictChangeListener;
     39 import com.android.tv.dvr.data.ScheduledRecording;
     40 import com.android.tv.util.Utils;
     41 
     42 import java.util.ArrayList;
     43 import java.util.Collections;
     44 import java.util.HashSet;
     45 import java.util.List;
     46 
     47 public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
     48     private static final String TAG = "DvrConflictFragment";
     49     private static final boolean DEBUG = false;
     50 
     51     private static final int ACTION_DELETE_CONFLICT = 1;
     52     private static final int ACTION_CANCEL = 2;
     53     private static final int ACTION_VIEW_SCHEDULES = 3;
     54 
     55     // The program count which will be listed in the description. This is the number of the
     56     // program strings in R.plurals.dvr_program_conflict_dialog_description_many.
     57     private static final int LISTED_PROGRAM_COUNT = 2;
     58 
     59     protected List<ScheduledRecording> mConflicts;
     60 
     61     void setConflicts(List<ScheduledRecording> conflicts) {
     62         mConflicts = conflicts;
     63     }
     64 
     65     List<ScheduledRecording> getConflicts() {
     66         return mConflicts;
     67     }
     68 
     69     @Override
     70     public int onProvideTheme() {
     71         return R.style.Theme_TV_Dvr_Conflict_GuidedStep;
     72     }
     73 
     74     @Override
     75     public void onCreateActions(@NonNull List<GuidedAction> actions,
     76             Bundle savedInstanceState) {
     77         actions.add(new GuidedAction.Builder(getContext())
     78                 .clickAction(GuidedAction.ACTION_ID_OK)
     79                 .build());
     80         actions.add(new GuidedAction.Builder(getContext())
     81                 .id(ACTION_VIEW_SCHEDULES)
     82                 .title(R.string.dvr_action_view_schedules)
     83                 .build());
     84     }
     85 
     86     @Override
     87     public void onTrackedGuidedActionClicked(GuidedAction action) {
     88         if (action.getId() == ACTION_VIEW_SCHEDULES) {
     89             DvrUiHelper.startSchedulesActivityForOneTimeRecordingConflict(
     90                     getContext(), getConflicts());
     91         }
     92         dismissDialog();
     93     }
     94 
     95     @Override
     96     public String getTrackerLabelForGuidedAction(GuidedAction action) {
     97         long actionId = getId();
     98         if (actionId == ACTION_VIEW_SCHEDULES) {
     99             return "view-schedules";
    100         } else {
    101             return super.getTrackerLabelForGuidedAction(action);
    102         }
    103     }
    104 
    105     String getConflictDescription() {
    106         List<String> titles = new ArrayList<>();
    107         HashSet<String> titleSet = new HashSet<>();
    108         for (ScheduledRecording schedule : getConflicts()) {
    109             String scheduleTitle = getScheduleTitle(schedule);
    110             if (scheduleTitle != null && !titleSet.contains(scheduleTitle)) {
    111                 titles.add(scheduleTitle);
    112                 titleSet.add(scheduleTitle);
    113             }
    114         }
    115         switch (titles.size()) {
    116             case 0:
    117                 Log.i(TAG, "Conflict has been resolved by any reason. Maybe input might have"
    118                         + " been deleted.");
    119                 return null;
    120             case 1:
    121                 return getResources().getString(
    122                         R.string.dvr_program_conflict_dialog_description_1, titles.get(0));
    123             case 2:
    124                 return getResources().getString(
    125                         R.string.dvr_program_conflict_dialog_description_2, titles.get(0),
    126                         titles.get(1));
    127             case 3:
    128                 return getResources().getString(
    129                         R.string.dvr_program_conflict_dialog_description_3, titles.get(0),
    130                         titles.get(1));
    131             default:
    132                 return getResources().getQuantityString(
    133                         R.plurals.dvr_program_conflict_dialog_description_many,
    134                         titles.size() - LISTED_PROGRAM_COUNT, titles.get(0), titles.get(1),
    135                         titles.size() - LISTED_PROGRAM_COUNT);
    136         }
    137     }
    138 
    139     @Nullable
    140     private String getScheduleTitle(ScheduledRecording schedule) {
    141         if (schedule.getType() == ScheduledRecording.TYPE_TIMED) {
    142             Channel channel = TvApplication.getSingletons(getContext()).getChannelDataManager()
    143                     .getChannel(schedule.getChannelId());
    144             if (channel != null) {
    145                 return channel.getDisplayName();
    146             } else {
    147                 return null;
    148             }
    149         } else {
    150             return schedule.getProgramTitle();
    151         }
    152     }
    153 
    154     /**
    155      * A fragment to show the program conflict.
    156      */
    157     public static class DvrProgramConflictFragment extends DvrConflictFragment {
    158         private Program mProgram;
    159         @Override
    160         public View onCreateView(LayoutInflater inflater, ViewGroup container,
    161                 Bundle savedInstanceState) {
    162             Bundle args = getArguments();
    163             if (args != null) {
    164                 mProgram = args.getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM);
    165             }
    166             SoftPreconditions.checkArgument(mProgram != null);
    167             TvInputInfo input = Utils.getTvInputInfoForProgram(getContext(), mProgram);
    168             SoftPreconditions.checkNotNull(input);
    169             List<ScheduledRecording> conflicts = null;
    170             if (input != null) {
    171                 conflicts = TvApplication.getSingletons(getContext()).getDvrManager()
    172                         .getConflictingSchedules(mProgram);
    173             }
    174             if (conflicts == null) {
    175                 conflicts = Collections.emptyList();
    176             }
    177             if (conflicts.isEmpty()) {
    178                 dismissDialog();
    179             }
    180             setConflicts(conflicts);
    181             return super.onCreateView(inflater, container, savedInstanceState);
    182         }
    183 
    184         @NonNull
    185         @Override
    186         public Guidance onCreateGuidance(Bundle savedInstanceState) {
    187             String title = getResources().getString(R.string.dvr_program_conflict_dialog_title);
    188             String descriptionPrefix = getString(
    189                     R.string.dvr_program_conflict_dialog_description_prefix, mProgram.getTitle());
    190             String description = getConflictDescription();
    191             if (description == null) {
    192                 dismissDialog();
    193             }
    194             Drawable icon = getResources().getDrawable(R.drawable.ic_error_white_48dp, null);
    195             return new Guidance(title, descriptionPrefix + " " + description, null, icon);
    196         }
    197 
    198         @Override
    199         public String getTrackerPrefix() {
    200             return "DvrProgramConflictFragment";
    201         }
    202     }
    203 
    204     /**
    205      * A fragment to show the channel recording conflict.
    206      */
    207     public static class DvrChannelRecordConflictFragment extends DvrConflictFragment {
    208         private Channel mChannel;
    209         private long mStartTimeMs;
    210         private long mEndTimeMs;
    211 
    212         @Override
    213         public View onCreateView(LayoutInflater inflater, ViewGroup container,
    214                 Bundle savedInstanceState) {
    215             Bundle args = getArguments();
    216             long channelId = args.getLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID);
    217             mChannel = TvApplication.getSingletons(getContext()).getChannelDataManager()
    218                     .getChannel(channelId);
    219             SoftPreconditions.checkArgument(mChannel != null);
    220             TvInputInfo input = Utils.getTvInputInfoForChannelId(getContext(), mChannel.getId());
    221             SoftPreconditions.checkNotNull(input);
    222             List<ScheduledRecording> conflicts = null;
    223             if (input != null) {
    224                 mStartTimeMs = args.getLong(DvrHalfSizedDialogFragment.KEY_START_TIME_MS);
    225                 mEndTimeMs = args.getLong(DvrHalfSizedDialogFragment.KEY_END_TIME_MS);
    226                 conflicts = TvApplication.getSingletons(getContext()).getDvrManager()
    227                         .getConflictingSchedules(mChannel.getId(), mStartTimeMs, mEndTimeMs);
    228             }
    229             if (conflicts == null) {
    230                 conflicts = Collections.emptyList();
    231             }
    232             if (conflicts.isEmpty()) {
    233                 dismissDialog();
    234             }
    235             setConflicts(conflicts);
    236             return super.onCreateView(inflater, container, savedInstanceState);
    237         }
    238 
    239         @NonNull
    240         @Override
    241         public Guidance onCreateGuidance(Bundle savedInstanceState) {
    242             String title = getResources().getString(R.string.dvr_channel_conflict_dialog_title);
    243             String descriptionPrefix = getString(
    244                     R.string.dvr_channel_conflict_dialog_description_prefix,
    245                     mChannel.getDisplayName());
    246             String description = getConflictDescription();
    247             if (description == null) {
    248                 dismissDialog();
    249             }
    250             Drawable icon = getResources().getDrawable(R.drawable.ic_error_white_48dp, null);
    251             return new Guidance(title, descriptionPrefix + " " + description, null, icon);
    252         }
    253 
    254         @Override
    255         public String getTrackerPrefix() {
    256             return "DvrChannelRecordConflictFragment";
    257         }
    258     }
    259 
    260     /**
    261      * A fragment to show the channel watching conflict.
    262      * <p>
    263      * This fragment is automatically closed when there are no upcoming conflicts.
    264      */
    265     public static class DvrChannelWatchConflictFragment extends DvrConflictFragment
    266             implements OnUpcomingConflictChangeListener {
    267         private long mChannelId;
    268 
    269         @Override
    270         public View onCreateView(LayoutInflater inflater, ViewGroup container,
    271                 Bundle savedInstanceState) {
    272             Bundle args = getArguments();
    273             if (args != null) {
    274                 mChannelId = args.getLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID);
    275             }
    276             SoftPreconditions.checkArgument(mChannelId != Channel.INVALID_ID);
    277             ConflictChecker checker = ((MainActivity) getContext()).getDvrConflictChecker();
    278             List<ScheduledRecording> conflicts = null;
    279             if (checker != null) {
    280                 checker.addOnUpcomingConflictChangeListener(this);
    281                 conflicts = checker.getUpcomingConflicts();
    282                 if (DEBUG) Log.d(TAG, "onCreateView: upcoming conflicts: " + conflicts);
    283                 if (conflicts.isEmpty()) {
    284                     dismissDialog();
    285                 }
    286             }
    287             if (conflicts == null) {
    288                 if (DEBUG) Log.d(TAG, "onCreateView: There's no conflict.");
    289                 conflicts = Collections.emptyList();
    290             }
    291             if (conflicts.isEmpty()) {
    292                 dismissDialog();
    293             }
    294             setConflicts(conflicts);
    295             return super.onCreateView(inflater, container, savedInstanceState);
    296         }
    297 
    298         @NonNull
    299         @Override
    300         public Guidance onCreateGuidance(Bundle savedInstanceState) {
    301             String title = getResources().getString(
    302                     R.string.dvr_epg_channel_watch_conflict_dialog_title);
    303             String description = getResources().getString(
    304                     R.string.dvr_epg_channel_watch_conflict_dialog_description);
    305             return new Guidance(title, description, null, null);
    306         }
    307 
    308         @Override
    309         public void onCreateActions(@NonNull List<GuidedAction> actions,
    310                 Bundle savedInstanceState) {
    311             actions.add(new GuidedAction.Builder(getContext())
    312                     .id(ACTION_DELETE_CONFLICT)
    313                     .title(R.string.dvr_action_delete_schedule)
    314                     .build());
    315             actions.add(new GuidedAction.Builder(getContext())
    316                     .id(ACTION_CANCEL)
    317                     .title(R.string.dvr_action_record_program)
    318                     .build());
    319         }
    320 
    321         @Override
    322         public void onTrackedGuidedActionClicked(GuidedAction action) {
    323             if (action.getId() == ACTION_CANCEL) {
    324                 ConflictChecker checker = ((MainActivity) getContext()).getDvrConflictChecker();
    325                 if (checker != null) {
    326                     checker.setCheckedConflictsForChannel(mChannelId, getConflicts());
    327                 }
    328             } else if (action.getId() == ACTION_DELETE_CONFLICT) {
    329                 for (ScheduledRecording schedule : mConflicts) {
    330                     if (schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
    331                         getDvrManager().stopRecording(schedule);
    332                     } else {
    333                         getDvrManager().removeScheduledRecording(schedule);
    334                     }
    335                 }
    336             }
    337             super.onGuidedActionClicked(action);
    338         }
    339 
    340         @Override
    341         public String getTrackerPrefix() {
    342             return "DvrChannelWatchConflictFragment";
    343         }
    344 
    345         @Override
    346         public String getTrackerLabelForGuidedAction(GuidedAction action) {
    347             long actionId = action.getId();
    348             if (actionId == ACTION_CANCEL) {
    349                 return "cancel";
    350             } else if (actionId == ACTION_DELETE_CONFLICT) {
    351                 return "delete";
    352             } else {
    353                 return super.getTrackerLabelForGuidedAction(action);
    354             }
    355         }
    356 
    357         @Override
    358         public void onDetach() {
    359             ConflictChecker checker = ((MainActivity) getContext()).getDvrConflictChecker();
    360             if (checker != null) {
    361                 checker.removeOnUpcomingConflictChangeListener(this);
    362             }
    363             super.onDetach();
    364         }
    365 
    366         @Override
    367         public void onUpcomingConflictChange() {
    368             ConflictChecker checker = ((MainActivity) getContext()).getDvrConflictChecker();
    369             if (checker == null || checker.getUpcomingConflicts().isEmpty()) {
    370                 if (DEBUG) Log.d(TAG, "onUpcomingConflictChange: There's no conflict.");
    371                 dismissDialog();
    372             }
    373         }
    374     }
    375 }
    376