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