/* * 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.app.FragmentManager; import android.app.ProgressDialog; import android.content.Context; 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.Log; import android.util.LongSparseArray; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ProgressBar; import com.android.tv.R; import com.android.tv.TvApplication; import com.android.tv.data.Channel; import com.android.tv.data.ChannelDataManager; import com.android.tv.data.Program; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.DvrUiHelper; import com.android.tv.dvr.EpisodicProgramLoadTask; import com.android.tv.dvr.SeriesRecording; import com.android.tv.dvr.SeriesRecording.ChannelOption; import com.android.tv.dvr.SeriesRecordingScheduler; import com.android.tv.dvr.SeriesRecordingScheduler.OnSeriesRecordingUpdatedListener; import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Fragment for DVR series recording settings. */ public class SeriesSettingsFragment extends GuidedStepFragment implements DvrDataManager.SeriesRecordingListener { private static final String TAG = "SeriesSettingsFragment"; private static final boolean DEBUG = false; 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 ChannelDataManager mChannelDataManager; private DvrManager mDvrManager; private SeriesRecording mSeriesRecording; private long mSeriesRecordingId; @ChannelOption int mChannelOption; private Comparator mChannelComparator; private long mSelectedChannelId; private int mBackStackCount; private boolean mShowViewScheduleOptionInDialog; private String mFragmentTitle; private String mProrityActionTitle; private String mProrityActionHighestText; private String mProrityActionLowestText; private String mChannelsActionTitle; private String mChannelsActionAllText; private LongSparseArray mId2Channel = new LongSparseArray<>(); private List mChannels = new ArrayList<>(); private EpisodicProgramLoadTask mEpisodicProgramLoadTask; private GuidedAction mPriorityGuidedAction; private GuidedAction mChannelsGuidedAction; @Override public void onAttach(Context context) { super.onAttach(context); mBackStackCount = getFragmentManager().getBackStackEntryCount(); mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager(); mSeriesRecordingId = getArguments().getLong(DvrSeriesSettingsActivity.SERIES_RECORDING_ID); mSeriesRecording = mDvrDataManager.getSeriesRecording(mSeriesRecordingId); if (mSeriesRecording == null) { getActivity().finish(); return; } mDvrManager = TvApplication.getSingletons(context).getDvrManager(); mShowViewScheduleOptionInDialog = getArguments().getBoolean( DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG); mDvrDataManager.addSeriesRecordingListener(this); long[] channelIds = getArguments().getLongArray(DvrSeriesSettingsActivity.CHANNEL_ID_LIST); mChannelDataManager = TvApplication.getSingletons(context).getChannelDataManager(); if (channelIds == null) { Channel channel = mChannelDataManager.getChannel(mSeriesRecording.getChannelId()); if (channel != null) { mId2Channel.put(channel.getId(), channel); mChannels.add(channel); } collectChannelsInBackground(); } else { for (long channelId : channelIds) { Channel channel = mChannelDataManager.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 = mChannelDataManager.getChannel(mSeriesRecording.getChannelId()); if (channel != null) { mSelectedChannelId = channel.getId(); } else { mChannelOption = SeriesRecording.OPTION_CHANNEL_ALL; } } mChannelComparator = new Channel.DefaultComparator(context, TvApplication.getSingletons(context).getTvInputManagerHelper()); mChannels.sort(mChannelComparator); 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 onDetach() { super.onDetach(); mDvrDataManager.removeSeriesRecordingListener(this); if (mEpisodicProgramLoadTask != null) { mEpisodicProgramLoadTask.cancel(true); mEpisodicProgramLoadTask = null; } } @Override public void onDestroy() { DvrManager dvrManager = TvApplication.getSingletons(getActivity()).getDvrManager(); if (getFragmentManager().getBackStackEntryCount() == mBackStackCount && getArguments() .getBoolean(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING) && dvrManager.canRemoveSeriesRecording(mSeriesRecordingId)) { dvrManager.removeSeriesRecording(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 actions, Bundle savedInstanceState) { mPriorityGuidedAction = new GuidedAction.Builder(getActivity()) .id(ACTION_ID_PRIORITY) .title(mProrityActionTitle) .build(); updatePriorityGuidedAction(false); 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 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 (mEpisodicProgramLoadTask != null) { mEpisodicProgramLoadTask.cancel(true); mEpisodicProgramLoadTask = null; } 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); } TvApplication.getSingletons(getContext()).getDvrManager() .updateSeriesRecording(builder.build()); SeriesRecordingScheduler scheduler = SeriesRecordingScheduler.getInstance(getContext()); // Since dialog is used even after the fragment is closed, we should // use application context. ProgressDialog dialog = ProgressDialog.show(getContext(), null, getString( R.string.dvr_series_schedules_progress_message_updating_programs)); scheduler.addOnSeriesRecordingUpdatedListener( new OnSeriesRecordingUpdatedListener() { @Override public void onSeriesRecordingUpdated(SeriesRecording... seriesRecordings) { for (SeriesRecording seriesRecording : seriesRecordings) { if (seriesRecording.getId() == mSeriesRecordingId) { dialog.dismiss(); scheduler.removeOnSeriesRecordingUpdatedListener(this); showConfirmDialog(); return; } } } }); } else { showConfirmDialog(); } } else if (actionId == GuidedAction.ACTION_ID_CANCEL) { finishGuidedStepFragments(); } else if (actionId == ACTION_ID_PRIORITY) { FragmentManager fragmentManager = getFragmentManager(); PrioritySettingsFragment fragment = new PrioritySettingsFragment(); Bundle args = new Bundle(); args.putLong(PrioritySettingsFragment.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 { mChannelsGuidedAction.setDescription(mId2Channel.get(mSelectedChannelId) .getDisplayText()); } if (notifyActionChanged) { notifyActionChanged(findActionPositionById(ACTION_ID_CHANNEL)); } } private void updatePriorityGuidedAction(boolean notifyActionChanged) { 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)); } if (notifyActionChanged) { notifyActionChanged(findActionPositionById(ACTION_ID_PRIORITY)); } } private void collectChannelsInBackground() { if (mEpisodicProgramLoadTask != null) { mEpisodicProgramLoadTask.cancel(true); } mEpisodicProgramLoadTask = new EpisodicProgramLoadTask(getContext(), mSeriesRecording) { @Override protected void onPostExecute(List programs) { mEpisodicProgramLoadTask = null; Set channelIds = new HashSet<>(); for (Program program : programs) { channelIds.add(program.getChannelId()); } boolean channelAdded = false; for (Long channelId : channelIds) { if (mId2Channel.get(channelId) != null) { continue; } Channel channel = mChannelDataManager.getChannel(channelId); if (channel != null) { channelAdded = true; mId2Channel.put(channelId, channel); mChannels.add(channel); if (DEBUG) Log.d(TAG, "Added channel: " + channel); } } if (!channelAdded) { return; } mChannels.sort(mChannelComparator); mChannelsGuidedAction.setSubActions(buildChannelSubAction()); notifyActionChanged(findActionPositionById(ACTION_ID_CHANNEL)); if (DEBUG) Log.d(TAG, "Complete EpisodicProgramLoadTask"); } }.setLoadCurrentProgram(true) .setLoadDisallowedProgram(true) .setLoadScheduledEpisode(true) .setIgnoreChannelOption(true); mEpisodicProgramLoadTask.execute(); } private List buildChannelSubAction() { List 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); finishGuidedStepFragments(); } @Override public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { } @Override public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) { } @Override public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) { for (SeriesRecording seriesRecording : seriesRecordings) { if (seriesRecording.getId() == mSeriesRecordingId) { mSeriesRecording = seriesRecording; updatePriorityGuidedAction(true); return; } } } }