blob: eadb3b9e05b280ebf600c1ac5bc155bded656bb4 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tv.dvr.ui;
import android.annotation.TargetApi;
import android.app.FragmentManager;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.v17.leanback.app.GuidedStepFragment;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
import android.support.v17.leanback.widget.GuidedActionsStylist;
import android.util.LongSparseArray;
import com.android.tv.R;
import com.android.tv.TvSingletons;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.ChannelImpl;
import com.android.tv.data.Program;
import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.data.SeasonEpisodeNumber;
import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.data.SeriesRecording.ChannelOption;
import com.android.tv.dvr.recorder.SeriesRecordingScheduler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** Fragment for DVR series recording settings. */
@TargetApi(Build.VERSION_CODES.N)
@SuppressWarnings("AndroidApiChecker") // TODO(b/32513850) remove when error prone is updated
public class DvrSeriesSettingsFragment extends GuidedStepFragment
implements DvrDataManager.SeriesRecordingListener {
private static final String TAG = "SeriesSettingsFragment";
private static final long ACTION_ID_PRIORITY = 10;
private static final long ACTION_ID_CHANNEL = 11;
private static final long SUB_ACTION_ID_CHANNEL_ALL = 102;
// Each channel's action id = SUB_ACTION_ID_CHANNEL_ONE_BASE + channel id
private static final long SUB_ACTION_ID_CHANNEL_ONE_BASE = 500;
private DvrDataManager mDvrDataManager;
private SeriesRecording mSeriesRecording;
private long mSeriesRecordingId;
@ChannelOption int mChannelOption;
private long mSelectedChannelId;
private int mBackStackCount;
private boolean mShowViewScheduleOptionInDialog;
private Program mCurrentProgram;
private String mFragmentTitle;
private String mProrityActionTitle;
private String mProrityActionHighestText;
private String mProrityActionLowestText;
private String mChannelsActionTitle;
private String mChannelsActionAllText;
private LongSparseArray<Channel> mId2Channel = new LongSparseArray<>();
private List<Channel> mChannels = new ArrayList<>();
private List<Program> mPrograms;
private GuidedAction mPriorityGuidedAction;
private GuidedAction mChannelsGuidedAction;
@Override
public void onAttach(Context context) {
super.onAttach(context);
mBackStackCount = getFragmentManager().getBackStackEntryCount();
mDvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager();
mSeriesRecordingId = getArguments().getLong(DvrSeriesSettingsActivity.SERIES_RECORDING_ID);
mSeriesRecording = mDvrDataManager.getSeriesRecording(mSeriesRecordingId);
if (mSeriesRecording == null) {
getActivity().finish();
return;
}
mShowViewScheduleOptionInDialog =
getArguments()
.getBoolean(DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG);
mCurrentProgram = getArguments().getParcelable(DvrSeriesSettingsActivity.CURRENT_PROGRAM);
mDvrDataManager.addSeriesRecordingListener(this);
mPrograms =
(List<Program>) BigArguments.getArgument(DvrSeriesSettingsActivity.PROGRAM_LIST);
BigArguments.reset();
if (mPrograms == null) {
getActivity().finish();
return;
}
Set<Long> channelIds = new HashSet<>();
ChannelDataManager channelDataManager =
TvSingletons.getSingletons(context).getChannelDataManager();
for (Program program : mPrograms) {
long channelId = program.getChannelId();
if (channelIds.add(channelId)) {
Channel channel = channelDataManager.getChannel(channelId);
if (channel != null) {
mId2Channel.put(channel.getId(), channel);
mChannels.add(channel);
}
}
}
mChannelOption = mSeriesRecording.getChannelOption();
mSelectedChannelId = Channel.INVALID_ID;
if (mChannelOption == SeriesRecording.OPTION_CHANNEL_ONE) {
Channel channel = channelDataManager.getChannel(mSeriesRecording.getChannelId());
if (channel != null) {
mSelectedChannelId = channel.getId();
} else {
mChannelOption = SeriesRecording.OPTION_CHANNEL_ALL;
}
}
mChannels.sort(ChannelImpl.CHANNEL_NUMBER_COMPARATOR);
mFragmentTitle = getString(R.string.dvr_series_settings_title);
mProrityActionTitle = getString(R.string.dvr_series_settings_priority);
mProrityActionHighestText = getString(R.string.dvr_series_settings_priority_highest);
mProrityActionLowestText = getString(R.string.dvr_series_settings_priority_lowest);
mChannelsActionTitle = getString(R.string.dvr_series_settings_channels);
mChannelsActionAllText = getString(R.string.dvr_series_settings_channels_all);
}
@Override
public void onResume() {
super.onResume();
// To avoid the order of series's priority has changed, but series doesn't get update.
updatePriorityGuidedAction();
}
@Override
public void onDetach() {
super.onDetach();
mDvrDataManager.removeSeriesRecordingListener(this);
}
@Override
public void onDestroy() {
if (getFragmentManager().getBackStackEntryCount() == mBackStackCount
&& getArguments()
.getBoolean(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING)) {
mDvrDataManager.checkAndRemoveEmptySeriesRecording(mSeriesRecordingId);
}
super.onDestroy();
}
@Override
public Guidance onCreateGuidance(Bundle savedInstanceState) {
String breadcrumb = mSeriesRecording.getTitle();
String title = mFragmentTitle;
return new Guidance(title, null, breadcrumb, null);
}
@Override
public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
mPriorityGuidedAction =
new GuidedAction.Builder(getActivity())
.id(ACTION_ID_PRIORITY)
.title(mProrityActionTitle)
.build();
actions.add(mPriorityGuidedAction);
mChannelsGuidedAction =
new GuidedAction.Builder(getActivity())
.id(ACTION_ID_CHANNEL)
.title(mChannelsActionTitle)
.subActions(buildChannelSubAction())
.build();
actions.add(mChannelsGuidedAction);
updateChannelsGuidedAction(false);
}
@Override
public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
actions.add(
new GuidedAction.Builder(getActivity())
.clickAction(GuidedAction.ACTION_ID_OK)
.build());
actions.add(
new GuidedAction.Builder(getActivity())
.clickAction(GuidedAction.ACTION_ID_CANCEL)
.build());
}
@Override
public void onGuidedActionClicked(GuidedAction action) {
long actionId = action.getId();
if (actionId == GuidedAction.ACTION_ID_OK) {
if (mChannelOption != mSeriesRecording.getChannelOption()
|| mSeriesRecording.isStopped()
|| (mChannelOption == SeriesRecording.OPTION_CHANNEL_ONE
&& mSeriesRecording.getChannelId() != mSelectedChannelId)) {
SeriesRecording.Builder builder =
SeriesRecording.buildFrom(mSeriesRecording)
.setChannelOption(mChannelOption)
.setState(SeriesRecording.STATE_SERIES_NORMAL);
if (mSelectedChannelId != Channel.INVALID_ID) {
builder.setChannelId(mSelectedChannelId);
}
DvrManager dvrManager = TvSingletons.getSingletons(getContext()).getDvrManager();
dvrManager.updateSeriesRecording(builder.build());
if (mCurrentProgram != null
&& (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL
|| mSelectedChannelId == mCurrentProgram.getChannelId())) {
dvrManager.addSchedule(mCurrentProgram);
}
updateSchedulesToSeries();
showConfirmDialog();
} else {
showConfirmDialog();
}
} else if (actionId == GuidedAction.ACTION_ID_CANCEL) {
finishGuidedStepFragments();
} else if (actionId == ACTION_ID_PRIORITY) {
FragmentManager fragmentManager = getFragmentManager();
DvrPrioritySettingsFragment fragment = new DvrPrioritySettingsFragment();
Bundle args = new Bundle();
args.putLong(
DvrPrioritySettingsFragment.COME_FROM_SERIES_RECORDING_ID,
mSeriesRecording.getId());
fragment.setArguments(args);
GuidedStepFragment.add(fragmentManager, fragment, R.id.dvr_settings_view_frame);
}
}
@Override
public boolean onSubGuidedActionClicked(GuidedAction action) {
long actionId = action.getId();
if (actionId == SUB_ACTION_ID_CHANNEL_ALL) {
mChannelOption = SeriesRecording.OPTION_CHANNEL_ALL;
mSelectedChannelId = Channel.INVALID_ID;
updateChannelsGuidedAction(true);
return true;
} else if (actionId > SUB_ACTION_ID_CHANNEL_ONE_BASE) {
mChannelOption = SeriesRecording.OPTION_CHANNEL_ONE;
mSelectedChannelId = actionId - SUB_ACTION_ID_CHANNEL_ONE_BASE;
updateChannelsGuidedAction(true);
return true;
}
return false;
}
@Override
public GuidedActionsStylist onCreateButtonActionsStylist() {
return new DvrGuidedActionsStylist(true);
}
private void updateChannelsGuidedAction(boolean notifyActionChanged) {
if (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL) {
mChannelsGuidedAction.setDescription(mChannelsActionAllText);
} else if (mId2Channel.get(mSelectedChannelId) != null) {
mChannelsGuidedAction.setDescription(
mId2Channel.get(mSelectedChannelId).getDisplayText());
}
if (notifyActionChanged) {
notifyActionChanged(findActionPositionById(ACTION_ID_CHANNEL));
}
}
private void updatePriorityGuidedAction() {
int totalSeriesCount = 0;
int priorityOrder = 0;
for (SeriesRecording seriesRecording : mDvrDataManager.getSeriesRecordings()) {
if (seriesRecording.getState() == SeriesRecording.STATE_SERIES_NORMAL
|| seriesRecording.getId() == mSeriesRecording.getId()) {
++totalSeriesCount;
}
if (seriesRecording.getState() == SeriesRecording.STATE_SERIES_NORMAL
&& seriesRecording.getId() != mSeriesRecording.getId()
&& seriesRecording.getPriority() > mSeriesRecording.getPriority()) {
++priorityOrder;
}
}
if (priorityOrder == 0) {
mPriorityGuidedAction.setDescription(mProrityActionHighestText);
} else if (priorityOrder >= totalSeriesCount - 1) {
mPriorityGuidedAction.setDescription(mProrityActionLowestText);
} else {
mPriorityGuidedAction.setDescription(
getString(R.string.dvr_series_settings_priority_rank, priorityOrder + 1));
}
notifyActionChanged(findActionPositionById(ACTION_ID_PRIORITY));
}
private void updateSchedulesToSeries() {
List<Program> recordingCandidates = new ArrayList<>();
Set<SeasonEpisodeNumber> scheduledEpisodes = new HashSet<>();
for (ScheduledRecording r : mDvrDataManager.getScheduledRecordings(mSeriesRecordingId)) {
if (r.getState() != ScheduledRecording.STATE_RECORDING_FAILED
&& r.getState() != ScheduledRecording.STATE_RECORDING_CLIPPED) {
scheduledEpisodes.add(
new SeasonEpisodeNumber(
r.getSeriesRecordingId(),
r.getSeasonNumber(),
r.getEpisodeNumber()));
}
}
for (Program program : mPrograms) {
// Removes current programs and scheduled episodes out, matches the channel option.
if (program.getStartTimeUtcMillis() >= System.currentTimeMillis()
&& mSeriesRecording.matchProgram(program)
&& !scheduledEpisodes.contains(
new SeasonEpisodeNumber(
mSeriesRecordingId,
program.getSeasonNumber(),
program.getEpisodeNumber()))) {
recordingCandidates.add(program);
}
}
if (recordingCandidates.isEmpty()) {
return;
}
List<Program> programsToSchedule =
SeriesRecordingScheduler.pickOneProgramPerEpisode(
mDvrDataManager,
Collections.singletonList(mSeriesRecording),
recordingCandidates)
.get(mSeriesRecordingId);
if (!programsToSchedule.isEmpty()) {
TvSingletons.getSingletons(getContext())
.getDvrManager()
.addScheduleToSeriesRecording(mSeriesRecording, programsToSchedule);
}
}
private List<GuidedAction> buildChannelSubAction() {
List<GuidedAction> channelSubActions = new ArrayList<>();
channelSubActions.add(
new GuidedAction.Builder(getActivity())
.id(SUB_ACTION_ID_CHANNEL_ALL)
.title(mChannelsActionAllText)
.build());
for (Channel channel : mChannels) {
channelSubActions.add(
new GuidedAction.Builder(getActivity())
.id(SUB_ACTION_ID_CHANNEL_ONE_BASE + channel.getId())
.title(channel.getDisplayText())
.build());
}
return channelSubActions;
}
private void showConfirmDialog() {
DvrUiHelper.startSeriesScheduledDialogActivity(
getContext(), mSeriesRecording, mShowViewScheduleOptionInDialog, mPrograms);
finishGuidedStepFragments();
}
@Override
public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) {}
@Override
public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) {
for (SeriesRecording series : seriesRecordings) {
if (series.getId() == mSeriesRecording.getId()) {
finishGuidedStepFragments();
return;
}
}
}
@Override
public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) {
for (SeriesRecording seriesRecording : seriesRecordings) {
if (seriesRecording.getId() == mSeriesRecordingId) {
mSeriesRecording = seriesRecording;
updatePriorityGuidedAction();
return;
}
}
}
}