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