private ScheduledRecording addSchedule(Program program, long priority) { TvInputInfo input = Utils.getTvInputInfoForProgram(mAppContext, program); if (input == null) { Log.e(TAG, "Can't find input for program: " + program); return null; } ScheduledRecording schedule; SeriesRecording seriesRecording = getSeriesRecording(program); schedule = createScheduledRecordingBuilder(input.getId(), program) .setPriority(priority) .setSeriesRecordingId(seriesRecording == null ? SeriesRecording.ID_NOT_SET : seriesRecording.getId()) .build(); mDataManager.addScheduledRecording(schedule); return schedule; }
/** * Adds a new series recording and schedules for the programs with the initial state. */ public SeriesRecording addSeriesRecording(Program selectedProgram, List<Program> programsToSchedule, @SeriesState int initialState) { Log.i(TAG, "Adding series recording for program " + selectedProgram + ", and schedules: " + programsToSchedule); if (!SoftPreconditions.checkState(mDataManager.isInitialized())) { return null; } TvInputInfo input = Utils.getTvInputInfoForProgram(mAppContext, selectedProgram); if (input == null) { Log.e(TAG, "Can't find input for program: " + selectedProgram); return null; } SeriesRecording seriesRecording = SeriesRecording.builder(input.getId(), selectedProgram) .setPriority(mScheduleManager.suggestNewSeriesPriority()) .setState(initialState) .build(); mDataManager.addSeriesRecording(seriesRecording); // The schedules for the recorded programs should be added not to create the schedule the // duplicate episodes. addRecordedProgramToSeriesRecording(seriesRecording); addScheduleToSeriesRecording(seriesRecording, programsToSchedule); return seriesRecording; }
/** * Returns {@code true} if the channel can be recorded. * <p> * Note that this method doesn't check the conflict of the schedule or available tuners. * This can be called from the UI before the schedules are loaded. */ public boolean isChannelRecordable(Channel channel) { if (!mDataManager.isDvrScheduleLoadFinished() || channel == null) { return false; } TvInputInfo info = Utils.getTvInputInfoForChannelId(mAppContext, channel.getId()); if (info == null) { Log.w(TAG, "Could not find TvInputInfo for " + channel); return false; } if (!info.canRecord()) { return false; } Program program = TvApplication.getSingletons(mAppContext).getProgramDataManager() .getCurrentProgram(channel.getId()); return program == null || !program.isRecordingProhibited(); }
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Bundle args = getArguments(); if (args != null) { mProgram = args.getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM); } SoftPreconditions.checkArgument(mProgram != null); TvInputInfo input = Utils.getTvInputInfoForProgram(getContext(), mProgram); SoftPreconditions.checkNotNull(input); List<ScheduledRecording> conflicts = null; if (input != null) { conflicts = TvApplication.getSingletons(getContext()).getDvrManager() .getConflictingSchedules(mProgram); } if (conflicts == null) { conflicts = Collections.emptyList(); } if (conflicts.isEmpty()) { dismissDialog(); } setConflicts(conflicts); return super.onCreateView(inflater, container, savedInstanceState); }
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Bundle args = getArguments(); long channelId = args.getLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID); mChannel = TvApplication.getSingletons(getContext()).getChannelDataManager() .getChannel(channelId); SoftPreconditions.checkArgument(mChannel != null); TvInputInfo input = Utils.getTvInputInfoForChannelId(getContext(), mChannel.getId()); SoftPreconditions.checkNotNull(input); List<ScheduledRecording> conflicts = null; if (input != null) { mStartTimeMs = args.getLong(DvrHalfSizedDialogFragment.KEY_START_TIME_MS); mEndTimeMs = args.getLong(DvrHalfSizedDialogFragment.KEY_END_TIME_MS); conflicts = TvApplication.getSingletons(getContext()).getDvrManager() .getConflictingSchedules(mChannel.getId(), mStartTimeMs, mEndTimeMs); } if (conflicts == null) { conflicts = Collections.emptyList(); } if (conflicts.isEmpty()) { dismissDialog(); } setConflicts(conflicts); return super.onCreateView(inflater, container, savedInstanceState); }
/** * Returns a sorted list of all scheduled recordings that will not be recorded if * this program is going to be recorded, with their priorities in decending order. * <p> * An empty list means there is no conflicts. If there is conflict, a priority higher than * the first recording in the returned list should be assigned to the new schedule of this * program to guarantee the program would be completely recorded. */ public List<ScheduledRecording> getConflictingSchedules(Program program) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); SoftPreconditions.checkState(Program.isValid(program), TAG, "Program is invalid: " + program); SoftPreconditions.checkState( program.getStartTimeUtcMillis() < program.getEndTimeUtcMillis(), TAG, "Program duration is empty: " + program); if (!mInitialized || !Program.isValid(program) || program.getStartTimeUtcMillis() >= program.getEndTimeUtcMillis()) { return Collections.emptyList(); } TvInputInfo input = Utils.getTvInputInfoForProgram(mContext, program); if (input == null || !input.canRecord() || input.getTunerCount() <= 0) { return Collections.emptyList(); } return getConflictingSchedules(input, Collections.singletonList( ScheduledRecording.builder(input.getId(), program) .setPriority(suggestHighestPriority()) .build())); }
/** * Returns a sorted list of all scheduled recordings that will not be recorded if * this channel is going to be recorded, with their priority in decending order. * <p> * An empty list means there is no conflicts. If there is conflict, a priority higher than * the first recording in the returned list should be assigned to the new schedule of this * channel to guarantee the channel would be completely recorded in the designated time range. */ public List<ScheduledRecording> getConflictingSchedules(long channelId, long startTimeMs, long endTimeMs) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); SoftPreconditions.checkState(channelId != Channel.INVALID_ID, TAG, "Invalid channel ID"); SoftPreconditions.checkState(startTimeMs < endTimeMs, TAG, "Recording duration is empty."); if (!mInitialized || channelId == Channel.INVALID_ID || startTimeMs >= endTimeMs) { return Collections.emptyList(); } TvInputInfo input = Utils.getTvInputInfoForChannelId(mContext, channelId); if (input == null || !input.canRecord() || input.getTunerCount() <= 0) { return Collections.emptyList(); } return getConflictingSchedules(input, Collections.singletonList( ScheduledRecording.builder(input.getId(), channelId, startTimeMs, endTimeMs) .setPriority(suggestHighestPriority()) .build())); }
/** * Returns priority ordered list of all scheduled recordings that will not be recorded if * the user keeps watching this channel. * <p> * Note that if the user keeps watching the channel, the channel can be recorded. */ public List<ScheduledRecording> getConflictingSchedulesForWatching(long channelId) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); SoftPreconditions.checkState(channelId != Channel.INVALID_ID, TAG, "Invalid channel ID"); TvInputInfo input = Utils.getTvInputInfoForChannelId(mContext, channelId); SoftPreconditions.checkState(input != null, TAG, "Can't find input for channel ID: " + channelId); if (!mInitialized || channelId == Channel.INVALID_ID || input == null) { return Collections.emptyList(); } List<ScheduledRecording> schedules = mInputScheduleMap.get(input.getId()); if (schedules == null || schedules.isEmpty()) { return Collections.emptyList(); } return getConflictingSchedulesForWatching(input.getId(), channelId, System.currentTimeMillis(), suggestNewPriority(), schedules, input.getTunerCount()); }
private void scheduleRecordingSoon(ScheduledRecording schedule) { TvInputInfo input = Utils.getTvInputInfoForInputId(mContext, schedule.getInputId()); if (input == null) { Log.e(TAG, "Can't find input for " + schedule); mDataManager.changeState(schedule, ScheduledRecording.STATE_RECORDING_FAILED); return; } if (!input.canRecord() || input.getTunerCount() <= 0) { Log.e(TAG, "TV input doesn't support recording: " + input); mDataManager.changeState(schedule, ScheduledRecording.STATE_RECORDING_FAILED); return; } InputTaskScheduler scheduler = mInputSchedulerMap.get(input.getId()); if (scheduler == null) { scheduler = new InputTaskScheduler(mContext, input, mLooper, mChannelDataManager, mDvrManager, mDataManager, mSessionManager, mClock); mInputSchedulerMap.put(input.getId(), scheduler); } scheduler.addSchedule(schedule); if (mLastStartTimePendingMs < schedule.getStartTimeMs()) { mLastStartTimePendingMs = schedule.getStartTimeMs(); } }
private void onStart() { if (!mStarted) { mStarted = true; mCancelLoadTask = false; if (!PermissionUtils.hasAccessWatchedHistory(mContext)) { mWatchedHistoryManager = new WatchedHistoryManager(mContext); mWatchedHistoryManager.setListener(this); mWatchedHistoryManager.start(); } else { mContext.getContentResolver().registerContentObserver( TvContract.WatchedPrograms.CONTENT_URI, true, mContentObserver); mHandler.obtainMessage(MSG_UPDATE_WATCH_HISTORY, TvContract.WatchedPrograms.CONTENT_URI) .sendToTarget(); } mTvInputManager = (TvInputManager) mContext.getSystemService(Context.TV_INPUT_SERVICE); mTvInputManager.registerCallback(mInternalCallback, mHandler); for (TvInputInfo input : mTvInputManager.getTvInputList()) { mInputs.add(input.getId()); } } if (mChannelRecordMapLoaded) { mHandler.sendEmptyMessage(MSG_NOTIFY_CHANNEL_RECORD_MAP_LOADED); } }
@Override public int compare(TvInputInfo lhs, TvInputInfo rhs) { boolean lhsIsNewInput = mSetupUtils.isNewInput(lhs.getId()); boolean rhsIsNewInput = mSetupUtils.isNewInput(rhs.getId()); if (lhsIsNewInput != rhsIsNewInput) { // New input first. return lhsIsNewInput ? -1 : 1; } if (!lhsIsNewInput) { // Checks only when the inputs are not new. boolean lhsSetupDone = mSetupUtils.isSetupDone(lhs.getId()); boolean rhsSetupDone = mSetupUtils.isSetupDone(rhs.getId()); if (lhsSetupDone != rhsSetupDone) { // An input which has not been setup comes first. return lhsSetupDone ? 1 : -1; } } return mInputManager.getDefaultTvInputInfoComparator().compare(lhs, rhs); }
@Override public void run() { List<TvInputInfo> infoList = mTvInputManagerHelper.getTvInputInfos(false, false); int systemInputCount = 0; int nonSystemInputCount = 0; for (TvInputInfo info : infoList) { if (mTvInputManagerHelper.isSystemInput(info)) { systemInputCount++; } else { nonSystemInputCount++; } } ConfigurationInfo configurationInfo = new ConfigurationInfo(systemInputCount, nonSystemInputCount); mTracker.sendConfigurationInfo(configurationInfo); }
private boolean isAvailable() { if (!mPipInput.isAvailable()) { return false; } // If this input shares the same parent with the current main input, you cannot select // it. (E.g. two HDMI CEC devices that are connected to HDMI port 1 through an A/V // receiver.) PipInput pipInput = mPipInputManager.getPipInput(getMainActivity().getCurrentChannel()); if (pipInput == null) { return false; } TvInputInfo mainInputInfo = pipInput.getInputInfo(); TvInputInfo pipInputInfo = mPipInput.getInputInfo(); return mainInputInfo == null || pipInputInfo == null || !TextUtils.equals(mainInputInfo.getId(), pipInputInfo.getId()) && !TextUtils.equals(mainInputInfo.getParentId(), pipInputInfo.getParentId()); }
public void updateLabel() { MainActivity mainActivity = (MainActivity) getContext(); Channel channel = mainActivity.getCurrentChannel(); if (channel == null || !channel.isPassthrough()) { return; } TvInputInfo input = mainActivity.getTvInputManagerHelper().getTvInputInfo( channel.getInputId()); CharSequence customLabel = input.loadCustomLabel(getContext()); CharSequence label = input.loadLabel(getContext()); if (TextUtils.isEmpty(customLabel) || customLabel.equals(label)) { mInputLabelTextView.setText(label); mSecondaryInputLabelTextView.setVisibility(View.GONE); } else { mInputLabelTextView.setText(customLabel); mSecondaryInputLabelTextView.setText(label); mSecondaryInputLabelTextView.setVisibility(View.VISIBLE); } }
@Override public void onInputAdded(String inputId) { if (DEBUG) Log.d(TAG, "onInputAdded " + inputId); if (isInBlackList(inputId)) { return; } TvInputInfo info = mTvInputManager.getTvInputInfo(inputId); if (info != null) { mInputMap.put(inputId, info); mInputStateMap.put(inputId, mTvInputManager.getInputState(inputId)); mInputIdToPartnerInputMap.put(inputId, isPartnerInput(info)); } mContentRatingsManager.update(); for (TvInputCallback callback : mCallbacks) { callback.onInputAdded(inputId); } }
public void start() { if (mStarted) { return; } if (DEBUG) Log.d(TAG, "start"); mStarted = true; mTvInputManager.registerCallback(mInternalCallback, mHandler); mInputMap.clear(); mInputStateMap.clear(); mInputIdToPartnerInputMap.clear(); for (TvInputInfo input : mTvInputManager.getTvInputList()) { if (DEBUG) Log.d(TAG, "Input detected " + input); String inputId = input.getId(); if (isInBlackList(inputId)) { continue; } mInputMap.put(inputId, input); int state = mTvInputManager.getInputState(inputId); mInputStateMap.put(inputId, state); mInputIdToPartnerInputMap.put(inputId, isPartnerInput(input)); } SoftPreconditions.checkState(mInputStateMap.size() == mInputMap.size(), TAG, "mInputStateMap not the same size as mInputMap"); mContentRatingsManager.update(); }
/** * Gets the size of inputs for PIP. * * <p>The hidden inputs are not counted. * * @param availableOnly If {@code true}, it counts only available PIP inputs. Please see {@link * PipInput#isAvailable()} for the details of availability. */ public int getPipInputSize(boolean availableOnly) { int count = 0; for (PipInput pipInput : mPipInputMap.values()) { if (!pipInput.isHidden() && (!availableOnly || pipInput.mAvailable)) { ++count; } if (pipInput.isPassthrough()) { TvInputInfo info = pipInput.getInputInfo(); // Do not count HDMI ports if a CEC device is directly connected to the port. if (info.getParentId() != null && !info.isConnectedToHdmiSwitch()) { --count; } } } return count; }
/** * Builds tuner input's info. */ @Nullable @TargetApi(Build.VERSION_CODES.N) public static TvInputInfo buildTunerInputInfo(Context context, boolean fromBuiltInTuner) { int numOfDevices = TunerHal.getTunerCount(context); if (numOfDevices == 0) { return null; } TvInputInfo.Builder builder = new TvInputInfo.Builder(context, new ComponentName(context, TunerTvInputService.class)); if (fromBuiltInTuner) { builder.setLabel(R.string.bt_app_name); } else { builder.setLabel(R.string.ut_app_name); } try { return builder.setCanRecord(CommonFeatures.DVR.isEnabled(context)) .setTunerCount(numOfDevices) .build(); } catch (NullPointerException e) { // TunerTvInputService is not enabled. return null; } }
/** * Updates tuner input's info. * * @param context {@link Context} instance */ public static void updateTunerInputInfo(Context context) { if (BuildCompat.isAtLeastN()) { if (DEBUG) Log.d(TAG, "updateTunerInputInfo()"); TvInputInfo info = buildTunerInputInfo(context, isBuiltInTuner(context)); if (info != null) { ((TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE)) .updateTvInputInfo(info); if (DEBUG) { Log.d(TAG, "TvInputInfo [" + info.loadLabel(context) + "] updated: " + info.toString()); } } else { if (DEBUG) { Log.d(TAG, "Updating tuner input's info failed. Input is not ready yet."); } } } }
public void testAddSchedule_consecutiveUseLessSession() throws Exception { TvInputInfo input = createTvInputInfo(TUNER_COUNT_TWO); mScheduler.updateTvInputInfo(input); long startTimeMs = mFakeClock.currentTimeMillis(); long endTimeMs = startTimeMs + TimeUnit.SECONDS.toMillis(1); long id = 0; mScheduler.handleAddSchedule( RecordingTestUtils.createTestRecordingWithIdAndPriorityAndPeriod(++id, CHANNEL_ID, LOW_PRIORITY, startTimeMs, endTimeMs)); mScheduler.handleBuildSchedule(); startTimeMs = endTimeMs; endTimeMs = startTimeMs + TimeUnit.SECONDS.toMillis(1); mScheduler.handleAddSchedule( RecordingTestUtils.createTestRecordingWithIdAndPriorityAndPeriod(++id, CHANNEL_ID, HIGH_PRIORITY, startTimeMs, endTimeMs)); mScheduler.handleBuildSchedule(); verify(mRecordingTasks.get(0), timeout((int) LISTENER_TIMEOUT_MS).times(1)).start(); verify(mRecordingTasks.get(0), timeout((int) LISTENER_TIMEOUT_MS).never()).stop(); // The second schedule should wait until the first one finishes rather than creating a new // session even though there are available tuners. assertTrue(mRecordingTasks.size() == 1); }
@Override public void onCreate(Bundle savedInstanceState) { Log.d(TAG, "onCreate SetupFragment"); super.onCreate(savedInstanceState); LocalBroadcastManager.getInstance(getActivity()).registerReceiver( mSyncStatusChangedReceiver, new IntentFilter(SyncJobService.ACTION_SYNC_STATUS_CHANGED)); mInputId = getActivity().getIntent().getStringExtra(TvInputInfo.EXTRA_INPUT_ID); new SetupRowTask().execute(); }
@Override public void onGuidedActionClicked(GuidedAction action) { if (action.getId() == ACTION_ONLINE_STORE) { mParentFragment.onActionClick(ACTION_CATEGORY, (int) action.getId()); return; } int index = (int) action.getId() - ACTION_INPUT_START; if (index >= 0) { TvInputInfo input = mInputs.get(index); Bundle params = new Bundle(); params.putString(ACTION_PARAM_KEY_INPUT_ID, input.getId()); mParentFragment.onActionClick(ACTION_CATEGORY, ACTION_SETUP_INPUT, params); } }
/** * Update tv input logo. It should be called when the visible child item in ProgramGrid * changed. */ public void updateInputLogo(int lastPosition, boolean forceShow) { if (mChannel == null) { mInputLogoView.setVisibility(View.GONE); mIsInputLogoVisible = false; return; } boolean showLogo = forceShow; if (!showLogo) { Channel lastChannel = mProgramManager.getChannel(lastPosition); if (lastChannel == null || !mChannel.getInputId().equals(lastChannel.getInputId())) { showLogo = true; } } if (showLogo) { if (!mIsInputLogoVisible) { mIsInputLogoVisible = true; TvInputInfo info = mTvInputManagerHelper.getTvInputInfo(mChannel.getInputId()); if (info != null) { LoadTvInputLogoTask task = new LoadTvInputLogoTask( itemView.getContext(), ImageCache.getInstance(), info); ImageLoader.loadBitmap(createTvInputLogoLoadedCallback(info, this), task); } } } else { mInputLogoView.setVisibility(View.GONE); mInputLogoView.setImageDrawable(null); mIsInputLogoVisible = false; } }
/** * Handles the global key KEYCODE_TV_INPUT. */ public void handleTvInputKey() { TvInputManager tvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE); List<TvInputInfo> tvInputs = tvInputManager.getTvInputList(); int inputCount = 0; boolean hasTunerInput = false; for (TvInputInfo input : tvInputs) { if (input.isPassthroughInput()) { if (!input.isHidden(this)) { ++inputCount; } } else if (!hasTunerInput) { hasTunerInput = true; ++inputCount; } } if (inputCount < 2) { return; } Activity activityToHandle = mMainActivityWrapper.isResumed() ? mMainActivityWrapper.getMainActivity() : mSelectInputActivity; if (activityToHandle != null) { // If startActivity is called, MainActivity.onPause is unnecessarily called. To // prevent it, MainActivity.dispatchKeyEvent is directly called. activityToHandle.dispatchKeyEvent( new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TV_INPUT)); activityToHandle.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_TV_INPUT)); } else if (mMainActivityWrapper.isStarted()) { Bundle extras = new Bundle(); extras.putString(Utils.EXTRA_KEY_ACTION, Utils.EXTRA_ACTION_SHOW_TV_INPUT); startMainActivity(extras); } else { startActivity(new Intent(this, SelectInputActivity.class).setFlags( Intent.FLAG_ACTIVITY_NEW_TASK)); } }
/** * Checks the input counts and enable/disable TvActivity. Also updates the input list in * {@link SetupUtils}. * * @param calledByTunerServiceChanged true if it is called when TunerTvInputService * is enabled or disabled. * @param tunerServiceEnabled it's available only when calledByTunerServiceChanged is true. * @param dontKillApp when TvActivity is enabled or disabled by this method, the app restarts * by default. But, if dontKillApp is true, the app won't restart. */ public void handleInputCountChanged(boolean calledByTunerServiceChanged, boolean tunerServiceEnabled, boolean dontKillApp) { TvInputManager inputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE); boolean enable = (calledByTunerServiceChanged && tunerServiceEnabled) || Features.UNHIDE.isEnabled(TvApplication.this); if (!enable) { List<TvInputInfo> inputs = inputManager.getTvInputList(); boolean skipTunerInputCheck = false; // Enable the TvActivity only if there is at least one tuner type input. if (!skipTunerInputCheck) { for (TvInputInfo input : inputs) { if (calledByTunerServiceChanged && !tunerServiceEnabled && TunerTvInputService.getInputId(this).equals(input.getId())) { continue; } if (input.getType() == TvInputInfo.TYPE_TUNER) { enable = true; break; } } } if (DEBUG) Log.d(TAG, "Enable MainActivity: " + enable); } PackageManager packageManager = getPackageManager(); ComponentName name = new ComponentName(this, TvActivity.class); int newState = enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; if (packageManager.getComponentEnabledSetting(name) != newState) { packageManager.setComponentEnabledSetting(name, newState, dontKillApp ? PackageManager.DONT_KILL_APP : 0); } SetupUtils.getInstance(TvApplication.this).onInputListUpdated(inputManager); }
/** * Adds a recording schedule with a time range. */ public void addSchedule(Channel channel, long startTime, long endTime) { Log.i(TAG, "Adding scheduled recording of channel " + channel + " starting at " + Utils.toTimeString(startTime) + " and ending at " + Utils.toTimeString(endTime)); if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { return; } TvInputInfo input = Utils.getTvInputInfoForChannelId(mAppContext, channel.getId()); if (input == null) { Log.e(TAG, "Can't find input for channel: " + channel); return; } addScheduleInternal(input.getId(), channel.getId(), startTime, endTime); }
/** * Adds {@link ScheduledRecording}s for the series recording. * <p> * This method doesn't add the series recording. */ public void addScheduleToSeriesRecording(SeriesRecording series, List<Program> programsToSchedule) { if (!SoftPreconditions.checkState(mDataManager.isDvrScheduleLoadFinished())) { return; } TvInputInfo input = Utils.getTvInputInfoForInputId(mAppContext, series.getInputId()); if (input == null) { Log.e(TAG, "Can't find input with ID: " + series.getInputId()); return; } List<ScheduledRecording> toAdd = new ArrayList<>(); List<ScheduledRecording> toUpdate = new ArrayList<>(); for (Program program : programsToSchedule) { ScheduledRecording scheduleWithSameProgram = mDataManager.getScheduledRecordingForProgramId(program.getId()); if (scheduleWithSameProgram != null) { if (scheduleWithSameProgram.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED) { ScheduledRecording r = ScheduledRecording.buildFrom(scheduleWithSameProgram) .setSeriesRecordingId(series.getId()) .build(); if (!r.equals(scheduleWithSameProgram)) { toUpdate.add(r); } } } else { toAdd.add(createScheduledRecordingBuilder(input.getId(), program) .setPriority(series.getPriority()) .setSeriesRecordingId(series.getId()) .build()); } } if (!toAdd.isEmpty()) { mDataManager.addScheduledRecording(ScheduledRecording.toArray(toAdd)); } if (!toUpdate.isEmpty()) { mDataManager.updateScheduledRecording(ScheduledRecording.toArray(toUpdate)); } }
/** * Returns {@code true} if the program can be recorded. * <p> * Note that this method doesn't check the conflict of the schedule or available tuners. * This can be called from the UI before the schedules are loaded. */ public boolean isProgramRecordable(Program program) { if (!mDataManager.isInitialized()) { return false; } TvInputInfo info = Utils.getTvInputInfoForProgram(mAppContext, program); if (info == null) { Log.w(TAG, "Could not find TvInputInfo for " + program); return false; } return info.canRecord() && !program.isRecordingProhibited(); }
public SeriesScheduleRowAdapter(Context context, ClassPresenterSelector classPresenterSelector, SeriesRecording seriesRecording) { super(context, classPresenterSelector); mSeriesRecording = seriesRecording; TvInputInfo input = Utils.getTvInputInfoForInputId(context, mSeriesRecording.getInputId()); if (SoftPreconditions.checkNotNull(input) != null) { mInputId = input.getId(); } else { mInputId = null; } ApplicationSingletons singletons = TvApplication.getSingletons(context); mDvrManager = singletons.getDvrManager(); mDataManager = singletons.getDvrDataManager(); setHasStableIds(true); }
/** * Returns list of all conflicting scheduled recordings with schedules belonging to {@code * seriesRecording} * recording. * <p> * Any empty list means there is no conflicts. */ public List<ScheduledRecording> getConflictingSchedules(SeriesRecording seriesRecording) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); SoftPreconditions.checkState(seriesRecording != null, TAG, "series recording is null"); if (!mInitialized || seriesRecording == null) { return Collections.emptyList(); } TvInputInfo input = Utils.getTvInputInfoForInputId(mContext, seriesRecording.getInputId()); if (input == null || !input.canRecord() || input.getTunerCount() <= 0) { return Collections.emptyList(); } List<ScheduledRecording> schedulesForSeries = mDataManager.getScheduledRecordings( seriesRecording.getId()); return getConflictingSchedules(input, schedulesForSeries); }
/** * Returns all the scheduled recordings that conflicts and will not be recorded or clipped for * the given input. */ @NonNull private Map<ScheduledRecording, Boolean> getConflictingSchedulesInfo(String inputId) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); TvInputInfo input = Utils.getTvInputInfoForInputId(mContext, inputId); SoftPreconditions.checkState(input != null, TAG, "Can't find input for : " + inputId); if (!mInitialized || input == null) { return Collections.emptyMap(); } List<ScheduledRecording> schedules = mInputScheduleMap.get(input.getId()); if (schedules == null || schedules.isEmpty()) { return Collections.emptyMap(); } return getConflictingSchedulesInfo(schedules, input.getTunerCount()); }
/** * Checks if the schedule is conflicting. * * <p>Note that the {@code schedule} should be the existing one. If not, this returns * {@code false}. */ public boolean isConflicting(ScheduledRecording schedule) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); TvInputInfo input = Utils.getTvInputInfoForInputId(mContext, schedule.getInputId()); SoftPreconditions.checkState(input != null, TAG, "Can't find input for channel ID : " + schedule.getChannelId()); if (!mInitialized || input == null) { return false; } Map<ScheduledRecording, Boolean> conflicts = mInputConflictInfoMap.get(input.getId()); return conflicts != null && conflicts.containsKey(schedule); }
/** * Checks if the schedule is partially conflicting, i.e., part of the scheduled program might be * recorded even if the priority of the schedule is not raised. * <p> * If the given schedule is not conflicting or is totally conflicting, i.e., cannot be recorded * at all, this method returns {@code false} in both cases. */ public boolean isPartiallyConflicting(@NonNull ScheduledRecording schedule) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); TvInputInfo input = Utils.getTvInputInfoForInputId(mContext, schedule.getInputId()); SoftPreconditions.checkState(input != null, TAG, "Can't find input for channel ID : " + schedule.getChannelId()); if (!mInitialized || input == null) { return false; } Map<ScheduledRecording, Boolean> conflicts = mInputConflictInfoMap.get(input.getId()); return conflicts != null && conflicts.getOrDefault(schedule, false); }
/** * Returns priority ordered list of all scheduled recordings that will not be recorded if * this channel is tuned to. */ public List<ScheduledRecording> getConflictingSchedulesForTune(long channelId) { SoftPreconditions.checkState(mInitialized, TAG, "Not initialized yet"); SoftPreconditions.checkState(channelId != Channel.INVALID_ID, TAG, "Invalid channel ID"); TvInputInfo input = Utils.getTvInputInfoForChannelId(mContext, channelId); SoftPreconditions.checkState(input != null, TAG, "Can't find input for channel ID: " + channelId); if (!mInitialized || channelId == Channel.INVALID_ID || input == null) { return Collections.emptyList(); } return getConflictingSchedulesForTune(input.getId(), channelId, System.currentTimeMillis(), suggestHighestPriority(), getStartedRecordings(input.getId()), input.getTunerCount()); }
private List<ScheduledRecording> getConflictingSchedules(TvInputInfo input, List<ScheduledRecording> schedulesToAdd) { SoftPreconditions.checkNotNull(input); if (input == null || !input.canRecord() || input.getTunerCount() <= 0) { return Collections.emptyList(); } List<ScheduledRecording> currentSchedules = mInputScheduleMap.get(input.getId()); if (currentSchedules == null || currentSchedules.isEmpty()) { return Collections.emptyList(); } return getConflictingSchedules(schedulesToAdd, currentSchedules, input.getTunerCount()); }
@Override public void onStorageMountChanged(boolean storageMounted) { for (TvInputInfo input : mInputManager.getTvInputInfos(true, true)) { if (Utils.isBundledInput(input.getId())) { if (storageMounted) { unhideInput(input.getId()); } else { hideInput(input.getId()); } } } }
@Override public void onTvInputInfoUpdated(TvInputInfo input) { InputTaskScheduler scheduler = mInputSchedulerMap.get(input.getId()); if (scheduler != null) { scheduler.updateTvInputInfo(input); } }
@VisibleForTesting String getInputLabelForChannel(Channel channel) { String label = mInputIdToLabelMap.get(channel.getInputId()); if (label == null) { TvInputInfo info = mInputManager.getTvInputInfo(channel.getInputId()); if (info != null) { label = Utils.loadLabel(mContext, info); if (label != null) { mInputIdToLabelMap.put(channel.getInputId(), label); } } } return label; }
private void resumeTvIfNeeded() { if (DEBUG) Log.d(TAG, "resumeTvIfNeeded()"); if (!mTvView.isPlaying() || mInitChannelUri != null || (mShouldTuneToTunerChannel && mChannelTuner.isCurrentChannelPassthrough())) { if (TvContract.isChannelUriForPassthroughInput(mInitChannelUri)) { // The target input may not be ready yet, especially, just after screen on. String inputId = mInitChannelUri.getPathSegments().get(1); TvInputInfo input = mTvInputManagerHelper.getTvInputInfo(inputId); if (input == null) { input = mTvInputManagerHelper.getTvInputInfo(mParentInputIdWhenScreenOff); if (input == null) { SoftPreconditions.checkState(false, TAG, "Input disappear."); finish(); } else { mInitChannelUri = TvContract.buildChannelUriForPassthroughInput(input.getId()); } } } mParentInputIdWhenScreenOff = null; startTv(mInitChannelUri); mInitChannelUri = null; } // Make sure TV app has the main TV view to handle the case that TvView is used in other // application. restoreMainTvView(); mTvView.setBlockScreenType(getDesiredBlockScreenType()); }
/** * Handles screen off to keep the current channel for next screen on. */ private void markCurrentChannelDuringScreenOff() { mInitChannelUri = mChannelTuner.getCurrentChannelUri(); if (mChannelTuner.isCurrentChannelPassthrough()) { // When ACTION_SCREEN_OFF is invoked, some CEC devices may be already // removed. So we need to get the input info from ChannelTuner instead of // TvInputManagerHelper. TvInputInfo input = mChannelTuner.getCurrentInputInfo(); mParentInputIdWhenScreenOff = input.getParentId(); if (DEBUG) Log.d(TAG, "Parent input: " + mParentInputIdWhenScreenOff); } }