• 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.content.Context;
20 import android.media.tv.TvInputManager;
21 import android.os.Bundle;
22 import android.support.v17.leanback.app.GuidedStepFragment;
23 import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
24 import android.support.v17.leanback.widget.GuidedAction;
25 import android.support.v17.leanback.widget.GuidedActionsStylist;
26 import android.text.TextUtils;
27 import android.view.ViewGroup.LayoutParams;
28 import android.widget.Toast;
29 import com.android.tv.R;
30 import com.android.tv.TvSingletons;
31 import com.android.tv.common.SoftPreconditions;
32 import com.android.tv.dvr.DvrDataManager;
33 import com.android.tv.dvr.DvrManager;
34 import com.android.tv.dvr.DvrWatchedPositionManager;
35 import com.android.tv.dvr.data.RecordedProgram;
36 import com.android.tv.dvr.data.SeriesRecording;
37 import com.android.tv.ui.GuidedActionsStylistWithDivider;
38 import com.android.tv.util.Utils;
39 import java.util.ArrayList;
40 import java.util.Collections;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Set;
44 import java.util.concurrent.TimeUnit;
45 
46 /** Fragment for DVR series recording settings. */
47 public class DvrSeriesDeletionFragment extends GuidedStepFragment {
48     private static final long WATCHED_TIME_UNIT_THRESHOLD = TimeUnit.MINUTES.toMillis(2);
49 
50     // Since recordings' IDs are used as its check actions' IDs, which are random positive numbers,
51     // negative values are used by other actions to prevent duplicated IDs.
52     private static final long ACTION_ID_SELECT_WATCHED = -110;
53     private static final long ACTION_ID_SELECT_ALL = -111;
54     private static final long ACTION_ID_DELETE = -112;
55 
56     private DvrDataManager mDvrDataManager;
57     private DvrWatchedPositionManager mDvrWatchedPositionManager;
58     private List<RecordedProgram> mRecordings;
59     private final Set<Long> mWatchedRecordings = new HashSet<>();
60     private boolean mAllSelected;
61     private long mSeriesRecordingId;
62     private int mOneLineActionHeight;
63 
64     @Override
onAttach(Context context)65     public void onAttach(Context context) {
66         super.onAttach(context);
67         mSeriesRecordingId =
68                 getArguments().getLong(DvrSeriesDeletionActivity.SERIES_RECORDING_ID, -1);
69         SoftPreconditions.checkArgument(mSeriesRecordingId != -1);
70         mDvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager();
71         mDvrWatchedPositionManager =
72                 TvSingletons.getSingletons(context).getDvrWatchedPositionManager();
73         mRecordings = mDvrDataManager.getRecordedPrograms(mSeriesRecordingId);
74         mOneLineActionHeight =
75                 getResources()
76                         .getDimensionPixelSize(
77                                 R.dimen.dvr_settings_one_line_action_container_height);
78         if (mRecordings.isEmpty()) {
79             Toast.makeText(
80                             getActivity(),
81                             getString(R.string.dvr_series_deletion_no_recordings),
82                             Toast.LENGTH_LONG)
83                     .show();
84             finishGuidedStepFragments();
85             return;
86         }
87         Collections.sort(mRecordings, RecordedProgram.EPISODE_COMPARATOR);
88     }
89 
90     @Override
onCreateGuidance(Bundle savedInstanceState)91     public Guidance onCreateGuidance(Bundle savedInstanceState) {
92         String breadcrumb = null;
93         SeriesRecording series = mDvrDataManager.getSeriesRecording(mSeriesRecordingId);
94         if (series != null) {
95             breadcrumb = series.getTitle();
96         }
97         return new Guidance(
98                 getString(R.string.dvr_series_deletion_title),
99                 getString(R.string.dvr_series_deletion_description),
100                 breadcrumb,
101                 null);
102     }
103 
104     @Override
onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState)105     public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
106         actions.add(
107                 new GuidedAction.Builder(getActivity())
108                         .id(ACTION_ID_SELECT_WATCHED)
109                         .title(getString(R.string.dvr_series_select_watched))
110                         .build());
111         actions.add(
112                 new GuidedAction.Builder(getActivity())
113                         .id(ACTION_ID_SELECT_ALL)
114                         .title(getString(R.string.dvr_series_select_all))
115                         .build());
116         actions.add(GuidedActionsStylistWithDivider.createDividerAction(getContext()));
117         for (RecordedProgram recording : mRecordings) {
118             long watchedPositionMs =
119                     mDvrWatchedPositionManager.getWatchedPosition(recording.getId());
120             String title = recording.getEpisodeDisplayTitle(getContext());
121             if (TextUtils.isEmpty(title)) {
122                 title =
123                         TextUtils.isEmpty(recording.getTitle())
124                                 ? getString(R.string.channel_banner_no_title)
125                                 : recording.getTitle();
126             }
127             String description;
128             if (watchedPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) {
129                 description = getWatchedString(watchedPositionMs, recording.getDurationMillis());
130                 mWatchedRecordings.add(recording.getId());
131             } else {
132                 description = getString(R.string.dvr_series_never_watched);
133             }
134             actions.add(
135                     new GuidedAction.Builder(getActivity())
136                             .id(recording.getId())
137                             .title(title)
138                             .description(description)
139                             .checkSetId(GuidedAction.CHECKBOX_CHECK_SET_ID)
140                             .build());
141         }
142     }
143 
144     @Override
onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState)145     public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
146         actions.add(
147                 new GuidedAction.Builder(getActivity())
148                         .id(ACTION_ID_DELETE)
149                         .title(getString(R.string.dvr_detail_delete))
150                         .build());
151         actions.add(
152                 new GuidedAction.Builder(getActivity())
153                         .clickAction(GuidedAction.ACTION_ID_CANCEL)
154                         .build());
155     }
156 
157     @Override
onGuidedActionClicked(GuidedAction action)158     public void onGuidedActionClicked(GuidedAction action) {
159         long actionId = action.getId();
160         if (actionId == ACTION_ID_DELETE) {
161             List<Long> idsToDelete = new ArrayList<>();
162             for (GuidedAction guidedAction : getActions()) {
163                 if (guidedAction.getCheckSetId() == GuidedAction.CHECKBOX_CHECK_SET_ID
164                         && guidedAction.isChecked()) {
165                     idsToDelete.add(guidedAction.getId());
166                 }
167             }
168             if (!idsToDelete.isEmpty()) {
169                 DvrManager dvrManager = TvSingletons.getSingletons(getActivity()).getDvrManager();
170                 dvrManager.removeRecordedPrograms(idsToDelete);
171             }
172             Toast.makeText(
173                             getContext(),
174                             getResources()
175                                     .getQuantityString(
176                                             R.plurals.dvr_msg_episodes_deleted,
177                                             idsToDelete.size(),
178                                             idsToDelete.size(),
179                                             mRecordings.size()),
180                             Toast.LENGTH_LONG)
181                     .show();
182             finishGuidedStepFragments();
183         } else if (actionId == GuidedAction.ACTION_ID_CANCEL) {
184             finishGuidedStepFragments();
185         } else if (actionId == ACTION_ID_SELECT_WATCHED) {
186             for (GuidedAction guidedAction : getActions()) {
187                 if (guidedAction.getCheckSetId() == GuidedAction.CHECKBOX_CHECK_SET_ID) {
188                     long recordingId = guidedAction.getId();
189                     if (mWatchedRecordings.contains(recordingId)) {
190                         guidedAction.setChecked(true);
191                     } else {
192                         guidedAction.setChecked(false);
193                     }
194                     notifyActionChanged(findActionPositionById(recordingId));
195                 }
196             }
197             mAllSelected = updateSelectAllState();
198         } else if (actionId == ACTION_ID_SELECT_ALL) {
199             mAllSelected = !mAllSelected;
200             for (GuidedAction guidedAction : getActions()) {
201                 if (guidedAction.getCheckSetId() == GuidedAction.CHECKBOX_CHECK_SET_ID) {
202                     guidedAction.setChecked(mAllSelected);
203                     notifyActionChanged(findActionPositionById(guidedAction.getId()));
204                 }
205             }
206             updateSelectAllState(action, mAllSelected);
207         } else {
208             mAllSelected = updateSelectAllState();
209         }
210     }
211 
212     @Override
onCreateButtonActionsStylist()213     public GuidedActionsStylist onCreateButtonActionsStylist() {
214         return new DvrGuidedActionsStylist(true);
215     }
216 
217     @Override
onCreateActionsStylist()218     public GuidedActionsStylist onCreateActionsStylist() {
219         return new GuidedActionsStylistWithDivider() {
220             @Override
221             public void onBindViewHolder(ViewHolder vh, GuidedAction action) {
222                 super.onBindViewHolder(vh, action);
223                 if (action.getId() == ACTION_DIVIDER) {
224                     return;
225                 }
226                 LayoutParams lp = vh.itemView.getLayoutParams();
227                 if (action.getCheckSetId() != GuidedAction.CHECKBOX_CHECK_SET_ID) {
228                     lp.height = mOneLineActionHeight;
229                 } else {
230                     vh.itemView.setLayoutParams(
231                             new LayoutParams(lp.width, LayoutParams.WRAP_CONTENT));
232                 }
233             }
234         };
235     }
236 
237     private String getWatchedString(long watchedPositionMs, long durationMs) {
238         if (durationMs > WATCHED_TIME_UNIT_THRESHOLD) {
239             return getResources()
240                     .getString(
241                             R.string.dvr_series_watched_info_minutes,
242                             Math.max(1, Utils.getRoundOffMinsFromMs(watchedPositionMs)),
243                             Utils.getRoundOffMinsFromMs(durationMs));
244         } else {
245             return getResources()
246                     .getString(
247                             R.string.dvr_series_watched_info_seconds,
248                             Math.max(1, TimeUnit.MILLISECONDS.toSeconds(watchedPositionMs)),
249                             TimeUnit.MILLISECONDS.toSeconds(durationMs));
250         }
251     }
252 
253     private boolean updateSelectAllState() {
254         for (GuidedAction guidedAction : getActions()) {
255             if (guidedAction.getCheckSetId() == GuidedAction.CHECKBOX_CHECK_SET_ID) {
256                 if (!guidedAction.isChecked()) {
257                     if (mAllSelected) {
258                         updateSelectAllState(findActionById(ACTION_ID_SELECT_ALL), false);
259                     }
260                     return false;
261                 }
262             }
263         }
264         if (!mAllSelected) {
265             updateSelectAllState(findActionById(ACTION_ID_SELECT_ALL), true);
266         }
267         return true;
268     }
269 
270     private void updateSelectAllState(GuidedAction selectAll, boolean select) {
271         selectAll.setTitle(
272                 select
273                         ? getString(R.string.dvr_series_deselect_all)
274                         : getString(R.string.dvr_series_select_all));
275         notifyActionChanged(findActionPositionById(ACTION_ID_SELECT_ALL));
276     }
277 }
278