• 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 
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 
setConflicts(List<ScheduledRecording> conflicts)61     void setConflicts(List<ScheduledRecording> conflicts) {
62         mConflicts = conflicts;
63     }
64 
getConflicts()65     List<ScheduledRecording> getConflicts() {
66         return mConflicts;
67     }
68 
69     @Override
onProvideTheme()70     public int onProvideTheme() {
71         return R.style.Theme_TV_Dvr_Conflict_GuidedStep;
72     }
73 
74     @Override
onCreateActions(@onNull List<GuidedAction> actions, Bundle savedInstanceState)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
onTrackedGuidedActionClicked(GuidedAction action)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
getTrackerLabelForGuidedAction(GuidedAction action)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 
getConflictDescription()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
getScheduleTitle(ScheduledRecording schedule)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
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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
onCreateGuidance(Bundle savedInstanceState)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
getTrackerPrefix()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
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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
onCreateGuidance(Bundle savedInstanceState)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
getTrackerPrefix()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
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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
onCreateGuidance(Bundle savedInstanceState)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
onCreateActions(@onNull List<GuidedAction> actions, Bundle savedInstanceState)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
onTrackedGuidedActionClicked(GuidedAction action)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
getTrackerPrefix()341         public String getTrackerPrefix() {
342             return "DvrChannelWatchConflictFragment";
343         }
344 
345         @Override
getTrackerLabelForGuidedAction(GuidedAction action)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
onDetach()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
onUpcomingConflictChange()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