diff options
Diffstat (limited to 'src/com/android/tv/dvr/ui')
65 files changed, 898 insertions, 3216 deletions
diff --git a/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java b/src/com/android/tv/dvr/ui/ActionPresenterSelector.java index 38a78f5d..8b8cd5c5 100644 --- a/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java +++ b/src/com/android/tv/dvr/ui/ActionPresenterSelector.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.graphics.drawable.Drawable; import android.support.v17.leanback.R; @@ -110,7 +110,11 @@ class ActionPresenterSelector extends PresenterSelector { .getDimensionPixelSize(R.dimen.lb_action_padding_horizontal); vh.view.setPaddingRelative(padding, 0, padding, 0); } - vh.mButton.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null); + if (vh.mLayoutDirection == View.LAYOUT_DIRECTION_RTL) { + vh.mButton.setCompoundDrawablesWithIntrinsicBounds(null, null, icon, null); + } else { + vh.mButton.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null); + } CharSequence line1 = action.getLabel1(); CharSequence line2 = action.getLabel2(); @@ -126,7 +130,7 @@ class ActionPresenterSelector extends PresenterSelector { @Override public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { ActionViewHolder vh = (ActionViewHolder) viewHolder; - vh.mButton.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null); + vh.mButton.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); vh.view.setPadding(0, 0, 0, 0); vh.mAction = null; } diff --git a/src/com/android/tv/dvr/ui/BigArguments.java b/src/com/android/tv/dvr/ui/BigArguments.java deleted file mode 100644 index ec3b5065..00000000 --- a/src/com/android/tv/dvr/ui/BigArguments.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.support.annotation.NonNull; - -import com.android.tv.common.SoftPreconditions; - -import java.util.HashMap; -import java.util.Map; - -/** - * Stores the object to pass through activities/fragments. - */ -public class BigArguments { - private final static String TAG = "BigArguments"; - private static Map<String, Object> sBigArgumentMap = new HashMap<>(); - - /** - * Sets the argument. - */ - public static void setArgument(String name, @NonNull Object value) { - SoftPreconditions.checkState(value != null, TAG, "Set argument, but value is null"); - sBigArgumentMap.put(name, value); - } - - /** - * Returns the argument which is associated to the name. - */ - public static Object getArgument(String name) { - return sBigArgumentMap.get(name); - } - - /** - * Resets the arguments. - */ - public static void reset() { - sBigArgumentMap.clear(); - } -} diff --git a/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java b/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java deleted file mode 100644 index cddece73..00000000 --- a/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.content.Context; -import android.graphics.Bitmap; -import android.graphics.Matrix; -import android.graphics.drawable.BitmapDrawable; -import android.transition.ChangeImageTransform; -import android.transition.TransitionValues; -import android.util.AttributeSet; -import android.view.View; -import android.widget.ImageView; -import android.widget.ImageView.ScaleType; - -import com.android.tv.R; - -import java.util.Map; - -/** - * TODO: Remove this class once b/32405620 is fixed. - * This class is for the workaround of b/32405620 and only for the shared element transition between - * {@link com.android.tv.dvr.ui.browse.RecordingCardView} and - * {@link com.android.tv.dvr.ui.browse.DvrDetailsActivity}. - */ -public class ChangeImageTransformWithScaledParent extends ChangeImageTransform { - private static final String PROPNAME_MATRIX = "android:changeImageTransform:matrix"; - - public ChangeImageTransformWithScaledParent(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public void captureStartValues(TransitionValues transitionValues) { - super.captureStartValues(transitionValues); - applyParentScale(transitionValues); - } - - @Override - public void captureEndValues(TransitionValues transitionValues) { - super.captureEndValues(transitionValues); - applyParentScale(transitionValues); - } - - private void applyParentScale(TransitionValues transitionValues) { - View view = transitionValues.view; - Map<String, Object> values = transitionValues.values; - Matrix matrix = (Matrix) values.get(PROPNAME_MATRIX); - if (matrix != null && view.getId() == R.id.details_overview_image - && view instanceof ImageView) { - ImageView imageView = (ImageView) view; - if (imageView.getScaleType() == ScaleType.CENTER_INSIDE - && imageView.getDrawable() instanceof BitmapDrawable) { - Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap(); - if (bitmap.getWidth() < imageView.getWidth() - && bitmap.getHeight() < imageView.getHeight()) { - float scale = imageView.getContext().getResources().getFraction( - R.fraction.lb_focus_zoom_factor_medium, 1, 1); - matrix.postScale(scale, scale, imageView.getWidth() / 2, - imageView.getHeight() / 2); - } - } - } - } -} diff --git a/src/com/android/tv/dvr/ui/CurrentRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/CurrentRecordingDetailsFragment.java new file mode 100644 index 00000000..5d8e20ff --- /dev/null +++ b/src/com/android/tv/dvr/ui/CurrentRecordingDetailsFragment.java @@ -0,0 +1,59 @@ +/* + * 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.content.res.Resources; +import android.support.v17.leanback.widget.Action; +import android.support.v17.leanback.widget.OnActionClickedListener; +import android.support.v17.leanback.widget.SparseArrayObjectAdapter; + +import com.android.tv.R; +import com.android.tv.TvApplication; +import com.android.tv.dvr.DvrManager; + +/** + * {@link RecordingDetailsFragment} for current recording in DVR. + */ +public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment { + private static final int ACTION_STOP_RECORDING = 1; + + @Override + protected SparseArrayObjectAdapter onCreateActionsAdapter() { + SparseArrayObjectAdapter adapter = + new SparseArrayObjectAdapter(new ActionPresenterSelector()); + Resources res = getResources(); + adapter.set(ACTION_STOP_RECORDING, new Action(ACTION_STOP_RECORDING, + res.getString(R.string.epg_dvr_dialog_message_stop_recording), null, + res.getDrawable(R.drawable.lb_ic_stop))); + return adapter; + } + + @Override + protected OnActionClickedListener onCreateOnActionClickedListener() { + return new OnActionClickedListener() { + @Override + public void onActionClicked(Action action) { + if (action.getId() == ACTION_STOP_RECORDING) { + DvrManager dvrManager = TvApplication.getSingletons(getActivity()) + .getDvrManager(); + dvrManager.stopRecording(getRecording()); + } + getActivity().finish(); + } + }; + } +} diff --git a/src/com/android/tv/dvr/ui/browse/DetailsContent.java b/src/com/android/tv/dvr/ui/DetailsContent.java index b43d1f12..19521fca 100644 --- a/src/com/android/tv/dvr/ui/browse/DetailsContent.java +++ b/src/com/android/tv/dvr/ui/DetailsContent.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.media.tv.TvContract; import android.support.annotation.Nullable; @@ -26,7 +26,7 @@ import com.android.tv.data.Channel; /** * A class for details content. */ -class DetailsContent { +public class DetailsContent { /** Constant for invalid time. */ public static final long INVALID_TIME = -1; diff --git a/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java b/src/com/android/tv/dvr/ui/DetailsContentPresenter.java index a2e3fe16..175f05bc 100644 --- a/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java +++ b/src/com/android/tv/dvr/ui/DetailsContentPresenter.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.app.Activity; import android.animation.Animator; @@ -38,14 +38,13 @@ import com.android.tv.util.Utils; /** * An {@link Presenter} for rendering a detailed description of an DVR item. - * Typically this Presenter will be used in a - * {@link android.support.v17.leanback.widget.DetailsOverviewRowPresenter}. + * Typically this Presenter will be used in a {@link DetailsOverviewRowPresenter}. * Most codes of this class is originated from * {@link android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter}. * The latter class are re-used to provide a customized version of * {@link android.support.v17.leanback.widget.DetailsOverviewRow}. */ -class DetailsContentPresenter extends Presenter { +public class DetailsContentPresenter extends Presenter { /** * The ViewHolder for the {@link DetailsContentPresenter}. */ @@ -114,20 +113,6 @@ class DetailsContentPresenter extends Presenter { public ViewHolder(final View view) { super(view); - view.addOnAttachStateChangeListener( - new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - // In case predraw listener was removed in detach, make sure - // we have the proper layout. - addPreDrawListener(); - } - - @Override - public void onViewDetachedFromWindow(View v) { - removePreDrawListener(); - } - }); mTitle = (TextView) view.findViewById(R.id.dvr_details_description_title); mSubtitle = (TextView) view.findViewById(R.id.dvr_details_description_subtitle); mBody = (TextView) view.findViewById(R.id.dvr_details_description_body); @@ -291,6 +276,22 @@ class DetailsContentPresenter extends Presenter { @Override public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { } + @Override + public void onViewAttachedToWindow(Presenter.ViewHolder holder) { + // In case predraw listener was removed in detach, make sure + // we have the proper layout. + ViewHolder vh = (ViewHolder) holder; + vh.addPreDrawListener(); + super.onViewAttachedToWindow(holder); + } + + @Override + public void onViewDetachedFromWindow(Presenter.ViewHolder holder) { + ViewHolder vh = (ViewHolder) holder; + vh.removePreDrawListener(); + super.onViewDetachedFromWindow(holder); + } + private void setTopMargin(View view, int topMargin) { ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); lp.topMargin = topMargin; diff --git a/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java b/src/com/android/tv/dvr/ui/DetailsViewBackgroundHelper.java index 82fe9ce3..6714ecd3 100644 --- a/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java +++ b/src/com/android/tv/dvr/ui/DetailsViewBackgroundHelper.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.app.Activity; import android.graphics.drawable.BitmapDrawable; @@ -26,7 +26,7 @@ import android.support.v17.leanback.app.BackgroundManager; /** * The Background Helper. */ -class DetailsViewBackgroundHelper { +public class DetailsViewBackgroundHelper { // Background delay serves to avoid kicking off expensive bitmap loading // in case multiple backgrounds are set in quick succession. private static final int SET_BACKGROUND_DELAY_MS = 100; diff --git a/src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java b/src/com/android/tv/dvr/ui/DvrActivity.java index 2b3dcb25..45fb1cf1 100644 --- a/src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java +++ b/src/com/android/tv/dvr/ui/DvrActivity.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.app.Activity; import android.os.Bundle; @@ -25,11 +25,11 @@ import com.android.tv.TvApplication; /** * {@link android.app.Activity} for DVR UI. */ -public class DvrBrowseActivity extends Activity { +public class DvrActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { TvApplication.setCurrentRunningProcess(this, true); super.onCreate(savedInstanceState); setContentView(R.layout.dvr_main); } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java b/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java index 936e9c31..9df228d1 100644 --- a/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java +++ b/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java @@ -28,9 +28,11 @@ import android.widget.Toast; import com.android.tv.R; import com.android.tv.TvApplication; +import com.android.tv.dvr.RecordedProgram; import com.android.tv.data.Program; import com.android.tv.dvr.DvrManager; -import com.android.tv.dvr.data.RecordedProgram; +import com.android.tv.dvr.DvrUiHelper; +import com.android.tv.util.Utils; import java.util.List; diff --git a/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java b/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java index 3c73cb47..78f21784 100644 --- a/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java +++ b/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java @@ -25,12 +25,15 @@ import android.support.annotation.NonNull; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.support.v17.leanback.widget.GuidedAction; import android.text.format.DateUtils; +import android.widget.Toast; import com.android.tv.R; import com.android.tv.TvApplication; import com.android.tv.data.Program; import com.android.tv.dvr.DvrManager; -import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.DvrUiHelper; +import com.android.tv.dvr.ScheduledRecording; +import com.android.tv.util.Utils; import java.util.List; diff --git a/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java b/src/com/android/tv/dvr/ui/DvrBrowseFragment.java index 803d1017..a6dd31d1 100644 --- a/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java +++ b/src/com/android/tv/dvr/ui/DvrBrowseFragment.java @@ -14,9 +14,10 @@ * limitations under the License */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.content.Context; +import android.media.tv.TvInputManager.TvInputCallback; import android.os.Bundle; import android.os.Handler; import android.support.v17.leanback.app.BrowseFragment; @@ -24,11 +25,11 @@ import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.ClassPresenterSelector; import android.support.v17.leanback.widget.HeaderItem; import android.support.v17.leanback.widget.ListRow; +import android.support.v17.leanback.widget.ListRowPresenter; import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.TitleViewAdapter; +import android.text.TextUtils; import android.util.Log; -import android.view.View; -import android.view.ViewTreeObserver.OnGlobalFocusChangeListener; import com.android.tv.ApplicationSingletons; import com.android.tv.R; @@ -41,10 +42,9 @@ import com.android.tv.dvr.DvrDataManager.RecordedProgramListener; import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; import com.android.tv.dvr.DvrDataManager.SeriesRecordingListener; import com.android.tv.dvr.DvrScheduleManager; -import com.android.tv.dvr.data.RecordedProgram; -import com.android.tv.dvr.data.ScheduledRecording; -import com.android.tv.dvr.data.SeriesRecording; -import com.android.tv.dvr.ui.SortedArrayAdapter; +import com.android.tv.dvr.RecordedProgram; +import com.android.tv.dvr.ScheduledRecording; +import com.android.tv.dvr.SeriesRecording; import java.util.ArrayList; import java.util.Arrays; @@ -79,20 +79,6 @@ public class DvrBrowseFragment extends BrowseFragment implements private ClassPresenterSelector mPresenterSelector; private final HashMap<String, RecordedProgram> mSeriesId2LatestProgram = new HashMap<>(); private final Handler mHandler = new Handler(); - private final OnGlobalFocusChangeListener mOnGlobalFocusChangeListener = - new OnGlobalFocusChangeListener() { - @Override - public void onGlobalFocusChanged(View oldFocus, View newFocus) { - if (oldFocus instanceof RecordingCardView) { - ((RecordingCardView) oldFocus).expandTitle(false, true); - } - if (newFocus instanceof RecordingCardView) { - // If the header transition is ongoing, expand cards immediately without - // animation to make a smooth transition. - ((RecordingCardView) newFocus).expandTitle(true, !isInHeadersTransition()); - } - } - }; private final Comparator<Object> RECORDED_PROGRAM_COMPARATOR = new Comparator<Object>() { @Override @@ -142,7 +128,7 @@ public class DvrBrowseFragment extends BrowseFragment implements public void onConflictStateChange(boolean conflict, ScheduledRecording... schedules) { if (mScheduleAdapter != null) { for (ScheduledRecording schedule : schedules) { - onScheduledRecordingConflictStatusChanged(schedule); + onScheduledRecordingStatusChanged(schedule); } } } @@ -168,12 +154,16 @@ public class DvrBrowseFragment extends BrowseFragment implements new ScheduledRecordingPresenter(context)) .addClassPresenter(RecordedProgram.class, new RecordedProgramPresenter(context)) .addClassPresenter(SeriesRecording.class, new SeriesRecordingPresenter(context)) - .addClassPresenter(FullScheduleCardHolder.class, - new FullSchedulesCardPresenter(context)); + .addClassPresenter(FullScheduleCardHolder.class, new FullSchedulesCardPresenter()); mGenreLabels = new ArrayList<>(Arrays.asList(GenreItems.getLabels(context))); mGenreLabels.add(getString(R.string.dvr_main_others)); - prepareUiElements(); - if (!startBrowseIfDvrInitialized()) { + setupUiElements(); + setupAdapters(); + mDvrScheudleManager.addOnConflictStateChangeListener(mOnConflictStateChangeListener); + prepareEntranceTransition(); + if (mDvrDataManager.isInitialized()) { + startEntranceTransition(); + } else { if (!mDvrDataManager.isDvrScheduleLoadFinished()) { mDvrDataManager.addDvrScheduleLoadFinishedListener(this); } @@ -184,19 +174,6 @@ public class DvrBrowseFragment extends BrowseFragment implements } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - view.getViewTreeObserver().addOnGlobalFocusChangeListener(mOnGlobalFocusChangeListener); - } - - @Override - public void onDestroyView() { - getView().getViewTreeObserver() - .removeOnGlobalFocusChangeListener(mOnGlobalFocusChangeListener); - super.onDestroyView(); - } - - @Override public void onDestroy() { if (DEBUG) Log.d(TAG, "onDestroy"); mHandler.removeCallbacks(mUpdateRowsRunnable); @@ -218,13 +195,25 @@ public class DvrBrowseFragment extends BrowseFragment implements @Override public void onDvrScheduleLoadFinished() { - startBrowseIfDvrInitialized(); + List<ScheduledRecording> scheduledRecordings = mDvrDataManager.getAllScheduledRecordings(); + onScheduledRecordingAdded(ScheduledRecording.toArray(scheduledRecordings)); + List<SeriesRecording> seriesRecordings = mDvrDataManager.getSeriesRecordings(); + onSeriesRecordingAdded(SeriesRecording.toArray(seriesRecordings)); + if (mDvrDataManager.isInitialized()) { + startEntranceTransition(); + } mDvrDataManager.removeDvrScheduleLoadFinishedListener(this); } @Override public void onRecordedProgramLoadFinished() { - startBrowseIfDvrInitialized(); + for (RecordedProgram recordedProgram : mDvrDataManager.getRecordedPrograms()) { + handleRecordedProgramAdded(recordedProgram, true); + } + updateRows(); + if (mDvrDataManager.isInitialized()) { + startEntranceTransition(); + } mDvrDataManager.removeRecordedProgramLoadFinishedListener(this); } @@ -281,18 +270,6 @@ public class DvrBrowseFragment extends BrowseFragment implements } } - private void onScheduledRecordingConflictStatusChanged(ScheduledRecording... schedules) { - for (ScheduledRecording schedule : schedules) { - if (needToShowScheduledRecording(schedule)) { - if (mScheduleAdapter.contains(schedule)) { - mScheduleAdapter.change(schedule); - } - } else { - mScheduleAdapter.removeWithId(schedule); - } - } - } - @Override public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { handleSeriesRecordingsAdded(Arrays.asList(seriesRecordings)); @@ -318,53 +295,44 @@ public class DvrBrowseFragment extends BrowseFragment implements super.showTitle(flags); } - private void prepareUiElements() { + private void setupUiElements() { setBadgeDrawable(getActivity().getDrawable(R.drawable.ic_dvr_badge)); setHeadersState(HEADERS_ENABLED); setHeadersTransitionOnBackEnabled(false); setBrandColor(getResources().getColor(R.color.program_guide_side_panel_background, null)); - mRowsAdapter = new ArrayObjectAdapter(new DvrListRowPresenter(getContext())); - setAdapter(mRowsAdapter); - prepareEntranceTransition(); } - private boolean startBrowseIfDvrInitialized() { - if (mDvrDataManager.isInitialized()) { - // Setup rows - mRecentAdapter = new RecordedProgramAdapter(MAX_RECENT_ITEM_COUNT); - mScheduleAdapter = new ScheduleAdapter(MAX_SCHEDULED_ITEM_COUNT); - mSeriesAdapter = new SeriesAdapter(); - for (int i = 0; i < mGenreAdapters.length; i++) { - mGenreAdapters[i] = new RecordedProgramAdapter(); - } - // Schedule Recordings. - List<ScheduledRecording> schedules = mDvrDataManager.getAllScheduledRecordings(); - onScheduledRecordingAdded(ScheduledRecording.toArray(schedules)); - mScheduleAdapter.addExtraItem(FullScheduleCardHolder.FULL_SCHEDULE_CARD_HOLDER); - // Recorded Programs. - for (RecordedProgram recordedProgram : mDvrDataManager.getRecordedPrograms()) { - handleRecordedProgramAdded(recordedProgram, false); - } - // Series Recordings. Series recordings should be added after recorded programs, because - // we build series recordings' latest program information while adding recorded programs. - List<SeriesRecording> recordings = mDvrDataManager.getSeriesRecordings(); - handleSeriesRecordingsAdded(recordings); - mRecentRow = new ListRow(new HeaderItem( - getString(R.string.dvr_main_recent)), mRecentAdapter); - mRowsAdapter.add(new ListRow(new HeaderItem( - getString(R.string.dvr_main_scheduled)), mScheduleAdapter)); - mSeriesRow = new ListRow(new HeaderItem( - getString(R.string.dvr_main_series)), mSeriesAdapter); - updateRows(); - // Initialize listeners - mDvrDataManager.addRecordedProgramListener(this); - mDvrDataManager.addScheduledRecordingListener(this); - mDvrDataManager.addSeriesRecordingListener(this); - mDvrScheudleManager.addOnConflictStateChangeListener(mOnConflictStateChangeListener); - startEntranceTransition(); - return true; - } - return false; + private void setupAdapters() { + mRecentAdapter = new RecordedProgramAdapter(MAX_RECENT_ITEM_COUNT); + mScheduleAdapter = new ScheduleAdapter(MAX_SCHEDULED_ITEM_COUNT); + mSeriesAdapter = new SeriesAdapter(); + for (int i = 0; i < mGenreAdapters.length; i++) { + mGenreAdapters[i] = new RecordedProgramAdapter(); + } + // Schedule Recordings. + List<ScheduledRecording> schedules = mDvrDataManager.getAllScheduledRecordings(); + onScheduledRecordingAdded(ScheduledRecording.toArray(schedules)); + mScheduleAdapter.addExtraItem(FullScheduleCardHolder.FULL_SCHEDULE_CARD_HOLDER); + // Recorded Programs. + for (RecordedProgram recordedProgram : mDvrDataManager.getRecordedPrograms()) { + handleRecordedProgramAdded(recordedProgram, false); + } + // Series Recordings. Series recordings should be added after recorded programs, because + // we build series recordings' latest program information while adding recorded programs. + List<SeriesRecording> recordings = mDvrDataManager.getSeriesRecordings(); + handleSeriesRecordingsAdded(recordings); + mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); + mRecentRow = new ListRow(new HeaderItem( + getString(R.string.dvr_main_recent)), mRecentAdapter); + mRowsAdapter.add(new ListRow(new HeaderItem( + getString(R.string.dvr_main_scheduled)), mScheduleAdapter)); + mSeriesRow = new ListRow(new HeaderItem( + getString(R.string.dvr_main_series)), mSeriesAdapter); + updateRows(); + mDvrDataManager.addRecordedProgramListener(this); + mDvrDataManager.addScheduledRecordingListener(this); + mDvrDataManager.addSeriesRecordingListener(this); + setAdapter(mRowsAdapter); } private void handleRecordedProgramAdded(RecordedProgram recordedProgram, @@ -621,11 +589,10 @@ public class DvrBrowseFragment extends BrowseFragment implements @Override public long getId(Object item) { - // We takes the inverse number for the ID of recorded programs to make the ID stable. if (item instanceof SeriesRecording) { return ((SeriesRecording) item).getId(); } else if (item instanceof RecordedProgram) { - return -((RecordedProgram) item).getId() - 1; + return ((RecordedProgram) item).getId(); } else { return -1; } diff --git a/src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java b/src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java index 880dc8ac..837d8ab2 100644 --- a/src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java +++ b/src/com/android/tv/dvr/ui/DvrChannelRecordDurationOptionFragment.java @@ -27,7 +27,7 @@ import com.android.tv.TvApplication; import com.android.tv.common.SoftPreconditions; import com.android.tv.data.Channel; import com.android.tv.dvr.DvrManager; -import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.ScheduledRecording; import com.android.tv.dvr.ui.DvrConflictFragment.DvrChannelRecordConflictFragment; import java.util.ArrayList; diff --git a/src/com/android/tv/dvr/ui/DvrConflictFragment.java b/src/com/android/tv/dvr/ui/DvrConflictFragment.java index 5985f56f..e7be4d0a 100644 --- a/src/com/android/tv/dvr/ui/DvrConflictFragment.java +++ b/src/com/android/tv/dvr/ui/DvrConflictFragment.java @@ -34,9 +34,10 @@ import com.android.tv.TvApplication; import com.android.tv.common.SoftPreconditions; import com.android.tv.data.Channel; import com.android.tv.data.Program; -import com.android.tv.dvr.recorder.ConflictChecker; -import com.android.tv.dvr.recorder.ConflictChecker.OnUpcomingConflictChangeListener; -import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.ConflictChecker; +import com.android.tv.dvr.ConflictChecker.OnUpcomingConflictChangeListener; +import com.android.tv.dvr.DvrUiHelper; +import com.android.tv.dvr.ScheduledRecording; import com.android.tv.util.Utils; import java.util.ArrayList; diff --git a/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java b/src/com/android/tv/dvr/ui/DvrDetailsActivity.java index 30c81e83..806c775c 100644 --- a/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java +++ b/src/com/android/tv/dvr/ui/DvrDetailsActivity.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.app.Activity; import android.os.Bundle; diff --git a/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java b/src/com/android/tv/dvr/ui/DvrDetailsFragment.java index 4d3698ef..21f9c4b4 100644 --- a/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java +++ b/src/com/android/tv/dvr/ui/DvrDetailsFragment.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.content.Context; import android.content.Intent; @@ -48,8 +48,8 @@ import com.android.tv.data.BaseProgram; import com.android.tv.data.Channel; import com.android.tv.data.ChannelDataManager; import com.android.tv.dialog.PinDialogFragment; -import com.android.tv.dvr.data.RecordedProgram; -import com.android.tv.dvr.ui.playback.DvrPlaybackActivity; +import com.android.tv.dvr.DvrPlaybackActivity; +import com.android.tv.dvr.RecordedProgram; import com.android.tv.parental.ParentalControlSettings; import com.android.tv.util.ImageLoader; import com.android.tv.util.ToastUtils; diff --git a/src/com/android/tv/dvr/ui/DvrForgetStorageErrorFragment.java b/src/com/android/tv/dvr/ui/DvrForgetStorageErrorFragment.java new file mode 100644 index 00000000..73ddcdd0 --- /dev/null +++ b/src/com/android/tv/dvr/ui/DvrForgetStorageErrorFragment.java @@ -0,0 +1,87 @@ +/* + * 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.Activity; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v17.leanback.widget.GuidanceStylist.Guidance; +import android.support.v17.leanback.widget.GuidedAction; +import android.text.TextUtils; + +import com.android.tv.R; +import com.android.tv.TvApplication; +import com.android.tv.common.SoftPreconditions; +import com.android.tv.dvr.DvrDataManager; +import com.android.tv.dvr.DvrManager; + +import java.util.List; + +public class DvrForgetStorageErrorFragment extends DvrGuidedStepFragment { + private static final int ACTION_CANCEL = 1; + private static final int ACTION_FORGET_STORAGE = 2; + private String mInputId; + + @Override + public void onCreate(Bundle savedInstanceState) { + Bundle args = getArguments(); + if (args != null) { + mInputId = args.getString(DvrHalfSizedDialogFragment.KEY_INPUT_ID); + } + SoftPreconditions.checkArgument(!TextUtils.isEmpty(mInputId)); + super.onCreate(savedInstanceState); + } + + @NonNull + @Override + public Guidance onCreateGuidance(Bundle savedInstanceState) { + String title = getResources().getString(R.string.dvr_error_forget_storage_title); + String description = getResources().getString( + R.string.dvr_error_forget_storage_description); + return new Guidance(title, description, null, null); + } + + @Override + public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) { + Activity activity = getActivity(); + actions.add(new GuidedAction.Builder(activity) + .id(ACTION_CANCEL) + .title(getResources().getString(R.string.dvr_action_error_cancel)) + .build()); + actions.add(new GuidedAction.Builder(activity) + .id(ACTION_FORGET_STORAGE) + .title(getResources().getString(R.string.dvr_action_error_forget_storage)) + .build()); + } + + @Override + public void onGuidedActionClicked(GuidedAction action) { + if (action.getId() != ACTION_FORGET_STORAGE) { + dismissDialog(); + return; + } + DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager(); + dvrManager.forgetStorage(mInputId); + Activity activity = getActivity(); + if (activity instanceof DvrDetailsActivity) { + // Since we removed everything, just finish the activity. + activity.finish(); + } else { + dismissDialog(); + } + } +} diff --git a/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java b/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java index 433588da..d26e6836 100644 --- a/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java +++ b/src/com/android/tv/dvr/ui/DvrGuidedStepFragment.java @@ -16,12 +16,10 @@ package com.android.tv.dvr.ui; -import android.app.Activity; import android.app.DialogFragment; import android.content.Context; import android.os.Bundle; import android.support.v17.leanback.app.GuidedStepFragment; -import android.support.v17.leanback.widget.GuidanceStylist; import android.support.v17.leanback.widget.GuidedAction; import android.support.v17.leanback.widget.VerticalGridView; import android.view.LayoutInflater; @@ -31,26 +29,11 @@ import android.view.ViewGroup; import com.android.tv.MainActivity; import com.android.tv.R; import com.android.tv.TvApplication; -import com.android.tv.dialog.HalfSizedDialogFragment.OnActionClickListener; import com.android.tv.dialog.SafeDismissDialogFragment; import com.android.tv.dvr.DvrManager; - -import java.util.List; +import com.android.tv.dvr.ui.HalfSizedDialogFragment.OnActionClickListener; public class DvrGuidedStepFragment extends GuidedStepFragment { - /** - * Action ID for "recording/scheduling the program anyway". - */ - public static final int ACTION_RECORD_ANYWAY = 1; - /** - * Action ID for "deleting existed recordings". - */ - public static final int ACTION_DELETE_RECORDINGS = 2; - /** - * Action ID for "cancelling current recording request". - */ - public static final int ACTION_CANCEL_RECORDING = 3; - private DvrManager mDvrManager; private OnActionClickListener mOnActionClickListener; @@ -103,35 +86,4 @@ public class DvrGuidedStepFragment extends GuidedStepFragment { protected void setOnActionClickListener(OnActionClickListener listener) { mOnActionClickListener = listener; } - - /** - * The inner guided step fragment for - * {@link com.android.tv.dvr.ui.DvrHalfSizedDialogFragment - * .DvrNoFreeSpaceErrorDialogFragment}. - */ - public static class DvrNoFreeSpaceErrorFragment - extends DvrGuidedStepFragment { - @Override - public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) { - return new GuidanceStylist.Guidance(getString(R.string.dvr_error_no_free_space_title), - getString(R.string.dvr_error_no_free_space_description), null, null); - } - - @Override - public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { - Activity activity = getActivity(); - actions.add(new GuidedAction.Builder(activity) - .id(ACTION_RECORD_ANYWAY) - .title(R.string.dvr_action_record_anyway) - .build()); - actions.add(new GuidedAction.Builder(activity) - .id(ACTION_DELETE_RECORDINGS) - .title(R.string.dvr_action_delete_recordings) - .build()); - actions.add(new GuidedAction.Builder(activity) - .id(ACTION_CANCEL_RECORDING) - .title(R.string.dvr_action_record_cancel) - .build()); - } - } }
\ No newline at end of file diff --git a/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java b/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java index 9054dd03..2b132db8 100644 --- a/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java +++ b/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java @@ -29,7 +29,6 @@ import android.view.ViewGroup; import com.android.tv.MainActivity; import com.android.tv.R; import com.android.tv.dvr.DvrStorageStatusManager; -import com.android.tv.dialog.HalfSizedDialogFragment; import com.android.tv.dvr.ui.DvrConflictFragment.DvrChannelWatchConflictFragment; import com.android.tv.dvr.ui.DvrConflictFragment.DvrProgramConflictFragment; import com.android.tv.guide.ProgramGuide; @@ -167,17 +166,6 @@ public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment { } /** - * A dialog fragment to show error message when there is no enough free space to record. - */ - public static class DvrNoFreeSpaceErrorDialogFragment - extends DvrGuidedStepDialogFragment { - @Override - protected DvrGuidedStepFragment onCreateGuidedStepFragment() { - return new DvrGuidedStepFragment.DvrNoFreeSpaceErrorFragment(); - } - } - - /** * A dialog fragment to show error message when the current storage is too small to * support DVR */ diff --git a/src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java b/src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java index 3c5df1a6..3b1dbfa0 100644 --- a/src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java +++ b/src/com/android/tv/dvr/ui/DvrInsufficientSpaceErrorFragment.java @@ -17,7 +17,6 @@ package com.android.tv.dvr.ui; import android.app.Activity; -import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; @@ -25,67 +24,19 @@ import android.support.v17.leanback.widget.GuidedAction; import com.android.tv.R; import com.android.tv.TvApplication; -import com.android.tv.common.SoftPreconditions; -import com.android.tv.dvr.ui.browse.DvrBrowseActivity; +import com.android.tv.dvr.DvrDataManager; -import java.util.ArrayList; import java.util.List; public class DvrInsufficientSpaceErrorFragment extends DvrGuidedStepFragment { - /** - * Key for the failed scheduled recordings information. - */ - public static final String FAILED_SCHEDULED_RECORDING_INFOS = - "failed_scheduled_recording_infos"; - - private static final String TAG = "DvrInsufficientSpaceErrorFragment"; - - private static final int ACTION_VIEW_RECENT_RECORDINGS = 1; - - private ArrayList<String> mFailedScheduledRecordingInfos; - - @Override - public void onAttach(Context context) { - super.onAttach(context); - Bundle args = getArguments(); - if (args != null) { - mFailedScheduledRecordingInfos = - args.getStringArrayList(FAILED_SCHEDULED_RECORDING_INFOS); - } - SoftPreconditions.checkState( - mFailedScheduledRecordingInfos != null && !mFailedScheduledRecordingInfos.isEmpty(), - TAG, "failed scheduled recording is null"); - } + private static final int ACTION_DONE = 1; + private static final int ACTION_OPEN_DVR = 2; @Override public Guidance onCreateGuidance(Bundle savedInstanceState) { - String title; - String description; - int failedScheduledRecordingSize = mFailedScheduledRecordingInfos.size(); - if (failedScheduledRecordingSize == 1) { - title = getString( - R.string.dvr_error_insufficient_space_title_one_recording, - mFailedScheduledRecordingInfos.get(0)); - description = getString( - R.string.dvr_error_insufficient_space_description_one_recording, - mFailedScheduledRecordingInfos.get(0)); - } else if (failedScheduledRecordingSize == 2) { - title = getString( - R.string.dvr_error_insufficient_space_title_two_recordings, - mFailedScheduledRecordingInfos.get(0), mFailedScheduledRecordingInfos.get(1)); - description = getString( - R.string.dvr_error_insufficient_space_description_two_recordings, - mFailedScheduledRecordingInfos.get(0), mFailedScheduledRecordingInfos.get(1)); - } else { - title = getString( - R.string.dvr_error_insufficient_space_title_three_or_more_recordings, - mFailedScheduledRecordingInfos.get(0), mFailedScheduledRecordingInfos.get(1), - mFailedScheduledRecordingInfos.get(2)); - description = getString( - R.string.dvr_error_insufficient_space_description_three_or_more_recordings, - mFailedScheduledRecordingInfos.get(0), mFailedScheduledRecordingInfos.get(1), - mFailedScheduledRecordingInfos.get(2)); - } + String title = getResources().getString(R.string.dvr_error_insufficient_space_title); + String description = getResources() + .getString(R.string.dvr_error_insufficient_space_description); return new Guidance(title, description, null, null); } @@ -93,21 +44,26 @@ public class DvrInsufficientSpaceErrorFragment extends DvrGuidedStepFragment { public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { Activity activity = getActivity(); actions.add(new GuidedAction.Builder(activity) - .clickAction(GuidedAction.ACTION_ID_OK) + .id(ACTION_DONE) + .title(getResources().getString(R.string.dvr_action_error_done)) .build()); - if (TvApplication.getSingletons(getContext()).getDvrManager().hasValidItems()) { - actions.add(new GuidedAction.Builder(activity) - .id(ACTION_VIEW_RECENT_RECORDINGS) - .title(getResources().getString( - R.string.dvr_error_insufficient_space_action_view_recent_recordings)) - .build()); + DvrDataManager dvrDataManager = TvApplication.getSingletons(getContext()) + .getDvrDataManager(); + if (!(dvrDataManager.getRecordedPrograms().isEmpty() + && dvrDataManager.getStartedRecordings().isEmpty() + && dvrDataManager.getNonStartedScheduledRecordings().isEmpty() + && dvrDataManager.getSeriesRecordings().isEmpty())) { + actions.add(new GuidedAction.Builder(activity) + .id(ACTION_OPEN_DVR) + .title(getResources().getString(R.string.dvr_action_error_open_dvr)) + .build()); } } @Override public void onGuidedActionClicked(GuidedAction action) { - if (action.getId() == ACTION_VIEW_RECENT_RECORDINGS) { - Intent intent = new Intent(getActivity(), DvrBrowseActivity.class); + if (action.getId() == ACTION_OPEN_DVR) { + Intent intent = new Intent(getActivity(), DvrActivity.class); getActivity().startActivity(intent); } dismissDialog(); diff --git a/src/com/android/tv/dvr/ui/browse/DvrItemPresenter.java b/src/com/android/tv/dvr/ui/DvrItemPresenter.java index 317b6af3..339e5d2f 100644 --- a/src/com/android/tv/dvr/ui/browse/DvrItemPresenter.java +++ b/src/com/android/tv/dvr/ui/DvrItemPresenter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.app.Activity; import android.support.annotation.CallSuper; @@ -22,17 +22,16 @@ import android.support.v17.leanback.widget.Presenter; import android.view.View; import android.view.View.OnClickListener; -import com.android.tv.dvr.ui.DvrUiHelper; +import com.android.tv.dvr.DvrUiHelper; import java.util.HashSet; +import java.util.Iterator; import java.util.Set; /** * An abstract class to present DVR items in {@link RecordingCardView}, which is mainly used in - * {@link DvrBrowseFragment}. DVR items might include: - * {@link com.android.tv.dvr.data.ScheduledRecording}, - * {@link com.android.tv.dvr.data.RecordedProgram}, and - * {@link com.android.tv.dvr.data.SeriesRecording}. + * {@link DvrBrowseFragment}. DVR items might include: {@link ScheduledRecording}, + * {@link RecordedProgram}, and {@link SeriesRecording}. */ public abstract class DvrItemPresenter extends Presenter { private final Set<ViewHolder> mBoundViewHolders = new HashSet<>(); @@ -50,8 +49,6 @@ public abstract class DvrItemPresenter extends Presenter { @CallSuper public void onUnbindViewHolder(ViewHolder viewHolder) { mBoundViewHolders.remove(viewHolder); - viewHolder.view.setTag(null); - viewHolder.view.setOnClickListener(null); } /** diff --git a/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java b/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java index 8dc9eb4e..2e2c2849 100644 --- a/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java +++ b/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java @@ -17,27 +17,29 @@ package com.android.tv.dvr.ui; import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Intent; import android.os.Bundle; -import android.provider.Settings; +import android.support.v17.leanback.app.GuidedStepFragment; import android.support.v17.leanback.widget.GuidanceStylist.Guidance; import android.support.v17.leanback.widget.GuidedAction; -import android.util.Log; +import android.text.TextUtils; import com.android.tv.R; -import com.android.tv.dvr.ui.browse.DvrDetailsActivity; +import com.android.tv.common.SoftPreconditions; import java.util.List; public class DvrMissingStorageErrorFragment extends DvrGuidedStepFragment { - private static String TAG = "DvrMissingStorageErrorFragment"; - - private static final int ACTION_OK = 1; - private static final int ACTION_OPEN_STORAGE_SETTINGS = 2; + private static final int ACTION_CANCEL = 1; + private static final int ACTION_FORGET_STORAGE = 2; + private String mInputId; @Override public void onCreate(Bundle savedInstanceState) { + Bundle args = getArguments(); + if (args != null) { + mInputId = args.getString(DvrHalfSizedDialogFragment.KEY_INPUT_ID); + } + SoftPreconditions.checkArgument(!TextUtils.isEmpty(mInputId)); super.onCreate(savedInstanceState); } @@ -53,31 +55,25 @@ public class DvrMissingStorageErrorFragment extends DvrGuidedStepFragment { public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) { Activity activity = getActivity(); actions.add(new GuidedAction.Builder(activity) - .id(ACTION_OK) - .title(android.R.string.ok) + .id(ACTION_CANCEL) + .title(getResources().getString(R.string.dvr_action_error_cancel)) .build()); actions.add(new GuidedAction.Builder(activity) - .id(ACTION_OPEN_STORAGE_SETTINGS) - .title(getResources().getString(R.string.dvr_action_error_storage_settings)) + .id(ACTION_FORGET_STORAGE) + .title(getResources().getString(R.string.dvr_action_error_forget_storage)) .build()); } @Override public void onGuidedActionClicked(GuidedAction action) { - Activity activity = getActivity(); - if (activity instanceof DvrDetailsActivity) { - activity.finish(); - } else { - dismissDialog(); - } - if (action.getId() != ACTION_OPEN_STORAGE_SETTINGS) { + if (action.getId() == ACTION_FORGET_STORAGE) { + DvrForgetStorageErrorFragment fragment = new DvrForgetStorageErrorFragment(); + Bundle args = new Bundle(); + args.putString(DvrHalfSizedDialogFragment.KEY_INPUT_ID, mInputId); + fragment.setArguments(args); + GuidedStepFragment.add(getFragmentManager(), fragment, R.id.halfsized_dialog_host); return; } - final Intent intent = new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS); - try { - getContext().startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.e(TAG, "Can't start internal storage settings activity", e); - } + dismissDialog(); } -} +}
\ No newline at end of file diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackCardPresenter.java b/src/com/android/tv/dvr/ui/DvrPlaybackCardPresenter.java index 4bd121b1..8c4c856c 100644 --- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackCardPresenter.java +++ b/src/com/android/tv/dvr/ui/DvrPlaybackCardPresenter.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package com.android.tv.dvr.ui.playback; +package com.android.tv.dvr.ui; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -25,25 +26,22 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import com.android.tv.R; -import com.android.tv.dvr.data.RecordedProgram; -import com.android.tv.dvr.ui.browse.RecordedProgramPresenter; -import com.android.tv.dvr.ui.browse.RecordingCardView; +import com.android.tv.dvr.RecordedProgram; +import com.android.tv.dvr.DvrPlaybackActivity; import com.android.tv.util.Utils; /** * This class is used to generate Views and bind Objects for related recordings in DVR playback. */ -class DvrPlaybackCardPresenter extends RecordedProgramPresenter { +public class DvrPlaybackCardPresenter extends RecordedProgramPresenter { private static final String TAG = "DvrPlaybackCardPresenter"; private static final boolean DEBUG = false; private final int mRelatedRecordingCardWidth; private final int mRelatedRecordingCardHeight; - private final DvrPlaybackOverlayFragment mFragment; - DvrPlaybackCardPresenter(Context context, DvrPlaybackOverlayFragment fragment) { + DvrPlaybackCardPresenter(Context context) { super(context); - mFragment = fragment; mRelatedRecordingCardWidth = context.getResources().getDimensionPixelSize(R.dimen.dvr_related_recordings_width); mRelatedRecordingCardHeight = @@ -52,8 +50,9 @@ class DvrPlaybackCardPresenter extends RecordedProgramPresenter { @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { + Resources res = parent.getResources(); RecordingCardView view = new RecordingCardView( - getContext(), mRelatedRecordingCardWidth, mRelatedRecordingCardHeight, true); + getContext(), mRelatedRecordingCardWidth, mRelatedRecordingCardHeight); return new ViewHolder(view); } @@ -62,10 +61,6 @@ class DvrPlaybackCardPresenter extends RecordedProgramPresenter { return new OnClickListener() { @Override public void onClick(View v) { - // Disable fading of overlay fragment to prevent the layout blinking while updating - // new playback states and info. The fading enabled status will be reset during - // playback state changing, in DvrPlaybackControlHelper.onStateChanged(). - mFragment.setFadingEnabled(false); long programId = ((RecordedProgram) v.getTag()).getId(); if (DEBUG) Log.d(TAG, "Play Related Recording:" + programId); Intent intent = new Intent(getContext(), DvrPlaybackActivity.class); @@ -74,4 +69,14 @@ class DvrPlaybackCardPresenter extends RecordedProgramPresenter { } }; } + + @Override + protected String getDescription(RecordedProgram program) { + String description = program.getDescription(); + if (TextUtils.isEmpty(description)) { + description = + getContext().getResources().getString(R.string.dvr_msg_no_program_description); + } + return description; + } }
\ No newline at end of file diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java b/src/com/android/tv/dvr/ui/DvrPlaybackControlHelper.java index 4658a328..0bc4ecb1 100644 --- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java +++ b/src/com/android/tv/dvr/ui/DvrPlaybackControlHelper.java @@ -14,26 +14,19 @@ * limitations under the License */ -package com.android.tv.dvr.ui.playback; +package com.android.tv.dvr.ui; import android.app.Activity; -import android.content.Context; import android.graphics.drawable.Drawable; import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaController.TransportControls; import android.media.session.PlaybackState; -import android.media.tv.TvTrackInfo; -import android.os.Bundle; import android.support.v17.leanback.app.PlaybackControlGlue; import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter; import android.support.v17.leanback.widget.Action; -import android.support.v17.leanback.widget.ArrayObjectAdapter; -import android.support.v17.leanback.widget.ControlButtonPresenterSelector; import android.support.v17.leanback.widget.OnActionClickedListener; import android.support.v17.leanback.widget.PlaybackControlsRow; -import android.support.v17.leanback.widget.PlaybackControlsRow.ClosedCaptioningAction; -import android.support.v17.leanback.widget.PlaybackControlsRow.MultiAction; import android.support.v17.leanback.widget.PlaybackControlsRowPresenter; import android.support.v17.leanback.widget.RowPresenter; import android.text.TextUtils; @@ -44,18 +37,19 @@ import android.view.View; import com.android.tv.R; import com.android.tv.util.TimeShiftUtils; -import java.util.ArrayList; - /** * A helper class to assist {@link DvrPlaybackOverlayFragment} to manage its controls row and * send command to the media controller. It also helps to update playback states displayed in the * fragment according to information the media session provides. */ -class DvrPlaybackControlHelper extends PlaybackControlGlue { +public class DvrPlaybackControlHelper extends PlaybackControlGlue { private static final String TAG = "DvrPlaybackControlHelper"; private static final boolean DEBUG = false; - private static final int AUDIO_ACTION_ID = 1001; + /** + * Indicates the ID of the media under playback is unknown. + */ + public static int UNKNOWN_MEDIA_ID = -1; private int mPlaybackState = PlaybackState.STATE_NONE; private int mPlaybackSpeedLevel; @@ -66,9 +60,6 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { private final MediaController.Callback mMediaControllerCallback = new MediaControllerCallback(); private final TransportControls mTransportControls; private final int mExtraPaddingTopForNoDescription; - private final ArrayObjectAdapter mSecondaryActionsAdapter; - private final MultiAction mClosedCaptioningAction; - private final MultiAction mMultiAudioAction; public DvrPlaybackControlHelper(Activity activity, DvrPlaybackOverlayFragment overlayFragment) { super(activity, overlayFragment, new int[TimeShiftUtils.MAX_SPEED_LEVEL + 1]); @@ -77,15 +68,11 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { mTransportControls = mMediaController.getTransportControls(); mExtraPaddingTopForNoDescription = activity.getResources() .getDimensionPixelOffset(R.dimen.dvr_playback_controls_extra_padding_top); - mSecondaryActionsAdapter = new ArrayObjectAdapter(new ControlButtonPresenterSelector()); - mClosedCaptioningAction = new ClosedCaptioningAction(activity); - mMultiAudioAction = new MultiAudioAction(activity); } @Override public PlaybackControlsRowPresenter createControlsRowAndPresenter() { PlaybackControlsRow controlsRow = new PlaybackControlsRow(this); - controlsRow.setSecondaryActionsAdapter(mSecondaryActionsAdapter); setControlsRow(controlsRow); AbstractDetailsDescriptionPresenter detailsPresenter = new AbstractDetailsDescriptionPresenter() { @@ -129,21 +116,7 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { @Override public void onActionClicked(Action action) { if (mReadyToControl) { - int trackType; - if (action.getId() == mClosedCaptioningAction.getId()) { - trackType = TvTrackInfo.TYPE_SUBTITLE; - } else if (action.getId() == AUDIO_ACTION_ID) { - trackType = TvTrackInfo.TYPE_AUDIO; - } else { - DvrPlaybackControlHelper.super.onActionClicked(action); - return; - } - ArrayList<TvTrackInfo> trackInfos = - ((DvrPlaybackOverlayFragment) getFragment()).getTracks(trackType); - if (!trackInfos.isEmpty()) { - showSideFragment(trackInfos, ((DvrPlaybackOverlayFragment) - getFragment()).getSelectedTrackId(trackType)); - } + DvrPlaybackControlHelper.super.onActionClicked(action); } } }); @@ -185,10 +158,10 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { /** * Returns the ID of the media under playback. */ - public String getMediaId() { + public long getMediaId() { MediaMetadata mediaMetadata = mMediaController.getMetadata(); - return mediaMetadata == null ? null - : mediaMetadata.getString(MediaMetadata.METADATA_KEY_MEDIA_ID); + return mediaMetadata == null ? UNKNOWN_MEDIA_ID + : mediaMetadata.getLong(MediaMetadata.METADATA_KEY_MEDIA_ID); } @Override @@ -244,37 +217,6 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { mMediaController.unregisterCallback(mMediaControllerCallback); } - /** - * Update the secondary controls row. - * @param hasClosedCaption {@code true} to show the closed caption selection button, - * {@code false} to hide it. - * @param hasMultiAudio {@code true} to show the audio track selection button, - * {@code false} to hide it. - */ - public void updateSecondaryRow(boolean hasClosedCaption, boolean hasMultiAudio) { - if (hasClosedCaption) { - if (mSecondaryActionsAdapter.indexOf(mClosedCaptioningAction) < 0) { - mSecondaryActionsAdapter.add(0, mClosedCaptioningAction); - } - } else { - mSecondaryActionsAdapter.remove(mClosedCaptioningAction); - } - if (hasMultiAudio) { - if (mSecondaryActionsAdapter.indexOf(mMultiAudioAction) < 0) { - mSecondaryActionsAdapter.add(mMultiAudioAction); - } - } else { - mSecondaryActionsAdapter.remove(mMultiAudioAction); - } - } - - /** - * Returns if the secondary controls row has any buttons and thus should be shown. - */ - public boolean hasSecondaryRow() { - return mSecondaryActionsAdapter.size() != 0; - } - @Override protected void startPlayback(int speedId) { if (getCurrentSpeedId() == speedId) { @@ -309,14 +251,6 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { // Do nothing. } - /** - * Notifies closed caption being enabled/disabled to update related UI. - */ - void onSubtitleTrackStateChanged(boolean enabled) { - mClosedCaptioningAction.setIndex(enabled ? - ClosedCaptioningAction.ON : ClosedCaptioningAction.OFF); - } - private void onStateChanged(int state, long positionMs, int speedLevel) { if (DEBUG) Log.d(TAG, "onStateChanged"); getControlsRow().setCurrentTime((int) positionMs); @@ -363,19 +297,6 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { onStateChanged(); } - private void showSideFragment(ArrayList<TvTrackInfo> trackInfos, String selectedTrackId) { - Bundle args = new Bundle(); - args.putParcelableArrayList(DvrPlaybackSideFragment.TRACK_INFOS, trackInfos); - args.putString(DvrPlaybackSideFragment.SELECTED_TRACK_ID, selectedTrackId); - DvrPlaybackSideFragment sideFragment = new DvrPlaybackSideFragment(); - sideFragment.setArguments(args); - getFragment().getFragmentManager().beginTransaction() - .hide(getFragment()) - .replace(R.id.dvr_playback_side_fragment, sideFragment) - .addToBackStack(null) - .commit(); - } - private class MediaControllerCallback extends MediaController.Callback { @Override public void onPlaybackStateChanged(PlaybackState state) { @@ -389,11 +310,4 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue { ((DvrPlaybackOverlayFragment) getFragment()).onMediaControllerUpdated(); } } - - private static class MultiAudioAction extends MultiAction { - MultiAudioAction(Context context) { - super(AUDIO_ACTION_ID); - setDrawables(new Drawable[]{context.getDrawable(R.drawable.ic_tvoption_multi_track)}); - } - } }
\ No newline at end of file diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java b/src/com/android/tv/dvr/ui/DvrPlaybackOverlayFragment.java index ff907182..51ec93b8 100644 --- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java +++ b/src/com/android/tv/dvr/ui/DvrPlaybackOverlayFragment.java @@ -14,14 +14,13 @@ * limitations under the License */ -package com.android.tv.dvr.ui.playback; +package com.android.tv.dvr.ui; import android.content.Context; import android.content.Intent; import android.graphics.Point; import android.hardware.display.DisplayManager; import android.media.tv.TvContentRating; -import android.media.tv.TvTrackInfo; import android.os.Bundle; import android.media.session.PlaybackState; import android.media.tv.TvInputManager; @@ -31,6 +30,7 @@ import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.ClassPresenterSelector; import android.support.v17.leanback.widget.HeaderItem; import android.support.v17.leanback.widget.ListRow; +import android.support.v17.leanback.widget.ListRowPresenter; import android.support.v17.leanback.widget.PlaybackControlsRow; import android.support.v17.leanback.widget.PlaybackControlsRowPresenter; import android.support.v17.leanback.widget.SinglePresenterSelector; @@ -38,25 +38,20 @@ import android.view.Display; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; +import android.text.TextUtils; import android.util.Log; import com.android.tv.R; import com.android.tv.TvApplication; import com.android.tv.data.BaseProgram; +import com.android.tv.dvr.RecordedProgram; import com.android.tv.dialog.PinDialogFragment; import com.android.tv.dvr.DvrDataManager; -import com.android.tv.dvr.data.RecordedProgram; -import com.android.tv.dvr.data.SeriesRecording; -import com.android.tv.dvr.ui.SortedArrayAdapter; -import com.android.tv.dvr.ui.browse.DvrListRowPresenter; +import com.android.tv.dvr.DvrPlayer; +import com.android.tv.dvr.DvrPlaybackMediaSessionHelper; import com.android.tv.parental.ContentRatingsManager; -import com.android.tv.util.TvSettings; -import com.android.tv.util.TvTrackInfoUtils; import com.android.tv.util.Utils; -import java.util.List; -import java.util.ArrayList; - public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { // TODO: Handles audio focus. Deals with block and ratings. private static final String TAG = "DvrPlaybackOverlayFragment"; @@ -67,7 +62,6 @@ public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { // mProgram is only used to store program from intent. Don't use it elsewhere. private RecordedProgram mProgram; - private DvrPlayer mDvrPlayer; private DvrPlaybackMediaSessionHelper mMediaSessionHelper; private DvrPlaybackControlHelper mPlaybackControlHelper; private ArrayObjectAdapter mRowsAdapter; @@ -78,30 +72,19 @@ public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { private TvView mTvView; private View mBlockScreenView; private ListRow mRelatedRecordingsRow; - private int mPaddingWithoutRelatedRow; - private int mPaddingWithoutSecondaryRow; + private int mExtraPaddingNoRelatedRow; private int mWindowWidth; private int mWindowHeight; private float mAppliedAspectRatio; private float mWindowAspectRatio; private boolean mPinChecked; - private DvrPlayer.OnTrackSelectedListener mOnSubtitleTrackSelectedListener = - new DvrPlayer.OnTrackSelectedListener() { - @Override - public void onTrackSelected(String selectedTrackId) { - mPlaybackControlHelper.onSubtitleTrackStateChanged(selectedTrackId != null); - mRowsAdapter.notifyArrayItemRangeChanged(0, 1); - } - }; @Override public void onCreate(Bundle savedInstanceState) { if (DEBUG) Log.d(TAG, "onCreate"); super.onCreate(savedInstanceState); - mPaddingWithoutRelatedRow = getActivity().getResources() - .getDimensionPixelOffset(R.dimen.dvr_playback_overlay_padding_top_no_related_row); - mPaddingWithoutSecondaryRow = getActivity().getResources() - .getDimensionPixelOffset(R.dimen.dvr_playback_overlay_padding_top_no_secondary_row); + mExtraPaddingNoRelatedRow = getActivity().getResources() + .getDimensionPixelOffset(R.dimen.dvr_playback_fragment_extra_padding_top); mDvrDataManager = TvApplication.getSingletons(getActivity()).getDvrDataManager(); mContentRatingsManager = TvApplication.getSingletons(getContext()) .getTvInputManagerHelper().getContentRatingsManager(); @@ -127,31 +110,13 @@ public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { super.onActivityCreated(savedInstanceState); mTvView = (TvView) getActivity().findViewById(R.id.dvr_tv_view); mBlockScreenView = getActivity().findViewById(R.id.block_screen); - mDvrPlayer = new DvrPlayer(mTvView); mMediaSessionHelper = new DvrPlaybackMediaSessionHelper( - getActivity(), MEDIA_SESSION_TAG, mDvrPlayer, this); + getActivity(), MEDIA_SESSION_TAG, new DvrPlayer(mTvView), this); mPlaybackControlHelper = new DvrPlaybackControlHelper(getActivity(), this); setUpRows(); - mDvrPlayer.setOnTracksAvailabilityChangedListener( - new DvrPlayer.OnTracksAvailabilityChangedListener() { - @Override - public void onTracksAvailabilityChanged(boolean hasClosedCaption, - boolean hasMultiAudio) { - mPlaybackControlHelper.updateSecondaryRow(hasClosedCaption, hasMultiAudio); - if (hasClosedCaption) { - mDvrPlayer.setOnTrackSelectedListener(TvTrackInfo.TYPE_SUBTITLE, - mOnSubtitleTrackSelectedListener); - selectBestMatchedTrack(TvTrackInfo.TYPE_SUBTITLE); - } else { - mDvrPlayer.setOnTrackSelectedListener(TvTrackInfo.TYPE_SUBTITLE, null); - } - if (hasMultiAudio) { - selectBestMatchedTrack(TvTrackInfo.TYPE_AUDIO); - } - onMediaControllerUpdated(); - } - }); - mDvrPlayer.setOnAspectRatioChangedListener(new DvrPlayer.OnAspectRatioChangedListener() { + preparePlayback(getActivity().getIntent()); + DvrPlayer dvrPlayer = mMediaSessionHelper.getDvrPlayer(); + dvrPlayer.setAspectRatioChangedListener(new DvrPlayer.AspectRatioChangedListener() { @Override public void onAspectRatioChanged(float videoAspectRatio) { updateAspectRatio(videoAspectRatio); @@ -159,7 +124,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { }); mPinChecked = getActivity().getIntent() .getBooleanExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_PIN_CHECKED, false); - mDvrPlayer.setOnContentBlockedListener(new DvrPlayer.OnContentBlockedListener() { + dvrPlayer.setContentBlockedListener(new DvrPlayer.ContentBlockedListener() { @Override public void onContentBlocked(TvContentRating rating) { if (mPinChecked) { @@ -184,7 +149,6 @@ public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { .show(getActivity().getFragmentManager(), PinDialogFragment.DIALOG_TAG); } }); - preparePlayback(getActivity().getIntent()); } @Override @@ -236,9 +200,6 @@ public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { updateAspectRatio(mAppliedAspectRatio); } - /** - * Returns next recorded episode in the same series as now playing program. - */ public RecordedProgram getNextEpisode(RecordedProgram program) { int position = mRelatedRecordingsRowAdapter.findInsertPosition(program); if (position == mRelatedRecordingsRowAdapter.size()) { @@ -248,92 +209,16 @@ public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { } } - /** - * Returns the tracks of the give type of the current playback. - - * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE} - * or {@link TvTrackInfo#TYPE_AUDIO}. Or returns {@code null}. - */ - public ArrayList<TvTrackInfo> getTracks(int trackType) { - if (trackType == TvTrackInfo.TYPE_AUDIO) { - return mDvrPlayer.getAudioTracks(); - } else if (trackType == TvTrackInfo.TYPE_SUBTITLE) { - return mDvrPlayer.getSubtitleTracks(); - } - return null; - } - - /** - * Returns the ID of the selected track of the given type. - */ - public String getSelectedTrackId(int trackType) { - return mDvrPlayer.getSelectedTrackId(trackType); - } - - /** - * Returns the language setting of the given track type. - - * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE} - * or {@link TvTrackInfo#TYPE_AUDIO}. - * @return {@code null} if no language has been set for the given track type. - */ - TvTrackInfo getTrackSetting(int trackType) { - return TvSettings.getDvrPlaybackTrackSettings(getContext(), trackType); - } - - /** - * Selects the given audio or subtitle track for DVR playback. - * @param trackType Should be {@link TvTrackInfo#TYPE_SUBTITLE} - * or {@link TvTrackInfo#TYPE_AUDIO}. - * @param selectedTrack {@code null} to disable the audio or subtitle track according to - * trackType. - */ - void selectTrack(int trackType, TvTrackInfo selectedTrack) { - if (mDvrPlayer.isPlaybackPrepared()) { - mDvrPlayer.selectTrack(trackType, selectedTrack); - } - } - - /** - * Notifies the content of controls row or related recordings row is changed and the UI should - * be updated according to the change. - */ void onMediaControllerUpdated() { - updateVerticalPosition(); - mRowsAdapter.notifyArrayItemRangeChanged(0, 2); - } - - private void selectBestMatchedTrack(int trackType) { - TvTrackInfo selectedTrack = getTrackSetting(trackType); - if (selectedTrack != null) { - TvTrackInfo bestMatchedTrack = TvTrackInfoUtils.getBestTrackInfo(getTracks(trackType), - selectedTrack.getId(), selectedTrack.getLanguage(), - trackType == TvTrackInfo.TYPE_AUDIO ? selectedTrack.getAudioChannelCount() : 0); - if (bestMatchedTrack != null && (trackType == TvTrackInfo.TYPE_AUDIO || Utils - .isEqualLanguage(bestMatchedTrack.getLanguage(), - selectedTrack.getLanguage()))) { - selectTrack(trackType, bestMatchedTrack); - return; - } - } - if (trackType == TvTrackInfo.TYPE_SUBTITLE) { - // Disables closed captioning if there's no matched language. - selectTrack(TvTrackInfo.TYPE_SUBTITLE, null); - } + mRowsAdapter.notifyArrayItemRangeChanged(0, 1); } private void updateAspectRatio(float videoAspectRatio) { - if (videoAspectRatio <= 0) { - // We don't have video's width or height information, use window's aspect ratio. - videoAspectRatio = mWindowAspectRatio; - } if (Math.abs(mAppliedAspectRatio - videoAspectRatio) < DISPLAY_ASPECT_RATIO_EPSILON) { // No need to change return; } - if (Math.abs(mWindowAspectRatio - videoAspectRatio) < DISPLAY_ASPECT_RATIO_EPSILON) { - ((ViewGroup) mTvView.getParent()).setPadding(0, 0, 0, 0); - } else if (videoAspectRatio < mWindowAspectRatio) { + if (videoAspectRatio < mWindowAspectRatio) { int newPadding = (mWindowWidth - Math.round(mWindowHeight * videoAspectRatio)) / 2; ((ViewGroup) mTvView.getParent()).setPadding(newPadding, 0, newPadding, 0); } else { @@ -345,7 +230,6 @@ public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { private void preparePlayback(Intent intent) { mMediaSessionHelper.setupPlayback(mProgram, getSeekTimeFromIntent(intent)); - mPlaybackControlHelper.updateSecondaryRow(false, false); getActivity().getMediaController().getTransportControls().prepare(); updateRelatedRecordingsRow(); } @@ -355,35 +239,24 @@ public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { mRelatedRecordingsRowAdapter.clear(); long programId = mProgram.getId(); String seriesId = mProgram.getSeriesId(); - SeriesRecording seriesRecording = mDvrDataManager.getSeriesRecording(seriesId); - if (seriesRecording != null) { + if (!TextUtils.isEmpty(seriesId)) { if (DEBUG) Log.d(TAG, "Update related recordings with:" + seriesId); - List<RecordedProgram> relatedPrograms = - mDvrDataManager.getRecordedPrograms(seriesRecording.getId()); - for (RecordedProgram program : relatedPrograms) { - if (programId != program.getId()) { + for (RecordedProgram program : mDvrDataManager.getRecordedPrograms()) { + if (seriesId.equals(program.getSeriesId()) && programId != program.getId()) { mRelatedRecordingsRowAdapter.add(program); } } } + View view = getView(); if (mRelatedRecordingsRowAdapter.size() == 0) { mRowsAdapter.remove(mRelatedRecordingsRow); + view.setPadding(view.getPaddingLeft(), mExtraPaddingNoRelatedRow, + view.getPaddingRight(), view.getPaddingBottom()); } else if (wasEmpty){ mRowsAdapter.add(mRelatedRecordingsRow); + view.setPadding(view.getPaddingLeft(), 0, + view.getPaddingRight(), view.getPaddingBottom()); } - onMediaControllerUpdated(); - } - - private void updateVerticalPosition() { - int verticalPadding = 0; - verticalPadding += - mRelatedRecordingsRowAdapter.size() == 0 ? mPaddingWithoutRelatedRow : 0; - verticalPadding += - mPlaybackControlHelper.hasSecondaryRow() ? 0 : mPaddingWithoutSecondaryRow; - if (DEBUG) Log.d(TAG, "New controls padding: " + verticalPadding); - View view = getView(); - view.setPadding(view.getPaddingLeft(), verticalPadding, - view.getPaddingRight(), view.getPaddingBottom()); } private void setUpRows() { @@ -392,7 +265,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { ClassPresenterSelector selector = new ClassPresenterSelector(); selector.addClassPresenter(PlaybackControlsRow.class, controlsRowPresenter); - selector.addClassPresenter(ListRow.class, new DvrListRowPresenter(getContext())); + selector.addClassPresenter(ListRow.class, new ListRowPresenter()); mRowsAdapter = new ArrayObjectAdapter(selector); mRowsAdapter.add(mPlaybackControlHelper.getControlsRow()); @@ -401,7 +274,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { } private ListRow getRelatedRecordingsRow() { - mRelatedRecordingCardPresenter = new DvrPlaybackCardPresenter(getActivity(), this); + mRelatedRecordingCardPresenter = new DvrPlaybackCardPresenter(getActivity()); mRelatedRecordingsRowAdapter = new RelatedRecordingsAdapter(mRelatedRecordingCardPresenter); HeaderItem header = new HeaderItem(0, getActivity().getString(R.string.dvr_playback_related_recordings)); @@ -424,7 +297,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackOverlayFragment { } @Override - public long getId(BaseProgram item) { + long getId(BaseProgram item) { return item.getId(); } } diff --git a/src/com/android/tv/dvr/ui/DvrScheduleFragment.java b/src/com/android/tv/dvr/ui/DvrScheduleFragment.java index d6008315..da6d1637 100644 --- a/src/com/android/tv/dvr/ui/DvrScheduleFragment.java +++ b/src/com/android/tv/dvr/ui/DvrScheduleFragment.java @@ -32,8 +32,9 @@ import com.android.tv.TvApplication; import com.android.tv.common.SoftPreconditions; import com.android.tv.data.Program; import com.android.tv.dvr.DvrManager; -import com.android.tv.dvr.data.ScheduledRecording; -import com.android.tv.dvr.data.SeriesRecording; +import com.android.tv.dvr.DvrUiHelper; +import com.android.tv.dvr.ScheduledRecording; +import com.android.tv.dvr.SeriesRecording; import com.android.tv.dvr.ui.DvrConflictFragment.DvrProgramConflictFragment; import com.android.tv.util.Utils; @@ -47,26 +48,18 @@ import java.util.List; */ @TargetApi(Build.VERSION_CODES.N) public class DvrScheduleFragment extends DvrGuidedStepFragment { - /** - * Key for the whether to add the current program to series. - * Type: boolean - */ - public static final String KEY_ADD_CURRENT_PROGRAM_TO_SERIES = "add_current_program_to_series"; - private static final String TAG = "DvrScheduleFragment"; private static final int ACTION_RECORD_EPISODE = 1; private static final int ACTION_RECORD_SERIES = 2; private Program mProgram; - private boolean mAddCurrentProgramToSeries; @Override public void onCreate(Bundle savedInstanceState) { Bundle args = getArguments(); if (args != null) { mProgram = args.getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM); - mAddCurrentProgramToSeries = args.getBoolean(KEY_ADD_CURRENT_PROGRAM_TO_SERIES, false); } DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager(); SoftPreconditions.checkArgument(mProgram != null && mProgram.isEpisodic(), TAG, @@ -146,10 +139,8 @@ public class DvrScheduleFragment extends DvrGuidedStepFragment { .build(); getDvrManager().updateSeriesRecording(seriesRecording); } - DvrUiHelper.startSeriesSettingsActivity(getContext(), - seriesRecording.getId(), null, true, true, true, - mAddCurrentProgramToSeries ? mProgram : null); + seriesRecording.getId(), null, true, true, true); dismissDialog(); } } diff --git a/src/com/android/tv/dvr/ui/list/DvrSchedulesActivity.java b/src/com/android/tv/dvr/ui/DvrSchedulesActivity.java index a0410bb3..f6e6ac26 100644 --- a/src/com/android/tv/dvr/ui/list/DvrSchedulesActivity.java +++ b/src/com/android/tv/dvr/ui/DvrSchedulesActivity.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.tv.dvr.ui.list; +package com.android.tv.dvr.ui; import android.app.Activity; import android.app.ProgressDialog; @@ -24,13 +24,15 @@ import android.support.annotation.IntDef; import com.android.tv.R; import com.android.tv.TvApplication; import com.android.tv.data.Program; -import com.android.tv.dvr.data.SeriesRecording; -import com.android.tv.dvr.provider.EpisodicProgramLoadTask; -import com.android.tv.dvr.recorder.SeriesRecordingScheduler; -import com.android.tv.dvr.ui.BigArguments; +import com.android.tv.dvr.EpisodicProgramLoadTask; +import com.android.tv.dvr.SeriesRecording; +import com.android.tv.dvr.SeriesRecordingScheduler; +import com.android.tv.dvr.ui.list.DvrSchedulesFragment; +import com.android.tv.dvr.ui.list.DvrSeriesSchedulesFragment; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -70,47 +72,33 @@ public class DvrSchedulesActivity extends Activity { getFragmentManager().beginTransaction().add( R.id.fragment_container, schedulesFragment).commit(); } else if (scheduleType == TYPE_SERIES_SCHEDULE) { - if (BigArguments.getArgument(DvrSeriesSchedulesFragment - .SERIES_SCHEDULES_KEY_SERIES_PROGRAMS) != null) { - // The programs will be passed to the DvrSeriesSchedulesFragment, so don't need - // to reset the BigArguments. - showDvrSeriesSchedulesFragment(getIntent().getExtras()); - } else { - final ProgressDialog dialog = ProgressDialog.show(this, null, getString( - R.string.dvr_series_progress_message_reading_programs)); - SeriesRecording seriesRecording = getIntent().getExtras() - .getParcelable(DvrSeriesSchedulesFragment - .SERIES_SCHEDULES_KEY_SERIES_RECORDING); - // To get programs faster, hold the update of the series schedules. - SeriesRecordingScheduler.getInstance(this).pauseUpdate(); - new EpisodicProgramLoadTask(this, Collections.singletonList(seriesRecording)) { - @Override - protected void onPostExecute(List<Program> programs) { - SeriesRecordingScheduler.getInstance(DvrSchedulesActivity.this) - .resumeUpdate(); - dialog.dismiss(); - Bundle args = getIntent().getExtras(); - BigArguments.reset(); - BigArguments.setArgument( - DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_PROGRAMS, - programs == null ? Collections.EMPTY_LIST : programs); - showDvrSeriesSchedulesFragment(args); - } - }.setLoadCurrentProgram(true) - .setLoadDisallowedProgram(true) - .setLoadScheduledEpisode(true) - .setIgnoreChannelOption(true) - .execute(); - } + final ProgressDialog dialog = ProgressDialog.show(this, null, getString( + R.string.dvr_series_schedules_progress_message_reading_programs)); + SeriesRecording seriesRecording = getIntent().getExtras() + .getParcelable(DvrSeriesSchedulesFragment + .SERIES_SCHEDULES_KEY_SERIES_RECORDING); + // To get programs faster, hold the update of the series schedules. + SeriesRecordingScheduler.getInstance(this).pauseUpdate(); + new EpisodicProgramLoadTask(this, Collections.singletonList(seriesRecording)) { + @Override + protected void onPostExecute(List<Program> programs) { + SeriesRecordingScheduler.getInstance(DvrSchedulesActivity.this).resumeUpdate(); + dialog.dismiss(); + Bundle args = getIntent().getExtras(); + args.putParcelableArrayList(DvrSeriesSchedulesFragment + .SERIES_SCHEDULES_KEY_SERIES_PROGRAMS, new ArrayList<>(programs)); + DvrSeriesSchedulesFragment schedulesFragment = new DvrSeriesSchedulesFragment(); + schedulesFragment.setArguments(args); + getFragmentManager().beginTransaction().add( + R.id.fragment_container, schedulesFragment).commit(); + } + }.setLoadCurrentProgram(true) + .setLoadDisallowedProgram(true) + .setLoadScheduledEpisode(true) + .setIgnoreChannelOption(true) + .execute(); } else { finish(); } } - - private void showDvrSeriesSchedulesFragment(Bundle args) { - DvrSeriesSchedulesFragment schedulesFragment = new DvrSeriesSchedulesFragment(); - schedulesFragment.setArguments(args); - getFragmentManager().beginTransaction().add( - R.id.fragment_container, schedulesFragment).commit(); - } } diff --git a/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java b/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java index 667af34a..f57e4b05 100644 --- a/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java +++ b/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java @@ -22,6 +22,9 @@ import android.support.v17.leanback.app.GuidedStepFragment; import com.android.tv.R; import com.android.tv.TvApplication; +import com.android.tv.common.SoftPreconditions; +import com.android.tv.dvr.ui.SeriesDeletionFragment; +import com.android.tv.ui.sidepanel.SettingsFragment; /** * Activity to show details view in DVR. @@ -39,7 +42,7 @@ public class DvrSeriesDeletionActivity extends Activity { setContentView(R.layout.activity_dvr_series_settings); // Check savedInstanceState to prevent that activity is being showed with animation. if (savedInstanceState == null) { - DvrSeriesDeletionFragment deletionFragment = new DvrSeriesDeletionFragment(); + SeriesDeletionFragment deletionFragment = new SeriesDeletionFragment(); deletionFragment.setArguments(getIntent().getExtras()); GuidedStepFragment.addAsRoot(this, deletionFragment, R.id.dvr_settings_view_frame); } diff --git a/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java b/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java index 8f880f16..1173df46 100644 --- a/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java +++ b/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java @@ -25,29 +25,22 @@ import android.support.v17.leanback.widget.GuidedAction; import com.android.tv.R; import com.android.tv.TvApplication; -import com.android.tv.data.Program; +import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrScheduleManager; -import com.android.tv.dvr.data.ScheduledRecording; -import com.android.tv.dvr.data.SeriesRecording; -import com.android.tv.dvr.ui.list.DvrSchedulesActivity; +import com.android.tv.dvr.ScheduledRecording; +import com.android.tv.dvr.SeriesRecording; import com.android.tv.dvr.ui.list.DvrSeriesSchedulesFragment; import java.util.List; public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment { - /** - * The key for program list which will be passed to {@link DvrSeriesSchedulesFragment}. - * Type: List<{@link Program}> - */ - public static final String SERIES_SCHEDULED_KEY_PROGRAMS = "series_scheduled_key_programs"; - private final static long SERIES_RECORDING_ID_NOT_SET = -1; private final static int ACTION_VIEW_SCHEDULES = 1; + private DvrScheduleManager mDvrScheduleManager; private SeriesRecording mSeriesRecording; private boolean mShowViewScheduleOption; - private List<Program> mPrograms; private int mSchedulesAddedCount = 0; private boolean mHasConflict = false; @@ -65,25 +58,22 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment { } mShowViewScheduleOption = getArguments().getBoolean( DvrSeriesScheduledDialogActivity.SHOW_VIEW_SCHEDULE_OPTION); + mDvrScheduleManager = TvApplication.getSingletons(context).getDvrScheduleManager(); mSeriesRecording = TvApplication.getSingletons(context).getDvrDataManager() .getSeriesRecording(seriesRecordingId); if (mSeriesRecording == null) { getActivity().finish(); return; } - mPrograms = (List<Program>) BigArguments.getArgument(SERIES_SCHEDULED_KEY_PROGRAMS); - BigArguments.reset(); mSchedulesAddedCount = TvApplication.getSingletons(getContext()).getDvrManager() .getAvailableScheduledRecording(mSeriesRecording.getId()).size(); - DvrScheduleManager dvrScheduleManager = - TvApplication.getSingletons(context).getDvrScheduleManager(); List<ScheduledRecording> conflictingRecordings = - dvrScheduleManager.getConflictingSchedules(mSeriesRecording); + mDvrScheduleManager.getConflictingSchedules(mSeriesRecording); mHasConflict = !conflictingRecordings.isEmpty(); for (ScheduledRecording recording : conflictingRecordings) { if (recording.getSeriesRecordingId() == mSeriesRecording.getId()) { ++mInThisSeriesConflictCount; - } else if (recording.getPriority() < mSeriesRecording.getPriority()) { + } else { ++mOutThisSeriesConflictCount; } } @@ -123,9 +113,6 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment { .TYPE_SERIES_SCHEDULE); intent.putExtra(DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_RECORDING, mSeriesRecording); - BigArguments.reset(); - BigArguments.setArgument(DvrSeriesSchedulesFragment - .SERIES_SCHEDULES_KEY_SERIES_PROGRAMS, mPrograms); startActivity(intent); } getActivity().finish(); @@ -134,30 +121,30 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment { private String getDescription() { if (!mHasConflict) { return getResources().getQuantityString( - R.plurals.dvr_series_scheduled_no_conflict, mSchedulesAddedCount, + R.plurals.dvr_series_recording_scheduled_no_conflict, mSchedulesAddedCount, mSchedulesAddedCount, mSeriesRecording.getTitle()); } else { // mInThisSeriesConflictCount equals 0 and mOutThisSeriesConflictCount equals 0 means // mHasConflict is false. So we don't need to check that case. if (mInThisSeriesConflictCount != 0 && mOutThisSeriesConflictCount != 0) { - return getResources().getQuantityString( - R.plurals.dvr_series_scheduled_this_and_other_series_conflict, + return getResources().getQuantityString(R.plurals + .dvr_series_recording_scheduled_this_and_other_series_conflict, mSchedulesAddedCount, mSchedulesAddedCount, mSeriesRecording.getTitle(), mInThisSeriesConflictCount + mOutThisSeriesConflictCount); } else if (mInThisSeriesConflictCount != 0) { - return getResources().getQuantityString( - R.plurals.dvr_series_recording_scheduled_only_this_series_conflict, + return getResources().getQuantityString(R.plurals + .dvr_series_recording_scheduled_only_this_series_conflict, mSchedulesAddedCount, mSchedulesAddedCount, mSeriesRecording.getTitle(), mInThisSeriesConflictCount); } else { if (mOutThisSeriesConflictCount == 1) { - return getResources().getQuantityString( - R.plurals.dvr_series_scheduled_only_other_series_one_conflict, + return getResources().getQuantityString(R.plurals + .dvr_series_recording_scheduled_only_other_series_one_conflict, mSchedulesAddedCount, mSchedulesAddedCount, mSeriesRecording.getTitle()); } else { - return getResources().getQuantityString( - R.plurals.dvr_series_scheduled_only_other_series_many_conflicts, + return getResources().getQuantityString(R.plurals + .dvr_series_recording_scheduled_only_other_series_conflict, mSchedulesAddedCount, mSchedulesAddedCount, mSeriesRecording.getTitle(), mOutThisSeriesConflictCount); } diff --git a/src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java b/src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java index 6dd20b3a..3f7671b3 100644 --- a/src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java +++ b/src/com/android/tv/dvr/ui/DvrSeriesSettingsActivity.java @@ -17,6 +17,7 @@ package com.android.tv.dvr.ui; import android.app.Activity; +import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.support.v17.leanback.app.GuidedStepFragment; @@ -37,34 +38,25 @@ public class DvrSeriesSettingsActivity extends Activity { /** * Name of the boolean flag to decide if the series recording with empty schedule and recording * will be removed. - * Type: boolean */ public static final String REMOVE_EMPTY_SERIES_RECORDING = "remove_empty_series_recording"; /** * Name of the boolean flag to decide if the setting fragment should be translucent. - * Type: boolean */ public static final String IS_WINDOW_TRANSLUCENT = "windows_translucent"; /** - * Name of the program list. The list contains the programs which belong to the series. - * Type: List<{@link com.android.tv.data.Program}> + * Name of the channel id list. If the channel list is given, we show the channels + * from the values in channel option. + * Type: Long array */ - public static final String PROGRAM_LIST = "program_list"; + public static final String CHANNEL_ID_LIST = "channel_id_list"; /** * Name of the boolean flag to check if the confirm dialog should show view schedule option. - * Type: boolean */ public static final String SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG = "show_view_schedule_option_in_dialog"; - /** - * Name of the current program added to series. The current program will be recorded only when - * the series recording is initialized from media controller. But for other case, the current - * program won't be recorded. - */ - public static final String CURRENT_PROGRAM = "current_program"; - @Override public void onCreate(Bundle savedInstanceState) { TvApplication.setCurrentRunningProcess(this, true); @@ -74,7 +66,7 @@ public class DvrSeriesSettingsActivity extends Activity { SoftPreconditions.checkArgument(seriesRecordingId != -1); if (savedInstanceState == null) { - DvrSeriesSettingsFragment settingFragment = new DvrSeriesSettingsFragment(); + SeriesSettingsFragment settingFragment = new SeriesSettingsFragment(); settingFragment.setArguments(getIntent().getExtras()); GuidedStepFragment.addAsRoot(this, settingFragment, R.id.dvr_settings_view_frame); } diff --git a/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java b/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java index b476fff7..c3867886 100644 --- a/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java +++ b/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java @@ -33,7 +33,7 @@ import com.android.tv.data.Channel; import com.android.tv.data.ChannelDataManager; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; -import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.ScheduledRecording; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -131,8 +131,15 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment { String title = getString(R.string.dvr_stop_recording_dialog_title); String description; if (mStopReason == REASON_ON_CONFLICT) { + String programTitle = mSchedule.getProgramTitle(); + if (TextUtils.isEmpty(programTitle)) { + ChannelDataManager channelDataManager = + TvApplication.getSingletons(getActivity()).getChannelDataManager(); + Channel channel = channelDataManager.getChannel(mSchedule.getChannelId()); + programTitle = channel.getDisplayName(); + } description = getString(R.string.dvr_stop_recording_dialog_description_on_conflict, - mSchedule.getProgramDisplayTitle(getContext())); + mSchedule.getProgramTitle()); } else { description = getString(R.string.dvr_stop_recording_dialog_description); } diff --git a/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java b/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java index fe3a4a60..feaa2357 100644 --- a/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java +++ b/src/com/android/tv/dvr/ui/DvrStopSeriesRecordingFragment.java @@ -31,8 +31,8 @@ import com.android.tv.R; import com.android.tv.TvApplication; 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.SeriesRecording; +import com.android.tv.dvr.ScheduledRecording; +import com.android.tv.dvr.SeriesRecording; import java.util.ArrayList; import java.util.List; diff --git a/src/com/android/tv/dvr/ui/DvrUiHelper.java b/src/com/android/tv/dvr/ui/DvrUiHelper.java deleted file mode 100644 index 507db6e7..00000000 --- a/src/com/android/tv/dvr/ui/DvrUiHelper.java +++ /dev/null @@ -1,575 +0,0 @@ -/* - * Copyright (C) 2015 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.Activity; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.media.tv.TvInputManager; -import android.os.Build; -import android.os.Bundle; -import android.support.annotation.MainThread; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.ActivityOptionsCompat; -import android.widget.ImageView; -import android.widget.Toast; - -import com.android.tv.MainActivity; -import com.android.tv.R; -import com.android.tv.TvApplication; -import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; -import com.android.tv.data.Program; -import com.android.tv.dialog.HalfSizedDialogFragment; -import com.android.tv.dvr.DvrManager; -import com.android.tv.dvr.DvrStorageStatusManager; -import com.android.tv.dvr.data.RecordedProgram; -import com.android.tv.dvr.data.ScheduledRecording; -import com.android.tv.dvr.data.SeriesRecording; -import com.android.tv.dvr.provider.EpisodicProgramLoadTask; -import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrAlreadyRecordedDialogFragment; -import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrAlreadyScheduledDialogFragment; -import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrChannelRecordDurationOptionDialogFragment; -import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrChannelWatchConflictDialogFragment; -import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrInsufficientSpaceErrorDialogFragment; -import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrMissingStorageErrorDialogFragment; -import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrNoFreeSpaceErrorDialogFragment; -import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrProgramConflictDialogFragment; -import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrScheduleDialogFragment; -import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrSmallSizedStorageErrorDialogFragment; -import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrStopRecordingDialogFragment; -import com.android.tv.dvr.ui.browse.DvrBrowseActivity; -import com.android.tv.dvr.ui.browse.DvrDetailsActivity; -import com.android.tv.dvr.ui.list.DvrSchedulesActivity; -import com.android.tv.dvr.ui.list.DvrSchedulesFragment; -import com.android.tv.dvr.ui.list.DvrSeriesSchedulesFragment; -import com.android.tv.util.ToastUtils; -import com.android.tv.util.Utils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -/** - * A helper class for DVR UI. - */ -@MainThread -@TargetApi(Build.VERSION_CODES.N) -public class DvrUiHelper { - private static String TAG = "DvrUiHelper"; - - private static ProgressDialog sProgressDialog = null; - - /** - * Checks if the storage status is good for recording and shows error messages if needed. - * - * @param recordingRequestRunnable if the storage status is OK to record or users choose to - * perform the operation anyway, this Runnable will run. - */ - public static void checkStorageStatusAndShowErrorMessage(Activity activity, String inputId, - Runnable recordingRequestRunnable) { - if (Utils.isBundledInput(inputId)) { - switch (TvApplication.getSingletons(activity).getDvrStorageStatusManager() - .getDvrStorageStatus()) { - case DvrStorageStatusManager.STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL: - showDvrSmallSizedStorageErrorDialog(activity); - return; - case DvrStorageStatusManager.STORAGE_STATUS_MISSING: - showDvrMissingStorageErrorDialog(activity); - return; - case DvrStorageStatusManager.STORAGE_STATUS_FREE_SPACE_INSUFFICIENT: - showDvrNoFreeSpaceErrorDialog(activity, recordingRequestRunnable); - return; - } - } - recordingRequestRunnable.run(); - } - - /** - * Shows the schedule dialog. - */ - public static void showScheduleDialog(Activity activity, Program program, - boolean addCurrentProgramToSeries) { - if (SoftPreconditions.checkNotNull(program) == null) { - return; - } - Bundle args = new Bundle(); - args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program); - args.putBoolean(DvrScheduleFragment.KEY_ADD_CURRENT_PROGRAM_TO_SERIES, - addCurrentProgramToSeries); - showDialogFragment(activity, new DvrScheduleDialogFragment(), args, true, true); - } - - /** - * Shows the recording duration options dialog. - */ - public static void showChannelRecordDurationOptions(Activity activity, Channel channel) { - if (SoftPreconditions.checkNotNull(channel) == null) { - return; - } - Bundle args = new Bundle(); - args.putLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID, channel.getId()); - showDialogFragment(activity, new DvrChannelRecordDurationOptionDialogFragment(), args); - } - - /** - * Shows the dialog which says that the new schedule conflicts with others. - */ - public static void showScheduleConflictDialog(Activity activity, Program program) { - if (program == null) { - return; - } - Bundle args = new Bundle(); - args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program); - showDialogFragment(activity, new DvrProgramConflictDialogFragment(), args, false, true); - } - - /** - * Shows the conflict dialog for the channel watching. - */ - public static void showChannelWatchConflictDialog(MainActivity activity, Channel channel) { - if (channel == null) { - return; - } - Bundle args = new Bundle(); - args.putLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID, channel.getId()); - showDialogFragment(activity, new DvrChannelWatchConflictDialogFragment(), args); - } - - /** - * Shows DVR insufficient space error dialog. - */ - public static void showDvrInsufficientSpaceErrorDialog(MainActivity activity, - Set<String> failedScheduledRecordingInfoSet) { - Bundle args = new Bundle(); - ArrayList<String> failedScheduledRecordingInfoArray = - new ArrayList<>(failedScheduledRecordingInfoSet); - args.putStringArrayList(DvrInsufficientSpaceErrorFragment.FAILED_SCHEDULED_RECORDING_INFOS, - failedScheduledRecordingInfoArray); - showDialogFragment(activity, new DvrInsufficientSpaceErrorDialogFragment(), args); - Utils.clearRecordingFailedReason(activity, - TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE); - Utils.clearFailedScheduledRecordingInfoSet(activity); - } - - /** - * Shows DVR no free space error dialog. - * - * @param recordingRequestRunnable the recording request to be executed when users choose - * {@link DvrGuidedStepFragment#ACTION_RECORD_ANYWAY}. - */ - public static void showDvrNoFreeSpaceErrorDialog(Activity activity, - Runnable recordingRequestRunnable) { - DvrHalfSizedDialogFragment fragment = new DvrNoFreeSpaceErrorDialogFragment(); - fragment.setOnActionClickListener(new HalfSizedDialogFragment.OnActionClickListener() { - @Override - public void onActionClick(long actionId) { - if (actionId == DvrGuidedStepFragment.ACTION_RECORD_ANYWAY) { - recordingRequestRunnable.run(); - } else if (actionId == DvrGuidedStepFragment.ACTION_DELETE_RECORDINGS) { - Intent intent = new Intent(activity, DvrBrowseActivity.class); - activity.startActivity(intent); - } - } - }); - showDialogFragment(activity, fragment, null); - } - - /** - * Shows DVR missing storage error dialog. - */ - private static void showDvrMissingStorageErrorDialog(Activity activity) { - showDialogFragment(activity, new DvrMissingStorageErrorDialogFragment(), null); - } - - /** - * Shows DVR small sized storage error dialog. - */ - public static void showDvrSmallSizedStorageErrorDialog(Activity activity) { - showDialogFragment(activity, new DvrSmallSizedStorageErrorDialogFragment(), null); - } - - /** - * Shows stop recording dialog. - */ - public static void showStopRecordingDialog(Activity activity, long channelId, int reason, - HalfSizedDialogFragment.OnActionClickListener listener) { - Bundle args = new Bundle(); - args.putLong(DvrHalfSizedDialogFragment.KEY_CHANNEL_ID, channelId); - args.putInt(DvrStopRecordingFragment.KEY_REASON, reason); - DvrHalfSizedDialogFragment fragment = new DvrStopRecordingDialogFragment(); - fragment.setOnActionClickListener(listener); - showDialogFragment(activity, fragment, args); - } - - /** - * Shows "already scheduled" dialog. - */ - public static void showAlreadyScheduleDialog(Activity activity, Program program) { - if (program == null) { - return; - } - Bundle args = new Bundle(); - args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program); - showDialogFragment(activity, new DvrAlreadyScheduledDialogFragment(), args, false, true); - } - - /** - * Shows "already recorded" dialog. - */ - public static void showAlreadyRecordedDialog(Activity activity, Program program) { - if (program == null) { - return; - } - Bundle args = new Bundle(); - args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program); - showDialogFragment(activity, new DvrAlreadyRecordedDialogFragment(), args, false, true); - } - - /** - * Handle the request of recording a current program. It will handle creating schedules and - * shows the proper dialog and toast message respectively for timed-recording and program - * recording cases. - * - * @param addProgramToSeries denotes whether the program to be recorded should be added into - * the series recording when users choose to record the entire series. - */ - public static void requestRecordingCurrentProgram(Activity activity, - Channel channel, Program program, boolean addProgramToSeries) { - if (program == null) { - DvrUiHelper.showChannelRecordDurationOptions(activity, channel); - } else if (DvrUiHelper.handleCreateSchedule(activity, program, addProgramToSeries)) { - String msg = activity.getString(R.string.dvr_msg_current_program_scheduled, - program.getTitle(), Utils.toTimeString(program.getEndTimeUtcMillis(), false)); - Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show(); - } - } - - /** - * Handle the request of recording a future program. It will handle creating schedules and - * shows the proper toast message. - * - * @param addProgramToSeries denotes whether the program to be recorded should be added into - * the series recording when users choose to record the entire series. - */ - public static void requestRecordingFutureProgram(Activity activity, - Program program, boolean addProgramToSeries) { - if (DvrUiHelper.handleCreateSchedule(activity, program, addProgramToSeries)) { - String msg = activity.getString( - R.string.dvr_msg_program_scheduled, program.getTitle()); - ToastUtils.show(activity, msg, Toast.LENGTH_SHORT); - } - } - - /** - * Handles the action to create the new schedule. It returns {@code true} if the schedule is - * added and there's no additional UI, otherwise {@code false}. - */ - private static boolean handleCreateSchedule(Activity activity, Program program, - boolean addProgramToSeries) { - if (program == null) { - return false; - } - DvrManager dvrManager = TvApplication.getSingletons(activity).getDvrManager(); - if (!program.isEpisodic()) { - // One time recording. - dvrManager.addSchedule(program); - if (!dvrManager.getConflictingSchedules(program).isEmpty()) { - DvrUiHelper.showScheduleConflictDialog(activity, program); - return false; - } - } else { - // Show recorded program rather than the schedule. - RecordedProgram recordedProgram = dvrManager.getRecordedProgram(program.getTitle(), - program.getSeasonNumber(), program.getEpisodeNumber()); - if (recordedProgram != null) { - DvrUiHelper.showAlreadyRecordedDialog(activity, program); - return false; - } - ScheduledRecording duplicate = dvrManager.getScheduledRecording(program.getTitle(), - program.getSeasonNumber(), program.getEpisodeNumber()); - if (duplicate != null - && (duplicate.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED - || duplicate.getState() - == ScheduledRecording.STATE_RECORDING_IN_PROGRESS)) { - DvrUiHelper.showAlreadyScheduleDialog(activity, program); - return false; - } - SeriesRecording seriesRecording = dvrManager.getSeriesRecording(program); - if (seriesRecording == null || seriesRecording.isStopped()) { - DvrUiHelper.showScheduleDialog(activity, program, addProgramToSeries); - return false; - } else { - // Just add the schedule. - dvrManager.addSchedule(program); - } - } - return true; - } - - private static void showDialogFragment(Activity activity, - DvrHalfSizedDialogFragment dialogFragment, Bundle args) { - showDialogFragment(activity, dialogFragment, args, false, false); - } - - private static void showDialogFragment(Activity activity, - DvrHalfSizedDialogFragment dialogFragment, Bundle args, boolean keepSidePanelHistory, - boolean keepProgramGuide) { - dialogFragment.setArguments(args); - if (activity instanceof MainActivity) { - ((MainActivity) activity).getOverlayManager() - .showDialogFragment(DvrHalfSizedDialogFragment.DIALOG_TAG, dialogFragment, - keepSidePanelHistory, keepProgramGuide); - } else { - dialogFragment.show(activity.getFragmentManager(), - DvrHalfSizedDialogFragment.DIALOG_TAG); - } - } - - /** - * Checks whether channel watch conflict dialog is open or not. - */ - public static boolean isChannelWatchConflictDialogShown(MainActivity activity) { - return activity.getOverlayManager().getCurrentDialog() instanceof - DvrChannelWatchConflictDialogFragment; - } - - private static ScheduledRecording getEarliestScheduledRecording(List<ScheduledRecording> - recordings) { - ScheduledRecording earlistScheduledRecording = null; - if (!recordings.isEmpty()) { - Collections.sort(recordings, - ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR); - earlistScheduledRecording = recordings.get(0); - } - return earlistScheduledRecording; - } - - /** - * Shows the schedules activity to resolve the tune conflict. - */ - public static void startSchedulesActivityForTuneConflict(Context context, Channel channel) { - if (channel == null) { - return; - } - List<ScheduledRecording> conflicts = TvApplication.getSingletons(context).getDvrManager() - .getConflictingSchedulesForTune(channel.getId()); - startSchedulesActivity(context, getEarliestScheduledRecording(conflicts)); - } - - /** - * Shows the schedules activity to resolve the one time recording conflict. - */ - public static void startSchedulesActivityForOneTimeRecordingConflict(Context context, - List<ScheduledRecording> conflicts) { - startSchedulesActivity(context, getEarliestScheduledRecording(conflicts)); - } - - /** - * Shows the schedules activity with full schedule. - */ - public static void startSchedulesActivity(Context context, ScheduledRecording - focusedScheduledRecording) { - Intent intent = new Intent(context, DvrSchedulesActivity.class); - intent.putExtra(DvrSchedulesActivity.KEY_SCHEDULES_TYPE, - DvrSchedulesActivity.TYPE_FULL_SCHEDULE); - if (focusedScheduledRecording != null) { - intent.putExtra(DvrSchedulesFragment.SCHEDULES_KEY_SCHEDULED_RECORDING, - focusedScheduledRecording); - } - context.startActivity(intent); - } - - /** - * Shows the schedules activity for series recording. - */ - public static void startSchedulesActivityForSeries(Context context, - SeriesRecording seriesRecording) { - Intent intent = new Intent(context, DvrSchedulesActivity.class); - intent.putExtra(DvrSchedulesActivity.KEY_SCHEDULES_TYPE, - DvrSchedulesActivity.TYPE_SERIES_SCHEDULE); - intent.putExtra(DvrSeriesSchedulesFragment.SERIES_SCHEDULES_KEY_SERIES_RECORDING, - seriesRecording); - context.startActivity(intent); - } - - /** - * Shows the series settings activity. - * - * @param programs list of programs which belong to the series. - */ - public static void startSeriesSettingsActivity(Context context, long seriesRecordingId, - @Nullable List<Program> programs, boolean removeEmptySeriesSchedule, - boolean isWindowTranslucent, boolean showViewScheduleOptionInDialog, - Program currentProgram) { - SeriesRecording series = TvApplication.getSingletons(context).getDvrDataManager() - .getSeriesRecording(seriesRecordingId); - if (series == null) { - return; - } - if (programs != null) { - startSeriesSettingsActivityInternal(context, seriesRecordingId, programs, - removeEmptySeriesSchedule, isWindowTranslucent, - showViewScheduleOptionInDialog, currentProgram); - } else { - EpisodicProgramLoadTask episodicProgramLoadTask = - new EpisodicProgramLoadTask(context, series) { - @Override - protected void onPostExecute(List<Program> loadedPrograms) { - sProgressDialog.dismiss(); - sProgressDialog = null; - startSeriesSettingsActivityInternal(context, seriesRecordingId, - loadedPrograms == null ? Collections.EMPTY_LIST : loadedPrograms, - removeEmptySeriesSchedule, isWindowTranslucent, - showViewScheduleOptionInDialog, currentProgram); - } - }.setLoadCurrentProgram(true) - .setLoadDisallowedProgram(true) - .setLoadScheduledEpisode(true) - .setIgnoreChannelOption(true); - sProgressDialog = ProgressDialog.show(context, null, context.getString( - R.string.dvr_series_progress_message_reading_programs), true, true, - new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialogInterface) { - episodicProgramLoadTask.cancel(true); - sProgressDialog = null; - } - }); - episodicProgramLoadTask.execute(); - } - } - - private static void startSeriesSettingsActivityInternal(Context context, long seriesRecordingId, - @NonNull List<Program> programs, boolean removeEmptySeriesSchedule, - boolean isWindowTranslucent, boolean showViewScheduleOptionInDialog, - Program currentProgram) { - SoftPreconditions.checkState(programs != null, - TAG, "Start series settings activity but programs is null"); - Intent intent = new Intent(context, DvrSeriesSettingsActivity.class); - intent.putExtra(DvrSeriesSettingsActivity.SERIES_RECORDING_ID, seriesRecordingId); - BigArguments.reset(); - BigArguments.setArgument(DvrSeriesSettingsActivity.PROGRAM_LIST, programs); - intent.putExtra(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING, - removeEmptySeriesSchedule); - intent.putExtra(DvrSeriesSettingsActivity.IS_WINDOW_TRANSLUCENT, isWindowTranslucent); - intent.putExtra(DvrSeriesSettingsActivity.SHOW_VIEW_SCHEDULE_OPTION_IN_DIALOG, - showViewScheduleOptionInDialog); - intent.putExtra(DvrSeriesSettingsActivity.CURRENT_PROGRAM, currentProgram); - context.startActivity(intent); - } - - /** - * Shows "series recording scheduled" dialog activity. - */ - public static void StartSeriesScheduledDialogActivity(Context context, - SeriesRecording seriesRecording, boolean showViewScheduleOptionInDialog, - List<Program> programs) { - if (seriesRecording == null) { - return; - } - Intent intent = new Intent(context, DvrSeriesScheduledDialogActivity.class); - intent.putExtra(DvrSeriesScheduledDialogActivity.SERIES_RECORDING_ID, - seriesRecording.getId()); - intent.putExtra(DvrSeriesScheduledDialogActivity.SHOW_VIEW_SCHEDULE_OPTION, - showViewScheduleOptionInDialog); - BigArguments.reset(); - BigArguments.setArgument(DvrSeriesScheduledFragment.SERIES_SCHEDULED_KEY_PROGRAMS, - programs); - context.startActivity(intent); - } - - /** - * Shows the details activity for the DVR items. The type of DVR items may be - * {@link ScheduledRecording}, {@link RecordedProgram}, or {@link SeriesRecording}. - */ - public static void startDetailsActivity(Activity activity, Object dvrItem, - @Nullable ImageView imageView, boolean hideViewSchedule) { - if (dvrItem == null) { - return; - } - Intent intent = new Intent(activity, DvrDetailsActivity.class); - long recordingId; - int viewType; - if (dvrItem instanceof ScheduledRecording) { - ScheduledRecording schedule = (ScheduledRecording) dvrItem; - recordingId = schedule.getId(); - if (schedule.getState() == ScheduledRecording.STATE_RECORDING_NOT_STARTED) { - viewType = DvrDetailsActivity.SCHEDULED_RECORDING_VIEW; - } else if (schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) { - viewType = DvrDetailsActivity.CURRENT_RECORDING_VIEW; - } else { - return; - } - } else if (dvrItem instanceof RecordedProgram) { - recordingId = ((RecordedProgram) dvrItem).getId(); - viewType = DvrDetailsActivity.RECORDED_PROGRAM_VIEW; - } else if (dvrItem instanceof SeriesRecording) { - recordingId = ((SeriesRecording) dvrItem).getId(); - viewType = DvrDetailsActivity.SERIES_RECORDING_VIEW; - } else { - return; - } - intent.putExtra(DvrDetailsActivity.RECORDING_ID, recordingId); - intent.putExtra(DvrDetailsActivity.DETAILS_VIEW_TYPE, viewType); - intent.putExtra(DvrDetailsActivity.HIDE_VIEW_SCHEDULE, hideViewSchedule); - Bundle bundle = null; - if (imageView != null) { - bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, imageView, - DvrDetailsActivity.SHARED_ELEMENT_NAME).toBundle(); - } - activity.startActivity(intent, bundle); - } - - /** - * Shows the cancel all dialog for series schedules list. - */ - public static void showCancelAllSeriesRecordingDialog(DvrSchedulesActivity activity, - SeriesRecording seriesRecording) { - DvrStopSeriesRecordingDialogFragment dvrStopSeriesRecordingDialogFragment = - new DvrStopSeriesRecordingDialogFragment(); - Bundle arguments = new Bundle(); - arguments.putParcelable(DvrStopSeriesRecordingFragment.KEY_SERIES_RECORDING, - seriesRecording); - dvrStopSeriesRecordingDialogFragment.setArguments(arguments); - dvrStopSeriesRecordingDialogFragment.show(activity.getFragmentManager(), - DvrStopSeriesRecordingDialogFragment.DIALOG_TAG); - } - - /** - * Shows the series deletion activity. - */ - public static void startSeriesDeletionActivity(Context context, long seriesRecordingId) { - Intent intent = new Intent(context, DvrSeriesDeletionActivity.class); - intent.putExtra(DvrSeriesDeletionActivity.SERIES_RECORDING_ID, seriesRecordingId); - context.startActivity(intent); - } - - public static void showAddScheduleToast(Context context, - String title, long startTimeMs, long endTimeMs) { - String msg = (startTimeMs > System.currentTimeMillis()) ? - context.getString(R.string.dvr_msg_program_scheduled, title) - : context.getString(R.string.dvr_msg_current_program_scheduled, title, - Utils.toTimeString(endTimeMs, false)); - Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); - } -}
\ No newline at end of file diff --git a/src/com/android/tv/dvr/ui/FadeBackground.java b/src/com/android/tv/dvr/ui/FadeBackground.java deleted file mode 100644 index 4f06ebcf..00000000 --- a/src/com/android/tv/dvr/ui/FadeBackground.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.animation.Animator; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.transition.Transition; -import android.transition.TransitionValues; -import android.transition.Visibility; -import android.util.AttributeSet; -import android.view.ViewGroup; - -import com.android.tv.R; - -/** - * This transition fades in/out of the background of the view by changing the background color. - */ -public class FadeBackground extends Transition { - private final int mMode; - - public FadeBackground(Context context, AttributeSet attrs) { - super(context, attrs); - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FadeBackground); - mMode = a.getInt(R.styleable.FadeBackground_fadingMode, Visibility.MODE_IN); - a.recycle(); - } - - @Override - public void captureStartValues(TransitionValues transitionValues) { } - - @Override - public void captureEndValues(TransitionValues transitionValues) { } - - @Override - public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, - TransitionValues endValues) { - if (startValues == null || endValues == null) { - return null; - } - Drawable background = endValues.view.getBackground(); - if (background instanceof ColorDrawable) { - int color = ((ColorDrawable) background).getColor(); - int transparentColor = Color.argb(0, Color.red(color), Color.green(color), - Color.blue(color)); - return mMode == Visibility.MODE_OUT - ? ObjectAnimator.ofArgb(background, "color", transparentColor) - : ObjectAnimator.ofArgb(background, "color", transparentColor, color); - } - return null; - } -} diff --git a/src/com/android/tv/dvr/ui/browse/FullScheduleCardHolder.java b/src/com/android/tv/dvr/ui/FullScheduleCardHolder.java index 311137a9..d4d4d8ab 100644 --- a/src/com/android/tv/dvr/ui/browse/FullScheduleCardHolder.java +++ b/src/com/android/tv/dvr/ui/FullScheduleCardHolder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; /** * Special object for schedule preview; diff --git a/src/com/android/tv/dvr/ui/browse/FullSchedulesCardPresenter.java b/src/com/android/tv/dvr/ui/FullSchedulesCardPresenter.java index 6d4763d4..7dd85f45 100644 --- a/src/com/android/tv/dvr/ui/browse/FullSchedulesCardPresenter.java +++ b/src/com/android/tv/dvr/ui/FullSchedulesCardPresenter.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.content.Context; -import android.graphics.drawable.Drawable; +import android.support.v17.leanback.widget.Presenter; import android.view.View; import android.view.ViewGroup; import com.android.tv.R; import com.android.tv.TvApplication; -import com.android.tv.dvr.data.ScheduledRecording; -import com.android.tv.dvr.ui.DvrUiHelper; +import com.android.tv.dvr.DvrUiHelper; +import com.android.tv.dvr.ScheduledRecording; import com.android.tv.util.Utils; import java.util.Collections; @@ -33,31 +33,23 @@ import java.util.List; /** * Presents a {@link ScheduledRecording} in the {@link DvrBrowseFragment}. */ -class FullSchedulesCardPresenter extends DvrItemPresenter { - private Context mContext; - private final Drawable mIconDrawable; - private final String mCardTitleText; - - public FullSchedulesCardPresenter(Context context) { - mContext = context; - mIconDrawable = mContext.getDrawable(R.drawable.dvr_full_schedule); - mCardTitleText = mContext.getString(R.string.dvr_full_schedule_card_view_title); - } - +public class FullSchedulesCardPresenter extends Presenter { @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { Context context = parent.getContext(); RecordingCardView view = new RecordingCardView(context); - return new ViewHolder(view); + return new ScheduledRecordingViewHolder(view); } @Override - public void onBindViewHolder(ViewHolder vh, Object o) { - final RecordingCardView cardView = (RecordingCardView) vh.view; + public void onBindViewHolder(ViewHolder baseHolder, Object o) { + final ScheduledRecordingViewHolder viewHolder = (ScheduledRecordingViewHolder) baseHolder; + final RecordingCardView cardView = (RecordingCardView) viewHolder.view; + final Context context = viewHolder.view.getContext(); - cardView.setImage(mIconDrawable); - cardView.setTitle(mCardTitleText); - List<ScheduledRecording> scheduledRecordings = TvApplication.getSingletons(mContext) + cardView.setImage(context.getDrawable(R.drawable.dvr_full_schedule)); + cardView.setTitle(context.getString(R.string.dvr_full_schedule_card_view_title)); + List<ScheduledRecording> scheduledRecordings = TvApplication.getSingletons(context) .getDvrDataManager().getAvailableScheduledRecordings(); int fullDays = 0; if (!scheduledRecordings.isEmpty()) { @@ -65,24 +57,28 @@ class FullSchedulesCardPresenter extends DvrItemPresenter { Collections.max(scheduledRecordings, ScheduledRecording.START_TIME_COMPARATOR) .getStartTimeMs()) + 1; } - cardView.setContent(mContext.getResources().getQuantityString( + cardView.setContent(context.getResources().getQuantityString( R.plurals.dvr_full_schedule_card_view_content, fullDays, fullDays), null); - super.onBindViewHolder(vh, o); + + View.OnClickListener clickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + DvrUiHelper.startSchedulesActivity(context, null); + } + }; + baseHolder.view.setOnClickListener(clickListener); } @Override - public void onUnbindViewHolder(ViewHolder vh) { - ((RecordingCardView) vh.view).reset(); - super.onUnbindViewHolder(vh); + public void onUnbindViewHolder(ViewHolder baseHolder) { + ScheduledRecordingViewHolder viewHolder = (ScheduledRecordingViewHolder) baseHolder; + final RecordingCardView cardView = (RecordingCardView) viewHolder.view; + cardView.reset(); } - @Override - protected View.OnClickListener onCreateOnClickListener() { - return new View.OnClickListener() { - @Override - public void onClick(View view) { - DvrUiHelper.startSchedulesActivity(mContext, null); - } - }; + private static final class ScheduledRecordingViewHolder extends ViewHolder { + ScheduledRecordingViewHolder(RecordingCardView view) { + super(view); + } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/HalfSizedDialogFragment.java b/src/com/android/tv/dvr/ui/HalfSizedDialogFragment.java new file mode 100644 index 00000000..d320816e --- /dev/null +++ b/src/com/android/tv/dvr/ui/HalfSizedDialogFragment.java @@ -0,0 +1,117 @@ +/* + * 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.content.DialogInterface; +import android.os.Bundle; +import android.os.Handler; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.tv.R; +import com.android.tv.dialog.SafeDismissDialogFragment; + +import java.util.concurrent.TimeUnit; + +public class HalfSizedDialogFragment extends SafeDismissDialogFragment { + public static final String DIALOG_TAG = HalfSizedDialogFragment.class.getSimpleName(); + public static final String TRACKER_LABEL = "Half sized dialog"; + + private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30); + + private OnActionClickListener mOnActionClickListener; + + private Handler mHandler = new Handler(); + private Runnable mAutoDismisser = new Runnable() { + @Override + public void run() { + dismiss(); + } + }; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.halfsized_dialog, container, false); + } + + @Override + public void onStart() { + super.onStart(); + getDialog().setOnKeyListener(new DialogInterface.OnKeyListener() { + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent keyEvent) { + mHandler.removeCallbacks(mAutoDismisser); + mHandler.postDelayed(mAutoDismisser, AUTO_DISMISS_TIME_THRESHOLD_MS); + return false; + } + }); + mHandler.postDelayed(mAutoDismisser, AUTO_DISMISS_TIME_THRESHOLD_MS); + } + + @Override + public void onPause() { + super.onPause(); + if (mOnActionClickListener != null) { + // Dismisses the dialog to prevent the callback being forgotten during + // fragment re-creating. + dismiss(); + } + } + + @Override + public void onStop() { + super.onStop(); + mHandler.removeCallbacks(mAutoDismisser); + } + + @Override + public int getTheme() { + return R.style.Theme_TV_dialog_HalfSizedDialog; + } + + @Override + public String getTrackerLabel() { + return TRACKER_LABEL; + } + + /** + * Sets {@link OnActionClickListener} for the dialog fragment. If listener is set, the dialog + * will be automatically closed when it's paused to prevent the fragment being re-created by + * the framework, which will result the listener being forgotten. + */ + public void setOnActionClickListener(OnActionClickListener listener) { + mOnActionClickListener = listener; + } + + /** + * Returns {@link OnActionClickListener} for sub-classes or any inner fragments. + */ + protected OnActionClickListener getOnActionClickListener() { + return mOnActionClickListener; + } + + /** + * An interface to provide callbacks for half-sized dialogs. Subclasses or inner fragments + * should invoke {@link OnActionClickListener#onActionClick(long)} and provide the identifier + * of the action user clicked. + */ + public interface OnActionClickListener { + void onActionClick(long actionId); + } +}
\ No newline at end of file diff --git a/src/com/android/tv/dvr/ui/DvrPrioritySettingsFragment.java b/src/com/android/tv/dvr/ui/PrioritySettingsFragment.java index 562898a3..158bd824 100644 --- a/src/com/android/tv/dvr/ui/DvrPrioritySettingsFragment.java +++ b/src/com/android/tv/dvr/ui/PrioritySettingsFragment.java @@ -33,7 +33,7 @@ import com.android.tv.TvApplication; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.DvrScheduleManager; -import com.android.tv.dvr.data.SeriesRecording; +import com.android.tv.dvr.SeriesRecording; import java.util.ArrayList; import java.util.List; @@ -41,7 +41,7 @@ import java.util.List; /** * Fragment for DVR series recording settings. */ -public class DvrPrioritySettingsFragment extends GuidedStepFragment { +public class PrioritySettingsFragment extends GuidedStepFragment { /** * Name of series recording id starting the fragment. * Type: Long @@ -162,6 +162,7 @@ public class DvrPrioritySettingsFragment extends GuidedStepFragment { return; } if (action.getId() < 0) { + int selectedPosition = mSeriesRecordings.indexOf(mSelectedRecording); mSelectedRecording = null; for (int i = 0; i < mSeriesRecordings.size(); ++i) { updateItem(i); @@ -247,4 +248,4 @@ public class DvrPrioritySettingsFragment extends GuidedStepFragment { titleView.setTypeface(titleView.getTypeface(), Typeface.NORMAL); } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java b/src/com/android/tv/dvr/ui/RecordedProgramDetailsFragment.java index fe9b9de5..e698b8a2 100644 --- a/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java +++ b/src/com/android/tv/dvr/ui/RecordedProgramDetailsFragment.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.content.res.Resources; import android.media.tv.TvInputManager; @@ -30,10 +30,10 @@ import com.android.tv.data.Channel; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.DvrWatchedPositionManager; -import com.android.tv.dvr.data.RecordedProgram; +import com.android.tv.dvr.RecordedProgram; /** - * {@link android.support.v17.leanback.app.DetailsFragment} for recorded program in DVR. + * {@link DetailsFragment} for recorded program in DVR. */ public class RecordedProgramDetailsFragment extends DvrDetailsFragment implements DvrDataManager.RecordedProgramListener { diff --git a/src/com/android/tv/dvr/ui/browse/RecordedProgramPresenter.java b/src/com/android/tv/dvr/ui/RecordedProgramPresenter.java index ee978797..1bf34310 100644 --- a/src/com/android/tv/dvr/ui/browse/RecordedProgramPresenter.java +++ b/src/com/android/tv/dvr/ui/RecordedProgramPresenter.java @@ -14,26 +14,31 @@ * limitations under the License. */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; +import android.app.Activity; import android.content.Context; import android.media.tv.TvContract; import android.media.tv.TvInputManager; +import android.net.Uri; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.style.TextAppearanceSpan; +import android.view.View; import android.view.ViewGroup; import com.android.tv.R; import com.android.tv.TvApplication; +import com.android.tv.dvr.RecordedProgram; import com.android.tv.data.Channel; import com.android.tv.data.ChannelDataManager; import com.android.tv.dvr.DvrWatchedPositionManager; import com.android.tv.dvr.DvrWatchedPositionManager.WatchedPositionChangedListener; -import com.android.tv.dvr.data.RecordedProgram; import com.android.tv.util.Utils; +import java.util.concurrent.TimeUnit; + /** * Presents a {@link RecordedProgram} in the {@link DvrBrowseFragment}. */ @@ -45,7 +50,6 @@ public class RecordedProgramPresenter extends DvrItemPresenter { private String mYesterdayString; private final int mProgressBarColor; private final boolean mShowEpisodeTitle; - private final boolean mExpandTitleWhenFocused; private static final class RecordedProgramViewHolder extends ViewHolder implements WatchedPositionChangedListener { @@ -75,27 +79,25 @@ public class RecordedProgramPresenter extends DvrItemPresenter { } } - public RecordedProgramPresenter(Context context, boolean showEpisodeTitle, - boolean expandTitleWhenFocused) { + public RecordedProgramPresenter(Context context, boolean showEpisodeTitle) { mContext = context; - mChannelDataManager = TvApplication.getSingletons(mContext).getChannelDataManager(); - mTodayString = mContext.getString(R.string.dvr_date_today); - mYesterdayString = mContext.getString(R.string.dvr_date_yesterday); + mChannelDataManager = TvApplication.getSingletons(context).getChannelDataManager(); + mTodayString = context.getString(R.string.dvr_date_today); + mYesterdayString = context.getString(R.string.dvr_date_yesterday); mDvrWatchedPositionManager = - TvApplication.getSingletons(mContext).getDvrWatchedPositionManager(); - mProgressBarColor = mContext.getResources() + TvApplication.getSingletons(context).getDvrWatchedPositionManager(); + mProgressBarColor = context.getResources() .getColor(R.color.play_controls_progress_bar_watched); mShowEpisodeTitle = showEpisodeTitle; - mExpandTitleWhenFocused = expandTitleWhenFocused; } public RecordedProgramPresenter(Context context) { - this(context, false, false); + this(context, false); } @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { - RecordingCardView view = new RecordingCardView(mContext, mExpandTitleWhenFocused); + RecordingCardView view = new RecordingCardView(mContext); return new RecordedProgramViewHolder(view, mProgressBarColor); } @@ -130,7 +132,8 @@ public class RecordedProgramPresenter extends DvrItemPresenter { isChannelLogo = true; } cardView.setImageUri(imageUri, isChannelLogo); - int durationMinutes = Math.max(1, Utils.getRoundOffMinsFromMs(program.getDurationMillis())); + int durationMinutes = + Math.max(1, (int) TimeUnit.MILLISECONDS.toMinutes(program.getDurationMillis())); String durationString = getContext().getResources().getQuantityString( R.plurals.dvr_program_duration, durationMinutes, durationMinutes); cardView.setContent(getDescription(program), durationString); diff --git a/src/com/android/tv/dvr/ui/browse/RecordingCardView.java b/src/com/android/tv/dvr/ui/RecordingCardView.java index 7b0a8cb9..51c3b03b 100644 --- a/src/com/android/tv/dvr/ui/browse/RecordingCardView.java +++ b/src/com/android/tv/dvr/ui/RecordingCardView.java @@ -14,69 +14,51 @@ * limitations under the License. */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; -import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.support.annotation.Nullable; import android.support.v17.leanback.widget.BaseCardView; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import com.android.tv.R; -import com.android.tv.dvr.data.RecordedProgram; -import com.android.tv.ui.ViewUtils; +import com.android.tv.dvr.RecordedProgram; import com.android.tv.util.ImageLoader; /** - * A CardView for displaying info about a {@link com.android.tv.dvr.data.ScheduledRecording} - * or {@link RecordedProgram} or {@link com.android.tv.dvr.data.SeriesRecording}. + * A CardView for displaying info about a {@link com.android.tv.dvr.ScheduledRecording} or + * {@link RecordedProgram} or + * {@link com.android.tv.dvr.SeriesRecording}. */ -public class RecordingCardView extends BaseCardView { - // This value should be the same with - // android.support.v17.leanback.widget.FocusHighlightHelper.BrowseItemFocusHighlight.DURATION_MS - private final static int ANIMATION_DURATION = 150; +class RecordingCardView extends BaseCardView { private final ImageView mImageView; private final int mImageWidth; private final int mImageHeight; private String mImageUri; + private final TextView mTitleView; private final TextView mMajorContentView; private final TextView mMinorContentView; private final ProgressBar mProgressBar; private final View mAffiliatedIconContainer; private final ImageView mAffiliatedIcon; private final Drawable mDefaultImage; - private final FrameLayout mTitleArea; - private final TextView mFoldedTitleView; - private final TextView mExpandedTitleView; - private final ValueAnimator mExpandTitleAnimator; - private final int mFoldedTitleHeight; - private final int mExpandedTitleHeight; - private final boolean mExpandTitleWhenFocused; - private boolean mExpanded; - public RecordingCardView(Context context) { - this(context, false); + RecordingCardView(Context context) { + this(context, + context.getResources().getDimensionPixelSize(R.dimen.dvr_card_image_layout_width), + context.getResources().getDimensionPixelSize(R.dimen.dvr_card_image_layout_height)); } - public RecordingCardView(Context context, boolean expandTitleWhenFocused) { - this(context, context.getResources().getDimensionPixelSize( - R.dimen.dvr_library_card_image_layout_width), context.getResources() - .getDimensionPixelSize(R.dimen.dvr_library_card_image_layout_height), - expandTitleWhenFocused); - } - - public RecordingCardView(Context context, int imageWidth, int imageHeight, - boolean expandTitleWhenFocused) { + RecordingCardView(Context context, int imageWidth, int imageHeight) { super(context); //TODO(dvr): move these to the layout XML. setCardType(BaseCardView.CARD_TYPE_INFO_UNDER_WITH_EXTRA); @@ -93,73 +75,13 @@ public class RecordingCardView extends BaseCardView { mProgressBar = (ProgressBar) findViewById(R.id.recording_progress); mAffiliatedIconContainer = findViewById(R.id.affiliated_icon_container); mAffiliatedIcon = (ImageView) findViewById(R.id.affiliated_icon); + mTitleView = (TextView) findViewById(R.id.title); mMajorContentView = (TextView) findViewById(R.id.content_major); mMinorContentView = (TextView) findViewById(R.id.content_minor); - mTitleArea = (FrameLayout) findViewById(R.id.title_area); - mFoldedTitleView = (TextView) findViewById(R.id.title_one_line); - mExpandedTitleView = (TextView) findViewById(R.id.title_two_lines); - mFoldedTitleHeight = getResources() - .getDimensionPixelSize(R.dimen.dvr_library_card_folded_title_height); - mExpandedTitleHeight = getResources() - .getDimensionPixelSize(R.dimen.dvr_library_card_expanded_title_height); - mExpandTitleAnimator = ValueAnimator.ofFloat(0.0f, 1.0f).setDuration(ANIMATION_DURATION); - mExpandTitleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - float value = (Float) valueAnimator.getAnimatedValue(); - mExpandedTitleView.setAlpha(value); - mFoldedTitleView.setAlpha(1.0f - value); - ViewUtils.setLayoutHeight(mTitleArea, (int) (mFoldedTitleHeight - + (mExpandedTitleHeight - mFoldedTitleHeight) * value)); - } - }); - mExpandTitleWhenFocused = expandTitleWhenFocused; - } - - @Override - protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { - super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); - if (mExpandTitleWhenFocused) { - if (gainFocus) { - expandTitle(true, true); - } else { - expandTitle(false, true); - } - } - } - - /** - * Expands/folds the title area to show program title with two/one lines. - * - * @param expand {@code true} to expand the title area, or {@code false} to fold it. - * @param withAnimation {@code true} to expand/fold with animation. - */ - public void expandTitle(boolean expand, boolean withAnimation) { - if (expand != mExpanded && mFoldedTitleView.getLayout().getEllipsisCount(0) > 0) { - if (withAnimation) { - if (expand) { - mExpandTitleAnimator.start(); - } else { - mExpandTitleAnimator.reverse(); - } - } else { - if (expand) { - mFoldedTitleView.setAlpha(0.0f); - mExpandedTitleView.setAlpha(1.0f); - ViewUtils.setLayoutHeight(mTitleArea, mExpandedTitleHeight); - } else { - mFoldedTitleView.setAlpha(1.0f); - mExpandedTitleView.setAlpha(0.0f); - ViewUtils.setLayoutHeight(mTitleArea, mFoldedTitleHeight); - } - } - mExpanded = expand; - } } void setTitle(CharSequence title) { - mFoldedTitleView.setText(title); - mExpandedTitleView.setText(title); + mTitleView.setText(title); } void setContent(CharSequence majorContent, CharSequence minorContent) { @@ -256,9 +178,8 @@ public class RecordingCardView extends BaseCardView { } public void reset() { - mFoldedTitleView.setText(null); - mExpandedTitleView.setText(null); + mTitleView.setText(null); setContent(null, null); mImageView.setImageDrawable(mDefaultImage); } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/RecordingDetailsFragment.java index a877e05f..4e19ec3f 100644 --- a/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java +++ b/src/com/android/tv/dvr/ui/RecordingDetailsFragment.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.os.Bundle; import android.support.v17.leanback.app.DetailsFragment; @@ -26,7 +26,7 @@ import android.text.style.TextAppearanceSpan; import com.android.tv.R; import com.android.tv.TvApplication; import com.android.tv.data.Channel; -import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.ScheduledRecording; /** * {@link DetailsFragment} for recordings in DVR. diff --git a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/ScheduledRecordingDetailsFragment.java index eb0f4f0d..60816bb5 100644 --- a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java +++ b/src/com/android/tv/dvr/ui/ScheduledRecordingDetailsFragment.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.content.res.Resources; import android.os.Bundle; @@ -26,7 +26,7 @@ import android.text.TextUtils; import com.android.tv.R; import com.android.tv.TvApplication; import com.android.tv.dvr.DvrManager; -import com.android.tv.dvr.ui.DvrUiHelper; +import com.android.tv.dvr.DvrUiHelper; /** * {@link RecordingDetailsFragment} for scheduled recording in DVR. diff --git a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java b/src/com/android/tv/dvr/ui/ScheduledRecordingPresenter.java index efc8785a..5f447f13 100644 --- a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java +++ b/src/com/android/tv/dvr/ui/ScheduledRecordingPresenter.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; +import android.app.Activity; import android.content.Context; import android.media.tv.TvContract; import android.os.Handler; @@ -31,7 +32,7 @@ import com.android.tv.TvApplication; import com.android.tv.data.Channel; import com.android.tv.data.ChannelDataManager; import com.android.tv.dvr.DvrManager; -import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.ScheduledRecording; import com.android.tv.util.Utils; import java.util.concurrent.TimeUnit; @@ -39,7 +40,7 @@ import java.util.concurrent.TimeUnit; /** * Presents a {@link ScheduledRecording} in the {@link DvrBrowseFragment}. */ -class ScheduledRecordingPresenter extends DvrItemPresenter { +public class ScheduledRecordingPresenter extends DvrItemPresenter { private static final long PROGRESS_UPDATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(5); private final ChannelDataManager mChannelDataManager; @@ -91,17 +92,18 @@ class ScheduledRecordingPresenter extends DvrItemPresenter { } public ScheduledRecordingPresenter(Context context) { - mContext = context; - ApplicationSingletons singletons = TvApplication.getSingletons(mContext); + ApplicationSingletons singletons = TvApplication.getSingletons(context); mChannelDataManager = singletons.getChannelDataManager(); mDvrManager = singletons.getDvrManager(); - mProgressBarColor = mContext.getResources() + mContext = context; + mProgressBarColor = context.getResources() .getColor(R.color.play_controls_recording_icon_color_on_focus); } @Override public ViewHolder onCreateViewHolder(ViewGroup parent) { - RecordingCardView view = new RecordingCardView(mContext); + Context context = parent.getContext(); + RecordingCardView view = new RecordingCardView(context); return new ScheduledRecordingViewHolder(view, mProgressBarColor); } @@ -142,8 +144,9 @@ class ScheduledRecordingPresenter extends DvrItemPresenter { public void onUnbindViewHolder(ViewHolder baseHolder) { ScheduledRecordingViewHolder viewHolder = (ScheduledRecordingViewHolder) baseHolder; viewHolder.stopUpdateProgressBar(); + final RecordingCardView cardView = (RecordingCardView) viewHolder.view; viewHolder.mScheduledRecording = null; - ((RecordingCardView) viewHolder.view).reset(); + cardView.reset(); super.onUnbindViewHolder(viewHolder); } @@ -171,4 +174,4 @@ class ScheduledRecordingPresenter extends DvrItemPresenter { cardView.setTitle(title); cardView.setImageUri(imageUri, isChannelLogo); } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java b/src/com/android/tv/dvr/ui/SeriesDeletionFragment.java index 8bf8560f..36e3cfc1 100644 --- a/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java +++ b/src/com/android/tv/dvr/ui/SeriesDeletionFragment.java @@ -33,10 +33,9 @@ import com.android.tv.common.SoftPreconditions; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.DvrWatchedPositionManager; -import com.android.tv.dvr.data.RecordedProgram; -import com.android.tv.dvr.data.SeriesRecording; +import com.android.tv.dvr.RecordedProgram; +import com.android.tv.dvr.SeriesRecording; import com.android.tv.ui.GuidedActionsStylistWithDivider; -import com.android.tv.util.Utils; import java.util.ArrayList; import java.util.Collections; @@ -48,7 +47,7 @@ import java.util.concurrent.TimeUnit; /** * Fragment for DVR series recording settings. */ -public class DvrSeriesDeletionFragment extends GuidedStepFragment { +public class SeriesDeletionFragment extends GuidedStepFragment { private static final long WATCHED_TIME_UNIT_THRESHOLD = TimeUnit.MINUTES.toMillis(2); // Since recordings' IDs are used as its check actions' IDs, which are random positive numbers, @@ -219,8 +218,8 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment { private String getWatchedString(long watchedPositionMs, long durationMs) { if (durationMs > WATCHED_TIME_UNIT_THRESHOLD) { return getResources().getString(R.string.dvr_series_watched_info_minutes, - Math.max(1, Utils.getRoundOffMinsFromMs(watchedPositionMs)), - Utils.getRoundOffMinsFromMs(durationMs)); + Math.max(1, TimeUnit.MILLISECONDS.toMinutes(watchedPositionMs)), + TimeUnit.MILLISECONDS.toMinutes(durationMs)); } else { return getResources().getString(R.string.dvr_series_watched_info_seconds, Math.max(1, TimeUnit.MILLISECONDS.toSeconds(watchedPositionMs)), diff --git a/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/SeriesRecordingDetailsFragment.java index f7b60b50..e9e391d4 100644 --- a/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java +++ b/src/com/android/tv/dvr/ui/SeriesRecordingDetailsFragment.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; import android.content.res.Resources; import android.graphics.drawable.Drawable; @@ -28,6 +28,7 @@ import android.support.v17.leanback.widget.DetailsOverviewRow; import android.support.v17.leanback.widget.DetailsOverviewRowPresenter; import android.support.v17.leanback.widget.HeaderItem; import android.support.v17.leanback.widget.ListRow; +import android.support.v17.leanback.widget.ListRowPresenter; import android.support.v17.leanback.widget.OnActionClickedListener; import android.support.v17.leanback.widget.PresenterSelector; import android.support.v17.leanback.widget.SparseArrayObjectAdapter; @@ -38,11 +39,12 @@ import com.android.tv.TvApplication; import com.android.tv.data.BaseProgram; import com.android.tv.data.Channel; import com.android.tv.dvr.DvrDataManager; +import com.android.tv.dvr.DvrManager; +import com.android.tv.dvr.DvrUiHelper; import com.android.tv.dvr.DvrWatchedPositionManager; -import com.android.tv.dvr.data.RecordedProgram; -import com.android.tv.dvr.data.SeriesRecording; -import com.android.tv.dvr.ui.DvrUiHelper; -import com.android.tv.dvr.ui.SortedArrayAdapter; +import com.android.tv.dvr.RecordedProgram; +import com.android.tv.dvr.ScheduledRecording; +import com.android.tv.dvr.SeriesRecording; import java.util.Collections; import java.util.Comparator; @@ -83,7 +85,7 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement mWatchLabel = getString(R.string.dvr_detail_watch); mResumeLabel = getString(R.string.dvr_detail_series_resume); mWatchDrawable = getResources().getDrawable(R.drawable.lb_ic_play, null); - mRecordedProgramPresenter = new RecordedProgramPresenter(getContext(), true, true); + mRecordedProgramPresenter = new RecordedProgramPresenter(getContext(), true); super.onCreate(savedInstanceState); } @@ -156,7 +158,7 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement DetailsOverviewRowPresenter rowPresenter) { ClassPresenterSelector presenterSelector = new ClassPresenterSelector(); presenterSelector.addClassPresenter(DetailsOverviewRow.class, rowPresenter); - presenterSelector.addClassPresenter(ListRow.class, new DvrListRowPresenter(getContext())); + presenterSelector.addClassPresenter(ListRow.class, new ListRowPresenter()); return presenterSelector; } @@ -201,7 +203,10 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement mDvrDataManager.removeSeriesRecordingListener(this); mDvrDataManager.removeRecordedProgramListener(this); if (mSeries != null) { - mDvrDataManager.checkAndRemoveEmptySeriesRecording(mSeries.getId()); + DvrManager dvrManager = TvApplication.getSingletons(getActivity()).getDvrManager(); + if (dvrManager.canRemoveSeriesRecording(mSeries.getId())) { + dvrManager.removeSeriesRecording(mSeries.getId()); + } } mRecordedProgramPresenter.unbindAllViewHolders(); } @@ -260,6 +265,7 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) { for (SeriesRecording series : seriesRecordings) { if (series.getId() == mSeries.getId()) { + mSeries = null; getActivity().finish(); return; } @@ -366,4 +372,4 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment implement return program.getId(); } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/browse/SeriesRecordingPresenter.java b/src/com/android/tv/dvr/ui/SeriesRecordingPresenter.java index af6ecc19..c2c0f596 100644 --- a/src/com/android/tv/dvr/ui/browse/SeriesRecordingPresenter.java +++ b/src/com/android/tv/dvr/ui/SeriesRecordingPresenter.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.android.tv.dvr.ui.browse; +package com.android.tv.dvr.ui; +import android.app.Activity; import android.content.Context; import android.media.tv.TvContract; import android.media.tv.TvInputManager; @@ -33,16 +34,16 @@ import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.DvrWatchedPositionManager; import com.android.tv.dvr.DvrWatchedPositionManager.WatchedPositionChangedListener; -import com.android.tv.dvr.data.RecordedProgram; -import com.android.tv.dvr.data.ScheduledRecording; -import com.android.tv.dvr.data.SeriesRecording; +import com.android.tv.dvr.RecordedProgram; +import com.android.tv.dvr.ScheduledRecording; +import com.android.tv.dvr.SeriesRecording; import java.util.List; /** * Presents a {@link SeriesRecording} in {@link DvrBrowseFragment}. */ -class SeriesRecordingPresenter extends DvrItemPresenter { +public class SeriesRecordingPresenter extends DvrItemPresenter { private final ChannelDataManager mChannelDataManager; private final DvrDataManager mDvrDataManager; private final DvrManager mDvrManager; diff --git a/src/com/android/tv/dvr/ui/DvrSeriesSettingsFragment.java b/src/com/android/tv/dvr/ui/SeriesSettingsFragment.java index f28382da..6c05c9c6 100644 --- a/src/com/android/tv/dvr/ui/DvrSeriesSettingsFragment.java +++ b/src/com/android/tv/dvr/ui/SeriesSettingsFragment.java @@ -17,13 +17,19 @@ 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; @@ -32,14 +38,14 @@ 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.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 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.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -47,7 +53,7 @@ import java.util.Set; /** * Fragment for DVR series recording settings. */ -public class DvrSeriesSettingsFragment extends GuidedStepFragment +public class SeriesSettingsFragment extends GuidedStepFragment implements DvrDataManager.SeriesRecordingListener { private static final String TAG = "SeriesSettingsFragment"; private static final boolean DEBUG = false; @@ -60,13 +66,15 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment 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<Channel> mChannelComparator; private long mSelectedChannelId; private int mBackStackCount; private boolean mShowViewScheduleOptionInDialog; - private Program mCurrentProgram; private String mFragmentTitle; private String mProrityActionTitle; @@ -76,7 +84,7 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment private String mChannelsActionAllText; private LongSparseArray<Channel> mId2Channel = new LongSparseArray<>(); private List<Channel> mChannels = new ArrayList<>(); - private List<Program> mPrograms; + private EpisodicProgramLoadTask mEpisodicProgramLoadTask; private GuidedAction mPriorityGuidedAction; private GuidedAction mChannelsGuidedAction; @@ -92,24 +100,22 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment getActivity().finish(); return; } + mDvrManager = TvApplication.getSingletons(context).getDvrManager(); 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 = - TvApplication.getSingletons(context).getChannelDataManager(); - for (Program program : mPrograms) { - long channelId = program.getChannelId(); - if (channelIds.add(channelId)) { - Channel channel = channelDataManager.getChannel(channelId); + 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); @@ -119,14 +125,16 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment mChannelOption = mSeriesRecording.getChannelOption(); mSelectedChannelId = Channel.INVALID_ID; if (mChannelOption == SeriesRecording.OPTION_CHANNEL_ONE) { - Channel channel = channelDataManager.getChannel(mSeriesRecording.getChannelId()); + Channel channel = mChannelDataManager.getChannel(mSeriesRecording.getChannelId()); if (channel != null) { mSelectedChannelId = channel.getId(); } else { mChannelOption = SeriesRecording.OPTION_CHANNEL_ALL; } } - mChannels.sort(Channel.CHANNEL_NUMBER_COMPARATOR); + 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); @@ -136,23 +144,23 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment } @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); + if (mEpisodicProgramLoadTask != null) { + mEpisodicProgramLoadTask.cancel(true); + mEpisodicProgramLoadTask = null; + } } @Override public void onDestroy() { - if (getFragmentManager().getBackStackEntryCount() == mBackStackCount && getArguments() - .getBoolean(DvrSeriesSettingsActivity.REMOVE_EMPTY_SERIES_RECORDING)) { - mDvrDataManager.checkAndRemoveEmptySeriesRecording(mSeriesRecordingId); + 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(); } @@ -170,6 +178,7 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment .id(ACTION_ID_PRIORITY) .title(mProrityActionTitle) .build(); + updatePriorityGuidedAction(false); actions.add(mPriorityGuidedAction); mChannelsGuidedAction = new GuidedAction.Builder(getActivity()) @@ -195,6 +204,10 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment 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 @@ -205,14 +218,28 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment if (mSelectedChannelId != Channel.INVALID_ID) { builder.setChannelId(mSelectedChannelId); } - DvrManager dvrManager = TvApplication.getSingletons(getContext()).getDvrManager(); - dvrManager.updateSeriesRecording(builder.build()); - if (mCurrentProgram != null && (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL - || mSelectedChannelId == mCurrentProgram.getChannelId())) { - dvrManager.addSchedule(mCurrentProgram); - } - updateSchedulesToSeries(); - showConfirmDialog(); + 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(); } @@ -220,9 +247,9 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment finishGuidedStepFragments(); } else if (actionId == ACTION_ID_PRIORITY) { FragmentManager fragmentManager = getFragmentManager(); - DvrPrioritySettingsFragment fragment = new DvrPrioritySettingsFragment(); + PrioritySettingsFragment fragment = new PrioritySettingsFragment(); Bundle args = new Bundle(); - args.putLong(DvrPrioritySettingsFragment.COME_FROM_SERIES_RECORDING_ID, + args.putLong(PrioritySettingsFragment.COME_FROM_SERIES_RECORDING_ID, mSeriesRecording.getId()); fragment.setArguments(args); GuidedStepFragment.add(fragmentManager, fragment, R.id.dvr_settings_view_frame); @@ -254,7 +281,7 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment private void updateChannelsGuidedAction(boolean notifyActionChanged) { if (mChannelOption == SeriesRecording.OPTION_CHANNEL_ALL) { mChannelsGuidedAction.setDescription(mChannelsActionAllText); - } else if (mId2Channel.get(mSelectedChannelId) != null){ + } else { mChannelsGuidedAction.setDescription(mId2Channel.get(mSelectedChannelId) .getDisplayText()); } @@ -263,7 +290,7 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment } } - private void updatePriorityGuidedAction() { + private void updatePriorityGuidedAction(boolean notifyActionChanged) { int totalSeriesCount = 0; int priorityOrder = 0; for (SeriesRecording seriesRecording : mDvrDataManager.getSeriesRecordings()) { @@ -285,38 +312,49 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment mPriorityGuidedAction.setDescription(getString( R.string.dvr_series_settings_priority_rank, priorityOrder + 1)); } - notifyActionChanged(findActionPositionById(ACTION_ID_PRIORITY)); + if (notifyActionChanged) { + 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())); - } + private void collectChannelsInBackground() { + if (mEpisodicProgramLoadTask != null) { + mEpisodicProgramLoadTask.cancel(true); } - 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); + mEpisodicProgramLoadTask = new EpisodicProgramLoadTask(getContext(), mSeriesRecording) { + @Override + protected void onPostExecute(List<Program> programs) { + mEpisodicProgramLoadTask = null; + Set<Long> 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"); } - } - if (recordingCandidates.isEmpty()) { - return; - } - List<Program> programsToSchedule = SeriesRecordingScheduler.pickOneProgramPerEpisode( - mDvrDataManager, Collections.singletonList(mSeriesRecording), recordingCandidates) - .get(mSeriesRecordingId); - if (!programsToSchedule.isEmpty()) { - TvApplication.getSingletons(getContext()).getDvrManager() - .addScheduleToSeriesRecording(mSeriesRecording, programsToSchedule); - } + }.setLoadCurrentProgram(true) + .setLoadDisallowedProgram(true) + .setLoadScheduledEpisode(true) + .setIgnoreChannelOption(true); + mEpisodicProgramLoadTask.execute(); } private List<GuidedAction> buildChannelSubAction() { @@ -335,8 +373,8 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment } private void showConfirmDialog() { - DvrUiHelper.StartSeriesScheduledDialogActivity(getContext(), mSeriesRecording, - mShowViewScheduleOptionInDialog, mPrograms); + DvrUiHelper.StartSeriesScheduledDialogActivity( + getContext(), mSeriesRecording, mShowViewScheduleOptionInDialog); finishGuidedStepFragments(); } @@ -344,23 +382,16 @@ public class DvrSeriesSettingsFragment extends GuidedStepFragment public void onSeriesRecordingAdded(SeriesRecording... seriesRecordings) { } @Override - public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) { - for (SeriesRecording series : seriesRecordings) { - if (series.getId() == mSeriesRecording.getId()) { - finishGuidedStepFragments(); - return; - } - } - } + public void onSeriesRecordingRemoved(SeriesRecording... seriesRecordings) { } @Override public void onSeriesRecordingChanged(SeriesRecording... seriesRecordings) { for (SeriesRecording seriesRecording : seriesRecordings) { if (seriesRecording.getId() == mSeriesRecordingId) { mSeriesRecording = seriesRecording; - updatePriorityGuidedAction(); + updatePriorityGuidedAction(true); return; } } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/SortedArrayAdapter.java b/src/com/android/tv/dvr/ui/SortedArrayAdapter.java index 8c0af9ed..393a5ff3 100644 --- a/src/com/android/tv/dvr/ui/SortedArrayAdapter.java +++ b/src/com/android/tv/dvr/ui/SortedArrayAdapter.java @@ -20,15 +20,11 @@ import android.support.annotation.VisibleForTesting; import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.PresenterSelector; -import com.android.tv.common.SoftPreconditions; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.HashSet; import java.util.List; -import java.util.Set; /** * Keeps a set of items sorted @@ -39,18 +35,16 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { private final Comparator<T> mComparator; private final int mMaxItemCount; private int mExtraItemCount; - private final Set<Long> mIds = new HashSet<>(); - public SortedArrayAdapter(PresenterSelector presenterSelector, Comparator<T> comparator) { + SortedArrayAdapter(PresenterSelector presenterSelector, Comparator<T> comparator) { this(presenterSelector, comparator, Integer.MAX_VALUE); } - public SortedArrayAdapter(PresenterSelector presenterSelector, Comparator<T> comparator, + SortedArrayAdapter(PresenterSelector presenterSelector, Comparator<T> comparator, int maxItemCount) { super(presenterSelector); mComparator = comparator; mMaxItemCount = maxItemCount; - setHasStableIds(true); } /** @@ -62,12 +56,7 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { final void setInitialItems(List<T> items) { List<T> itemsCopy = new ArrayList<>(items); Collections.sort(itemsCopy, mComparator); - for (T item : itemsCopy) { - add(item, true); - if (size() == mMaxItemCount) { - break; - } - } + addAll(0, itemsCopy.subList(0, Math.min(mMaxItemCount, itemsCopy.size()))); } /** @@ -93,9 +82,6 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { * the end to save search time. */ public final void add(T item, boolean insertToEnd) { - long newItemId = getId(item); - SoftPreconditions.checkState(!mIds.contains(newItemId)); - mIds.add(newItemId); int i; if (insertToEnd) { i = findInsertPosition(item); @@ -103,9 +89,8 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { i = findInsertPositionBinary(item); } super.add(i, item); - if (mMaxItemCount < Integer.MAX_VALUE && size() > mMaxItemCount + mExtraItemCount) { - Object removedItem = get(mMaxItemCount); - remove(removedItem); + if (size() > mMaxItemCount + mExtraItemCount) { + removeItems(mMaxItemCount, size() - mMaxItemCount - mExtraItemCount); } } @@ -115,97 +100,48 @@ public abstract class SortedArrayAdapter<T> extends ArrayObjectAdapter { * They will be presented in their insertion order. */ public int addExtraItem(T item) { - long newItemId = getId(item); - SoftPreconditions.checkState(!mIds.contains(newItemId)); - mIds.add(newItemId); super.add(item); return ++mExtraItemCount; } - @Override - public boolean remove(Object item) { - return removeWithId((T) item); - } - /** * Removes an item which has the same ID as {@code item}. */ public boolean removeWithId(T item) { - int index = indexWithId(item); - return index >= 0 && index < size() && removeItems(index, 1) == 1; - } - - @Override - public int removeItems(int position, int count) { - int upperBound = Math.min(position + count, size()); - for (int i = position; i < upperBound; i++) { - mIds.remove(getId((T) get(i))); - } - if (upperBound > size() - mExtraItemCount) { - mExtraItemCount -= upperBound - Math.max(size() - mExtraItemCount, position); - } - return super.removeItems(position, count); - } - - @Override - public void replace(int position, Object item) { - boolean wasExtra = position >= size() - mExtraItemCount; - removeItems(position, 1); - if (!wasExtra) { - add(item); - } else { - addExtraItem((T) item); - } - } - - @Override - public void clear() { - mIds.clear(); - super.clear(); + int index = indexWithTypeAndId(item); + return index >= 0 && index < size() && remove(get(index)); } /** - * Changes an item in the list. + * Change an item in the list. * @param item The item to change. */ public final void change(T item) { - int oldIndex = indexWithId(item); + int oldIndex = indexWithTypeAndId(item); if (oldIndex != -1) { T old = (T) get(oldIndex); if (mComparator.compare(old, item) == 0) { replace(oldIndex, item); return; } - remove(old); + removeItems(oldIndex, 1); } add(item); } /** - * Checks whether the item is in the list. - */ - public final boolean contains(T item) { - return indexWithId(item) != -1; - } - - @Override - public long getId(int position) { - return getId((T) get(position)); - } - - /** * Returns the id of the the given {@code item}, which will be used in {@link #change} to * decide if the given item is already existed in the adapter. * * The id must be stable. */ - protected abstract long getId(T item); + abstract long getId(T item); - private int indexWithId(T item) { + private int indexWithTypeAndId(T item) { long id = getId(item); for (int i = 0; i < size() - mExtraItemCount; i++) { T r = (T) get(i); - if (getId(r) == id) { + if (r.getClass() == item.getClass() && getId(r) == id) { return i; } } diff --git a/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java deleted file mode 100644 index c8f6a03f..00000000 --- a/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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.browse; - -import android.content.Context; -import android.content.res.Resources; -import android.support.v17.leanback.widget.Action; -import android.support.v17.leanback.widget.OnActionClickedListener; -import android.support.v17.leanback.widget.SparseArrayObjectAdapter; - -import com.android.tv.R; -import com.android.tv.TvApplication; -import com.android.tv.dialog.HalfSizedDialogFragment; -import com.android.tv.dvr.DvrDataManager; -import com.android.tv.dvr.DvrManager; -import com.android.tv.dvr.data.ScheduledRecording; -import com.android.tv.dvr.ui.DvrStopRecordingFragment; -import com.android.tv.dvr.ui.DvrUiHelper; - -/** - * {@link RecordingDetailsFragment} for current recording in DVR. - */ -public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment { - private static final int ACTION_STOP_RECORDING = 1; - - private DvrDataManager mDvrDataManger; - private final DvrDataManager.ScheduledRecordingListener mScheduledRecordingListener = - new DvrDataManager.ScheduledRecordingListener() { - @Override - public void onScheduledRecordingAdded(ScheduledRecording... schedules) { } - - @Override - public void onScheduledRecordingRemoved(ScheduledRecording... schedules) { - for (ScheduledRecording schedule : schedules) { - if (schedule.getId() == getRecording().getId()) { - getActivity().finish(); - return; - } - } - } - - @Override - public void onScheduledRecordingStatusChanged(ScheduledRecording... schedules) { - for (ScheduledRecording schedule : schedules) { - if (schedule.getId() == getRecording().getId() - && schedule.getState() - != ScheduledRecording.STATE_RECORDING_IN_PROGRESS) { - getActivity().finish(); - return; - } - } - } - }; - - @Override - public void onAttach(Context context) { - super.onAttach(context); - mDvrDataManger = TvApplication.getSingletons(context).getDvrDataManager(); - mDvrDataManger.addScheduledRecordingListener(mScheduledRecordingListener); - } - - @Override - protected SparseArrayObjectAdapter onCreateActionsAdapter() { - SparseArrayObjectAdapter adapter = - new SparseArrayObjectAdapter(new ActionPresenterSelector()); - Resources res = getResources(); - adapter.set(ACTION_STOP_RECORDING, new Action(ACTION_STOP_RECORDING, - res.getString(R.string.epg_dvr_dialog_message_stop_recording), null, - res.getDrawable(R.drawable.lb_ic_stop))); - return adapter; - } - - @Override - protected OnActionClickedListener onCreateOnActionClickedListener() { - return new OnActionClickedListener() { - @Override - public void onActionClicked(Action action) { - if (action.getId() == ACTION_STOP_RECORDING) { - DvrUiHelper.showStopRecordingDialog(getActivity(), - getRecording().getChannelId(), - DvrStopRecordingFragment.REASON_USER_STOP, - new HalfSizedDialogFragment.OnActionClickListener() { - @Override - public void onActionClick(long actionId) { - if (actionId == DvrStopRecordingFragment.ACTION_STOP) { - DvrManager dvrManager = - TvApplication.getSingletons(getContext()) - .getDvrManager(); - dvrManager.stopRecording(getRecording()); - getActivity().finish(); - } - } - }); - } - } - }; - } - - @Override - public void onDetach() { - if (mDvrDataManger != null) { - mDvrDataManger.removeScheduledRecordingListener(mScheduledRecordingListener); - } - super.onDetach(); - } -} diff --git a/src/com/android/tv/dvr/ui/browse/DvrListRowPresenter.java b/src/com/android/tv/dvr/ui/browse/DvrListRowPresenter.java deleted file mode 100644 index 37a72eaf..00000000 --- a/src/com/android/tv/dvr/ui/browse/DvrListRowPresenter.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2017 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.browse; - -import android.content.Context; -import android.support.v17.leanback.widget.ListRowPresenter; -import android.view.ViewGroup; - -import com.android.tv.R; - -/** A list row presenter to display expand/fold card views list. */ -public class DvrListRowPresenter extends ListRowPresenter { - public DvrListRowPresenter(Context context) { - super(); - setRowHeight(ViewGroup.LayoutParams.WRAP_CONTENT); - setExpandedRowHeight( - context.getResources() - .getDimensionPixelSize(R.dimen.dvr_library_expanded_row_height)); - } -} diff --git a/src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java b/src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java index 5abd52a1..d28f026c 100644 --- a/src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java +++ b/src/com/android/tv/dvr/ui/list/BaseDvrSchedulesFragment.java @@ -29,7 +29,7 @@ import com.android.tv.R; import com.android.tv.TvApplication; import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrScheduleManager; -import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.ScheduledRecording; /** * A base fragment to show the list of schedule recordings. diff --git a/src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java b/src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java index 3cbb500a..722c9b6e 100644 --- a/src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java +++ b/src/com/android/tv/dvr/ui/list/DvrSchedulesFragment.java @@ -18,9 +18,12 @@ package com.android.tv.dvr.ui.list; import android.os.Bundle; import android.support.v17.leanback.widget.ClassPresenterSelector; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; import com.android.tv.R; -import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.ScheduledRecording; import com.android.tv.dvr.ui.list.SchedulesHeaderRowPresenter.DateHeaderRowPresenter; /** diff --git a/src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java b/src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java index 57e7a88f..42a1e72b 100644 --- a/src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java +++ b/src/com/android/tv/dvr/ui/list/DvrSeriesSchedulesFragment.java @@ -17,7 +17,6 @@ package com.android.tv.dvr.ui.list; import android.annotation.TargetApi; -import android.content.Context; import android.database.ContentObserver; import android.media.tv.TvContract.Programs; import android.net.Uri; @@ -36,13 +35,11 @@ import com.android.tv.R; import com.android.tv.TvApplication; import com.android.tv.data.ChannelDataManager; import com.android.tv.data.Program; -import com.android.tv.dvr.DvrDataManager; import com.android.tv.dvr.DvrDataManager.SeriesRecordingListener; -import com.android.tv.dvr.data.SeriesRecording; -import com.android.tv.dvr.provider.EpisodicProgramLoadTask; -import com.android.tv.dvr.ui.BigArguments; +import com.android.tv.dvr.EpisodicProgramLoadTask; +import com.android.tv.dvr.SeriesRecording; +import com.android.tv.dvr.ui.list.SchedulesHeaderRowPresenter.SeriesRecordingHeaderRowPresenter; -import java.util.Collections; import java.util.List; /** @@ -50,22 +47,20 @@ import java.util.List; */ @TargetApi(Build.VERSION_CODES.N) public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { + private static final String TAG = "DvrSeriesSchedulesFragment"; /** * The key for series recording whose scheduled recording list will be displayed. - * Type: {@link SeriesRecording} */ public static final String SERIES_SCHEDULES_KEY_SERIES_RECORDING = "series_schedules_key_series_recording"; /** - * The key for programs which belong to the series recording whose scheduled recording list - * will be displayed. - * Type: List<{@link Program}> + * The key for programs belong to the series recording whose scheduled recording + * list will be displayed. */ public static final String SERIES_SCHEDULES_KEY_SERIES_PROGRAMS = "series_schedules_key_series_programs"; private ChannelDataManager mChannelDataManager; - private DvrDataManager mDvrDataManager; private SeriesRecording mSeriesRecording; private List<Program> mPrograms; private EpisodicProgramLoadTask mProgramLoadTask; @@ -92,22 +87,20 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { && getRowsAdapter() instanceof SeriesScheduleRowAdapter) { ((SeriesScheduleRowAdapter) getRowsAdapter()) .onSeriesRecordingUpdated(r); - mSeriesRecording = r; - updateEmptyMessage(); return; } } } }; - private final Handler mHandler = new Handler(Looper.getMainLooper()); - private final ContentObserver mContentObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange, Uri uri) { - super.onChange(selfChange, uri); - executeProgramLoadingTask(); - } - }; + private final ContentObserver mContentObserver = + new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + executeProgramLoadingTask(); + } + }; private final ChannelDataManager.Listener mChannelListener = new ChannelDataManager.Listener() { @Override @@ -127,28 +120,17 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { } @Override - public void onAttach(Context context) { - super.onAttach(context); + public void onCreate(Bundle savedInstanceState) { Bundle args = getArguments(); if (args != null) { mSeriesRecording = args.getParcelable(SERIES_SCHEDULES_KEY_SERIES_RECORDING); - mPrograms = (List<Program>) BigArguments.getArgument( - SERIES_SCHEDULES_KEY_SERIES_PROGRAMS); - BigArguments.reset(); + mPrograms = args.getParcelableArrayList(SERIES_SCHEDULES_KEY_SERIES_PROGRAMS); } - if (args == null || mPrograms == null) { - getActivity().finish(); - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ApplicationSingletons singletons = TvApplication.getSingletons(getContext()); + singletons.getDvrDataManager().addSeriesRecordingListener(mSeriesRecordingListener); mChannelDataManager = singletons.getChannelDataManager(); mChannelDataManager.addListener(mChannelListener); - mDvrDataManager = singletons.getDvrDataManager(); - mDvrDataManager.addSeriesRecordingListener(mSeriesRecordingListener); getContext().getContentResolver().registerContentObserver(Programs.CONTENT_URI, true, mContentObserver); } @@ -162,16 +144,8 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { private void onProgramsUpdated() { ((SeriesScheduleRowAdapter) getRowsAdapter()).setPrograms(mPrograms); - updateEmptyMessage(); - } - - private void updateEmptyMessage() { if (mPrograms == null || mPrograms.isEmpty()) { - if (mSeriesRecording.getState() == SeriesRecording.STATE_SERIES_STOPPED) { - showEmptyMessage(R.string.dvr_series_schedules_stopped_empty_state); - } else { - showEmptyMessage(R.string.dvr_series_schedules_empty_state); - } + showEmptyMessage(R.string.dvr_series_schedules_empty_state); } else { hideEmptyMessage(); } @@ -184,15 +158,15 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { mProgramLoadTask = null; } getContext().getContentResolver().unregisterContentObserver(mContentObserver); - mHandler.removeCallbacksAndMessages(null); mChannelDataManager.removeListener(mChannelListener); - mDvrDataManager.removeSeriesRecordingListener(mSeriesRecordingListener); + TvApplication.getSingletons(getContext()).getDvrDataManager() + .removeSeriesRecordingListener(mSeriesRecordingListener); super.onDestroy(); } @Override public SchedulesHeaderRowPresenter onCreateHeaderRowPresenter() { - return new SchedulesHeaderRowPresenter.SeriesRecordingHeaderRowPresenter(getContext()); + return new SeriesRecordingHeaderRowPresenter(getContext()); } @Override @@ -221,7 +195,7 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { mProgramLoadTask = new EpisodicProgramLoadTask(getContext(), mSeriesRecording) { @Override protected void onPostExecute(List<Program> programs) { - mPrograms = programs == null ? Collections.EMPTY_LIST : programs; + mPrograms = programs; onProgramsUpdated(); } }; @@ -231,4 +205,4 @@ public class DvrSeriesSchedulesFragment extends BaseDvrSchedulesFragment { .setIgnoreChannelOption(true) .execute(); } -} +}
\ No newline at end of file diff --git a/src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java b/src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java index 9b0ad105..23aebf59 100644 --- a/src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java +++ b/src/com/android/tv/dvr/ui/list/EpisodicProgramRow.java @@ -19,13 +19,13 @@ package com.android.tv.dvr.ui.list; import android.content.Context; import com.android.tv.data.Program; -import com.android.tv.dvr.data.ScheduledRecording; -import com.android.tv.dvr.data.ScheduledRecording.Builder; +import com.android.tv.dvr.ScheduledRecording; +import com.android.tv.dvr.ScheduledRecording.Builder; /** * A class for the episodic program. */ -class EpisodicProgramRow extends ScheduleRow { +public class EpisodicProgramRow extends ScheduleRow { private final String mInputId; private final Program mProgram; diff --git a/src/com/android/tv/dvr/ui/list/ScheduleRow.java b/src/com/android/tv/dvr/ui/list/ScheduleRow.java index 66e96f94..3fc92e8a 100644 --- a/src/com/android/tv/dvr/ui/list/ScheduleRow.java +++ b/src/com/android/tv/dvr/ui/list/ScheduleRow.java @@ -20,12 +20,12 @@ import android.content.Context; import android.support.annotation.Nullable; import com.android.tv.common.SoftPreconditions; -import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.ScheduledRecording; /** * A class for schedule recording row. */ -class ScheduleRow { +public class ScheduleRow { private final SchedulesHeaderRow mHeaderRow; @Nullable private ScheduledRecording mSchedule; private boolean mStopRecordingRequested; diff --git a/src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java b/src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java index 97d60473..9cc82653 100644 --- a/src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java +++ b/src/com/android/tv/dvr/ui/list/ScheduleRowAdapter.java @@ -30,8 +30,8 @@ import com.android.tv.R; import com.android.tv.TvApplication; import com.android.tv.common.SoftPreconditions; import com.android.tv.dvr.DvrManager; +import com.android.tv.dvr.ScheduledRecording; import com.android.tv.dvr.ui.list.SchedulesHeaderRow.DateHeaderRow; -import com.android.tv.dvr.data.ScheduledRecording; import com.android.tv.util.Utils; import java.util.ArrayList; @@ -43,7 +43,7 @@ import java.util.concurrent.TimeUnit; /** * An adapter for {@link ScheduleRow}. */ -class ScheduleRowAdapter extends ArrayObjectAdapter { +public class ScheduleRowAdapter extends ArrayObjectAdapter { private static final String TAG = "ScheduleRowAdapter"; private static final boolean DEBUG = false; diff --git a/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java b/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java index dc4e3c41..1257e725 100644 --- a/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java +++ b/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java @@ -42,24 +42,25 @@ import com.android.tv.R; import com.android.tv.TvApplication; import com.android.tv.common.SoftPreconditions; import com.android.tv.data.Channel; -import com.android.tv.dialog.HalfSizedDialogFragment; import com.android.tv.dvr.DvrManager; import com.android.tv.dvr.DvrScheduleManager; -import com.android.tv.dvr.data.ScheduledRecording; +import com.android.tv.dvr.DvrUiHelper; +import com.android.tv.dvr.ScheduledRecording; import com.android.tv.dvr.ui.DvrStopRecordingFragment; -import com.android.tv.dvr.ui.DvrUiHelper; +import com.android.tv.dvr.ui.HalfSizedDialogFragment; import com.android.tv.util.ToastUtils; import com.android.tv.util.Utils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.concurrent.TimeUnit; /** * A RowPresenter for {@link ScheduleRow}. */ @TargetApi(Build.VERSION_CODES.N) -class ScheduleRowPresenter extends RowPresenter { +public class ScheduleRowPresenter extends RowPresenter { private static final String TAG = "ScheduleRowPresenter"; @Retention(RetentionPolicy.SOURCE) @@ -344,9 +345,7 @@ class ScheduleRowPresenter extends RowPresenter { viewHolder.mInfoContainer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - if (isInfoClickable(row)) { - onInfoClicked(row); - } + onInfoClicked(row); } }); @@ -367,7 +366,8 @@ class ScheduleRowPresenter extends RowPresenter { viewHolder.mTimeView.setText(onGetRecordingTimeText(row)); String programInfoText = onGetProgramInfoText(row); if (TextUtils.isEmpty(programInfoText)) { - int durationMins = Math.max(1, Utils.getRoundOffMinsFromMs(row.getDuration())); + int durationMins = + Math.max((int) TimeUnit.MILLISECONDS.toMinutes(row.getDuration()), 1); programInfoText = mContext.getResources().getQuantityString( R.plurals.dvr_schedules_recording_duration, durationMins, durationMins); } @@ -403,7 +403,6 @@ class ScheduleRowPresenter extends RowPresenter { } else { viewHolder.whiteBackInfo(); } - viewHolder.mInfoContainer.setFocusable(isInfoClickable(row)); updateActionContainer(viewHolder, viewHolder.isSelected()); } @@ -455,13 +454,11 @@ class ScheduleRowPresenter extends RowPresenter { /** * Called when user click Info in {@link ScheduleRow}. */ - protected void onInfoClicked(ScheduleRow row) { - DvrUiHelper.startDetailsActivity((Activity) mContext, row.getSchedule(), null, true); - } - - private boolean isInfoClickable(ScheduleRow row) { - return row.getSchedule() != null - && (row.getSchedule().isNotStarted() || row.getSchedule().isInProgress()); + protected void onInfoClicked(ScheduleRow scheduleRow) { + ScheduledRecording schedule = scheduleRow.getSchedule(); + if (schedule != null) { + DvrUiHelper.startDetailsActivity((Activity) mContext, schedule, null, true); + } } /** @@ -548,7 +545,7 @@ class ScheduleRowPresenter extends RowPresenter { // This row has been deleted. return; } - if (row.isRecordingInProgress() && !row.isStopRecordingRequested()) { + if (row.isOnAir() && row.isRecordingInProgress() && !row.isStopRecordingRequested()) { row.setStopRecordingRequested(true); mDvrManager.stopRecording(row.getSchedule()); CharSequence deletedInfo = onGetProgramInfoText(row); @@ -673,9 +670,10 @@ class ScheduleRowPresenter extends RowPresenter { hideActionView(viewHolder.mFirstActionContainer, View.GONE); } }; - mLastFocusedViewId = R.id.info_container; - SoftPreconditions.checkState(viewHolder.mInfoContainer.isFocusable(), TAG, - "No focusable view in this row: " + viewHolder); + if (mLastFocusedViewId == R.id.action_first_container + || mLastFocusedViewId == R.id.action_second_container) { + mLastFocusedViewId = R.id.info_container; + } break; } View view = viewHolder.view.findViewById(mLastFocusedViewId); @@ -685,10 +683,8 @@ class ScheduleRowPresenter extends RowPresenter { // requestFocus() explicitly. if (view.hasFocus()) { viewHolder.mPendingAnimationRunnable.run(); - } else if (view.isFocusable()){ - view.requestFocus(); } else { - viewHolder.view.requestFocus(); + view.requestFocus(); } } } else { @@ -741,10 +737,10 @@ class ScheduleRowPresenter extends RowPresenter { @ScheduleRowAction protected int[] getAvailableActions(ScheduleRow row) { if (row.getSchedule() != null) { - if (row.isRecordingInProgress()) { - return new int[]{ACTION_STOP_RECORDING}; - } else if (row.isOnAir()) { - if (row.isRecordingNotStarted()) { + if (row.isOnAir()) { + if (row.isRecordingInProgress()) { + return new int[] {ACTION_STOP_RECORDING}; + } else if (row.isRecordingNotStarted()) { if (canResolveConflict()) { // The "START" action can change the conflict states. return new int[] {ACTION_REMOVE_SCHEDULE, ACTION_START_RECORDING}; diff --git a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java index 715ecb8c..0fb0924d 100644 --- a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java +++ b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRow.java @@ -16,15 +16,12 @@ package com.android.tv.dvr.ui.list; -import com.android.tv.data.Program; -import com.android.tv.dvr.data.SeriesRecording; - -import java.util.List; +import com.android.tv.dvr.SeriesRecording; /** * A base class for the rows for schedules' header. */ -abstract class SchedulesHeaderRow { +public abstract class SchedulesHeaderRow { private String mTitle; private String mDescription; private int mItemCount; @@ -101,20 +98,11 @@ abstract class SchedulesHeaderRow { */ public static class SeriesRecordingHeaderRow extends SchedulesHeaderRow { private SeriesRecording mSeriesRecording; - private List<Program> mPrograms; public SeriesRecordingHeaderRow(String title, String description, int itemCount, - SeriesRecording series, List<Program> programs) { + SeriesRecording series) { super(title, description, itemCount); mSeriesRecording = series; - mPrograms = programs; - } - - /** - * Returns the list of programs which belong to the series. - */ - public List<Program> getPrograms() { - return mPrograms; } /** @@ -131,4 +119,4 @@ abstract class SchedulesHeaderRow { mSeriesRecording = seriesRecording; } } -}
\ No newline at end of file +} diff --git a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java index fe2033ba..69c33a96 100644 --- a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java +++ b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java @@ -30,14 +30,15 @@ import android.widget.TextView; import com.android.tv.R; import com.android.tv.TvApplication; -import com.android.tv.dvr.data.SeriesRecording; -import com.android.tv.dvr.ui.DvrUiHelper; +import com.android.tv.dvr.DvrUiHelper; +import com.android.tv.dvr.SeriesRecording; +import com.android.tv.dvr.ui.DvrSchedulesActivity; import com.android.tv.dvr.ui.list.SchedulesHeaderRow.SeriesRecordingHeaderRow; /** * A base class for RowPresenter for {@link SchedulesHeaderRow} */ -abstract class SchedulesHeaderRowPresenter extends RowPresenter { +public abstract class SchedulesHeaderRowPresenter extends RowPresenter { private Context mContext; public SchedulesHeaderRowPresenter(Context context) { @@ -78,7 +79,7 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { } /** - * A presenter for {@link SchedulesHeaderRow.DateHeaderRow}. + * A presenter for {@link com.android.tv.dvr.ui.list.SchedulesHeaderRow.DateHeaderRow}. */ public static class DateHeaderRowPresenter extends SchedulesHeaderRowPresenter { public DateHeaderRowPresenter(Context context) { @@ -92,7 +93,7 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { /** * A ViewHolder for - * {@link SchedulesHeaderRow.DateHeaderRow}. + * {@link com.android.tv.dvr.ui.list.SchedulesHeaderRow.DateHeaderRow}. */ public static class DateHeaderRowViewHolder extends SchedulesHeaderRowViewHolder { public DateHeaderRowViewHolder(Context context, ViewGroup parent) { @@ -151,9 +152,9 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { headerViewHolder.mSeriesSettingsButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { + // TODO: pass channel list for settings. DvrUiHelper.startSeriesSettingsActivity(getContext(), - header.getSeriesRecording().getId(), - header.getPrograms(), false, false, false, null); + header.getSeriesRecording().getId(), null, false, false, false); } }); headerViewHolder.mToggleStartStopButton.setOnClickListener(new OnClickListener() { @@ -168,9 +169,9 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { .build(); TvApplication.getSingletons(getContext()).getDvrManager() .updateSeriesRecording(seriesRecording); + // TODO: pass channel list for settings. DvrUiHelper.startSeriesSettingsActivity(getContext(), - header.getSeriesRecording().getId(), - header.getPrograms(), false, false, false, null); + header.getSeriesRecording().getId(), null, false, false, false); } else { DvrUiHelper.showCancelAllSeriesRecordingDialog( (DvrSchedulesActivity) view.getContext(), @@ -181,8 +182,11 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter { } private void setTextDrawable(TextView textView, Drawable drawableStart) { - textView.setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStart, null, null, - null); + if (mLtr) { + textView.setCompoundDrawablesWithIntrinsicBounds(drawableStart, null, null, null); + } else { + textView.setCompoundDrawablesWithIntrinsicBounds(null, null, drawableStart, null); + } } /** diff --git a/src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java b/src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java index 6b6de8b8..3b493774 100644 --- a/src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java +++ b/src/com/android/tv/dvr/ui/list/SeriesScheduleRowAdapter.java @@ -31,8 +31,8 @@ import com.android.tv.common.SoftPreconditions; import com.android.tv.data.Program; 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.SeriesRecording; +import com.android.tv.dvr.ScheduledRecording; +import com.android.tv.dvr.SeriesRecording; import com.android.tv.dvr.ui.list.SchedulesHeaderRow.SeriesRecordingHeaderRow; import com.android.tv.util.Utils; @@ -46,7 +46,7 @@ import java.util.Map; * An adapter for series schedule row. */ @TargetApi(Build.VERSION_CODES.N) -class SeriesScheduleRowAdapter extends ScheduleRowAdapter { +public class SeriesScheduleRowAdapter extends ScheduleRowAdapter { private static final String TAG = "SeriesRowAdapter"; private static final boolean DEBUG = false; @@ -96,7 +96,7 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter { Collections.sort(sortedPrograms); List<EpisodicProgramRow> rows = new ArrayList<>(); mHeaderRow = new SeriesRecordingHeaderRow(mSeriesRecording.getTitle(), - null, sortedPrograms.size(), mSeriesRecording, programs); + null, sortedPrograms.size(), mSeriesRecording); for (Program program : sortedPrograms) { ScheduledRecording schedule = mDataManager.getScheduledRecordingForProgramId(program.getId()); @@ -145,7 +145,7 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter { if (index != -1) { EpisodicProgramRow row = (EpisodicProgramRow) get(index); if (!row.isStartRecordingRequested()) { - setScheduleToRow(row, schedule); + row.setSchedule(schedule); notifyArrayItemRangeChanged(index, 1); } } @@ -195,10 +195,12 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter { if (!isStartOrStopRequested()) { executePendingUpdate(); } - setScheduleToRow(row, schedule); + row.setSchedule(schedule); } + } else if (willBeKept(schedule)) { + row.setSchedule(schedule); } else { - setScheduleToRow(row, schedule); + row.setSchedule(null); } notifyArrayItemRangeChanged(index, 1); } @@ -211,14 +213,6 @@ class SeriesScheduleRowAdapter extends ScheduleRowAdapter { } } - private void setScheduleToRow(ScheduleRow row, ScheduledRecording schedule) { - if (schedule != null && willBeKept(schedule)) { - row.setSchedule(schedule); - } else { - row.setSchedule(null); - } - } - private int findRowIndexByProgramId(long programId) { for (int i = 0; i < size(); i++) { Object item = get(i); diff --git a/src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java b/src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java index c8503e0d..5d88579a 100644 --- a/src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java +++ b/src/com/android/tv/dvr/ui/list/SeriesScheduleRowPresenter.java @@ -22,13 +22,13 @@ import android.view.ViewGroup; import com.android.tv.R; import com.android.tv.common.SoftPreconditions; -import com.android.tv.dvr.ui.DvrUiHelper; +import com.android.tv.dvr.DvrUiHelper; import com.android.tv.util.Utils; /** * A RowPresenter for series schedule row. */ -class SeriesScheduleRowPresenter extends ScheduleRowPresenter { +public class SeriesScheduleRowPresenter extends ScheduleRowPresenter { private static final String TAG = "SeriesRowPresenter"; private boolean mLtr; @@ -74,8 +74,13 @@ class SeriesScheduleRowPresenter extends ScheduleRowPresenter { viewHolder.getProgramTitleView().setCompoundDrawablePadding(getContext() .getResources().getDimensionPixelOffset( R.dimen.dvr_schedules_warning_icon_padding)); - viewHolder.getProgramTitleView().setCompoundDrawablesRelativeWithIntrinsicBounds( - R.drawable.ic_warning_gray600_36dp, 0, 0, 0); + if (mLtr) { + viewHolder.getProgramTitleView().setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_warning_gray600_36dp, 0, 0, 0); + } else { + viewHolder.getProgramTitleView().setCompoundDrawablesWithIntrinsicBounds( + 0, 0, R.drawable.ic_warning_gray600_36dp, 0); + } } else { viewHolder.getProgramTitleView().setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); } @@ -83,7 +88,9 @@ class SeriesScheduleRowPresenter extends ScheduleRowPresenter { @Override protected void onInfoClicked(ScheduleRow row) { - DvrUiHelper.startSchedulesActivity(getContext(), row.getSchedule()); + if (row.getSchedule() != null) { + DvrUiHelper.startSchedulesActivity(getContext(), row.getSchedule()); + } } @Override diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java deleted file mode 100644 index 2437d1f5..00000000 --- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.playback; - -import android.app.Activity; -import android.content.Intent; -import android.content.res.Configuration; -import android.os.Bundle; -import android.util.Log; - -import com.android.tv.R; -import com.android.tv.TvApplication; -import com.android.tv.dvr.data.RecordedProgram; - -/** - * Activity to play a {@link RecordedProgram}. - */ -public class DvrPlaybackActivity extends Activity { - private static final String TAG = "DvrPlaybackActivity"; - private static final boolean DEBUG = false; - - private DvrPlaybackOverlayFragment mOverlayFragment; - - @Override - public void onCreate(Bundle savedInstanceState) { - TvApplication.setCurrentRunningProcess(this, true); - if (DEBUG) Log.d(TAG, "onCreate"); - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_dvr_playback); - mOverlayFragment = (DvrPlaybackOverlayFragment) getFragmentManager() - .findFragmentById(R.id.dvr_playback_controls_fragment); - } - - @Override - public void onVisibleBehindCanceled() { - if (DEBUG) Log.d(TAG, "onVisibleBehindCanceled"); - super.onVisibleBehindCanceled(); - finish(); - } - - @Override - protected void onNewIntent(Intent intent) { - mOverlayFragment.onNewIntent(intent); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - float density = getResources().getDisplayMetrics().density; - mOverlayFragment.onWindowSizeChanged((int) (newConfig.screenWidthDp * density), - (int) (newConfig.screenHeightDp * density)); - } -}
\ No newline at end of file diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java deleted file mode 100644 index 843d2dbe..00000000 --- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * 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.playback; - -import android.app.Activity; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.media.MediaMetadata; -import android.media.session.MediaController; -import android.media.session.MediaSession; -import android.media.session.PlaybackState; -import android.media.tv.TvContract; -import android.os.AsyncTask; -import android.support.annotation.Nullable; -import android.text.TextUtils; - -import com.android.tv.R; -import com.android.tv.TvApplication; -import com.android.tv.common.SoftPreconditions; -import com.android.tv.data.Channel; -import com.android.tv.data.ChannelDataManager; -import com.android.tv.dvr.DvrWatchedPositionManager; -import com.android.tv.dvr.data.RecordedProgram; -import com.android.tv.util.ImageLoader; -import com.android.tv.util.TimeShiftUtils; -import com.android.tv.util.Utils; - -class DvrPlaybackMediaSessionHelper { - private static final String TAG = "DvrPlaybackMediaSessionHelper"; - private static final boolean DEBUG = false; - - private int mNowPlayingCardWidth; - private int mNowPlayingCardHeight; - private int mSpeedLevel; - private long mProgramDurationMs; - - private Activity mActivity; - private DvrPlayer mDvrPlayer; - private MediaSession mMediaSession; - private final DvrWatchedPositionManager mDvrWatchedPositionManager; - private final ChannelDataManager mChannelDataManager; - - public DvrPlaybackMediaSessionHelper(Activity activity, String mediaSessionTag, - DvrPlayer dvrPlayer, DvrPlaybackOverlayFragment overlayFragment) { - mActivity = activity; - mDvrPlayer = dvrPlayer; - mDvrWatchedPositionManager = - TvApplication.getSingletons(activity).getDvrWatchedPositionManager(); - mChannelDataManager = TvApplication.getSingletons(activity).getChannelDataManager(); - mDvrPlayer.setCallback(new DvrPlayer.DvrPlayerCallback() { - @Override - public void onPlaybackStateChanged(int playbackState, int playbackSpeed) { - updateMediaSessionPlaybackState(); - } - - @Override - public void onPlaybackPositionChanged(long positionMs) { - updateMediaSessionPlaybackState(); - if (mDvrPlayer.isPlaybackPrepared()) { - mDvrWatchedPositionManager - .setWatchedPosition(mDvrPlayer.getProgram().getId(), positionMs); - } - } - - @Override - public void onPlaybackEnded() { - // TODO: Deal with watched over recordings in DVR library - RecordedProgram nextEpisode = - overlayFragment.getNextEpisode(mDvrPlayer.getProgram()); - if (nextEpisode == null) { - mDvrPlayer.reset(); - mActivity.finish(); - } else { - Intent intent = new Intent(activity, DvrPlaybackActivity.class); - intent.putExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, nextEpisode.getId()); - mActivity.startActivity(intent); - } - } - }); - initializeMediaSession(mediaSessionTag); - } - - /** - * Stops DVR player and release media session. - */ - public void release() { - if (mDvrPlayer != null) { - mDvrPlayer.reset(); - } - if (mMediaSession != null) { - mMediaSession.release(); - mMediaSession = null; - } - } - - /** - * Updates media session's playback state and speed. - */ - public void updateMediaSessionPlaybackState() { - mMediaSession.setPlaybackState(new PlaybackState.Builder() - .setState(mDvrPlayer.getPlaybackState(), mDvrPlayer.getPlaybackPosition(), - mSpeedLevel).build()); - } - - /** - * Sets the recorded program for playback. - * - * @param program The recorded program to play. {@code null} to reset the DVR player. - */ - public void setupPlayback(RecordedProgram program, long seekPositionMs) { - if (program != null) { - mDvrPlayer.setProgram(program, seekPositionMs); - setupMediaSession(program); - } else { - mDvrPlayer.reset(); - mMediaSession.setActive(false); - } - } - - /** - * Returns the recorded program now playing. - */ - public RecordedProgram getProgram() { - return mDvrPlayer.getProgram(); - } - - /** - * Checks if the recorded program is the same as now playing one. - */ - public boolean isCurrentProgram(RecordedProgram program) { - return program != null && program.equals(getProgram()); - } - - /** - * Returns playback state. - */ - public int getPlaybackState() { - return mDvrPlayer.getPlaybackState(); - } - - /** - * Returns the underlying DVR player. - */ - public DvrPlayer getDvrPlayer() { - return mDvrPlayer; - } - - private void initializeMediaSession(String mediaSessionTag) { - mMediaSession = new MediaSession(mActivity, mediaSessionTag); - mMediaSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS - | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); - mNowPlayingCardWidth = mActivity.getResources() - .getDimensionPixelSize(R.dimen.notif_card_img_max_width); - mNowPlayingCardHeight = mActivity.getResources() - .getDimensionPixelSize(R.dimen.notif_card_img_height); - mMediaSession.setCallback(new MediaSessionCallback()); - mActivity.setMediaController( - new MediaController(mActivity, mMediaSession.getSessionToken())); - updateMediaSessionPlaybackState(); - } - - private void setupMediaSession(RecordedProgram program) { - mProgramDurationMs = program.getDurationMillis(); - String cardTitleText = program.getTitle(); - if (TextUtils.isEmpty(cardTitleText)) { - Channel channel = mChannelDataManager.getChannel(program.getChannelId()); - cardTitleText = (channel != null) ? channel.getDisplayName() - : mActivity.getString(R.string.no_program_information); - } - final MediaMetadata currentMetadata = updateMetadataTextInfo(program.getId(), cardTitleText, - program.getDescription(), mProgramDurationMs); - String posterArtUri = program.getPosterArtUri(); - if (posterArtUri == null) { - posterArtUri = TvContract.buildChannelLogoUri(program.getChannelId()).toString(); - } - updatePosterArt(program, currentMetadata, null, posterArtUri); - mMediaSession.setActive(true); - } - - private void updatePosterArt(RecordedProgram program, MediaMetadata currentMetadata, - @Nullable Bitmap posterArt, @Nullable String posterArtUri) { - if (posterArt != null) { - updateMetadataImageInfo(program, currentMetadata, posterArt, 0); - } else if (posterArtUri != null) { - ImageLoader.loadBitmap(mActivity, posterArtUri, mNowPlayingCardWidth, - mNowPlayingCardHeight, - new ProgramPosterArtCallback(mActivity, program, currentMetadata)); - } else { - updateMetadataImageInfo(program, currentMetadata, null, R.drawable.default_now_card); - } - } - - private class ProgramPosterArtCallback extends - ImageLoader.ImageLoaderCallback<Activity> { - private final RecordedProgram mRecordedProgram; - private final MediaMetadata mCurrentMetadata; - - public ProgramPosterArtCallback(Activity activity, RecordedProgram program, - MediaMetadata metadata) { - super(activity); - mRecordedProgram = program; - mCurrentMetadata = metadata; - } - - @Override - public void onBitmapLoaded(Activity activity, @Nullable Bitmap posterArt) { - if (isCurrentProgram(mRecordedProgram)) { - updatePosterArt(mRecordedProgram, mCurrentMetadata, posterArt, null); - } - } - } - - private MediaMetadata updateMetadataTextInfo(final long programId, final String title, - final String subtitle, final long duration) { - MediaMetadata.Builder builder = new MediaMetadata.Builder(); - builder.putString(MediaMetadata.METADATA_KEY_MEDIA_ID, Long.toString(programId)) - .putString(MediaMetadata.METADATA_KEY_TITLE, title) - .putLong(MediaMetadata.METADATA_KEY_DURATION, duration); - if (subtitle != null) { - builder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, subtitle); - } - MediaMetadata metadata = builder.build(); - mMediaSession.setMetadata(metadata); - return metadata; - } - - private void updateMetadataImageInfo(final RecordedProgram program, - final MediaMetadata currentMetadata, final Bitmap posterArt, final int imageResId) { - if (mMediaSession != null && (posterArt != null || imageResId != 0)) { - MediaMetadata.Builder builder = new MediaMetadata.Builder(currentMetadata); - if (posterArt != null) { - builder.putBitmap(MediaMetadata.METADATA_KEY_ART, posterArt); - mMediaSession.setMetadata(builder.build()); - } else { - new AsyncTask<Void, Void, Bitmap>() { - @Override - protected Bitmap doInBackground(Void... arg0) { - return BitmapFactory.decodeResource(mActivity.getResources(), imageResId); - } - - @Override - protected void onPostExecute(Bitmap programPosterArt) { - if (mMediaSession != null && programPosterArt != null - && isCurrentProgram(program)) { - builder.putBitmap(MediaMetadata.METADATA_KEY_ART, programPosterArt); - mMediaSession.setMetadata(builder.build()); - } - } - }.execute(); - } - } - } - - // An event was triggered by MediaController.TransportControls and must be handled here. - // Here we update the media itself to act on the event that was triggered. - private class MediaSessionCallback extends MediaSession.Callback { - @Override - public void onPrepare() { - if (!mDvrPlayer.isPlaybackPrepared()) { - mDvrPlayer.prepare(true); - } - } - - @Override - public void onPlay() { - if (mDvrPlayer.isPlaybackPrepared()) { - mDvrPlayer.play(); - } - } - - @Override - public void onPause() { - if (mDvrPlayer.isPlaybackPrepared()) { - mDvrPlayer.pause(); - } - } - - @Override - public void onFastForward() { - if (!mDvrPlayer.isPlaybackPrepared()) { - return; - } - if (mDvrPlayer.getPlaybackState() == PlaybackState.STATE_FAST_FORWARDING) { - if (mSpeedLevel < TimeShiftUtils.MAX_SPEED_LEVEL) { - mSpeedLevel++; - } else { - return; - } - } else { - mSpeedLevel = 0; - } - mDvrPlayer.fastForward( - TimeShiftUtils.getPlaybackSpeed(mSpeedLevel, mProgramDurationMs)); - } - - @Override - public void onRewind() { - if (!mDvrPlayer.isPlaybackPrepared()) { - return; - } - if (mDvrPlayer.getPlaybackState() == PlaybackState.STATE_REWINDING) { - if (mSpeedLevel < TimeShiftUtils.MAX_SPEED_LEVEL) { - mSpeedLevel++; - } else { - return; - } - } else { - mSpeedLevel = 0; - } - mDvrPlayer.rewind(TimeShiftUtils.getPlaybackSpeed(mSpeedLevel, mProgramDurationMs)); - } - - @Override - public void onSeekTo(long positionMs) { - if (mDvrPlayer.isPlaybackPrepared()) { - mDvrPlayer.seekTo(positionMs); - } - } - } -} diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackSideFragment.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackSideFragment.java deleted file mode 100644 index e49870f1..00000000 --- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackSideFragment.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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.playback; - -import android.media.tv.TvTrackInfo; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v17.leanback.app.GuidedStepFragment; -import android.support.v17.leanback.widget.GuidedAction; -import android.text.TextUtils; -import android.transition.Transition; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.android.tv.R; -import com.android.tv.util.TvSettings; - -import java.util.List; -import java.util.Locale; - -/** - * Fragment for DVR playback closed-caption/multi-audio settings. - */ -public class DvrPlaybackSideFragment extends GuidedStepFragment { - /** - * The tag for passing track infos to side fragments. - */ - public static final String TRACK_INFOS = "dvr_key_track_infos"; - /** - * The tag for passing selected track's ID to side fragments. - */ - public static final String SELECTED_TRACK_ID = "dvr_key_selected_track_id"; - - private static final int ACTION_ID_NO_SUBTITLE = -1; - private static final int CHECK_SET_ID = 1; - - private List<TvTrackInfo> mTrackInfos; - private String mSelectedTrackId; - private TvTrackInfo mSelectedTrack; - private int mTrackType; - private DvrPlaybackOverlayFragment mOverlayFragment; - - @Override - public void onCreate(Bundle savedInstanceState) { - mTrackInfos = getArguments().getParcelableArrayList(TRACK_INFOS); - mTrackType = mTrackInfos.get(0).getType(); - mSelectedTrackId = getArguments().getString(SELECTED_TRACK_ID); - mOverlayFragment = ((DvrPlaybackOverlayFragment) getFragmentManager() - .findFragmentById(R.id.dvr_playback_controls_fragment)); - super.onCreate(savedInstanceState); - } - - @Override - public View onCreateBackgroundView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View backgroundView = super.onCreateBackgroundView(inflater, container, savedInstanceState); - backgroundView.setBackgroundColor(getResources() - .getColor(R.color.lb_playback_controls_background_light)); - return backgroundView; - } - - @Override - public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) { - if (mTrackType == TvTrackInfo.TYPE_SUBTITLE) { - actions.add(new GuidedAction.Builder(getActivity()) - .id(ACTION_ID_NO_SUBTITLE) - .title(getString(R.string.closed_caption_option_item_off)) - .checkSetId(CHECK_SET_ID) - .checked(mSelectedTrackId == null) - .build()); - } - for (int i = 0; i < mTrackInfos.size(); i++) { - TvTrackInfo info = mTrackInfos.get(i); - boolean checked = TextUtils.equals(info.getId(), mSelectedTrackId); - GuidedAction action = new GuidedAction.Builder(getActivity()) - .id(i) - .title(getTrackLabel(info, i)) - .checkSetId(CHECK_SET_ID) - .checked(checked) - .build(); - actions.add(action); - if (checked) { - mSelectedTrack = info; - } - } - } - - @Override - public void onGuidedActionFocused(GuidedAction action) { - int actionId = (int) action.getId(); - mOverlayFragment.selectTrack(mTrackType, actionId < 0 ? null : mTrackInfos.get(actionId)); - } - - @Override - public void onGuidedActionClicked(GuidedAction action) { - int actionId = (int) action.getId(); - mSelectedTrack = actionId < 0 ? null : mTrackInfos.get(actionId); - TvSettings.setDvrPlaybackTrackSettings(getContext(), mTrackType, mSelectedTrack); - getFragmentManager().popBackStack(); - } - - @Override - public void onStart() { - super.onStart(); - // Workaround: when overlay fragment is faded out, any focus will lost due to overlay - // fragment's implementation. So we disable overlay fragment's fading here to prevent - // losing focus while users are interacting with the side fragment. - mOverlayFragment.setFadingEnabled(false); - } - - @Override - public void onStop() { - super.onStop(); - // We disable fading of overlay fragment to prevent side fragment from losing focus, - // therefore we should resume it here. - mOverlayFragment.setFadingEnabled(true); - mOverlayFragment.selectTrack(mTrackType, mSelectedTrack); - } - - private String getTrackLabel(TvTrackInfo track, int trackIndex) { - if (track.getLanguage() != null) { - return new Locale(track.getLanguage()).getDisplayName(); - } - return track.getType() == TvTrackInfo.TYPE_SUBTITLE ? - getString(R.string.closed_caption_unknown_language, trackIndex + 1) - : getString(R.string.multi_audio_unknown_language); - } - - @Override - protected void onProvideFragmentTransitions() { - super.onProvideFragmentTransitions(); - // Excludes the background scrim from transition to prevent the blinking caused by - // hiding the overlay fragment and sliding in the side fragment at the same time. - Transition t = getEnterTransition(); - if (t != null) { - t.excludeTarget(R.id.guidedstep_background, true); - } - } -}
\ No newline at end of file diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlayer.java b/src/com/android/tv/dvr/ui/playback/DvrPlayer.java deleted file mode 100644 index 780bfb2f..00000000 --- a/src/com/android/tv/dvr/ui/playback/DvrPlayer.java +++ /dev/null @@ -1,583 +0,0 @@ -/* - * 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.playback; - -import android.media.PlaybackParams; -import android.media.tv.TvContentRating; -import android.media.tv.TvInputManager; -import android.media.tv.TvTrackInfo; -import android.media.tv.TvView; -import android.media.session.PlaybackState; -import android.text.TextUtils; -import android.util.Log; - -import com.android.tv.dvr.data.RecordedProgram; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -class DvrPlayer { - private static final String TAG = "DvrPlayer"; - private static final boolean DEBUG = false; - - /** - * The max rewinding speed supported by DVR player. - */ - public static final int MAX_REWIND_SPEED = 256; - /** - * The max fast-forwarding speed supported by DVR player. - */ - public static final int MAX_FAST_FORWARD_SPEED = 256; - - private static final long SEEK_POSITION_MARGIN_MS = TimeUnit.SECONDS.toMillis(2); - private static final long REWIND_POSITION_MARGIN_MS = 32; // Workaround value. b/29994826 - - private RecordedProgram mProgram; - private long mInitialSeekPositionMs; - private final TvView mTvView; - private DvrPlayerCallback mCallback; - private OnAspectRatioChangedListener mOnAspectRatioChangedListener; - private OnContentBlockedListener mOnContentBlockedListener; - private OnTracksAvailabilityChangedListener mOnTracksAvailabilityChangedListener; - private OnTrackSelectedListener mOnAudioTrackSelectedListener; - private OnTrackSelectedListener mOnSubtitleTrackSelectedListener; - private String mSelectedAudioTrackId; - private String mSelectedSubtitleTrackId; - private float mAspectRatio = Float.NaN; - private int mPlaybackState = PlaybackState.STATE_NONE; - private long mTimeShiftCurrentPositionMs; - private boolean mPauseOnPrepared; - private boolean mHasClosedCaption; - private boolean mHasMultiAudio; - private final PlaybackParams mPlaybackParams = new PlaybackParams(); - private final DvrPlayerCallback mEmptyCallback = new DvrPlayerCallback(); - private long mStartPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME; - private boolean mTimeShiftPlayAvailable; - - public static class DvrPlayerCallback { - /** - * Called when the playback position is changed. The normal updating frequency is - * around 1 sec., which is restricted to the implementation of - * {@link android.media.tv.TvInputService}. - */ - public void onPlaybackPositionChanged(long positionMs) { } - /** - * Called when the playback state or the playback speed is changed. - */ - public void onPlaybackStateChanged(int playbackState, int playbackSpeed) { } - /** - * Called when the playback toward the end. - */ - public void onPlaybackEnded() { } - } - - public interface OnAspectRatioChangedListener { - /** - * Called when the Video's aspect ratio is changed. - * - * @param videoAspectRatio The aspect ratio of video. 0 stands for unknown ratios. - * Listeners should handle it carefully. - */ - void onAspectRatioChanged(float videoAspectRatio); - } - - public interface OnContentBlockedListener { - /** - * Called when the Video's aspect ratio is changed. - */ - void onContentBlocked(TvContentRating rating); - } - - public interface OnTracksAvailabilityChangedListener { - /** - * Called when the Video's subtitle or audio tracks are changed. - */ - void onTracksAvailabilityChanged(boolean hasClosedCaption, boolean hasMultiAudio); - } - - public interface OnTrackSelectedListener { - /** - * Called when certain subtitle or audio track is selected. - */ - void onTrackSelected(String selectedTrackId); - } - - public DvrPlayer(TvView tvView) { - mTvView = tvView; - mTvView.setCaptionEnabled(true); - mPlaybackParams.setSpeed(1.0f); - setTvViewCallbacks(); - setCallback(null); - } - - /** - * Prepares playback. - * - * @param doPlay indicates DVR player do or do not start playback after media is prepared. - */ - public void prepare(boolean doPlay) throws IllegalStateException { - if (DEBUG) Log.d(TAG, "prepare()"); - if (mProgram == null) { - throw new IllegalStateException("Recorded program not set"); - } else if (mPlaybackState != PlaybackState.STATE_NONE) { - throw new IllegalStateException("Playback is already prepared"); - } - mTvView.timeShiftPlay(mProgram.getInputId(), mProgram.getUri()); - mPlaybackState = PlaybackState.STATE_CONNECTING; - mPauseOnPrepared = !doPlay; - mCallback.onPlaybackStateChanged(mPlaybackState, 1); - } - - /** - * Resumes playback. - */ - public void play() throws IllegalStateException { - if (DEBUG) Log.d(TAG, "play()"); - if (!isPlaybackPrepared()) { - throw new IllegalStateException("Recorded program not set or video not ready yet"); - } - switch (mPlaybackState) { - case PlaybackState.STATE_FAST_FORWARDING: - case PlaybackState.STATE_REWINDING: - setPlaybackSpeed(1); - break; - default: - mTvView.timeShiftResume(); - } - mPlaybackState = PlaybackState.STATE_PLAYING; - mCallback.onPlaybackStateChanged(mPlaybackState, 1); - } - - /** - * Pauses playback. - */ - public void pause() throws IllegalStateException { - if (DEBUG) Log.d(TAG, "pause()"); - if (!isPlaybackPrepared()) { - throw new IllegalStateException("Recorded program not set or playback not started yet"); - } - switch (mPlaybackState) { - case PlaybackState.STATE_FAST_FORWARDING: - case PlaybackState.STATE_REWINDING: - setPlaybackSpeed(1); - // falls through - case PlaybackState.STATE_PLAYING: - mTvView.timeShiftPause(); - mPlaybackState = PlaybackState.STATE_PAUSED; - break; - default: - break; - } - mCallback.onPlaybackStateChanged(mPlaybackState, 1); - } - - /** - * Fast-forwards playback with the given speed. If the given speed is larger than - * {@value #MAX_FAST_FORWARD_SPEED}, uses {@value #MAX_FAST_FORWARD_SPEED}. - */ - public void fastForward(int speed) throws IllegalStateException { - if (DEBUG) Log.d(TAG, "fastForward()"); - if (!isPlaybackPrepared()) { - throw new IllegalStateException("Recorded program not set or playback not started yet"); - } - if (speed <= 0) { - throw new IllegalArgumentException("Speed cannot be negative or 0"); - } - if (mTimeShiftCurrentPositionMs >= mProgram.getDurationMillis() - SEEK_POSITION_MARGIN_MS) { - return; - } - speed = Math.min(speed, MAX_FAST_FORWARD_SPEED); - if (DEBUG) Log.d(TAG, "Let's play with speed: " + speed); - setPlaybackSpeed(speed); - mPlaybackState = PlaybackState.STATE_FAST_FORWARDING; - mCallback.onPlaybackStateChanged(mPlaybackState, speed); - } - - /** - * Rewinds playback with the given speed. If the given speed is larger than - * {@value #MAX_REWIND_SPEED}, uses {@value #MAX_REWIND_SPEED}. - */ - public void rewind(int speed) throws IllegalStateException { - if (DEBUG) Log.d(TAG, "rewind()"); - if (!isPlaybackPrepared()) { - throw new IllegalStateException("Recorded program not set or playback not started yet"); - } - if (speed <= 0) { - throw new IllegalArgumentException("Speed cannot be negative or 0"); - } - if (mTimeShiftCurrentPositionMs <= REWIND_POSITION_MARGIN_MS) { - return; - } - speed = Math.min(speed, MAX_REWIND_SPEED); - if (DEBUG) Log.d(TAG, "Let's play with speed: " + speed); - setPlaybackSpeed(-speed); - mPlaybackState = PlaybackState.STATE_REWINDING; - mCallback.onPlaybackStateChanged(mPlaybackState, speed); - } - - /** - * Seeks playback to the specified position. - */ - public void seekTo(long positionMs) throws IllegalStateException { - if (DEBUG) Log.d(TAG, "seekTo()"); - if (!isPlaybackPrepared()) { - throw new IllegalStateException("Recorded program not set or playback not started yet"); - } - if (mProgram == null || mPlaybackState == PlaybackState.STATE_NONE) { - return; - } - positionMs = getRealSeekPosition(positionMs, SEEK_POSITION_MARGIN_MS); - if (DEBUG) Log.d(TAG, "Now: " + getPlaybackPosition() + ", shift to: " + positionMs); - mTvView.timeShiftSeekTo(positionMs + mStartPositionMs); - if (mPlaybackState == PlaybackState.STATE_FAST_FORWARDING || - mPlaybackState == PlaybackState.STATE_REWINDING) { - mPlaybackState = PlaybackState.STATE_PLAYING; - mTvView.timeShiftResume(); - mCallback.onPlaybackStateChanged(mPlaybackState, 1); - } - } - - /** - * Resets playback. - */ - public void reset() { - if (DEBUG) Log.d(TAG, "reset()"); - mCallback.onPlaybackStateChanged(PlaybackState.STATE_NONE, 1); - mPlaybackState = PlaybackState.STATE_NONE; - mTvView.reset(); - mTimeShiftPlayAvailable = false; - mStartPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME; - mTimeShiftCurrentPositionMs = 0; - mPlaybackParams.setSpeed(1.0f); - mProgram = null; - mSelectedAudioTrackId = null; - mSelectedSubtitleTrackId = null; - } - - /** - * Sets callbacks for playback. - */ - public void setCallback(DvrPlayerCallback callback) { - if (callback != null) { - mCallback = callback; - } else { - mCallback = mEmptyCallback; - } - } - - /** - * Sets the listener to aspect ratio changing. - */ - public void setOnAspectRatioChangedListener(OnAspectRatioChangedListener listener) { - mOnAspectRatioChangedListener = listener; - } - - /** - * Sets the listener to content blocking. - */ - public void setOnContentBlockedListener(OnContentBlockedListener listener) { - mOnContentBlockedListener = listener; - } - - /** - * Sets the listener to tracks changing. - */ - public void setOnTracksAvailabilityChangedListener( - OnTracksAvailabilityChangedListener listener) { - mOnTracksAvailabilityChangedListener = listener; - } - - /** - * Sets the listener to tracks of the given type being selected. - * - * @param trackType should be either {@link TvTrackInfo#TYPE_AUDIO} - * or {@link TvTrackInfo#TYPE_SUBTITLE}. - */ - public void setOnTrackSelectedListener(int trackType, OnTrackSelectedListener listener) { - if (trackType == TvTrackInfo.TYPE_AUDIO) { - mOnAudioTrackSelectedListener = listener; - } else if (trackType == TvTrackInfo.TYPE_SUBTITLE) { - mOnSubtitleTrackSelectedListener = listener; - } - } - - /** - * Gets the listener to tracks of the given type being selected. - */ - public OnTrackSelectedListener getOnTrackSelectedListener(int trackType) { - if (trackType == TvTrackInfo.TYPE_AUDIO) { - return mOnAudioTrackSelectedListener; - } else if (trackType == TvTrackInfo.TYPE_SUBTITLE) { - return mOnSubtitleTrackSelectedListener; - } - return null; - } - - /** - * Sets recorded programs for playback. If the player is playing another program, stops it. - */ - public void setProgram(RecordedProgram program, long initialSeekPositionMs) { - if (mProgram != null && mProgram.equals(program)) { - return; - } - if (mPlaybackState != PlaybackState.STATE_NONE) { - reset(); - } - mInitialSeekPositionMs = initialSeekPositionMs; - mProgram = program; - } - - /** - * Returns the recorded program now playing. - */ - public RecordedProgram getProgram() { - return mProgram; - } - - /** - * Returns the currrent playback posistion in msecs. - */ - public long getPlaybackPosition() { - return mTimeShiftCurrentPositionMs; - } - - /** - * Returns the playback speed currently used. - */ - public int getPlaybackSpeed() { - return (int) mPlaybackParams.getSpeed(); - } - - /** - * Returns the playback state defined in {@link android.media.session.PlaybackState}. - */ - public int getPlaybackState() { - return mPlaybackState; - } - - /** - * Returns the subtitle tracks of the current playback. - */ - public ArrayList<TvTrackInfo> getSubtitleTracks() { - return new ArrayList<>(mTvView.getTracks(TvTrackInfo.TYPE_SUBTITLE)); - } - - /** - * Returns the audio tracks of the current playback. - */ - public ArrayList<TvTrackInfo> getAudioTracks() { - return new ArrayList<>(mTvView.getTracks(TvTrackInfo.TYPE_AUDIO)); - } - - /** - * Returns the ID of the selected track of the given type. - */ - public String getSelectedTrackId(int trackType) { - if (trackType == TvTrackInfo.TYPE_AUDIO) { - return mSelectedAudioTrackId; - } else if (trackType == TvTrackInfo.TYPE_SUBTITLE) { - return mSelectedSubtitleTrackId; - } - return null; - } - - /** - * Returns if playback of the recorded program is started. - */ - public boolean isPlaybackPrepared() { - return mPlaybackState != PlaybackState.STATE_NONE - && mPlaybackState != PlaybackState.STATE_CONNECTING; - } - - /** - * Selects the given track. - * - * @return ID of the selected track. - */ - String selectTrack(int trackType, TvTrackInfo selectedTrack) { - String oldSelectedTrackId = getSelectedTrackId(trackType); - String newSelectedTrackId = selectedTrack == null ? null : selectedTrack.getId(); - if (!TextUtils.equals(oldSelectedTrackId, newSelectedTrackId)) { - if (selectedTrack == null) { - mTvView.selectTrack(trackType, null); - return null; - } else { - List<TvTrackInfo> tracks = mTvView.getTracks(trackType); - if (tracks != null && tracks.contains(selectedTrack)) { - mTvView.selectTrack(trackType, newSelectedTrackId); - return newSelectedTrackId; - } else if (trackType == TvTrackInfo.TYPE_SUBTITLE && oldSelectedTrackId != null) { - // Track not found, disabled closed caption. - mTvView.selectTrack(trackType, null); - return null; - } - } - } - return oldSelectedTrackId; - } - - private void setSelectedTrackId(int trackType, String trackId) { - if (trackType == TvTrackInfo.TYPE_AUDIO) { - mSelectedAudioTrackId = trackId; - } else if (trackType == TvTrackInfo.TYPE_SUBTITLE) { - mSelectedSubtitleTrackId = trackId; - } - } - - private void setPlaybackSpeed(int speed) { - mPlaybackParams.setSpeed(speed); - mTvView.timeShiftSetPlaybackParams(mPlaybackParams); - } - - private long getRealSeekPosition(long seekPositionMs, long endMarginMs) { - return Math.max(0, Math.min(seekPositionMs, mProgram.getDurationMillis() - endMarginMs)); - } - - private void setTvViewCallbacks() { - mTvView.setTimeShiftPositionCallback(new TvView.TimeShiftPositionCallback() { - @Override - public void onTimeShiftStartPositionChanged(String inputId, long timeMs) { - if (DEBUG) Log.d(TAG, "onTimeShiftStartPositionChanged:" + timeMs); - mStartPositionMs = timeMs; - if (mTimeShiftPlayAvailable) { - resumeToWatchedPositionIfNeeded(); - } - } - - @Override - public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) { - if (DEBUG) Log.d(TAG, "onTimeShiftCurrentPositionChanged: " + timeMs); - if (!mTimeShiftPlayAvailable) { - // Workaround of b/31436263 - return; - } - // Workaround of b/32211561, TIF won't report start position when TIS report - // its start position as 0. In that case, we have to do the prework of playback - // on the first time we get current position, and the start position should be 0 - // at that time. - if (mStartPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME) { - mStartPositionMs = 0; - resumeToWatchedPositionIfNeeded(); - } - timeMs -= mStartPositionMs; - if (mPlaybackState == PlaybackState.STATE_REWINDING - && timeMs <= REWIND_POSITION_MARGIN_MS) { - play(); - } else { - mTimeShiftCurrentPositionMs = getRealSeekPosition(timeMs, 0); - mCallback.onPlaybackPositionChanged(mTimeShiftCurrentPositionMs); - if (timeMs >= mProgram.getDurationMillis()) { - pause(); - mCallback.onPlaybackEnded(); - } - } - } - }); - mTvView.setCallback(new TvView.TvInputCallback() { - @Override - public void onTimeShiftStatusChanged(String inputId, int status) { - if (DEBUG) Log.d(TAG, "onTimeShiftStatusChanged:" + status); - if (status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE - && mPlaybackState == PlaybackState.STATE_CONNECTING) { - mTimeShiftPlayAvailable = true; - if (mStartPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) { - // onTimeShiftStatusChanged is sometimes called after - // onTimeShiftStartPositionChanged is called. In this case, - // resumeToWatchedPositionIfNeeded needs to be called here. - resumeToWatchedPositionIfNeeded(); - } - } - } - - @Override - public void onTracksChanged(String inputId, List<TvTrackInfo> tracks) { - boolean hasClosedCaption = - !mTvView.getTracks(TvTrackInfo.TYPE_SUBTITLE).isEmpty(); - boolean hasMultiAudio = mTvView.getTracks(TvTrackInfo.TYPE_AUDIO).size() > 1; - if ((hasClosedCaption != mHasClosedCaption || hasMultiAudio != mHasMultiAudio) - && mOnTracksAvailabilityChangedListener != null) { - mOnTracksAvailabilityChangedListener - .onTracksAvailabilityChanged(hasClosedCaption, hasMultiAudio); - } - mHasClosedCaption = hasClosedCaption; - mHasMultiAudio = hasMultiAudio; - } - - @Override - public void onTrackSelected(String inputId, int type, String trackId) { - if (type == TvTrackInfo.TYPE_AUDIO || type == TvTrackInfo.TYPE_SUBTITLE) { - setSelectedTrackId(type, trackId); - OnTrackSelectedListener listener = getOnTrackSelectedListener(type); - if (listener != null) { - listener.onTrackSelected(trackId); - } - } else if (type == TvTrackInfo.TYPE_VIDEO && trackId != null - && mOnAspectRatioChangedListener != null) { - List<TvTrackInfo> trackInfos = mTvView.getTracks(TvTrackInfo.TYPE_VIDEO); - if (trackInfos != null) { - for (TvTrackInfo trackInfo : trackInfos) { - if (trackInfo.getId().equals(trackId)) { - float videoAspectRatio; - int videoWidth = trackInfo.getVideoWidth(); - int videoHeight = trackInfo.getVideoHeight(); - if (videoWidth > 0 && videoHeight > 0) { - videoAspectRatio = trackInfo.getVideoPixelAspectRatio() - * trackInfo.getVideoWidth() / trackInfo.getVideoHeight(); - } else { - // Aspect ratio is unknown. Pass the message to listeners. - videoAspectRatio = 0; - } - if (DEBUG) Log.d(TAG, "Aspect Ratio: " + videoAspectRatio); - if (mAspectRatio != videoAspectRatio || videoAspectRatio == 0) { - mOnAspectRatioChangedListener - .onAspectRatioChanged(videoAspectRatio); - mAspectRatio = videoAspectRatio; - return; - } - } - } - } - } - } - - @Override - public void onContentBlocked(String inputId, TvContentRating rating) { - if (mOnContentBlockedListener != null) { - mOnContentBlockedListener.onContentBlocked(rating); - } - } - }); - } - - private void resumeToWatchedPositionIfNeeded() { - if (mInitialSeekPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) { - mTvView.timeShiftSeekTo(getRealSeekPosition(mInitialSeekPositionMs, - SEEK_POSITION_MARGIN_MS) + mStartPositionMs); - mInitialSeekPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME; - } - if (mPauseOnPrepared) { - mTvView.timeShiftPause(); - mPlaybackState = PlaybackState.STATE_PAUSED; - mPauseOnPrepared = false; - } else { - mTvView.timeShiftResume(); - mPlaybackState = PlaybackState.STATE_PLAYING; - } - mCallback.onPlaybackStateChanged(mPlaybackState, 1); - } -}
\ No newline at end of file |