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