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.list;
18 
19 import android.annotation.TargetApi;
20 import android.content.Context;
21 import android.database.ContentObserver;
22 import android.media.tv.TvContract.Programs;
23 import android.net.Uri;
24 import android.os.Build;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.os.Looper;
28 import android.transition.Fade;
29 import android.view.LayoutInflater;
30 import android.view.View;
31 import android.view.ViewGroup;
32 
33 import androidx.leanback.widget.ClassPresenterSelector;
34 
35 import com.android.tv.R;
36 import com.android.tv.TvSingletons;
37 import com.android.tv.data.ChannelDataManager;
38 import com.android.tv.data.ProgramImpl;
39 import com.android.tv.data.api.Program;
40 import com.android.tv.dvr.DvrDataManager;
41 import com.android.tv.dvr.DvrDataManager.SeriesRecordingListener;
42 import com.android.tv.dvr.data.SeriesRecording;
43 import com.android.tv.dvr.provider.EpisodicProgramLoadTask;
44 import com.android.tv.dvr.ui.BigArguments;
45 
46 import java.util.Collections;
47 import java.util.List;
48 
49 /** A fragment to show the list of series schedule recordings. */
50 @TargetApi(Build.VERSION_CODES.N)
51 public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment {
52     /**
53      * The key for series recording whose scheduled recording list will be displayed. Type: {@link
54      * SeriesRecording}
55      */
56     public static final String SERIES_SCHEDULES_KEY_SERIES_RECORDING =
57             "series_schedules_key_series_recording";
58     /**
59      * The key for programs which belong to the series recording whose scheduled recording list will
60      * be displayed. Type: List<{@link ProgramImpl}>
61      */
62     public static final String SERIES_SCHEDULES_KEY_SERIES_PROGRAMS =
63             "series_schedules_key_series_programs";
64 
65     private ChannelDataManager mChannelDataManager;
66     private DvrDataManager mDvrDataManager;
67     private SeriesRecording mSeriesRecording;
68     private List<Program> mPrograms;
69     private EpisodicProgramLoadTask mProgramLoadTask;
70 
71     private final SeriesRecordingListener mSeriesRecordingListener =
72             new SeriesRecordingListener() {
73                 @Override
74                 public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) {}
75 
76                 @Override
77                 public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) {
78                     for (SeriesRecording r : seriesRecordings) {
79                         if (r.getId() == mSeriesRecording.getId()) {
80                             getActivity().finish();
81                             return;
82                         }
83                     }
84                 }
85 
86                 @Override
87                 public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) {
88                     for (SeriesRecording r : seriesRecordings) {
89                         if (r.getId() == mSeriesRecording.getId()
90                                 && getRowsAdapter() instanceof SeriesScheduleRowAdapter) {
91                             ((SeriesScheduleRowAdapter) getRowsAdapter())
92                                     .onSeriesRecordingUpdated(r);
93                             mSeriesRecording = r;
94                             updateEmptyMessage();
95                             return;
96                         }
97                     }
98                 }
99             };
100 
101     private final Handler mHandler = new Handler(Looper.getMainLooper());
102     private final ContentObserver mContentObserver =
103             new ContentObserver(mHandler) {
104                 @Override
105                 public void onChange(boolean selfChange, Uri uri) {
106                     super.onChange(selfChange, uri);
107                     executeProgramLoadingTask();
108                 }
109             };
110 
111     private final ChannelDataManager.Listener mChannelListener =
112             new ChannelDataManager.Listener() {
113                 @Override
114                 public void onLoadFinished() {}
115 
116                 @Override
117                 public void onChannelListUpdated() {
118                     executeProgramLoadingTask();
119                 }
120 
121                 @Override
122                 public void onChannelBrowsableChanged() {}
123             };
124 
DvrSeriesSchedulesFragment()125     public DvrSeriesSchedulesFragment() {
126         setEnterTransition(new Fade(Fade.IN));
127     }
128 
129     @Override
onAttach(Context context)130     public void onAttach(Context context) {
131         super.onAttach(context);
132         Bundle args = getArguments();
133         if (args != null) {
134             mSeriesRecording = args.getParcelable(SERIES_SCHEDULES_KEY_SERIES_RECORDING);
135             mPrograms =
136                     (List<Program>) BigArguments.getArgument(SERIES_SCHEDULES_KEY_SERIES_PROGRAMS);
137             BigArguments.reset();
138         }
139         if (args == null || mPrograms == null) {
140             getActivity().finish();
141         }
142     }
143 
144     @Override
onCreate(Bundle savedInstanceState)145     public void onCreate(Bundle savedInstanceState) {
146         super.onCreate(savedInstanceState);
147         TvSingletons singletons = TvSingletons.getSingletons(getContext());
148         mChannelDataManager = singletons.getChannelDataManager();
149         mChannelDataManager.addListener(mChannelListener);
150         mDvrDataManager = singletons.getDvrDataManager();
151         mDvrDataManager.addSeriesRecordingListener(mSeriesRecordingListener);
152         getContext()
153                 .getContentResolver()
154                 .registerContentObserver(Programs.CONTENT_URI, true, mContentObserver);
155     }
156 
157     @Override
onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)158     public View onCreateView(
159             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
160         onProgramsUpdated();
161         return super.onCreateView(inflater, container, savedInstanceState);
162     }
163 
onProgramsUpdated()164     private void onProgramsUpdated() {
165         ((SeriesScheduleRowAdapter) getRowsAdapter()).setPrograms(mPrograms);
166         updateEmptyMessage();
167     }
168 
updateEmptyMessage()169     private void updateEmptyMessage() {
170         if (mPrograms == null || mPrograms.isEmpty()) {
171             if (mSeriesRecording.getState() == SeriesRecording.STATE_SERIES_STOPPED) {
172                 showEmptyMessage(R.string.dvr_series_schedules_stopped_empty_state);
173             } else {
174                 showEmptyMessage(R.string.dvr_series_schedules_empty_state);
175             }
176         } else {
177             hideEmptyMessage();
178         }
179     }
180 
181     @Override
onDestroy()182     public void onDestroy() {
183         if (mProgramLoadTask != null) {
184             mProgramLoadTask.cancel(true);
185             mProgramLoadTask = null;
186         }
187         getContext().getContentResolver().unregisterContentObserver(mContentObserver);
188         mHandler.removeCallbacksAndMessages(null);
189         mChannelDataManager.removeListener(mChannelListener);
190         mDvrDataManager.removeSeriesRecordingListener(mSeriesRecordingListener);
191         super.onDestroy();
192     }
193 
194     @Override
onCreateHeaderRowPresenter()195     public SchedulesHeaderRowPresenter onCreateHeaderRowPresenter() {
196         return new SchedulesHeaderRowPresenter.SeriesRecordingHeaderRowPresenter(getContext());
197     }
198 
199     @Override
onCreateRowPresenter()200     public ScheduleRowPresenter onCreateRowPresenter() {
201         return new SeriesScheduleRowPresenter(getContext());
202     }
203 
204     @Override
onCreateRowsAdapter(ClassPresenterSelector presenterSelector)205     public ScheduleRowAdapter onCreateRowsAdapter(ClassPresenterSelector presenterSelector) {
206         return new SeriesScheduleRowAdapter(getContext(), presenterSelector, mSeriesRecording);
207     }
208 
209     @Override
getFirstItemPosition()210     protected int getFirstItemPosition() {
211         if (mSeriesRecording != null
212                 && mSeriesRecording.getState() == SeriesRecording.STATE_SERIES_STOPPED) {
213             return 0;
214         }
215         return super.getFirstItemPosition();
216     }
217 
executeProgramLoadingTask()218     private void executeProgramLoadingTask() {
219         if (mProgramLoadTask != null) {
220             mProgramLoadTask.cancel(true);
221         }
222         mProgramLoadTask =
223                 new EpisodicProgramLoadTask(getContext(), mSeriesRecording) {
224                     @Override
225                     protected void onPostExecute(List<Program> programs) {
226                         mPrograms = programs == null ? Collections.EMPTY_LIST : programs;
227                         onProgramsUpdated();
228                     }
229                 };
230         mProgramLoadTask
231                 .setLoadCurrentProgram(true)
232                 .setLoadDisallowedProgram(true)
233                 .setLoadScheduledEpisode(true)
234                 .setIgnoreChannelOption(true)
235                 .execute();
236     }
237 }
238