1 /* 2 * Copyright (C) 2019 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.annotation.TargetApi; 20 import android.app.DialogFragment; 21 import android.content.Context; 22 import android.os.Build; 23 import android.os.Bundle; 24 25 import com.android.tv.MainActivity; 26 import com.android.tv.R; 27 import com.android.tv.TvSingletons; 28 import com.android.tv.data.ProgramImpl; 29 import com.android.tv.data.api.Program; 30 import com.android.tv.dialog.SafeDismissDialogFragment; 31 import com.android.tv.dvr.DvrManager; 32 import com.android.tv.dvr.data.ScheduledRecording; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.concurrent.TimeUnit; 37 38 import androidx.leanback.app.GuidedStepFragment; 39 import androidx.leanback.widget.GuidanceStylist.Guidance; 40 import androidx.leanback.widget.GuidedAction; 41 import androidx.leanback.widget.GuidedActionsStylist; 42 43 /** Fragment for DVR recording settings. */ 44 @TargetApi(Build.VERSION_CODES.N) 45 @SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated 46 public class DvrRecordingSettingsFragment extends GuidedStepFragment { 47 private static final String TAG = "RecordingSettingsFragment"; 48 49 private static final long ACTION_ID_START_EARLY = 100; 50 private static final long ACTION_ID_END_LATE = 101; 51 52 private static final int SUB_ACTION_ID_START_ON_TIME = 1; 53 private static final int SUB_ACTION_ID_START_ONE_MIN = 2; 54 private static final int SUB_ACTION_ID_START_FIVE_MIN = 3; 55 private static final int SUB_ACTION_ID_START_FIFTEEN_MIN = 4; 56 private static final int SUB_ACTION_ID_START_HALF_HOUR = 5; 57 58 private static final int SUB_ACTION_ID_END_ON_TIME = 6; 59 private static final int SUB_ACTION_ID_END_ONE_MIN = 7; 60 private static final int SUB_ACTION_ID_END_FIFTEEN_MIN = 8; 61 private static final int SUB_ACTION_ID_END_HALF_HOUR = 9; 62 private static final int SUB_ACTION_ID_END_ONE_HOUR = 10; 63 private static final int SUB_ACTION_ID_END_TWO_HOURS = 11; 64 private static final int SUB_ACTION_ID_END_THREE_HOURS = 12; 65 66 private Program mProgram; 67 private String mFragmentTitle; 68 private String mStartEarlyActionTitle; 69 private String mEndLateActionTitle; 70 private String mTimeActionOnTimeText; 71 private String mTimeActionOneMinText; 72 private String mTimeActionFiveMinText; 73 private String mTimeActionFifteenMinText; 74 private String mTimeActionHalfHourText; 75 private String mTimeActionOneHourText; 76 private String mTimeActionTwoHoursText; 77 private String mTimeActionThreeHoursText; 78 79 private GuidedAction mStartEarlyGuidedAction; 80 private GuidedAction mEndLateGuidedAction; 81 private long mStartEarlyTime = 0; 82 private long mEndLateTime = 0; 83 private DvrManager mDvrManager; 84 85 @Override onAttach(Context context)86 public void onAttach(Context context) { 87 super.onAttach(context); 88 mDvrManager = TvSingletons.getSingletons(getContext()).getDvrManager(); 89 mProgram = getArguments().getParcelable(DvrRecordingSettingsActivity.PROGRAM); 90 if (mProgram == null) { 91 getActivity().finish(); 92 return; 93 } 94 mFragmentTitle = getString(R.string.dvr_recording_settings_title); 95 mStartEarlyActionTitle = getString(R.string.dvr_start_early_title); 96 mEndLateActionTitle = getString(R.string.dvr_end_late_title); 97 mTimeActionOnTimeText = getString(R.string.dvr_recording_settings_time_none); 98 mTimeActionOneMinText = getString(R.string.dvr_recording_settings_time_one_min); 99 mTimeActionFiveMinText = getString(R.string.dvr_recording_settings_time_five_mins); 100 mTimeActionFifteenMinText = getString(R.string.dvr_recording_settings_time_fifteen_mins); 101 mTimeActionHalfHourText = getString(R.string.dvr_recording_settings_time_half_hour); 102 mTimeActionOneHourText = getString(R.string.dvr_recording_settings_time_one_hour); 103 mTimeActionTwoHoursText = getString(R.string.dvr_recording_settings_time_two_hours); 104 mTimeActionThreeHoursText = getString(R.string.dvr_recording_settings_time_three_hours); 105 } 106 107 @Override onCreateGuidance(Bundle savedInstanceState)108 public Guidance onCreateGuidance(Bundle savedInstanceState) { 109 String breadcrumb = mProgram.getTitle(); 110 String title = mFragmentTitle; 111 String description = mProgram.getEpisodeTitle() + "\n" + mProgram.getDescription(); 112 return new Guidance(title, description, breadcrumb, null); 113 } 114 115 @Override onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState)116 public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { 117 mStartEarlyGuidedAction = 118 new GuidedAction.Builder(getActivity()) 119 .id(ACTION_ID_START_EARLY) 120 .title(mStartEarlyActionTitle) 121 .description(mTimeActionOnTimeText) 122 .subActions(buildChannelSubActionStart()) 123 .build(); 124 actions.add(mStartEarlyGuidedAction); 125 126 mEndLateGuidedAction = 127 new GuidedAction.Builder(getActivity()) 128 .id(ACTION_ID_END_LATE) 129 .title(mEndLateActionTitle) 130 .description(mTimeActionOnTimeText) 131 .subActions(buildChannelSubActionEnd()) 132 .build(); 133 actions.add(mEndLateGuidedAction); 134 } 135 136 @Override onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState)137 public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) { 138 actions.add( 139 new GuidedAction.Builder(getActivity()) 140 .clickAction(GuidedAction.ACTION_ID_OK) 141 .build()); 142 actions.add( 143 new GuidedAction.Builder(getActivity()) 144 .clickAction(GuidedAction.ACTION_ID_CANCEL) 145 .build()); 146 } 147 148 @Override onGuidedActionClicked(GuidedAction action)149 public void onGuidedActionClicked(GuidedAction action) { 150 long actionId = action.getId(); 151 if (actionId == GuidedAction.ACTION_ID_OK) { 152 long startEarlyTimeMs = TimeUnit.MINUTES.toMillis(mStartEarlyTime); 153 long endLateTimeMs = TimeUnit.MINUTES.toMillis(mEndLateTime); 154 long startTimeMs = mProgram.getStartTimeUtcMillis() - startEarlyTimeMs; 155 if (startTimeMs < System.currentTimeMillis()) { 156 startTimeMs = System.currentTimeMillis(); 157 startEarlyTimeMs = mProgram.getStartTimeUtcMillis() - startTimeMs; 158 } 159 long endTimeMs = mProgram.getEndTimeUtcMillis() + endLateTimeMs; 160 Program customizedProgram = 161 new ProgramImpl.Builder(mProgram) 162 .setStartTimeUtcMillis(startTimeMs) 163 .setEndTimeUtcMillis(endTimeMs) 164 .build(); 165 mDvrManager.addSchedule(customizedProgram , startEarlyTimeMs, endLateTimeMs); 166 List<ScheduledRecording> conflicts = 167 mDvrManager.getConflictingSchedules(customizedProgram); 168 if (conflicts.isEmpty()) { 169 DvrUiHelper.showAddScheduleToast( 170 getContext(), 171 customizedProgram.getTitle(), 172 customizedProgram.getStartTimeUtcMillis(), 173 customizedProgram.getEndTimeUtcMillis()); 174 dismissDialog(); 175 finishGuidedStepFragments(); 176 } else { 177 DvrUiHelper.showScheduleConflictDialog(getActivity(), customizedProgram); 178 } 179 } else if (actionId == GuidedAction.ACTION_ID_CANCEL) { 180 finishGuidedStepFragments(); 181 } 182 } 183 184 @Override onSubGuidedActionClicked(GuidedAction action)185 public boolean onSubGuidedActionClicked(GuidedAction action) { 186 long actionId = action.getId(); 187 switch ((int) actionId) { 188 case SUB_ACTION_ID_START_ON_TIME : 189 mStartEarlyTime = 0; 190 updateGuidedActions(true, mTimeActionOnTimeText); 191 break; 192 case SUB_ACTION_ID_START_ONE_MIN : 193 mStartEarlyTime = 1; 194 updateGuidedActions(true, mTimeActionOneMinText); 195 break; 196 case SUB_ACTION_ID_START_FIVE_MIN : 197 mStartEarlyTime = 5; 198 updateGuidedActions(true, mTimeActionFiveMinText); 199 break; 200 case SUB_ACTION_ID_START_FIFTEEN_MIN : 201 mStartEarlyTime = 15; 202 updateGuidedActions(true, mTimeActionFifteenMinText); 203 break; 204 case SUB_ACTION_ID_START_HALF_HOUR : 205 mStartEarlyTime = 30; 206 updateGuidedActions(true, mTimeActionHalfHourText); 207 break; 208 case SUB_ACTION_ID_END_ON_TIME : 209 mEndLateTime = 0; 210 updateGuidedActions(false, mTimeActionOnTimeText); 211 break; 212 case SUB_ACTION_ID_END_ONE_MIN : 213 mEndLateTime = 1; 214 updateGuidedActions(false, mTimeActionOneMinText); 215 break; 216 case SUB_ACTION_ID_END_FIFTEEN_MIN : 217 mEndLateTime = 15; 218 updateGuidedActions(false, mTimeActionFifteenMinText); 219 break; 220 case SUB_ACTION_ID_END_HALF_HOUR : 221 mEndLateTime = 30; 222 updateGuidedActions(false, mTimeActionHalfHourText); 223 break; 224 case SUB_ACTION_ID_END_ONE_HOUR : 225 mEndLateTime = 60; 226 updateGuidedActions(false, mTimeActionOneHourText); 227 break; 228 case SUB_ACTION_ID_END_TWO_HOURS : 229 mEndLateTime = 120; 230 updateGuidedActions(false, mTimeActionTwoHoursText); 231 break; 232 case SUB_ACTION_ID_END_THREE_HOURS : 233 mEndLateTime = 180; 234 updateGuidedActions(false, mTimeActionThreeHoursText); 235 break; 236 default : 237 mStartEarlyTime = 0; 238 mEndLateTime = 0; 239 updateGuidedActions(true, mTimeActionOnTimeText); 240 updateGuidedActions(false, mTimeActionOnTimeText); 241 break; 242 } 243 return true; 244 } 245 updateGuidedActions(boolean start, CharSequence description)246 private void updateGuidedActions(boolean start, CharSequence description) { 247 if (start) { 248 mStartEarlyGuidedAction.setDescription(description); 249 notifyActionChanged(findActionPositionById(ACTION_ID_START_EARLY)); 250 } else { 251 mEndLateGuidedAction.setDescription(description); 252 notifyActionChanged(findActionPositionById(ACTION_ID_END_LATE)); 253 } 254 } 255 256 @Override onCreateButtonActionsStylist()257 public GuidedActionsStylist onCreateButtonActionsStylist() { 258 return new DvrGuidedActionsStylist(true); 259 } 260 buildChannelSubActionStart()261 private List<GuidedAction> buildChannelSubActionStart() { 262 List<GuidedAction> timeSubActions = new ArrayList<>(); 263 timeSubActions.add( 264 new GuidedAction.Builder(getActivity()) 265 .id(SUB_ACTION_ID_START_ON_TIME) 266 .title(mTimeActionOnTimeText) 267 .build()); 268 timeSubActions.add( 269 new GuidedAction.Builder(getActivity()) 270 .id(SUB_ACTION_ID_START_ONE_MIN) 271 .title(mTimeActionOneMinText) 272 .build()); 273 timeSubActions.add( 274 new GuidedAction.Builder(getActivity()) 275 .id(SUB_ACTION_ID_START_FIVE_MIN) 276 .title(mTimeActionFiveMinText) 277 .build()); 278 timeSubActions.add( 279 new GuidedAction.Builder(getActivity()) 280 .id(SUB_ACTION_ID_START_FIFTEEN_MIN) 281 .title(mTimeActionFifteenMinText) 282 .build()); 283 timeSubActions.add( 284 new GuidedAction.Builder(getActivity()) 285 .id(SUB_ACTION_ID_START_HALF_HOUR) 286 .title(mTimeActionHalfHourText) 287 .build()); 288 return timeSubActions; 289 } 290 buildChannelSubActionEnd()291 private List<GuidedAction> buildChannelSubActionEnd() { 292 List<GuidedAction> timeSubActions = new ArrayList<>(); 293 timeSubActions.add( 294 new GuidedAction.Builder(getActivity()) 295 .id(SUB_ACTION_ID_END_ON_TIME) 296 .title(mTimeActionOnTimeText) 297 .build()); 298 timeSubActions.add( 299 new GuidedAction.Builder(getActivity()) 300 .id(SUB_ACTION_ID_END_ONE_MIN) 301 .title(mTimeActionOneMinText) 302 .build()); 303 timeSubActions.add( 304 new GuidedAction.Builder(getActivity()) 305 .id(SUB_ACTION_ID_END_FIFTEEN_MIN) 306 .title(mTimeActionFifteenMinText) 307 .build()); 308 timeSubActions.add( 309 new GuidedAction.Builder(getActivity()) 310 .id(SUB_ACTION_ID_END_HALF_HOUR) 311 .title(mTimeActionHalfHourText) 312 .build()); 313 timeSubActions.add( 314 new GuidedAction.Builder(getActivity()) 315 .id(SUB_ACTION_ID_END_ONE_HOUR) 316 .title(mTimeActionOneHourText) 317 .build()); 318 timeSubActions.add( 319 new GuidedAction.Builder(getActivity()) 320 .id(SUB_ACTION_ID_END_TWO_HOURS) 321 .title(mTimeActionTwoHoursText) 322 .build()); 323 timeSubActions.add( 324 new GuidedAction.Builder(getActivity()) 325 .id(SUB_ACTION_ID_END_THREE_HOURS) 326 .title(mTimeActionThreeHoursText) 327 .build()); 328 return timeSubActions; 329 } 330 dismissDialog()331 protected void dismissDialog() { 332 if (getActivity() instanceof MainActivity) { 333 SafeDismissDialogFragment currentDialog = 334 ((MainActivity) getActivity()).getOverlayManager().getCurrentDialog(); 335 if (currentDialog instanceof DvrHalfSizedDialogFragment) { 336 currentDialog.dismiss(); 337 } 338 } else if (getParentFragment() instanceof DialogFragment) { 339 ((DialogFragment) getParentFragment()).dismiss(); 340 } 341 } 342 } 343