• 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.app.ProgressDialog;
21 import android.content.Context;
22 import android.os.Bundle;
23 import android.support.v17.leanback.app.GuidedStepFragment;
24 import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
25 import android.support.v17.leanback.widget.GuidedAction;
26 import android.support.v17.leanback.widget.GuidedActionsStylist;
27 import android.util.Log;
28 import android.util.LongSparseArray;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.widget.FrameLayout;
32 import android.widget.ProgressBar;
33 
34 import com.android.tv.R;
35 import com.android.tv.TvApplication;
36 import com.android.tv.data.Channel;
37 import com.android.tv.data.ChannelDataManager;
38 import com.android.tv.data.Program;
39 import com.android.tv.dvr.DvrDataManager;
40 import com.android.tv.dvr.DvrManager;
41 import com.android.tv.dvr.DvrUiHelper;
42 import com.android.tv.dvr.EpisodicProgramLoadTask;
43 import com.android.tv.dvr.SeriesRecording;
44 import com.android.tv.dvr.SeriesRecording.ChannelOption;
45 import com.android.tv.dvr.SeriesRecordingScheduler;
46 import com.android.tv.dvr.SeriesRecordingScheduler.OnSeriesRecordingUpdatedListener;
47 import java.util.ArrayList;
48 import java.util.Comparator;
49 import java.util.HashSet;
50 import java.util.List;
51 import java.util.Set;
52 
53 /**
54  * Fragment for DVR series recording settings.
55  */
56 public class SeriesSettingsFragment extends GuidedStepFragment
57         implements DvrDataManager.SeriesRecordingListener {
58     private static final String TAG = "SeriesSettingsFragment";
59     private static final boolean DEBUG = false;
60 
61     private static final long ACTION_ID_PRIORITY = 10;
62     private static final long ACTION_ID_CHANNEL = 11;
63 
64     private static final long SUB_ACTION_ID_CHANNEL_ALL = 102;
65     // Each channel's action id = SUB_ACTION_ID_CHANNEL_ONE_BASE + channel id
66     private static final long SUB_ACTION_ID_CHANNEL_ONE_BASE = 500;
67 
68     private DvrDataManager mDvrDataManager;
69     private ChannelDataManager mChannelDataManager;
70     private DvrManager mDvrManager;
71     private SeriesRecording mSeriesRecording;
72     private long mSeriesRecordingId;
73     @ChannelOption int mChannelOption;
74     private Comparator<Channel> mChannelComparator;
75     private long mSelectedChannelId;
76     private int mBackStackCount;
77     private boolean mShowViewScheduleOptionInDialog;
78 
79     private String mFragmentTitle;
80     private String mProrityActionTitle;
81     private String mProrityActionHighestText;
82     private String mProrityActionLowestText;
83     private String mChannelsActionTitle;
84     private String mChannelsActionAllText;
85     private LongSparseArray<Channel> mId2Channel = new LongSparseArray<>();
86     private List<Channel> mChannels = new ArrayList<>();
87     private EpisodicProgramLoadTask mEpisodicProgramLoadTask;
88 
89     private GuidedAction mPriorityGuidedAction;
90     private GuidedAction mChannelsGuidedAction;
91 
92     @Override
onAttach(Context context)93     public void onAttach(Context context) {
94         super.onAttach(context);
95         mBackStackCount = getFragmentManager().getBackStackEntryCount();
96         mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager();
97         mSeriesRecordingId = getArguments().getLong(DvrSeriesSettingsActivity.SERIES_RECORDING_ID);
98         mSeriesRecording = mDvrDataManager.getSeriesRecording(mSeriesRecordingId);
99         if (mSeriesRecording == null) {
100             getActivity().finish();
101             return;
102         }
103         mDvrManager = TvApplication.getSingletons(context).getDvrManager();
104         mShowViewScheduleOptionInDialog = getArguments().getBoolean(
105                 DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG);
106         mDvrDataManager.addSeriesRecordingListener(this);
107         long[] channelIds = getArguments().getLongArray(DvrSeriesSettingsActivity.CHANNEL_ID_LIST);
108         mChannelDataManager = TvApplication.getSingletons(context).getChannelDataManager();
109         if (channelIds == null) {
110             Channel channel = mChannelDataManager.getChannel(mSeriesRecording.getChannelId());
111             if (channel != null) {
112                 mId2Channel.put(channel.getId(), channel);
113                 mChannels.add(channel);
114             }
115             collectChannelsInBackground();
116         } else {
117             for (long channelId : channelIds) {
118                 Channel channel = mChannelDataManager.getChannel(channelId);
119                 if (channel != null) {
120                     mId2Channel.put(channel.getId(), channel);
121                     mChannels.add(channel);
122                 }
123             }
124         }
125         mChannelOption = mSeriesRecording.getChannelOption();
126         mSelectedChannelId = Channel.INVALID_ID;
127         if (mChannelOption == SeriesRecording.OPTION_CHANNEL_ONE) {
128             Channel channel = mChannelDataManager.getChannel(mSeriesRecording.getChannelId());
129             if (channel != null) {
130                 mSelectedChannelId = channel.getId();
131             } else {
132                 mChannelOption = SeriesRecording.OPTION_CHANNEL_ALL;
133             }
134         }
135         mChannelComparator = new Channel.DefaultComparator(context,
136                 TvApplication.getSingletons(context).getTvInputManagerHelper());
137         mChannels.sort(mChannelComparator);
138         mFragmentTitle = getString(R.string.dvr_series_settings_title);
139         mProrityActionTitle = getString(R.string.dvr_series_settings_priority);
140         mProrityActionHighestText = getString(R.string.dvr_series_settings_priority_highest);
141         mProrityActionLowestText = getString(R.string.dvr_series_settings_priority_lowest);
142         mChannelsActionTitle = getString(R.string.dvr_series_settings_channels);
143         mChannelsActionAllText = getString(R.string.dvr_series_settings_channels_all);
144     }
145 
146     @Override
onDetach()147     public void onDetach() {
148         super.onDetach();
149         mDvrDataManager.removeSeriesRecordingListener(this);
150         if (mEpisodicProgramLoadTask != null) {
151             mEpisodicProgramLoadTask.cancel(true);
152             mEpisodicProgramLoadTask = null;
153         }
154     }
155 
156     @Override
onDestroy()157     public void onDestroy() {
158         DvrManager dvrManager = TvApplication.getSingletons(getActivity()).getDvrManager();
159         if (getFragmentManager().getBackStackEntryCount() == mBackStackCount
160                 && getArguments()
161                         .getBoolean(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING)
162                 && dvrManager.canRemoveSeriesRecording(mSeriesRecordingId)) {
163             dvrManager.removeSeriesRecording(mSeriesRecordingId);
164         }
165         super.onDestroy();
166     }
167 
168     @Override
onCreateGuidance(Bundle savedInstanceState)169     public Guidance onCreateGuidance(Bundle savedInstanceState) {
170         String breadcrumb = mSeriesRecording.getTitle();
171         String title = mFragmentTitle;
172         return new Guidance(title, null, breadcrumb, null);
173     }
174 
175     @Override
onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState)176     public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
177         mPriorityGuidedAction = new GuidedAction.Builder(getActivity())
178                 .id(ACTION_ID_PRIORITY)
179                 .title(mProrityActionTitle)
180                 .build();
181         updatePriorityGuidedAction(false);
182         actions.add(mPriorityGuidedAction);
183 
184         mChannelsGuidedAction = new GuidedAction.Builder(getActivity())
185                 .id(ACTION_ID_CHANNEL)
186                 .title(mChannelsActionTitle)
187                 .subActions(buildChannelSubAction())
188                 .build();
189         actions.add(mChannelsGuidedAction);
190         updateChannelsGuidedAction(false);
191     }
192 
193     @Override
onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState)194     public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
195         actions.add(new GuidedAction.Builder(getActivity())
196                 .clickAction(GuidedAction.ACTION_ID_OK)
197                 .build());
198         actions.add(new GuidedAction.Builder(getActivity())
199                 .clickAction(GuidedAction.ACTION_ID_CANCEL)
200                 .build());
201     }
202 
203     @Override
onGuidedActionClicked(GuidedAction action)204     public void onGuidedActionClicked(GuidedAction action) {
205         long actionId = action.getId();
206         if (actionId == GuidedAction.ACTION_ID_OK) {
207             if (mEpisodicProgramLoadTask != null) {
208                 mEpisodicProgramLoadTask.cancel(true);
209                 mEpisodicProgramLoadTask = null;
210             }
211             if (mChannelOption != mSeriesRecording.getChannelOption()
212                     || mSeriesRecording.isStopped()
213                     || (mChannelOption == SeriesRecording.OPTION_CHANNEL_ONE
214                             && mSeriesRecording.getChannelId() != mSelectedChannelId)) {
215                 SeriesRecording.Builder builder = SeriesRecording.buildFrom(mSeriesRecording)
216                         .setChannelOption(mChannelOption)
217                         .setState(SeriesRecording.STATE_SERIES_NORMAL);
218                 if (mSelectedChannelId != Channel.INVALID_ID) {
219                     builder.setChannelId(mSelectedChannelId);
220                 }
221                 TvApplication.getSingletons(getContext()).getDvrManager()
222                         .updateSeriesRecording(builder.build());
223                 SeriesRecordingScheduler scheduler =
224                         SeriesRecordingScheduler.getInstance(getContext());
225                 // Since dialog is used even after the fragment is closed, we should
226                 // use application context.
227                 ProgressDialog dialog = ProgressDialog.show(getContext(), null, getString(
228                                 R.string.dvr_series_schedules_progress_message_updating_programs));
229                 scheduler.addOnSeriesRecordingUpdatedListener(
230                         new OnSeriesRecordingUpdatedListener() {
231                     @Override
232                     public void onSeriesRecordingUpdated(SeriesRecording... seriesRecordings) {
233                         for (SeriesRecording seriesRecording : seriesRecordings) {
234                             if (seriesRecording.getId() == mSeriesRecordingId) {
235                                 dialog.dismiss();
236                                 scheduler.removeOnSeriesRecordingUpdatedListener(this);
237                                 showConfirmDialog();
238                                 return;
239                             }
240                         }
241                     }
242                 });
243             } else {
244                 showConfirmDialog();
245             }
246         } else if (actionId == GuidedAction.ACTION_ID_CANCEL) {
247             finishGuidedStepFragments();
248         } else if (actionId == ACTION_ID_PRIORITY) {
249             FragmentManager fragmentManager = getFragmentManager();
250             PrioritySettingsFragment fragment = new PrioritySettingsFragment();
251             Bundle args = new Bundle();
252             args.putLong(PrioritySettingsFragment.COME_FROM_SERIES_RECORDING_ID,
253                     mSeriesRecording.getId());
254             fragment.setArguments(args);
255             GuidedStepFragment.add(fragmentManager, fragment, R.id.dvr_settings_view_frame);
256         }
257     }
258 
259     @Override
onSubGuidedActionClicked(GuidedAction action)260     public boolean onSubGuidedActionClicked(GuidedAction action) {
261         long actionId = action.getId();
262         if (actionId == SUB_ACTION_ID_CHANNEL_ALL) {
263             mChannelOption = SeriesRecording.OPTION_CHANNEL_ALL;
264             mSelectedChannelId = Channel.INVALID_ID;
265             updateChannelsGuidedAction(true);
266             return true;
267         } else if (actionId > SUB_ACTION_ID_CHANNEL_ONE_BASE) {
268             mChannelOption = SeriesRecording.OPTION_CHANNEL_ONE;
269             mSelectedChannelId = actionId - SUB_ACTION_ID_CHANNEL_ONE_BASE;
270             updateChannelsGuidedAction(true);
271             return true;
272         }
273         return false;
274     }
275 
276     @Override
onCreateButtonActionsStylist()277     public GuidedActionsStylist onCreateButtonActionsStylist() {
278         return new DvrGuidedActionsStylist(true);
279     }
280 
updateChannelsGuidedAction(boolean notifyActionChanged)281     private void updateChannelsGuidedAction(boolean notifyActionChanged) {
282         if (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL) {
283             mChannelsGuidedAction.setDescription(mChannelsActionAllText);
284         } else {
285             mChannelsGuidedAction.setDescription(mId2Channel.get(mSelectedChannelId)
286                     .getDisplayText());
287         }
288         if (notifyActionChanged) {
289             notifyActionChanged(findActionPositionById(ACTION_ID_CHANNEL));
290         }
291     }
292 
updatePriorityGuidedAction(boolean notifyActionChanged)293     private void updatePriorityGuidedAction(boolean notifyActionChanged) {
294         int totalSeriesCount = 0;
295         int priorityOrder = 0;
296         for (SeriesRecording seriesRecording : mDvrDataManager.getSeriesRecordings()) {
297             if (seriesRecording.getState() == SeriesRecording.STATE_SERIES_NORMAL
298                     || seriesRecording.getId() == mSeriesRecording.getId()) {
299                 ++totalSeriesCount;
300             }
301             if (seriesRecording.getState() == SeriesRecording.STATE_SERIES_NORMAL
302                     && seriesRecording.getId() != mSeriesRecording.getId()
303                     && seriesRecording.getPriority() > mSeriesRecording.getPriority()) {
304                 ++priorityOrder;
305             }
306         }
307         if (priorityOrder == 0) {
308             mPriorityGuidedAction.setDescription(mProrityActionHighestText);
309         } else if (priorityOrder >= totalSeriesCount - 1) {
310             mPriorityGuidedAction.setDescription(mProrityActionLowestText);
311         } else {
312             mPriorityGuidedAction.setDescription(getString(
313                     R.string.dvr_series_settings_priority_rank, priorityOrder + 1));
314         }
315         if (notifyActionChanged) {
316             notifyActionChanged(findActionPositionById(ACTION_ID_PRIORITY));
317         }
318     }
319 
collectChannelsInBackground()320     private void collectChannelsInBackground() {
321         if (mEpisodicProgramLoadTask != null) {
322             mEpisodicProgramLoadTask.cancel(true);
323         }
324         mEpisodicProgramLoadTask = new EpisodicProgramLoadTask(getContext(), mSeriesRecording) {
325             @Override
326             protected void onPostExecute(List<Program> programs) {
327                 mEpisodicProgramLoadTask = null;
328                 Set<Long> channelIds = new HashSet<>();
329                 for (Program program : programs) {
330                     channelIds.add(program.getChannelId());
331                 }
332                 boolean channelAdded = false;
333                 for (Long channelId : channelIds) {
334                     if (mId2Channel.get(channelId) != null) {
335                         continue;
336                     }
337                     Channel channel = mChannelDataManager.getChannel(channelId);
338                     if (channel != null) {
339                         channelAdded = true;
340                         mId2Channel.put(channelId, channel);
341                         mChannels.add(channel);
342                         if (DEBUG) Log.d(TAG, "Added channel: " + channel);
343                     }
344                 }
345                 if (!channelAdded) {
346                     return;
347                 }
348                 mChannels.sort(mChannelComparator);
349                 mChannelsGuidedAction.setSubActions(buildChannelSubAction());
350                 notifyActionChanged(findActionPositionById(ACTION_ID_CHANNEL));
351                 if (DEBUG) Log.d(TAG, "Complete EpisodicProgramLoadTask");
352             }
353         }.setLoadCurrentProgram(true)
354                 .setLoadDisallowedProgram(true)
355                 .setLoadScheduledEpisode(true)
356                 .setIgnoreChannelOption(true);
357         mEpisodicProgramLoadTask.execute();
358     }
359 
buildChannelSubAction()360     private List<GuidedAction> buildChannelSubAction() {
361         List<GuidedAction> channelSubActions = new ArrayList<>();
362         channelSubActions.add(new GuidedAction.Builder(getActivity())
363                 .id(SUB_ACTION_ID_CHANNEL_ALL)
364                 .title(mChannelsActionAllText)
365                 .build());
366         for (Channel channel : mChannels) {
367             channelSubActions.add(new GuidedAction.Builder(getActivity())
368                     .id(SUB_ACTION_ID_CHANNEL_ONE_BASE + channel.getId())
369                     .title(channel.getDisplayText())
370                     .build());
371         }
372         return channelSubActions;
373     }
374 
showConfirmDialog()375     private void showConfirmDialog() {
376         DvrUiHelper.StartSeriesScheduledDialogActivity(
377                 getContext(), mSeriesRecording, mShowViewScheduleOptionInDialog);
378         finishGuidedStepFragments();
379     }
380 
381     @Override
onSeriesRecordingAdded(SeriesRecording... seriesRecordings)382     public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { }
383 
384     @Override
onSeriesRecordingRemoved(SeriesRecording... seriesRecordings)385     public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) { }
386 
387     @Override
onSeriesRecordingChanged(SeriesRecording... seriesRecordings)388     public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) {
389         for (SeriesRecording seriesRecording : seriesRecordings) {
390             if (seriesRecording.getId() == mSeriesRecordingId) {
391                 mSeriesRecording = seriesRecording;
392                 updatePriorityGuidedAction(true);
393                 return;
394             }
395         }
396     }
397 }
398