• 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.app.FragmentManager;
20 import android.content.Context;
21 import android.os.Bundle;
22 import android.support.v17.leanback.app.GuidedStepFragment;
23 import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
24 import android.support.v17.leanback.widget.GuidedAction;
25 import android.support.v17.leanback.widget.GuidedActionsStylist;
26 import android.util.LongSparseArray;
27 
28 import com.android.tv.R;
29 import com.android.tv.TvApplication;
30 import com.android.tv.data.Channel;
31 import com.android.tv.data.ChannelDataManager;
32 import com.android.tv.data.Program;
33 import com.android.tv.dvr.DvrDataManager;
34 import com.android.tv.dvr.DvrManager;
35 import com.android.tv.dvr.data.ScheduledRecording;
36 import com.android.tv.dvr.data.SeasonEpisodeNumber;
37 import com.android.tv.dvr.data.SeriesRecording;
38 import com.android.tv.dvr.data.SeriesRecording.ChannelOption;
39 import com.android.tv.dvr.recorder.SeriesRecordingScheduler;
40 
41 import java.util.ArrayList;
42 import java.util.Collections;
43 import java.util.HashSet;
44 import java.util.List;
45 import java.util.Set;
46 
47 /**
48  * Fragment for DVR series recording settings.
49  */
50 public class DvrSeriesSettingsFragment extends GuidedStepFragment
51         implements DvrDataManager.SeriesRecordingListener {
52     private static final String TAG = "SeriesSettingsFragment";
53     private static final boolean DEBUG = false;
54 
55     private static final long ACTION_ID_PRIORITY = 10;
56     private static final long ACTION_ID_CHANNEL = 11;
57 
58     private static final long SUB_ACTION_ID_CHANNEL_ALL = 102;
59     // Each channel's action id = SUB_ACTION_ID_CHANNEL_ONE_BASE + channel id
60     private static final long SUB_ACTION_ID_CHANNEL_ONE_BASE = 500;
61 
62     private DvrDataManager mDvrDataManager;
63     private SeriesRecording mSeriesRecording;
64     private long mSeriesRecordingId;
65     @ChannelOption int mChannelOption;
66     private long mSelectedChannelId;
67     private int mBackStackCount;
68     private boolean mShowViewScheduleOptionInDialog;
69     private Program mCurrentProgram;
70 
71     private String mFragmentTitle;
72     private String mProrityActionTitle;
73     private String mProrityActionHighestText;
74     private String mProrityActionLowestText;
75     private String mChannelsActionTitle;
76     private String mChannelsActionAllText;
77     private LongSparseArray<Channel> mId2Channel = new LongSparseArray<>();
78     private List<Channel> mChannels = new ArrayList<>();
79     private List<Program> mPrograms;
80 
81     private GuidedAction mPriorityGuidedAction;
82     private GuidedAction mChannelsGuidedAction;
83 
84     @Override
onAttach(Context context)85     public void onAttach(Context context) {
86         super.onAttach(context);
87         mBackStackCount = getFragmentManager().getBackStackEntryCount();
88         mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager();
89         mSeriesRecordingId = getArguments().getLong(DvrSeriesSettingsActivity.SERIES_RECORDING_ID);
90         mSeriesRecording = mDvrDataManager.getSeriesRecording(mSeriesRecordingId);
91         if (mSeriesRecording == null) {
92             getActivity().finish();
93             return;
94         }
95         mShowViewScheduleOptionInDialog = getArguments().getBoolean(
96                 DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG);
97         mCurrentProgram = getArguments().getParcelable(DvrSeriesSettingsActivity.CURRENT_PROGRAM);
98         mDvrDataManager.addSeriesRecordingListener(this);
99         mPrograms = (List<Program>) BigArguments.getArgument(
100                 DvrSeriesSettingsActivity.PROGRAM_LIST);
101         BigArguments.reset();
102         if (mPrograms == null) {
103             getActivity().finish();
104             return;
105         }
106         Set<Long> channelIds = new HashSet<>();
107         ChannelDataManager channelDataManager =
108                 TvApplication.getSingletons(context).getChannelDataManager();
109         for (Program program : mPrograms) {
110             long channelId = program.getChannelId();
111             if (channelIds.add(channelId)) {
112                 Channel channel = channelDataManager.getChannel(channelId);
113                 if (channel != null) {
114                     mId2Channel.put(channel.getId(), channel);
115                     mChannels.add(channel);
116                 }
117             }
118         }
119         mChannelOption = mSeriesRecording.getChannelOption();
120         mSelectedChannelId = Channel.INVALID_ID;
121         if (mChannelOption == SeriesRecording.OPTION_CHANNEL_ONE) {
122             Channel channel = channelDataManager.getChannel(mSeriesRecording.getChannelId());
123             if (channel != null) {
124                 mSelectedChannelId = channel.getId();
125             } else {
126                 mChannelOption = SeriesRecording.OPTION_CHANNEL_ALL;
127             }
128         }
129         mChannels.sort(Channel.CHANNEL_NUMBER_COMPARATOR);
130         mFragmentTitle = getString(R.string.dvr_series_settings_title);
131         mProrityActionTitle = getString(R.string.dvr_series_settings_priority);
132         mProrityActionHighestText = getString(R.string.dvr_series_settings_priority_highest);
133         mProrityActionLowestText = getString(R.string.dvr_series_settings_priority_lowest);
134         mChannelsActionTitle = getString(R.string.dvr_series_settings_channels);
135         mChannelsActionAllText = getString(R.string.dvr_series_settings_channels_all);
136     }
137 
138     @Override
onResume()139     public void onResume() {
140         super.onResume();
141         // To avoid the order of series's priority has changed, but series doesn't get update.
142         updatePriorityGuidedAction();
143     }
144 
145     @Override
onDetach()146     public void onDetach() {
147         super.onDetach();
148         mDvrDataManager.removeSeriesRecordingListener(this);
149     }
150 
151     @Override
onDestroy()152     public void onDestroy() {
153         if (getFragmentManager().getBackStackEntryCount() == mBackStackCount && getArguments()
154                 .getBoolean(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING)) {
155             mDvrDataManager.checkAndRemoveEmptySeriesRecording(mSeriesRecordingId);
156         }
157         super.onDestroy();
158     }
159 
160     @Override
onCreateGuidance(Bundle savedInstanceState)161     public Guidance onCreateGuidance(Bundle savedInstanceState) {
162         String breadcrumb = mSeriesRecording.getTitle();
163         String title = mFragmentTitle;
164         return new Guidance(title, null, breadcrumb, null);
165     }
166 
167     @Override
onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState)168     public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
169         mPriorityGuidedAction = new GuidedAction.Builder(getActivity())
170                 .id(ACTION_ID_PRIORITY)
171                 .title(mProrityActionTitle)
172                 .build();
173         actions.add(mPriorityGuidedAction);
174 
175         mChannelsGuidedAction = new GuidedAction.Builder(getActivity())
176                 .id(ACTION_ID_CHANNEL)
177                 .title(mChannelsActionTitle)
178                 .subActions(buildChannelSubAction())
179                 .build();
180         actions.add(mChannelsGuidedAction);
181         updateChannelsGuidedAction(false);
182     }
183 
184     @Override
onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState)185     public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
186         actions.add(new GuidedAction.Builder(getActivity())
187                 .clickAction(GuidedAction.ACTION_ID_OK)
188                 .build());
189         actions.add(new GuidedAction.Builder(getActivity())
190                 .clickAction(GuidedAction.ACTION_ID_CANCEL)
191                 .build());
192     }
193 
194     @Override
onGuidedActionClicked(GuidedAction action)195     public void onGuidedActionClicked(GuidedAction action) {
196         long actionId = action.getId();
197         if (actionId == GuidedAction.ACTION_ID_OK) {
198             if (mChannelOption != mSeriesRecording.getChannelOption()
199                     || mSeriesRecording.isStopped()
200                     || (mChannelOption == SeriesRecording.OPTION_CHANNEL_ONE
201                             && mSeriesRecording.getChannelId() != mSelectedChannelId)) {
202                 SeriesRecording.Builder builder = SeriesRecording.buildFrom(mSeriesRecording)
203                         .setChannelOption(mChannelOption)
204                         .setState(SeriesRecording.STATE_SERIES_NORMAL);
205                 if (mSelectedChannelId != Channel.INVALID_ID) {
206                     builder.setChannelId(mSelectedChannelId);
207                 }
208                 DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager();
209                         dvrManager.updateSeriesRecording(builder.build());
210                 if (mCurrentProgram != null && (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL
211                         || mSelectedChannelId == mCurrentProgram.getChannelId())) {
212                     dvrManager.addSchedule(mCurrentProgram);
213                 }
214                 updateSchedulesToSeries();
215                 showConfirmDialog();
216             } else {
217                 showConfirmDialog();
218             }
219         } else if (actionId == GuidedAction.ACTION_ID_CANCEL) {
220             finishGuidedStepFragments();
221         } else if (actionId == ACTION_ID_PRIORITY) {
222             FragmentManager fragmentManager = getFragmentManager();
223             DvrPrioritySettingsFragment fragment = new DvrPrioritySettingsFragment();
224             Bundle args = new Bundle();
225             args.putLong(DvrPrioritySettingsFragment.COME_FROM_SERIES_RECORDING_ID,
226                     mSeriesRecording.getId());
227             fragment.setArguments(args);
228             GuidedStepFragment.add(fragmentManager, fragment, R.id.dvr_settings_view_frame);
229         }
230     }
231 
232     @Override
onSubGuidedActionClicked(GuidedAction action)233     public boolean onSubGuidedActionClicked(GuidedAction action) {
234         long actionId = action.getId();
235         if (actionId == SUB_ACTION_ID_CHANNEL_ALL) {
236             mChannelOption = SeriesRecording.OPTION_CHANNEL_ALL;
237             mSelectedChannelId = Channel.INVALID_ID;
238             updateChannelsGuidedAction(true);
239             return true;
240         } else if (actionId > SUB_ACTION_ID_CHANNEL_ONE_BASE) {
241             mChannelOption = SeriesRecording.OPTION_CHANNEL_ONE;
242             mSelectedChannelId = actionId - SUB_ACTION_ID_CHANNEL_ONE_BASE;
243             updateChannelsGuidedAction(true);
244             return true;
245         }
246         return false;
247     }
248 
249     @Override
onCreateButtonActionsStylist()250     public GuidedActionsStylist onCreateButtonActionsStylist() {
251         return new DvrGuidedActionsStylist(true);
252     }
253 
updateChannelsGuidedAction(boolean notifyActionChanged)254     private void updateChannelsGuidedAction(boolean notifyActionChanged) {
255         if (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL) {
256             mChannelsGuidedAction.setDescription(mChannelsActionAllText);
257         } else if (mId2Channel.get(mSelectedChannelId) != null){
258             mChannelsGuidedAction.setDescription(mId2Channel.get(mSelectedChannelId)
259                     .getDisplayText());
260         }
261         if (notifyActionChanged) {
262             notifyActionChanged(findActionPositionById(ACTION_ID_CHANNEL));
263         }
264     }
265 
updatePriorityGuidedAction()266     private void updatePriorityGuidedAction() {
267         int totalSeriesCount = 0;
268         int priorityOrder = 0;
269         for (SeriesRecording seriesRecording : mDvrDataManager.getSeriesRecordings()) {
270             if (seriesRecording.getState() == SeriesRecording.STATE_SERIES_NORMAL
271                     || seriesRecording.getId() == mSeriesRecording.getId()) {
272                 ++totalSeriesCount;
273             }
274             if (seriesRecording.getState() == SeriesRecording.STATE_SERIES_NORMAL
275                     && seriesRecording.getId() != mSeriesRecording.getId()
276                     && seriesRecording.getPriority() > mSeriesRecording.getPriority()) {
277                 ++priorityOrder;
278             }
279         }
280         if (priorityOrder == 0) {
281             mPriorityGuidedAction.setDescription(mProrityActionHighestText);
282         } else if (priorityOrder >= totalSeriesCount - 1) {
283             mPriorityGuidedAction.setDescription(mProrityActionLowestText);
284         } else {
285             mPriorityGuidedAction.setDescription(getString(
286                     R.string.dvr_series_settings_priority_rank, priorityOrder + 1));
287         }
288         notifyActionChanged(findActionPositionById(ACTION_ID_PRIORITY));
289     }
290 
updateSchedulesToSeries()291     private void updateSchedulesToSeries() {
292         List<Program> recordingCandidates = new ArrayList<>();
293         Set<SeasonEpisodeNumber> scheduledEpisodes = new HashSet<>();
294         for (ScheduledRecording r : mDvrDataManager.getScheduledRecordings(mSeriesRecordingId)) {
295             if (r.getState() != ScheduledRecording.STATE_RECORDING_FAILED
296                     && r.getState() != ScheduledRecording.STATE_RECORDING_CLIPPED) {
297                 scheduledEpisodes.add(new SeasonEpisodeNumber(
298                         r.getSeriesRecordingId(), r.getSeasonNumber(), r.getEpisodeNumber()));
299             }
300         }
301         for (Program program : mPrograms) {
302             // Removes current programs and scheduled episodes out, matches the channel option.
303             if (program.getStartTimeUtcMillis() >= System.currentTimeMillis()
304                     && mSeriesRecording.matchProgram(program)
305                     && !scheduledEpisodes.contains(new SeasonEpisodeNumber(
306                     mSeriesRecordingId, program.getSeasonNumber(), program.getEpisodeNumber()))) {
307                 recordingCandidates.add(program);
308             }
309         }
310         if (recordingCandidates.isEmpty()) {
311             return;
312         }
313         List<Program> programsToSchedule = SeriesRecordingScheduler.pickOneProgramPerEpisode(
314                 mDvrDataManager, Collections.singletonList(mSeriesRecording), recordingCandidates)
315                 .get(mSeriesRecordingId);
316         if (!programsToSchedule.isEmpty()) {
317             TvApplication.getSingletons(getContext()).getDvrManager()
318                     .addScheduleToSeriesRecording(mSeriesRecording, programsToSchedule);
319         }
320     }
321 
buildChannelSubAction()322     private List<GuidedAction> buildChannelSubAction() {
323         List<GuidedAction> channelSubActions = new ArrayList<>();
324         channelSubActions.add(new GuidedAction.Builder(getActivity())
325                 .id(SUB_ACTION_ID_CHANNEL_ALL)
326                 .title(mChannelsActionAllText)
327                 .build());
328         for (Channel channel : mChannels) {
329             channelSubActions.add(new GuidedAction.Builder(getActivity())
330                     .id(SUB_ACTION_ID_CHANNEL_ONE_BASE + channel.getId())
331                     .title(channel.getDisplayText())
332                     .build());
333         }
334         return channelSubActions;
335     }
336 
showConfirmDialog()337     private void showConfirmDialog() {
338         DvrUiHelper.StartSeriesScheduledDialogActivity(getContext(), mSeriesRecording,
339                 mShowViewScheduleOptionInDialog, mPrograms);
340         finishGuidedStepFragments();
341     }
342 
343     @Override
onSeriesRecordingAdded(SeriesRecording... seriesRecordings)344     public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { }
345 
346     @Override
onSeriesRecordingRemoved(SeriesRecording... seriesRecordings)347     public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) {
348         for (SeriesRecording series : seriesRecordings) {
349             if (series.getId() == mSeriesRecording.getId()) {
350                 finishGuidedStepFragments();
351                 return;
352             }
353         }
354     }
355 
356     @Override
onSeriesRecordingChanged(SeriesRecording... seriesRecordings)357     public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) {
358         for (SeriesRecording seriesRecording : seriesRecordings) {
359             if (seriesRecording.getId() == mSeriesRecordingId) {
360                 mSeriesRecording = seriesRecording;
361                 updatePriorityGuidedAction();
362                 return;
363             }
364         }
365     }
366 }