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