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