aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/dvr/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/dvr/ui')
-rw-r--r--src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java4
-rw-r--r--src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java2
-rw-r--r--src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java2
-rw-r--r--src/com/android/tv/dvr/ui/DvrConflictFragment.java4
-rw-r--r--src/com/android/tv/dvr/ui/DvrFutureProgramInfoFragment.java87
-rw-r--r--src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java27
-rw-r--r--src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java4
-rw-r--r--src/com/android/tv/dvr/ui/DvrScheduleFragment.java8
-rw-r--r--src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java70
-rw-r--r--src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java78
-rw-r--r--src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java4
-rw-r--r--src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java2
-rw-r--r--src/com/android/tv/dvr/ui/DvrUiHelper.java43
-rw-r--r--src/com/android/tv/dvr/ui/DvrWriteStoragePermissionRationaleFragment.java60
-rw-r--r--src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java8
-rw-r--r--src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java69
-rw-r--r--src/com/android/tv/dvr/ui/browse/DetailsContent.java94
-rw-r--r--src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java8
-rw-r--r--src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java2
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java6
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java213
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java144
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java15
-rw-r--r--src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java22
-rw-r--r--src/com/android/tv/dvr/ui/browse/RecordingCardView.java38
-rw-r--r--src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java3
-rw-r--r--src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java17
-rw-r--r--src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java14
-rw-r--r--src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java17
-rw-r--r--src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java147
-rw-r--r--src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java8
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java6
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java35
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java11
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java30
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlayer.java43
36 files changed, 739 insertions, 606 deletions
diff --git a/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java b/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java
index 32679421..9cd91a64 100644
--- a/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java
+++ b/src/com/android/tv/dvr/ui/ChangeImageTransformWithScaledParent.java
@@ -27,13 +27,15 @@ import android.view.View;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import com.android.tv.R;
+import com.android.tv.ui.DetailsActivity;
+
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}.
+ * DetailsActivity}.
*/
public class ChangeImageTransformWithScaledParent extends ChangeImageTransform {
private static final String PROPNAME_MATRIX = "android:changeImageTransform:matrix";
diff --git a/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java b/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java
index fce94230..5e3caa9c 100644
--- a/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrAlreadyRecordedFragment.java
@@ -71,7 +71,7 @@ public class DvrAlreadyRecordedFragment extends DvrGuidedStepFragment {
public Guidance onCreateGuidance(Bundle savedInstanceState) {
String title = getString(R.string.dvr_already_recorded_dialog_title);
String description = getString(R.string.dvr_already_recorded_dialog_description);
- Drawable image = getResources().getDrawable(R.drawable.ic_warning_white_96dp, null);
+ Drawable image = getResources().getDrawable(R.drawable.quantum_ic_warning_white_96, null);
return new Guidance(title, description, null, image);
}
diff --git a/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java b/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java
index 456ad830..a6bbe137 100644
--- a/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrAlreadyScheduledFragment.java
@@ -78,7 +78,7 @@ public class DvrAlreadyScheduledFragment extends DvrGuidedStepFragment {
getContext(),
mDuplicate.getStartTimeMs(),
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE));
- Drawable image = getResources().getDrawable(R.drawable.ic_warning_white_96dp, null);
+ Drawable image = getResources().getDrawable(R.drawable.quantum_ic_warning_white_96, null);
return new Guidance(title, description, null, image);
}
diff --git a/src/com/android/tv/dvr/ui/DvrConflictFragment.java b/src/com/android/tv/dvr/ui/DvrConflictFragment.java
index 65759555..649cc89a 100644
--- a/src/com/android/tv/dvr/ui/DvrConflictFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrConflictFragment.java
@@ -205,7 +205,7 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
if (description == null) {
dismissDialog();
}
- Drawable icon = getResources().getDrawable(R.drawable.ic_error_white_48dp, null);
+ Drawable icon = getResources().getDrawable(R.drawable.quantum_ic_error_white_48, null);
return new Guidance(title, descriptionPrefix + " " + description, null, icon);
}
@@ -265,7 +265,7 @@ public abstract class DvrConflictFragment extends DvrGuidedStepFragment {
if (description == null) {
dismissDialog();
}
- Drawable icon = getResources().getDrawable(R.drawable.ic_error_white_48dp, null);
+ Drawable icon = getResources().getDrawable(R.drawable.quantum_ic_error_white_48, null);
return new Guidance(title, descriptionPrefix + " " + description, null, icon);
}
diff --git a/src/com/android/tv/dvr/ui/DvrFutureProgramInfoFragment.java b/src/com/android/tv/dvr/ui/DvrFutureProgramInfoFragment.java
deleted file mode 100644
index 677a6cbb..00000000
--- a/src/com/android/tv/dvr/ui/DvrFutureProgramInfoFragment.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2018 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.v17.leanback.widget.GuidanceStylist;
-import android.support.v17.leanback.widget.GuidedAction;
-import com.android.tv.TvSingletons;
-import com.android.tv.data.Program;
-import com.android.tv.dvr.data.ScheduledRecording;
-import com.android.tv.util.Utils;
-import java.util.List;
-
-/**
- * A fragment which shows the formation of a program.
- */
-public class DvrFutureProgramInfoFragment extends DvrGuidedStepFragment {
- private static final long ACTION_ID_VIEW_SCHEDULE = 1;
- private ScheduledRecording mScheduledRecording;
- private Program mProgram;
-
- @Override
- public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
- long startTime = mProgram.getStartTimeUtcMillis();
- // TODO(b/71717923): use R.string when the strings are finalized
- StringBuilder description = new StringBuilder()
- .append("This program will start at ")
- .append(Utils.getDurationString(getContext(), startTime, startTime, false));
- if (mScheduledRecording != null) {
- description.append("\nThis program has been scheduled for recording.");
- }
- return new GuidanceStylist.Guidance(
- mProgram.getTitle(), description.toString(), null, null);
- }
-
- @Override
- public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
- Activity activity = getActivity();
- mProgram = getArguments().getParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM);
- mScheduledRecording =
- TvSingletons.getSingletons(getContext())
- .getDvrDataManager()
- .getScheduledRecordingForProgramId(mProgram.getId());
- actions.add(
- new GuidedAction.Builder(activity)
- .id(GuidedAction.ACTION_ID_OK)
- .title(android.R.string.ok)
- .build());
- if (mScheduledRecording != null) {
- actions.add(
- new GuidedAction.Builder(activity)
- .id(ACTION_ID_VIEW_SCHEDULE)
- .title("View schedules")
- .build());
- }
-
- }
-
- @Override
- public void onTrackedGuidedActionClicked(GuidedAction action) {
- if (action.getId() == ACTION_ID_VIEW_SCHEDULE) {
- DvrUiHelper.startSchedulesActivity(getContext(), mScheduledRecording);
- return;
- }
- dismissDialog();
- }
-
- @Override
- public String getTrackerPrefix() {
- return "DvrFutureProgramInfoFragment";
- }
-}
diff --git a/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java b/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java
index 4a713703..e6b54f67 100644
--- a/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrHalfSizedDialogFragment.java
@@ -18,17 +18,20 @@ package com.android.tv.dvr.ui;
import android.app.Activity;
import android.content.Context;
+import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v17.leanback.app.GuidedStepFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+
import com.android.tv.MainActivity;
import com.android.tv.R;
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;
+import com.android.tv.ui.DetailsActivity;
public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment {
/** Key for input ID. Type: String. */
@@ -187,11 +190,27 @@ public class DvrHalfSizedDialogFragment extends HalfSizedDialogFragment {
}
}
- /** A dialog fragment for {@link DvrFutureProgramInfoFragment}. */
- public static class DvrFutureProgramInfoDialogFragment extends DvrGuidedStepDialogFragment {
+ /** A dialog fragment for {@link DvrWriteStoragePermissionRationaleFragment}. */
+ public static class DvrWriteStoragePermissionRationaleDialogFragment
+ extends DvrGuidedStepDialogFragment {
@Override
- protected DvrGuidedStepFragment onCreateGuidedStepFragment() {
- return new DvrFutureProgramInfoFragment();
+ protected DvrWriteStoragePermissionRationaleFragment onCreateGuidedStepFragment() {
+ return new DvrWriteStoragePermissionRationaleFragment();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ Activity activity = getActivity();
+ if (activity instanceof DetailsActivity) {
+ activity.requestPermissions(
+ new String[] {"android.permission.WRITE_EXTERNAL_STORAGE"},
+ DetailsActivity.REQUEST_DELETE);
+ } else if (activity instanceof DvrSeriesDeletionActivity) {
+ activity.requestPermissions(
+ new String[] {"android.permission.WRITE_EXTERNAL_STORAGE"},
+ DvrSeriesDeletionActivity.REQUEST_DELETE);
+ }
+ super.onDismiss(dialog);
}
}
}
diff --git a/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java b/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java
index e5f40260..02b2da1d 100644
--- a/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrMissingStorageErrorFragment.java
@@ -25,7 +25,7 @@ import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
import android.util.Log;
import com.android.tv.R;
-import com.android.tv.dvr.ui.browse.DvrDetailsActivity;
+import com.android.tv.ui.DetailsActivity;
import java.util.List;
public class DvrMissingStorageErrorFragment extends DvrGuidedStepFragment {
@@ -65,7 +65,7 @@ public class DvrMissingStorageErrorFragment extends DvrGuidedStepFragment {
@Override
public void onTrackedGuidedActionClicked(GuidedAction action) {
Activity activity = getActivity();
- if (activity instanceof DvrDetailsActivity) {
+ if (activity instanceof DetailsActivity) {
activity.finish();
} else {
dismissDialog();
diff --git a/src/com/android/tv/dvr/ui/DvrScheduleFragment.java b/src/com/android/tv/dvr/ui/DvrScheduleFragment.java
index 5251e140..72603d03 100644
--- a/src/com/android/tv/dvr/ui/DvrScheduleFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrScheduleFragment.java
@@ -34,7 +34,6 @@ 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.ui.DvrConflictFragment.DvrProgramConflictFragment;
-import com.android.tv.util.Utils;
import java.util.Collections;
import java.util.List;
@@ -104,12 +103,7 @@ public class DvrScheduleFragment extends DvrGuidedStepFragment {
mProgram.getEndTimeUtcMillis(),
DateUtils.FORMAT_SHOW_TIME));
} else {
- description =
- Utils.getDurationString(
- context,
- mProgram.getStartTimeUtcMillis(),
- mProgram.getEndTimeUtcMillis(),
- true);
+ description = mProgram.getDurationString(context);
}
actions.add(
new GuidedAction.Builder(context)
diff --git a/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java b/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java
index a2ae1f97..a237f1d2 100644
--- a/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java
+++ b/src/com/android/tv/dvr/ui/DvrSeriesDeletionActivity.java
@@ -17,16 +17,34 @@
package com.android.tv.dvr.ui;
import android.app.Activity;
+import android.content.pm.PackageManager;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v17.leanback.app.GuidedStepFragment;
+import android.util.Log;
+import android.widget.Toast;
+
import com.android.tv.R;
import com.android.tv.Starter;
+import com.android.tv.TvSingletons;
+import com.android.tv.dvr.DvrManager;
+
+import java.util.ArrayList;
+import java.util.List;
/** Activity to show details view in DVR. */
public class DvrSeriesDeletionActivity extends Activity {
+ private static final String TAG = "DvrSeriesDeletionActivity";
+
/** Name of series id added to the Intent. */
public static final String SERIES_RECORDING_ID = "series_recording_id";
+ public static final int REQUEST_DELETE = 1;
+ public static final long INVALID_SERIES_RECORDING_ID = -1;
+
+ private long mSeriesRecordingId = INVALID_SERIES_RECORDING_ID;
+ private final List<Long> mIdsToDelete = new ArrayList<>();
+
@Override
public void onCreate(Bundle savedInstanceState) {
Starter.start(this);
@@ -34,9 +52,61 @@ 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) {
+ mSeriesRecordingId =
+ getIntent().getLongExtra(SERIES_RECORDING_ID, INVALID_SERIES_RECORDING_ID);
DvrSeriesDeletionFragment deletionFragment = new DvrSeriesDeletionFragment();
deletionFragment.setArguments(getIntent().getExtras());
GuidedStepFragment.addAsRoot(this, deletionFragment, R.id.dvr_settings_view_frame);
}
}
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ switch (requestCode) {
+ case REQUEST_DELETE:
+ // If request is cancelled, the result arrays are empty.
+ if (grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ deleteSelectedIds(true);
+ } else {
+ // NOTE: If Live TV ever supports both embedded and separate DVR inputs
+ // then we should try to do the delete regardless.
+ Log.i(
+ TAG,
+ "Write permission denied, Not trying to delete the files for series "
+ + mSeriesRecordingId);
+ deleteSelectedIds(false);
+ }
+ break;
+ default:
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+ }
+
+ private void deleteSelectedIds(boolean deleteFiles) {
+ TvSingletons singletons = TvSingletons.getSingletons(this);
+ int recordingSize =
+ singletons.getDvrDataManager().getRecordedPrograms(mSeriesRecordingId).size();
+ if (!mIdsToDelete.isEmpty()) {
+ DvrManager dvrManager = singletons.getDvrManager();
+ dvrManager.removeRecordedPrograms(mIdsToDelete, deleteFiles);
+ }
+ Toast.makeText(
+ this,
+ getResources()
+ .getQuantityString(
+ R.plurals.dvr_msg_episodes_deleted,
+ mIdsToDelete.size(),
+ mIdsToDelete.size(),
+ recordingSize),
+ Toast.LENGTH_LONG)
+ .show();
+ finish();
+ }
+
+ void setIdsToDelete(List<Long> ids) {
+ mIdsToDelete.clear();
+ mIdsToDelete.addAll(ids);
+ }
}
diff --git a/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java b/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java
index 685f0a58..ff213231 100644
--- a/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrSeriesDeletionFragment.java
@@ -29,6 +29,7 @@ import android.widget.Toast;
import com.android.tv.R;
import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.util.PermissionUtils;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.DvrWatchedPositionManager;
@@ -53,10 +54,12 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment {
private static final long ACTION_ID_SELECT_ALL = -111;
private static final long ACTION_ID_DELETE = -112;
+ private DvrManager mDvrManager;
private DvrDataManager mDvrDataManager;
private DvrWatchedPositionManager mDvrWatchedPositionManager;
private List<RecordedProgram> mRecordings;
private final Set<Long> mWatchedRecordings = new HashSet<>();
+ private final List<Long> mIdsToDelete = new ArrayList<>();
private boolean mAllSelected;
private long mSeriesRecordingId;
private int mOneLineActionHeight;
@@ -67,9 +70,10 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment {
mSeriesRecordingId =
getArguments().getLong(DvrSeriesDeletionActivity.SERIES_RECORDING_ID, -1);
SoftPreconditions.checkArgument(mSeriesRecordingId != -1);
- mDvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager();
- mDvrWatchedPositionManager =
- TvSingletons.getSingletons(context).getDvrWatchedPositionManager();
+ TvSingletons singletons = TvSingletons.getSingletons(context);
+ mDvrManager = singletons.getDvrManager();
+ mDvrDataManager = singletons.getDvrDataManager();
+ mDvrWatchedPositionManager = singletons.getDvrWatchedPositionManager();
mRecordings = mDvrDataManager.getRecordedPrograms(mSeriesRecordingId);
mOneLineActionHeight =
getResources()
@@ -158,28 +162,7 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment {
public void onGuidedActionClicked(GuidedAction action) {
long actionId = action.getId();
if (actionId == ACTION_ID_DELETE) {
- List<Long> idsToDelete = new ArrayList<>();
- for (GuidedAction guidedAction : getActions()) {
- if (guidedAction.getCheckSetId() == GuidedAction.CHECKBOX_CHECK_SET_ID
- && guidedAction.isChecked()) {
- idsToDelete.add(guidedAction.getId());
- }
- }
- if (!idsToDelete.isEmpty()) {
- DvrManager dvrManager = TvSingletons.getSingletons(getActivity()).getDvrManager();
- dvrManager.removeRecordedPrograms(idsToDelete);
- }
- Toast.makeText(
- getContext(),
- getResources()
- .getQuantityString(
- R.plurals.dvr_msg_episodes_deleted,
- idsToDelete.size(),
- idsToDelete.size(),
- mRecordings.size()),
- Toast.LENGTH_LONG)
- .show();
- finishGuidedStepFragments();
+ delete();
} else if (actionId == GuidedAction.ACTION_ID_CANCEL) {
finishGuidedStepFragments();
} else if (actionId == ACTION_ID_SELECT_WATCHED) {
@@ -234,6 +217,51 @@ public class DvrSeriesDeletionFragment extends GuidedStepFragment {
};
}
+ private void delete() {
+ mIdsToDelete.clear();
+ for (GuidedAction guidedAction : getActions()) {
+ if (guidedAction.getCheckSetId() == GuidedAction.CHECKBOX_CHECK_SET_ID
+ && guidedAction.isChecked()) {
+ mIdsToDelete.add(guidedAction.getId());
+ }
+ }
+ ((DvrSeriesDeletionActivity) getActivity()).setIdsToDelete(mIdsToDelete);
+ if (!PermissionUtils.hasWriteExternalStorage(getContext())
+ && doesAnySelectedRecordedProgramNeedWritePermission()) {
+ DvrUiHelper.showWriteStoragePermissionRationaleDialog(getActivity());
+ } else {
+ deleteSelectedIds();
+ }
+ }
+
+ private boolean doesAnySelectedRecordedProgramNeedWritePermission() {
+ for (RecordedProgram r : mRecordings) {
+ if (mIdsToDelete.contains(r.getId())
+ && DvrManager.isFile(r.getDataUri())
+ && !DvrManager.isFromBundledInput(r)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void deleteSelectedIds() {
+ if (!mIdsToDelete.isEmpty()) {
+ mDvrManager.removeRecordedPrograms(mIdsToDelete, true);
+ }
+ Toast.makeText(
+ getContext(),
+ getResources()
+ .getQuantityString(
+ R.plurals.dvr_msg_episodes_deleted,
+ mIdsToDelete.size(),
+ mIdsToDelete.size(),
+ mRecordings.size()),
+ Toast.LENGTH_LONG)
+ .show();
+ finishGuidedStepFragments();
+ }
+
private String getWatchedString(long watchedPositionMs, long durationMs) {
if (durationMs > WATCHED_TIME_UNIT_THRESHOLD) {
return getResources()
diff --git a/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java b/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java
index edb62c96..c6e26850 100644
--- a/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrSeriesScheduledFragment.java
@@ -101,9 +101,9 @@ public class DvrSeriesScheduledFragment extends DvrGuidedStepFragment {
String title = getString(R.string.dvr_series_recording_dialog_title);
Drawable icon;
if (!mHasConflict) {
- icon = getResources().getDrawable(R.drawable.ic_check_circle_white_48dp, null);
+ icon = getResources().getDrawable(R.drawable.quantum_ic_check_circle_white_48, null);
} else {
- icon = getResources().getDrawable(R.drawable.ic_error_white_48dp, null);
+ icon = getResources().getDrawable(R.drawable.quantum_ic_error_white_48, null);
}
return new GuidanceStylist.Guidance(title, getDescription(), null, icon);
}
diff --git a/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java b/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java
index e93387ab..1ab4c500 100644
--- a/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java
+++ b/src/com/android/tv/dvr/ui/DvrStopRecordingFragment.java
@@ -126,7 +126,7 @@ public class DvrStopRecordingFragment extends DvrGuidedStepFragment {
} else {
description = getString(R.string.dvr_stop_recording_dialog_description);
}
- Drawable image = getResources().getDrawable(R.drawable.ic_warning_white_96dp, null);
+ Drawable image = getResources().getDrawable(R.drawable.quantum_ic_warning_white_96, null);
return new Guidance(title, description, null, image);
}
diff --git a/src/com/android/tv/dvr/ui/DvrUiHelper.java b/src/com/android/tv/dvr/ui/DvrUiHelper.java
index 16afbdef..a121cf99 100644
--- a/src/com/android/tv/dvr/ui/DvrUiHelper.java
+++ b/src/com/android/tv/dvr/ui/DvrUiHelper.java
@@ -37,10 +37,10 @@ import android.text.TextUtils;
import android.text.style.TextAppearanceSpan;
import android.widget.ImageView;
import android.widget.Toast;
+
import com.android.tv.MainActivity;
import com.android.tv.R;
import com.android.tv.TvSingletons;
-import com.android.tv.common.BuildConfig;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.recording.RecordingStorageStatusManager;
import com.android.tv.common.util.CommonUtils;
@@ -57,7 +57,6 @@ import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrAlreadyRecordedDialog
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.DvrFutureProgramInfoDialogFragment;
import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrInsufficientSpaceErrorDialogFragment;
import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrMissingStorageErrorDialogFragment;
import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrNoFreeSpaceErrorDialogFragment;
@@ -65,15 +64,17 @@ import com.android.tv.dvr.ui.DvrHalfSizedDialogFragment.DvrProgramConflictDialog
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.DvrHalfSizedDialogFragment.DvrWriteStoragePermissionRationaleDialogFragment;
import com.android.tv.dvr.ui.browse.DvrBrowseActivity;
-import com.android.tv.dvr.ui.browse.DvrDetailsActivity;
import com.android.tv.dvr.ui.list.DvrHistoryActivity;
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.dvr.ui.playback.DvrPlaybackActivity;
+import com.android.tv.ui.DetailsActivity;
import com.android.tv.util.ToastUtils;
import com.android.tv.util.Utils;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -241,13 +242,9 @@ public class DvrUiHelper {
}
/** Shows program information dialog. */
- public static void showProgramInfoDialog(Activity activity, Program program) {
- if (program == null || !BuildConfig.ENG) {
- return;
- }
- Bundle args = new Bundle();
- args.putParcelable(DvrHalfSizedDialogFragment.KEY_PROGRAM, program);
- showDialogFragment(activity, new DvrFutureProgramInfoDialogFragment(), args, false, true);
+ public static void showWriteStoragePermissionRationaleDialog(Activity activity) {
+ showDialogFragment(activity, new DvrWriteStoragePermissionRationaleDialogFragment(),
+ new Bundle(), false, false);
}
/**
@@ -577,47 +574,43 @@ public class DvrUiHelper {
if (dvrItem == null) {
return;
}
- Intent intent = new Intent(activity, DvrDetailsActivity.class);
+ Intent intent = new Intent(activity, DetailsActivity.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;
+ viewType = DetailsActivity.SCHEDULED_RECORDING_VIEW;
} else if (schedule.getState() == ScheduledRecording.STATE_RECORDING_IN_PROGRESS) {
- viewType = DvrDetailsActivity.CURRENT_RECORDING_VIEW;
+ viewType = DetailsActivity.CURRENT_RECORDING_VIEW;
} else if (schedule.getState() == ScheduledRecording.STATE_RECORDING_FINISHED
&& schedule.getRecordedProgramId() != null) {
recordingId = schedule.getRecordedProgramId();
- viewType = DvrDetailsActivity.RECORDED_PROGRAM_VIEW;
+ viewType = DetailsActivity.RECORDED_PROGRAM_VIEW;
} else if (schedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
- viewType = DvrDetailsActivity.SCHEDULED_RECORDING_VIEW;
+ viewType = DetailsActivity.SCHEDULED_RECORDING_VIEW;
hideViewSchedule = true;
- // TODO(b/72638385): pass detailed error message
- intent.putExtra(
- DvrDetailsActivity.EXTRA_FAILED_MESSAGE,
- activity.getString(R.string.dvr_recording_failed));
} else {
return;
}
} else if (dvrItem instanceof RecordedProgram) {
recordingId = ((RecordedProgram) dvrItem).getId();
- viewType = DvrDetailsActivity.RECORDED_PROGRAM_VIEW;
+ viewType = DetailsActivity.RECORDED_PROGRAM_VIEW;
} else if (dvrItem instanceof SeriesRecording) {
recordingId = ((SeriesRecording) dvrItem).getId();
- viewType = DvrDetailsActivity.SERIES_RECORDING_VIEW;
+ viewType = DetailsActivity.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);
+ intent.putExtra(DetailsActivity.RECORDING_ID, recordingId);
+ intent.putExtra(DetailsActivity.DETAILS_VIEW_TYPE, viewType);
+ intent.putExtra(DetailsActivity.HIDE_VIEW_SCHEDULE, hideViewSchedule);
Bundle bundle = null;
if (imageView != null) {
bundle =
ActivityOptionsCompat.makeSceneTransitionAnimation(
- activity, imageView, DvrDetailsActivity.SHARED_ELEMENT_NAME)
+ activity, imageView, DetailsActivity.SHARED_ELEMENT_NAME)
.toBundle();
}
activity.startActivity(intent, bundle);
diff --git a/src/com/android/tv/dvr/ui/DvrWriteStoragePermissionRationaleFragment.java b/src/com/android/tv/dvr/ui/DvrWriteStoragePermissionRationaleFragment.java
new file mode 100644
index 00000000..c93f5831
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/DvrWriteStoragePermissionRationaleFragment.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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.content.res.Resources;
+import android.os.Bundle;
+import android.support.v17.leanback.widget.GuidanceStylist;
+import android.support.v17.leanback.widget.GuidedAction;
+
+import com.android.tv.R;
+
+import java.util.List;
+
+/**
+ * A fragment which shows the rationale when requesting android.permission.WRITE_EXTERNAL_STORAGE.
+ */
+public class DvrWriteStoragePermissionRationaleFragment extends DvrGuidedStepFragment {
+ @Override
+ public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
+ Resources res = getContext().getResources();
+ String title = res.getString(R.string.write_storage_permission_rationale_title);
+ String description = res.getString(R.string.write_storage_permission_rationale_description);
+ return new GuidanceStylist.Guidance(title, description, null, null);
+ }
+
+ @Override
+ public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
+ Activity activity = getActivity();
+ actions.add(
+ new GuidedAction.Builder(activity)
+ .id(GuidedAction.ACTION_ID_OK)
+ .title(android.R.string.ok)
+ .build());
+ }
+
+ @Override
+ public void onTrackedGuidedActionClicked(GuidedAction action) {
+ dismissDialog();
+ }
+
+ @Override
+ public String getTrackerPrefix() {
+ return "DvrWriteStoragePermissionRationaleFragment";
+ }
+}
diff --git a/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java b/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java
index f3a6fea4..41ace9a4 100644
--- a/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java
+++ b/src/com/android/tv/dvr/ui/browse/ActionPresenterSelector.java
@@ -27,9 +27,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
-// This class is adapted from Leanback's library, which does not support action icon with one-line
-// label. This class modified its getPresenter method to support the above situation.
-class ActionPresenterSelector extends PresenterSelector {
+/**
+ * This class is adapted from Leanback's library, which does not support action icon with one-line
+ * label. This class modified its getPresenter method to support the above situation.
+ */
+public class ActionPresenterSelector extends PresenterSelector {
private final Presenter mOneLineActionPresenter = new OneLineActionPresenter();
private final Presenter mTwoLineActionPresenter = new TwoLineActionPresenter();
private final Presenter[] mPresenters =
diff --git a/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java
index 7e7e1f75..8c311d68 100644
--- a/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/CurrentRecordingDetailsFragment.java
@@ -18,23 +18,34 @@ package com.android.tv.dvr.ui.browse;
import android.content.Context;
import android.content.res.Resources;
+import android.media.tv.TvInputManager;
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.TvSingletons;
+import com.android.tv.common.flags.has.HasConcurrentDvrPlaybackFlags;
import com.android.tv.dialog.HalfSizedDialogFragment;
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.ScheduledRecording;
import com.android.tv.dvr.ui.DvrStopRecordingFragment;
import com.android.tv.dvr.ui.DvrUiHelper;
+import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags;
/** {@link RecordingDetailsFragment} for current recording in DVR. */
public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment {
private static final int ACTION_STOP_RECORDING = 1;
+ private static final int ACTION_RESUME_PLAYING = 2;
+ private static final int ACTION_PLAY_FROM_BEGINNING = 3;
private DvrDataManager mDvrDataManger;
+ private RecordedProgram mRecordedProgram;
+ private DvrWatchedPositionManager mDvrWatchedPositionManager;
+ private ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags;
+ private boolean mPaused;
private final DvrDataManager.ScheduledRecordingListener mScheduledRecordingListener =
new DvrDataManager.ScheduledRecordingListener() {
@Override
@@ -68,10 +79,32 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment {
super.onAttach(context);
mDvrDataManger = TvSingletons.getSingletons(context).getDvrDataManager();
mDvrDataManger.addScheduledRecordingListener(mScheduledRecordingListener);
+ mDvrWatchedPositionManager =
+ TvSingletons.getSingletons(getActivity()).getDvrWatchedPositionManager();
+ mConcurrentDvrPlaybackFlags = HasConcurrentDvrPlaybackFlags.fromContext(context);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (mPaused) {
+ updateActions();
+ mPaused = false;
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mPaused = true;
}
@Override
protected SparseArrayObjectAdapter onCreateActionsAdapter() {
+ Long recordedProgramId = getRecording().getRecordedProgramId();
+ if (recordedProgramId != null) {
+ mRecordedProgram = mDvrDataManger.getRecordedProgram(recordedProgramId);
+ }
SparseArrayObjectAdapter adapter =
new SparseArrayObjectAdapter(new ActionPresenterSelector());
Resources res = getResources();
@@ -82,6 +115,35 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment {
res.getString(R.string.dvr_detail_stop_recording),
null,
res.getDrawable(R.drawable.lb_ic_stop)));
+ if (mConcurrentDvrPlaybackFlags.enabled()
+ && mRecordedProgram != null
+ && mRecordedProgram.isPartial()) {
+ if (mDvrWatchedPositionManager.getWatchedStatus(mRecordedProgram)
+ == DvrWatchedPositionManager.DVR_WATCHED_STATUS_WATCHING) {
+ adapter.set(
+ ACTION_RESUME_PLAYING,
+ new Action(
+ ACTION_RESUME_PLAYING,
+ res.getString(R.string.dvr_detail_resume_play),
+ null,
+ res.getDrawable(R.drawable.lb_ic_play)));
+ adapter.set(
+ ACTION_PLAY_FROM_BEGINNING,
+ new Action(
+ ACTION_PLAY_FROM_BEGINNING,
+ res.getString(R.string.dvr_detail_play_from_beginning),
+ null,
+ res.getDrawable(R.drawable.lb_ic_replay)));
+ } else {
+ adapter.set(
+ ACTION_PLAY_FROM_BEGINNING,
+ new Action(
+ ACTION_PLAY_FROM_BEGINNING,
+ res.getString(R.string.dvr_detail_watch),
+ null,
+ res.getDrawable(R.drawable.lb_ic_play)));
+ }
+ }
return adapter;
}
@@ -107,6 +169,13 @@ public class CurrentRecordingDetailsFragment extends RecordingDetailsFragment {
}
}
});
+ } else if (action.getId() == ACTION_RESUME_PLAYING) {
+ startPlayback(
+ mRecordedProgram,
+ mDvrWatchedPositionManager.getWatchedPosition(
+ mRecordedProgram.getId()));
+ } else if (action.getId() == ACTION_PLAY_FROM_BEGINNING) {
+ startPlayback(mRecordedProgram, TvInputManager.TIME_SHIFT_INVALID_TIME);
}
}
};
diff --git a/src/com/android/tv/dvr/ui/browse/DetailsContent.java b/src/com/android/tv/dvr/ui/browse/DetailsContent.java
index cba6293b..e179743c 100644
--- a/src/com/android/tv/dvr/ui/browse/DetailsContent.java
+++ b/src/com/android/tv/dvr/ui/browse/DetailsContent.java
@@ -22,6 +22,7 @@ import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.android.tv.R;
import com.android.tv.TvSingletons;
+import com.android.tv.data.Program;
import com.android.tv.data.api.Channel;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.data.ScheduledRecording;
@@ -29,7 +30,7 @@ import com.android.tv.dvr.data.SeriesRecording;
import com.android.tv.dvr.ui.DvrUiHelper;
/** A class for details content. */
-class DetailsContent {
+public class DetailsContent {
/** Constant for invalid time. */
public static final long INVALID_TIME = -1;
@@ -40,6 +41,7 @@ class DetailsContent {
private String mLogoImageUri;
private String mBackgroundImageUri;
private boolean mUsingChannelLogo;
+ private boolean mShowErrorMessage;
static DetailsContent createFromRecordedProgram(
Context context, RecordedProgram recordedProgram) {
@@ -59,6 +61,23 @@ class DetailsContent {
.build(context);
}
+ public static DetailsContent createFromProgram(Context context, Program program) {
+ return new DetailsContent.Builder()
+ .setChannelId(program.getChannelId())
+ .setProgramTitle(program.getTitle())
+ .setSeasonNumber(program.getSeasonNumber())
+ .setEpisodeNumber(program.getEpisodeNumber())
+ .setStartTimeUtcMillis(program.getStartTimeUtcMillis())
+ .setEndTimeUtcMillis(program.getEndTimeUtcMillis())
+ .setDescription(
+ TextUtils.isEmpty(program.getLongDescription())
+ ? program.getDescription()
+ : program.getLongDescription())
+ .setPosterArtUri(program.getPosterArtUri())
+ .setThumbnailUri(program.getThumbnailUri())
+ .build(context);
+ }
+
static DetailsContent createFromSeriesRecording(
Context context, SeriesRecording seriesRecording) {
return new DetailsContent.Builder()
@@ -79,37 +98,9 @@ class DetailsContent {
TvSingletons.getSingletons(context)
.getChannelDataManager()
.getChannel(scheduledRecording.getChannelId());
- String description =
- !TextUtils.isEmpty(scheduledRecording.getProgramDescription())
- ? scheduledRecording.getProgramDescription()
- : scheduledRecording.getProgramLongDescription();
- if (TextUtils.isEmpty(description)) {
- description = channel != null ? channel.getDescription() : null;
- }
- return new DetailsContent.Builder()
- .setChannelId(scheduledRecording.getChannelId())
- .setProgramTitle(scheduledRecording.getProgramTitle())
- .setSeasonNumber(scheduledRecording.getSeasonNumber())
- .setEpisodeNumber(scheduledRecording.getEpisodeNumber())
- .setStartTimeUtcMillis(scheduledRecording.getStartTimeMs())
- .setEndTimeUtcMillis(scheduledRecording.getEndTimeMs())
- .setDescription(description)
- .setPosterArtUri(scheduledRecording.getProgramPosterArtUri())
- .setThumbnailUri(scheduledRecording.getProgramThumbnailUri())
- .build(context);
- }
-
- static DetailsContent createFromFailedScheduledRecording(
- Context context, ScheduledRecording scheduledRecording, String errMsg) {
- Channel channel =
- TvSingletons.getSingletons(context)
- .getChannelDataManager()
- .getChannel(scheduledRecording.getChannelId());
String description;
- if (scheduledRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED
- && errMsg != null) {
- description = errMsg
- + " (Error code: " + scheduledRecording.getFailedReason() + ")";
+ if (scheduledRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
+ description = getErrorMessage(context, scheduledRecording);
} else {
description =
!TextUtils.isEmpty(scheduledRecording.getProgramDescription())
@@ -129,9 +120,39 @@ class DetailsContent {
.setDescription(description)
.setPosterArtUri(scheduledRecording.getProgramPosterArtUri())
.setThumbnailUri(scheduledRecording.getProgramThumbnailUri())
+ .setShowErrorMessage(
+ scheduledRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED)
.build(context);
}
+ private static String getErrorMessage(Context context, ScheduledRecording recording) {
+ int reason = recording.getFailedReason() == null
+ ? ScheduledRecording.FAILED_REASON_OTHER
+ : recording.getFailedReason();
+ switch (reason) {
+ case ScheduledRecording.FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED:
+ return context.getString(R.string.dvr_recording_failed_not_started);
+ case ScheduledRecording.FAILED_REASON_RESOURCE_BUSY:
+ return context.getString(R.string.dvr_recording_failed_resource_busy);
+ case ScheduledRecording.FAILED_REASON_INPUT_UNAVAILABLE:
+ return context.getString(
+ R.string.dvr_recording_failed_input_unavailable,
+ recording.getInputId());
+ case ScheduledRecording.FAILED_REASON_INPUT_DVR_UNSUPPORTED:
+ return context.getString(R.string.dvr_recording_failed_input_dvr_unsupported);
+ case ScheduledRecording.FAILED_REASON_INSUFFICIENT_SPACE:
+ return context.getString(R.string.dvr_recording_failed_insufficient_space);
+ case ScheduledRecording.FAILED_REASON_OTHER: // fall through
+ case ScheduledRecording.FAILED_REASON_NOT_FINISHED: // fall through
+ case ScheduledRecording.FAILED_REASON_SCHEDULER_STOPPED: // fall through
+ case ScheduledRecording.FAILED_REASON_INVALID_CHANNEL: // fall through
+ case ScheduledRecording.FAILED_REASON_MESSAGE_NOT_SENT: // fall through
+ case ScheduledRecording.FAILED_REASON_CONNECTION_FAILED: // fall through
+ default:
+ return context.getString(R.string.dvr_recording_failed_system_failure, reason);
+ }
+ }
+
private DetailsContent() {}
/** Returns title. */
@@ -169,6 +190,11 @@ class DetailsContent {
return mUsingChannelLogo;
}
+ /** Returns if the error message should be shown. */
+ public boolean shouldShowErrorMessage() {
+ return mShowErrorMessage;
+ }
+
/** Copies other details content. */
public void copyFrom(DetailsContent other) {
if (this == other) {
@@ -181,6 +207,7 @@ class DetailsContent {
mLogoImageUri = other.mLogoImageUri;
mBackgroundImageUri = other.mBackgroundImageUri;
mUsingChannelLogo = other.mUsingChannelLogo;
+ mShowErrorMessage = other.mShowErrorMessage;
}
/** A class for building details content. */
@@ -266,6 +293,11 @@ class DetailsContent {
return this;
}
+ private Builder setShowErrorMessage(boolean showErrorMessage) {
+ mDetailsContent.mShowErrorMessage = showErrorMessage;
+ return this;
+ }
+
private void createStyledTitle(Context context, Channel channel) {
CharSequence title =
DvrUiHelper.getStyledTitleWithEpisodeNumber(
diff --git a/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java b/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java
index aec8c411..6b5fd1fd 100644
--- a/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java
+++ b/src/com/android/tv/dvr/ui/browse/DetailsContentPresenter.java
@@ -45,12 +45,13 @@ import com.android.tv.util.Utils;
* 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}. */
public static class ViewHolder extends Presenter.ViewHolder {
final TextView mTitle;
final TextView mSubtitle;
final LinearLayout mDescriptionContainer;
+ final LinearLayout mErrorMessage;
final TextView mBody;
final TextView mReadMoreView;
final int mTitleMargin;
@@ -150,6 +151,8 @@ class DetailsContentPresenter extends Presenter {
});
mTitle = (TextView) view.findViewById(R.id.dvr_details_description_title);
mSubtitle = (TextView) view.findViewById(R.id.dvr_details_description_subtitle);
+ mErrorMessage =
+ (LinearLayout) view.findViewById(R.id.dvr_details_description_error_message);
mBody = (TextView) view.findViewById(R.id.dvr_details_description_body);
mDescriptionContainer =
(LinearLayout) view.findViewById(R.id.dvr_details_description_container);
@@ -321,6 +324,9 @@ class DetailsContentPresenter extends Presenter {
if (TextUtils.isEmpty(detailsContent.getDescription())) {
vh.mBody.setVisibility(View.GONE);
} else {
+ if (detailsContent.shouldShowErrorMessage()) {
+ vh.mErrorMessage.setVisibility(View.VISIBLE);
+ }
vh.mBody.setText(detailsContent.getDescription());
vh.mBody.setVisibility(View.VISIBLE);
vh.mBody.setLineSpacing(
diff --git a/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java b/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java
index 849360b8..4e41daee 100644
--- a/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java
+++ b/src/com/android/tv/dvr/ui/browse/DetailsViewBackgroundHelper.java
@@ -24,7 +24,7 @@ import android.os.Handler;
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/browse/DvrBrowseActivity.java
index 6cc1c7a1..5743ea5c 100644
--- a/src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java
+++ b/src/com/android/tv/dvr/ui/browse/DvrBrowseActivity.java
@@ -22,9 +22,15 @@ import android.media.tv.TvInputManager;
import android.os.Bundle;
import com.android.tv.R;
import com.android.tv.Starter;
+import com.android.tv.perf.PerformanceMonitorManagerFactory;
/** {@link android.app.Activity} for DVR UI. */
public class DvrBrowseActivity extends Activity {
+
+ {
+ PerformanceMonitorManagerFactory.create().getStartupMeasure().onActivityInit();
+ }
+
private DvrBrowseFragment mFragment;
@Override
diff --git a/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java b/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java
index 40b3a1f0..17ba1939 100644
--- a/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/DvrBrowseFragment.java
@@ -31,9 +31,7 @@ import android.support.v17.leanback.widget.TitleViewAdapter;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
-
import com.android.tv.R;
-import com.android.tv.TvFeatures;
import com.android.tv.TvSingletons;
import com.android.tv.data.GenreItems;
import com.android.tv.dvr.DvrDataManager;
@@ -47,7 +45,7 @@ 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.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
@@ -66,7 +64,7 @@ public class DvrBrowseFragment extends BrowseFragment
private static final String TAG = "DvrBrowseFragment";
private static final boolean DEBUG = false;
- private static final int MAX_RECENT_ITEM_COUNT = 10;
+ private static final int MAX_RECENT_ITEM_COUNT = 4;
private static final int MAX_SCHEDULED_ITEM_COUNT = 4;
private boolean mShouldShowScheduleRow;
@@ -104,93 +102,84 @@ public class DvrBrowseFragment extends BrowseFragment
};
private final Comparator<Object> RECORDED_PROGRAM_COMPARATOR =
- new Comparator<Object>() {
- @Override
- public int compare(Object lhs, Object rhs) {
- if (lhs instanceof SeriesRecording) {
- lhs = mSeriesId2LatestProgram.get(((SeriesRecording) lhs).getSeriesId());
- }
- if (rhs instanceof SeriesRecording) {
- rhs = mSeriesId2LatestProgram.get(((SeriesRecording) rhs).getSeriesId());
- }
- if (lhs instanceof RecordedProgram) {
- if (rhs instanceof RecordedProgram) {
- return RecordedProgram.START_TIME_THEN_ID_COMPARATOR
- .reversed()
- .compare((RecordedProgram) lhs, (RecordedProgram) rhs);
- } else {
- return -1;
- }
- } else if (rhs instanceof RecordedProgram) {
- return 1;
+ (Object lhs, Object rhs) -> {
+ if (lhs instanceof SeriesRecording) {
+ lhs = mSeriesId2LatestProgram.get(((SeriesRecording) lhs).getSeriesId());
+ }
+ if (rhs instanceof SeriesRecording) {
+ rhs = mSeriesId2LatestProgram.get(((SeriesRecording) rhs).getSeriesId());
+ }
+ if (lhs instanceof RecordedProgram) {
+ if (rhs instanceof RecordedProgram) {
+ return RecordedProgram.START_TIME_THEN_ID_COMPARATOR
+ .reversed()
+ .compare((RecordedProgram) lhs, (RecordedProgram) rhs);
} else {
- return 0;
+ return -1;
}
+ } else if (rhs instanceof RecordedProgram) {
+ return 1;
+ } else {
+ return 0;
}
};
private static final Comparator<Object> SCHEDULE_COMPARATOR =
- new Comparator<Object>() {
- @Override
- public int compare(Object lhs, Object rhs) {
- if (lhs instanceof ScheduledRecording) {
- if (rhs instanceof ScheduledRecording) {
- return ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR
- .compare((ScheduledRecording) lhs, (ScheduledRecording) rhs);
- } else {
- return -1;
- }
- } else if (rhs instanceof ScheduledRecording) {
- return 1;
+ (Object lhs, Object rhs) -> {
+ if (lhs instanceof ScheduledRecording) {
+ if (rhs instanceof ScheduledRecording) {
+ return ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR
+ .compare((ScheduledRecording) lhs, (ScheduledRecording) rhs);
} else {
- return 0;
+ return -1;
}
+ } else if (rhs instanceof ScheduledRecording) {
+ return 1;
+ } else {
+ return 0;
}
};
static final Comparator<Object> RECENT_ROW_COMPARATOR =
- new Comparator<Object>() {
- @Override
- public int compare(Object lhs, Object rhs) {
- if (lhs instanceof ScheduledRecording) {
- if (rhs instanceof ScheduledRecording) {
- return ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR
- .reversed()
- .compare((ScheduledRecording) lhs, (ScheduledRecording) rhs);
- } else if (rhs instanceof RecordedProgram) {
- ScheduledRecording scheduled = (ScheduledRecording) lhs;
- RecordedProgram recorded = (RecordedProgram) rhs;
- int compare =
- Long.compare(
- recorded.getStartTimeUtcMillis(),
- scheduled.getStartTimeMs());
- // recorded program first when the start times are the same
- return compare == 0 ? 1 : compare;
- } else {
- return -1;
- }
- } else if (lhs instanceof RecordedProgram) {
- if (rhs instanceof RecordedProgram) {
- return RecordedProgram.START_TIME_THEN_ID_COMPARATOR
- .reversed()
- .compare((RecordedProgram) lhs, (RecordedProgram) rhs);
- } else if (rhs instanceof ScheduledRecording) {
- RecordedProgram recorded = (RecordedProgram) lhs;
- ScheduledRecording scheduled = (ScheduledRecording) rhs;
- int compare =
- Long.compare(
- scheduled.getStartTimeMs(),
- recorded.getStartTimeUtcMillis());
- // recorded program first when the start times are the same
- return compare == 0 ? -1 : compare;
- } else {
- return -1;
- }
+ (Object lhs, Object rhs) -> {
+ if (lhs instanceof ScheduledRecording) {
+ if (rhs instanceof ScheduledRecording) {
+ return ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR
+ .reversed()
+ .compare((ScheduledRecording) lhs, (ScheduledRecording) rhs);
+ } else if (rhs instanceof RecordedProgram) {
+ ScheduledRecording scheduled = (ScheduledRecording) lhs;
+ RecordedProgram recorded = (RecordedProgram) rhs;
+ int compare =
+ Long.compare(
+ recorded.getStartTimeUtcMillis(),
+ scheduled.getStartTimeMs());
+ // recorded program first when the start times are the same
+ return compare == 0 ? 1 : compare;
+ } else {
+ return -1;
+ }
+ } else if (lhs instanceof RecordedProgram) {
+ if (rhs instanceof RecordedProgram) {
+ return RecordedProgram.START_TIME_THEN_ID_COMPARATOR
+ .reversed()
+ .compare((RecordedProgram) lhs, (RecordedProgram) rhs);
+ } else if (rhs instanceof ScheduledRecording) {
+ RecordedProgram recorded = (RecordedProgram) lhs;
+ ScheduledRecording scheduled = (ScheduledRecording) rhs;
+ int compare =
+ Long.compare(
+ scheduled.getStartTimeMs(),
+ recorded.getStartTimeUtcMillis());
+ // recorded program first when the start times are the same
+ return compare == 0 ? -1 : compare;
} else {
- return !(rhs instanceof RecordedProgram)
- && !(rhs instanceof ScheduledRecording)
- ? 0 : 1;
+ return -1;
}
+ } else {
+ return !(rhs instanceof RecordedProgram) && !(rhs instanceof ScheduledRecording)
+ ? 0
+ : 1;
}
};
@@ -207,13 +196,7 @@ public class DvrBrowseFragment extends BrowseFragment
}
};
- private final Runnable mUpdateRowsRunnable =
- new Runnable() {
- @Override
- public void run() {
- updateRows();
- }
- };
+ private final Runnable mUpdateRowsRunnable = this::updateRows;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -233,13 +216,10 @@ public class DvrBrowseFragment extends BrowseFragment
SeriesRecording.class, new SeriesRecordingPresenter(context))
.addClassPresenter(
FullScheduleCardHolder.class,
- new FullSchedulesCardPresenter(context));
+ new FullSchedulesCardPresenter(context))
+ .addClassPresenter(
+ DvrHistoryCardHolder.class, new DvrHistoryCardPresenter(context));
- if (TvFeatures.DVR_FAILED_LIST.isEnabled(context)) {
- mPresenterSelector.addClassPresenter(
- DvrHistoryCardHolder.class,
- new DvrHistoryCardPresenter(context));
- }
mGenreLabels = new ArrayList<>(Arrays.asList(GenreItems.getLabels(context)));
mGenreLabels.add(getString(R.string.dvr_main_others));
prepareUiElements();
@@ -310,7 +290,9 @@ public class DvrBrowseFragment extends BrowseFragment
@Override
public void onRecordedProgramsChanged(RecordedProgram... recordedPrograms) {
for (RecordedProgram recordedProgram : recordedPrograms) {
- handleRecordedProgramChanged(recordedProgram);
+ if (recordedProgram.isVisible()) {
+ handleRecordedProgramChanged(recordedProgram);
+ }
}
postUpdateRows();
}
@@ -340,6 +322,9 @@ public class DvrBrowseFragment extends BrowseFragment
public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) {
for (ScheduledRecording scheduleRecording : scheduledRecordings) {
mScheduleAdapter.remove(scheduleRecording);
+ if (scheduleRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
+ mRecentAdapter.remove(scheduleRecording);
+ }
}
}
@@ -351,6 +336,9 @@ public class DvrBrowseFragment extends BrowseFragment
} else {
mScheduleAdapter.removeWithId(scheduleRecording);
}
+ if (scheduleRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
+ mRecentAdapter.change(scheduleRecording);
+ }
}
}
@@ -443,16 +431,17 @@ public class DvrBrowseFragment extends BrowseFragment
mScheduleAdapter.addExtraItem(FullScheduleCardHolder.FULL_SCHEDULE_CARD_HOLDER);
// Recorded Programs.
for (RecordedProgram recordedProgram : mDvrDataManager.getRecordedPrograms()) {
- handleRecordedProgramAdded(recordedProgram, false);
- }
- if (TvFeatures.DVR_FAILED_LIST.isEnabled(getContext())) {
- // only get failed recordings
- for (ScheduledRecording scheduledRecording
- : mDvrDataManager.getFailedScheduledRecordings()) {
- onScheduledRecordingAdded(scheduledRecording);
+ if (recordedProgram.isVisible()) {
+ handleRecordedProgramAdded(recordedProgram, false);
}
- mRecentAdapter.addExtraItem(DvrHistoryCardHolder.DVR_HISTORY_CARD_HOLDER);
}
+ // only get failed recordings
+ for (ScheduledRecording scheduledRecording :
+ mDvrDataManager.getFailedScheduledRecordings()) {
+ onScheduledRecordingAdded(scheduledRecording);
+ }
+ mRecentAdapter.addExtraItem(DvrHistoryCardHolder.DVR_HISTORY_CARD_HOLDER);
+
// Series Recordings. Series recordings should be added after recorded programs, because
// we build series recordings' latest program information while adding recorded
// programs.
@@ -592,9 +581,9 @@ public class DvrBrowseFragment extends BrowseFragment
}
}
- private List<RecordedProgramAdapter> getGenreAdapters(String[] genres) {
+ private List<RecordedProgramAdapter> getGenreAdapters(ImmutableList<String> genres) {
List<RecordedProgramAdapter> result = new ArrayList<>();
- if (genres == null || genres.length == 0) {
+ if (genres == null || genres.isEmpty()) {
result.add(mGenreAdapters[mGenreAdapters.length - 1]);
} else {
for (String genre : genres) {
@@ -642,8 +631,8 @@ public class DvrBrowseFragment extends BrowseFragment
private void updateRows() {
int visibleRowsCount = 1; // Schedule's Row will never be empty
- int recentRowMinSize = TvFeatures.DVR_FAILED_LIST.isEnabled(getContext()) ? 1 : 0;
- if (mRecentAdapter.size() <= recentRowMinSize) {
+ if (mRecentAdapter.size() <= 1) {
+ // remove the row if there is only the DVR history card
mRowsAdapter.remove(mRecentRow);
} else {
if (mRowsAdapter.indexOf(mRecentRow) < 0) {
@@ -673,6 +662,9 @@ public class DvrBrowseFragment extends BrowseFragment
}
}
}
+ if (getSelectedPosition() >= mRowsAdapter.size()) {
+ setSelectedPosition(mRecentAdapter.size() - 1);
+ }
}
private boolean needToShowScheduledRecording(ScheduledRecording recording) {
@@ -713,16 +705,13 @@ public class DvrBrowseFragment extends BrowseFragment
SeriesAdapter() {
super(
mPresenterSelector,
- new Comparator<SeriesRecording>() {
- @Override
- public int compare(SeriesRecording lhs, SeriesRecording rhs) {
- if (lhs.isStopped() && !rhs.isStopped()) {
- return 1;
- } else if (!lhs.isStopped() && rhs.isStopped()) {
- return -1;
- }
- return SeriesRecording.PRIORITY_COMPARATOR.compare(lhs, rhs);
+ (SeriesRecording lhs, SeriesRecording rhs) -> {
+ if (lhs.isStopped() && !rhs.isStopped()) {
+ return 1;
+ } else if (!lhs.isStopped() && rhs.isStopped()) {
+ return -1;
}
+ return SeriesRecording.PRIORITY_COMPARATOR.compare(lhs, rhs);
});
}
diff --git a/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java b/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java
deleted file mode 100644
index 0336b319..00000000
--- a/src/com/android/tv/dvr/ui/browse/DvrDetailsActivity.java
+++ /dev/null
@@ -1,144 +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.app.Activity;
-import android.os.Bundle;
-import android.support.v17.leanback.app.DetailsFragment;
-import android.transition.Transition;
-import android.transition.Transition.TransitionListener;
-import android.view.View;
-import com.android.tv.R;
-import com.android.tv.Starter;
-import com.android.tv.dialog.PinDialogFragment;
-
-/** Activity to show details view in DVR. */
-public class DvrDetailsActivity extends Activity implements PinDialogFragment.OnPinCheckedListener {
- /** Name of record id added to the Intent. */
- public static final String RECORDING_ID = "record_id";
-
- /**
- * Name of flag added to the Intent to determine if details view should hide "View schedule"
- * button.
- */
- public static final String HIDE_VIEW_SCHEDULE = "hide_view_schedule";
-
- /** Name of details view's type added to the intent. */
- public static final String DETAILS_VIEW_TYPE = "details_view_type";
-
- /** Name of shared element between activities. */
- public static final String SHARED_ELEMENT_NAME = "shared_element";
-
- /** Name of error message of a failed recording */
- public static final String EXTRA_FAILED_MESSAGE = "failed_message";
-
- /** CURRENT_RECORDING_VIEW refers to Current Recordings in DVR. */
- public static final int CURRENT_RECORDING_VIEW = 1;
-
- /** SCHEDULED_RECORDING_VIEW refers to Scheduled Recordings in DVR. */
- public static final int SCHEDULED_RECORDING_VIEW = 2;
-
- /** RECORDED_PROGRAM_VIEW refers to Recorded programs in DVR. */
- public static final int RECORDED_PROGRAM_VIEW = 3;
-
- /** SERIES_RECORDING_VIEW refers to series recording in DVR. */
- public static final int SERIES_RECORDING_VIEW = 4;
-
- private PinDialogFragment.OnPinCheckedListener mOnPinCheckedListener;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- Starter.start(this);
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_dvr_details);
- long recordId = getIntent().getLongExtra(RECORDING_ID, -1);
- int detailsViewType = getIntent().getIntExtra(DETAILS_VIEW_TYPE, -1);
- boolean hideViewSchedule = getIntent().getBooleanExtra(HIDE_VIEW_SCHEDULE, false);
- String failedMsg = getIntent().getStringExtra(EXTRA_FAILED_MESSAGE);
- if (recordId != -1 && detailsViewType != -1 && savedInstanceState == null) {
- Bundle args = new Bundle();
- args.putLong(RECORDING_ID, recordId);
- DetailsFragment detailsFragment = null;
- if (detailsViewType == CURRENT_RECORDING_VIEW) {
- detailsFragment = new CurrentRecordingDetailsFragment();
- } else if (detailsViewType == SCHEDULED_RECORDING_VIEW) {
- args.putBoolean(HIDE_VIEW_SCHEDULE, hideViewSchedule);
- args.putString(EXTRA_FAILED_MESSAGE, failedMsg);
- detailsFragment = new ScheduledRecordingDetailsFragment();
- } else if (detailsViewType == RECORDED_PROGRAM_VIEW) {
- detailsFragment = new RecordedProgramDetailsFragment();
- } else if (detailsViewType == SERIES_RECORDING_VIEW) {
- detailsFragment = new SeriesRecordingDetailsFragment();
- }
- detailsFragment.setArguments(args);
- getFragmentManager()
- .beginTransaction()
- .replace(R.id.dvr_details_view_frame, detailsFragment)
- .commit();
- }
-
- // This is a workaround for the focus on O device
- addTransitionListener();
- }
-
- @Override
- public void onPinChecked(boolean checked, int type, String rating) {
- if (mOnPinCheckedListener != null) {
- mOnPinCheckedListener.onPinChecked(checked, type, rating);
- }
- }
-
- void setOnPinCheckListener(PinDialogFragment.OnPinCheckedListener listener) {
- mOnPinCheckedListener = listener;
- }
-
- private void addTransitionListener() {
- getWindow()
- .getSharedElementEnterTransition()
- .addListener(
- new TransitionListener() {
- @Override
- public void onTransitionStart(Transition transition) {
- // Do nothing
- }
-
- @Override
- public void onTransitionEnd(Transition transition) {
- View actions = findViewById(R.id.details_overview_actions);
- if (actions != null) {
- actions.requestFocus();
- }
- }
-
- @Override
- public void onTransitionCancel(Transition transition) {
- // Do nothing
-
- }
-
- @Override
- public void onTransitionPause(Transition transition) {
- // Do nothing
- }
-
- @Override
- public void onTransitionResume(Transition transition) {
- // Do nothing
- }
- });
- }
-}
diff --git a/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java
index 8f4e4dab..f90981f0 100644
--- a/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java
@@ -47,8 +47,10 @@ import com.android.tv.dialog.PinDialogFragment.OnPinCheckedListener;
import com.android.tv.dvr.data.RecordedProgram;
import com.android.tv.dvr.ui.DvrUiHelper;
import com.android.tv.parental.ParentalControlSettings;
+import com.android.tv.ui.DetailsActivity;
import com.android.tv.util.ToastUtils;
import com.android.tv.util.images.ImageLoader;
+import com.google.common.collect.ImmutableList;
import java.io.File;
abstract class DvrDetailsFragment extends DetailsFragment {
@@ -89,7 +91,7 @@ abstract class DvrDetailsFragment extends DetailsFragment {
rowPresenter.setBackgroundColor(
getResources().getColor(R.color.common_tv_background, null));
rowPresenter.setSharedElementEnterTransition(
- getActivity(), DvrDetailsActivity.SHARED_ELEMENT_NAME);
+ getActivity(), DetailsActivity.SHARED_ELEMENT_NAME);
rowPresenter.setOnActionClickedListener(onCreateOnActionClickedListener());
mRowsAdapter = new ArrayObjectAdapter(onCreatePresenterSelector(rowPresenter));
setAdapter(mRowsAdapter);
@@ -221,7 +223,7 @@ abstract class DvrDetailsFragment extends DetailsFragment {
checkPinToPlay(recordedProgram, seekTimeMs);
return;
}
- TvContentRating[] ratings = recordedProgram.getContentRatings();
+ ImmutableList<TvContentRating> ratings = recordedProgram.getContentRatings();
TvContentRating blockRatings = parental.getBlockedRating(ratings);
if (blockRatings != null) {
checkPinToPlay(recordedProgram, seekTimeMs);
@@ -245,15 +247,14 @@ abstract class DvrDetailsFragment extends DetailsFragment {
}
private void checkPinToPlay(RecordedProgram recordedProgram, long seekTimeMs) {
- SoftPreconditions.checkState(getActivity() instanceof DvrDetailsActivity);
- if (getActivity() instanceof DvrDetailsActivity) {
- ((DvrDetailsActivity) getActivity())
+ SoftPreconditions.checkState(getActivity() instanceof DetailsActivity);
+ if (getActivity() instanceof DetailsActivity) {
+ ((DetailsActivity) getActivity())
.setOnPinCheckListener(
new OnPinCheckedListener() {
@Override
public void onPinChecked(boolean checked, int type, String rating) {
- ((DvrDetailsActivity) getActivity())
- .setOnPinCheckListener(null);
+ ((DetailsActivity) getActivity()).setOnPinCheckListener(null);
if (checked
&& type
== PinDialogFragment
diff --git a/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java
index 47b1a198..bf963547 100644
--- a/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/RecordedProgramDetailsFragment.java
@@ -24,10 +24,13 @@ import android.support.v17.leanback.widget.OnActionClickedListener;
import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
import com.android.tv.R;
import com.android.tv.TvSingletons;
+import com.android.tv.common.util.PermissionUtils;
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.ui.DvrUiHelper;
+import com.android.tv.ui.DetailsActivity;
/** {@link android.support.v17.leanback.app.DetailsFragment} for recorded program in DVR. */
public class RecordedProgramDetailsFragment extends DvrDetailsFragment
@@ -80,7 +83,7 @@ public class RecordedProgramDetailsFragment extends DvrDetailsFragment
@Override
protected boolean onLoadRecordingDetails(Bundle args) {
- long recordedProgramId = args.getLong(DvrDetailsActivity.RECORDING_ID);
+ long recordedProgramId = args.getLong(DetailsActivity.RECORDING_ID);
mRecordedProgram = mDvrDataManager.getRecordedProgram(recordedProgramId);
return mRecordedProgram != null;
}
@@ -138,15 +141,24 @@ public class RecordedProgramDetailsFragment extends DvrDetailsFragment
mDvrWatchedPositionManager.getWatchedPosition(
mRecordedProgram.getId()));
} else if (action.getId() == ACTION_DELETE_RECORDING) {
- DvrManager dvrManager =
- TvSingletons.getSingletons(getActivity()).getDvrManager();
- dvrManager.removeRecordedProgram(mRecordedProgram);
- getActivity().finish();
+ delete();
}
}
};
}
+ private void delete() {
+ if (!PermissionUtils.hasWriteExternalStorage(getContext())
+ && DvrManager.isFile(mRecordedProgram.getDataUri())
+ && !DvrManager.isFromBundledInput(mRecordedProgram)) {
+ DvrUiHelper.showWriteStoragePermissionRationaleDialog(getActivity());
+ } else {
+ DvrManager dvrManager = TvSingletons.getSingletons(getActivity()).getDvrManager();
+ dvrManager.removeRecordedProgram(mRecordedProgram, true);
+ getActivity().finish();
+ }
+ }
+
@Override
public void onRecordedProgramsAdded(RecordedProgram... recordedPrograms) {}
diff --git a/src/com/android/tv/dvr/ui/browse/RecordingCardView.java b/src/com/android/tv/dvr/ui/browse/RecordingCardView.java
index fe3c52d9..c83ceaf0 100644
--- a/src/com/android/tv/dvr/ui/browse/RecordingCardView.java
+++ b/src/com/android/tv/dvr/ui/browse/RecordingCardView.java
@@ -48,11 +48,10 @@ public class RecordingCardView extends BaseCardView {
private final int mImageWidth;
private final int mImageHeight;
private String mImageUri;
+ private final ImageView mContentIconView;
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;
@@ -94,8 +93,7 @@ public class RecordingCardView extends BaseCardView {
mImageWidth = imageWidth;
mImageHeight = imageHeight;
mProgressBar = (ProgressBar) findViewById(R.id.recording_progress);
- mAffiliatedIconContainer = findViewById(R.id.affiliated_icon_container);
- mAffiliatedIcon = (ImageView) findViewById(R.id.affiliated_icon);
+ mContentIconView = (ImageView) findViewById(R.id.content_icon);
mMajorContentView = (TextView) findViewById(R.id.content_major);
mMinorContentView = (TextView) findViewById(R.id.content_minor);
mTitleArea = (FrameLayout) findViewById(R.id.title_area);
@@ -184,6 +182,7 @@ public class RecordingCardView extends BaseCardView {
}
void setContent(CharSequence majorContent, CharSequence minorContent) {
+ mContentIconView.setVisibility(View.GONE);
if (!TextUtils.isEmpty(majorContent)) {
mMajorContentView.setText(majorContent);
mMajorContentView.setVisibility(View.VISIBLE);
@@ -198,6 +197,24 @@ public class RecordingCardView extends BaseCardView {
}
}
+ void setRecordingFailedContent(Context context) {
+ mContentIconView.setVisibility(View.VISIBLE);
+ mContentIconView.setImageResource(R.drawable.ic_error_outline_pink_24dp);
+ mMajorContentView.setText(context.getString(R.string.dvr_recording_failed_no_period));
+ mMajorContentView.setVisibility(View.VISIBLE);
+ mMajorContentView.setTextColor(
+ getResources().getColor(R.color.dvr_recording_failed_text_color, null));
+ }
+
+ void setRecordingConflictContent(Context context) {
+ mContentIconView.setVisibility(View.VISIBLE);
+ mContentIconView.setImageResource(R.drawable.ic_warning_yellow_24dp);
+ mMajorContentView.setText(context.getString(R.string.dvr_recording_conflict));
+ mMajorContentView.setVisibility(View.VISIBLE);
+ mMajorContentView.setTextColor(
+ getResources().getColor(R.color.dvr_recording_conflict_text_color, null));
+ }
+
/** Sets progress bar. If progress is {@code null}, hides progress bar. */
void setProgressBar(Integer progress) {
if (progress == null) {
@@ -245,19 +262,6 @@ public class RecordingCardView extends BaseCardView {
}
/**
- * Sets the affiliated icon of the card view, which will be displayed at the lower-right corner
- * of the poster.
- */
- public void setAffiliatedIcon(int imageResId) {
- if (imageResId > 0) {
- mAffiliatedIconContainer.setVisibility(View.VISIBLE);
- mAffiliatedIcon.setImageResource(imageResId);
- } else {
- mAffiliatedIconContainer.setVisibility(View.INVISIBLE);
- }
- }
-
- /**
* Sets the background image URI of the card view, which will be displayed as background when
* the view is clicked and shows its details fragment.
*/
diff --git a/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java
index aa2ccf75..243681c6 100644
--- a/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/RecordingDetailsFragment.java
@@ -20,6 +20,7 @@ import android.os.Bundle;
import android.support.v17.leanback.app.DetailsFragment;
import com.android.tv.TvSingletons;
import com.android.tv.dvr.data.ScheduledRecording;
+import com.android.tv.ui.DetailsActivity;
/** {@link DetailsFragment} for recordings in DVR. */
abstract class RecordingDetailsFragment extends DvrDetailsFragment {
@@ -33,7 +34,7 @@ abstract class RecordingDetailsFragment extends DvrDetailsFragment {
@Override
protected boolean onLoadRecordingDetails(Bundle args) {
- long scheduledRecordingId = args.getLong(DvrDetailsActivity.RECORDING_ID);
+ long scheduledRecordingId = args.getLong(DetailsActivity.RECORDING_ID);
mRecording =
TvSingletons.getSingletons(getContext())
.getDvrDataManager()
diff --git a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java
index 302b8318..f08bb12b 100644
--- a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/ScheduledRecordingDetailsFragment.java
@@ -21,10 +21,12 @@ import android.os.Bundle;
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.TvSingletons;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.ui.DvrUiHelper;
+import com.android.tv.ui.DetailsActivity;
/** {@link RecordingDetailsFragment} for scheduled recording in DVR. */
public class ScheduledRecordingDetailsFragment extends RecordingDetailsFragment {
@@ -34,14 +36,12 @@ public class ScheduledRecordingDetailsFragment extends RecordingDetailsFragment
private DvrManager mDvrManager;
private Action mScheduleAction;
private boolean mHideViewSchedule;
- private String mFailedMessage;
@Override
public void onCreate(Bundle savedInstance) {
Bundle args = getArguments();
mDvrManager = TvSingletons.getSingletons(getContext()).getDvrManager();
- mHideViewSchedule = args.getBoolean(DvrDetailsActivity.HIDE_VIEW_SCHEDULE);
- mFailedMessage = args.getString(DvrDetailsActivity.EXTRA_FAILED_MESSAGE);
+ mHideViewSchedule = args.getBoolean(DetailsActivity.HIDE_VIEW_SCHEDULE);
super.onCreate(savedInstance);
}
@@ -54,17 +54,6 @@ public class ScheduledRecordingDetailsFragment extends RecordingDetailsFragment
}
@Override
- protected void onCreateInternal() {
- if (mFailedMessage == null) {
- super.onCreateInternal();
- return;
- }
- setDetailsOverviewRow(
- DetailsContent.createFromFailedScheduledRecording(
- getContext(), getScheduledRecording(), mFailedMessage));
- }
-
- @Override
protected SparseArrayObjectAdapter onCreateActionsAdapter() {
SparseArrayObjectAdapter adapter =
new SparseArrayObjectAdapter(new ActionPresenterSelector());
diff --git a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java b/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java
index 8e028689..3d279354 100644
--- a/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java
+++ b/src/com/android/tv/dvr/ui/browse/ScheduledRecordingPresenter.java
@@ -119,21 +119,17 @@ class ScheduledRecordingPresenter extends DvrItemPresenter<ScheduledRecording> {
DetailsContent details = DetailsContent.createFromScheduledRecording(mContext, recording);
cardView.setTitle(details.getTitle());
cardView.setImageUri(details.getLogoImageUri(), details.isUsingChannelLogo());
- if (mDvrManager.isConflicting(recording)) {
- cardView.setAffiliatedIcon(R.drawable.ic_warning_white_32dp);
- } else if (recording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
- cardView.setAffiliatedIcon(R.drawable.ic_error_white_48dp);
+ if (recording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
+ cardView.setRecordingFailedContent(mContext);
+ } else if (mDvrManager.isConflicting(recording)) {
+ cardView.setRecordingConflictContent(mContext);
} else {
- cardView.setAffiliatedIcon(0);
+ cardView.setContent(generateMajorContent(recording), null);
}
- cardView.setContent(generateMajorContent(recording), null);
cardView.setDetailBackgroundImageUri(details.getBackgroundImageUri());
}
private String generateMajorContent(ScheduledRecording recording) {
- if (recording.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
- return mContext.getString(R.string.dvr_recording_failed);
- }
int dateDifference =
Utils.computeDateDifference(System.currentTimeMillis(), recording.getStartTimeMs());
if (dateDifference <= 0) {
diff --git a/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java
index 2cd191a7..9104ef10 100644
--- a/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java
+++ b/src/com/android/tv/dvr/ui/browse/SeriesRecordingDetailsFragment.java
@@ -20,6 +20,7 @@ import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.media.tv.TvInputManager;
import android.os.Bundle;
+import android.support.annotation.Nullable;
import android.support.v17.leanback.app.DetailsFragment;
import android.support.v17.leanback.widget.Action;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
@@ -41,6 +42,7 @@ 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.ui.DetailsActivity;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -135,7 +137,7 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment
@Override
protected boolean onLoadRecordingDetails(Bundle args) {
- long recordId = args.getLong(DvrDetailsActivity.RECORDING_ID);
+ long recordId = args.getLong(DetailsActivity.RECORDING_ID);
mSeries =
TvSingletons.getSingletons(getActivity())
.getDvrDataManager()
@@ -215,6 +217,7 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment
}
/** The programs are sorted by season number and episode number. */
+ @Nullable
private RecordedProgram getRecommendProgram(List<RecordedProgram> programs) {
for (int i = programs.size() - 1; i >= 0; i--) {
RecordedProgram program = programs.get(i);
@@ -289,7 +292,8 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment
}
}
}
- if (recordedProgram.getId() == mRecommendRecordedProgram.getId()) {
+ if (mRecommendRecordedProgram != null
+ && recordedProgram.getId() == mRecommendRecordedProgram.getId()) {
updateWatchAction();
}
}
@@ -339,14 +343,7 @@ public class SeriesRecordingDetailsFragment extends DvrDetailsFragment
new ListRow(
header,
new SeasonRowAdapter(
- selector,
- new Comparator<RecordedProgram>() {
- @Override
- public int compare(RecordedProgram lhs, RecordedProgram rhs) {
- return BaseProgram.EPISODE_COMPARATOR.compare(lhs, rhs);
- }
- },
- seasonNumber));
+ selector, BaseProgram.EPISODE_COMPARATOR::compare, seasonNumber));
getRowsAdapter().add(position, row);
return row;
}
diff --git a/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java b/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java
index 38d3d582..11680a0d 100644
--- a/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java
+++ b/src/com/android/tv/dvr/ui/list/ScheduleRowPresenter.java
@@ -37,7 +37,6 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.android.tv.R;
-import com.android.tv.TvFeatures;
import com.android.tv.TvSingletons;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.data.api.Channel;
@@ -90,18 +89,19 @@ class ScheduleRowPresenter extends RowPresenter {
private ScheduleRowPresenter mPresenter;
@ScheduleRowAction private int[] mActions;
private boolean mLtr;
- private LinearLayout mInfoContainer;
+ private final LinearLayout mInfoContainer;
// The first action is on the right of the second action.
- private RelativeLayout mSecondActionContainer;
- private RelativeLayout mFirstActionContainer;
- private View mSelectorView;
- private TextView mTimeView;
- private TextView mProgramTitleView;
- private TextView mInfoSeparatorView;
- private TextView mChannelNameView;
- private TextView mConflictInfoView;
- private ImageView mSecondActionView;
- private ImageView mFirstActionView;
+ private final RelativeLayout mSecondActionContainer;
+ private final RelativeLayout mFirstActionContainer;
+ private final View mSelectorView;
+ private final TextView mTimeView;
+ private final TextView mProgramTitleView;
+ private final TextView mInfoSeparatorView;
+ private final TextView mChannelNameView;
+ private final ImageView mExtraInfoIcon;
+ private final TextView mExtraInfoView;
+ private final ImageView mSecondActionView;
+ private final ImageView mFirstActionView;
private Runnable mPendingAnimationRunnable;
@@ -117,14 +117,11 @@ class ScheduleRowPresenter extends RowPresenter {
@Override
public void onFocusChange(View view, boolean focused) {
view.post(
- new Runnable() {
- @Override
- public void run() {
- if (view.isFocused()) {
- mPresenter.mLastFocusedViewId = view.getId();
- }
- updateSelector();
+ () -> {
+ if (view.isFocused()) {
+ mPresenter.mLastFocusedViewId = view.getId();
}
+ updateSelector();
});
}
};
@@ -146,7 +143,8 @@ class ScheduleRowPresenter extends RowPresenter {
mProgramTitleView = (TextView) view.findViewById(R.id.program_title);
mInfoSeparatorView = (TextView) view.findViewById(R.id.info_separator);
mChannelNameView = (TextView) view.findViewById(R.id.channel_name);
- mConflictInfoView = (TextView) view.findViewById(R.id.conflict_info);
+ mExtraInfoIcon = (ImageView) view.findViewById(R.id.extra_info_icon);
+ mExtraInfoView = (TextView) view.findViewById(R.id.extra_info);
Resources res = view.getResources();
mSelectorTranslationDelta =
res.getDimensionPixelSize(R.dimen.dvr_schedules_item_section_margin)
@@ -311,7 +309,7 @@ class ScheduleRowPresenter extends RowPresenter {
mInfoContainer
.getResources()
.getColor(R.color.dvr_schedules_item_info_grey, null));
- mConflictInfoView.setTextColor(
+ mExtraInfoView.setTextColor(
mInfoContainer
.getResources()
.getColor(R.color.dvr_schedules_item_info_grey, null));
@@ -327,7 +325,7 @@ class ScheduleRowPresenter extends RowPresenter {
mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_info, null));
mChannelNameView.setTextColor(
mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_info, null));
- mConflictInfoView.setTextColor(
+ mExtraInfoView.setTextColor(
mInfoContainer.getResources().getColor(R.color.dvr_schedules_item_info, null));
}
}
@@ -426,39 +424,76 @@ class ScheduleRowPresenter extends RowPresenter {
}
}
ScheduledRecording schedule = row.getSchedule();
- if (mDvrManager.isConflicting(schedule)
- || (TvFeatures.DVR_FAILED_LIST.isEnabled(getContext())
- && schedule != null
- && schedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED)) {
- String conflictInfo;
- if (TvFeatures.DVR_FAILED_LIST.isEnabled(getContext())
- && schedule != null
- && schedule.getState() == ScheduledRecording.STATE_RECORDING_FAILED) {
- // TODO(b/72638385): show real error messages
- // TODO(b/72638385): use a better name for ConflictInfoXXX
- conflictInfo = "Failed";
- if (schedule.getFailedReason() != null) {
- conflictInfo += " (Error code: " + schedule.getFailedReason() + ")";
- }
+ viewHolder.mExtraInfoIcon.setVisibility(View.GONE);
+ if (mDvrManager.isConflicting(schedule) || isFailedRecording(schedule)) {
+ String extraInfo;
+ if (isFailedRecording(schedule)) {
+ extraInfo =
+ mContext.getString(R.string.dvr_recording_failed_short)
+ + " "
+ + getErrorMessage(schedule);
+ viewHolder.mExtraInfoIcon.setVisibility(View.VISIBLE);
} else if (mDvrScheduleManager.isPartiallyConflicting(row.getSchedule())) {
- conflictInfo = mTunerConflictWillBePartiallyRecordedInfo;
+ extraInfo = mTunerConflictWillBePartiallyRecordedInfo;
} else {
- conflictInfo = mTunerConflictWillNotBeRecordedInfo;
+ extraInfo = mTunerConflictWillNotBeRecordedInfo;
}
- viewHolder.mConflictInfoView.setText(conflictInfo);
- viewHolder.mConflictInfoView.setVisibility(View.VISIBLE);
+ viewHolder.mExtraInfoView.setText(extraInfo);
+ viewHolder.mExtraInfoView.setVisibility(View.VISIBLE);
} else {
- viewHolder.mConflictInfoView.setVisibility(View.GONE);
+ viewHolder.mExtraInfoView.setVisibility(View.GONE);
}
if (shouldBeGrayedOut(row)) {
viewHolder.greyOutInfo();
} else {
viewHolder.whiteBackInfo();
}
+ if (isFailedRecording(schedule)) {
+ viewHolder.mExtraInfoView.setTextColor(
+ viewHolder
+ .mInfoContainer
+ .getResources()
+ .getColor(R.color.dvr_recording_failed_text_color, null));
+ }
viewHolder.mInfoContainer.setFocusable(isInfoClickable(row));
updateActionContainer(viewHolder, viewHolder.isSelected());
}
+ private boolean isFailedRecording(ScheduledRecording scheduledRecording) {
+ return scheduledRecording != null
+ && scheduledRecording.getState() == ScheduledRecording.STATE_RECORDING_FAILED;
+ }
+
+ private String getErrorMessage(ScheduledRecording recording) {
+ int reason =
+ recording.getFailedReason() == null
+ ? ScheduledRecording.FAILED_REASON_OTHER
+ : recording.getFailedReason();
+ switch (reason) {
+ case ScheduledRecording.FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED:
+ return mContext.getString(R.string.dvr_recording_failed_not_started_short);
+ case ScheduledRecording.FAILED_REASON_RESOURCE_BUSY:
+ return mContext.getString(R.string.dvr_recording_failed_resource_busy_short);
+ case ScheduledRecording.FAILED_REASON_INPUT_UNAVAILABLE:
+ return mContext.getString(
+ R.string.dvr_recording_failed_input_unavailable_short,
+ recording.getInputId());
+ case ScheduledRecording.FAILED_REASON_INPUT_DVR_UNSUPPORTED:
+ return mContext.getString(
+ R.string.dvr_recording_failed_input_dvr_unsupported_short);
+ case ScheduledRecording.FAILED_REASON_INSUFFICIENT_SPACE:
+ return mContext.getString(R.string.dvr_recording_failed_insufficient_space_short);
+ case ScheduledRecording.FAILED_REASON_OTHER: // fall through
+ case ScheduledRecording.FAILED_REASON_NOT_FINISHED: // fall through
+ case ScheduledRecording.FAILED_REASON_SCHEDULER_STOPPED: // fall through
+ case ScheduledRecording.FAILED_REASON_INVALID_CHANNEL: // fall through
+ case ScheduledRecording.FAILED_REASON_MESSAGE_NOT_SENT: // fall through
+ case ScheduledRecording.FAILED_REASON_CONNECTION_FAILED: // fall through
+ default:
+ return mContext.getString(R.string.dvr_recording_failed_system_failure, reason);
+ }
+ }
+
private int getImageForAction(@ScheduleRowAction int action) {
switch (action) {
case ACTION_START_RECORDING:
@@ -512,7 +547,8 @@ class ScheduleRowPresenter extends RowPresenter {
return schedule != null
&& (schedule.isNotStarted()
|| schedule.isInProgress()
- || schedule.isFinished());
+ || schedule.isFinished()
+ || schedule.isFailed());
}
/** Called when the button in a row is clicked. */
@@ -702,23 +738,17 @@ class ScheduleRowPresenter extends RowPresenter {
prepareShowActionView(viewHolder.mSecondActionContainer);
prepareShowActionView(viewHolder.mFirstActionContainer);
viewHolder.mPendingAnimationRunnable =
- new Runnable() {
- @Override
- public void run() {
- showActionView(viewHolder.mSecondActionContainer);
- showActionView(viewHolder.mFirstActionContainer);
- }
+ () -> {
+ showActionView(viewHolder.mSecondActionContainer);
+ showActionView(viewHolder.mFirstActionContainer);
};
break;
case 1:
prepareShowActionView(viewHolder.mFirstActionContainer);
viewHolder.mPendingAnimationRunnable =
- new Runnable() {
- @Override
- public void run() {
- hideActionView(viewHolder.mSecondActionContainer, View.GONE);
- showActionView(viewHolder.mFirstActionContainer);
- }
+ () -> {
+ hideActionView(viewHolder.mSecondActionContainer, View.GONE);
+ showActionView(viewHolder.mFirstActionContainer);
};
if (mLastFocusedViewId == R.id.action_second_container) {
mLastFocusedViewId = R.id.info_container;
@@ -727,12 +757,9 @@ class ScheduleRowPresenter extends RowPresenter {
case 0:
default:
viewHolder.mPendingAnimationRunnable =
- new Runnable() {
- @Override
- public void run() {
- hideActionView(viewHolder.mSecondActionContainer, View.GONE);
- hideActionView(viewHolder.mFirstActionContainer, View.GONE);
- }
+ () -> {
+ hideActionView(viewHolder.mSecondActionContainer, View.GONE);
+ hideActionView(viewHolder.mFirstActionContainer, View.GONE);
};
mLastFocusedViewId = R.id.info_container;
SoftPreconditions.checkState(
diff --git a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java
index eb01aba2..28a44bf3 100644
--- a/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java
+++ b/src/com/android/tv/dvr/ui/list/SchedulesHeaderRowPresenter.java
@@ -211,13 +211,7 @@ abstract class SchedulesHeaderRowPresenter extends RowPresenter {
new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean focused) {
- view.post(
- new Runnable() {
- @Override
- public void run() {
- updateSelector(view);
- }
- });
+ view.post(() -> updateSelector(view));
}
};
mSeriesSettingsButton.setOnFocusChangeListener(onFocusChangeListener);
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java
index b8b19adc..f24ad2c0 100644
--- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java
+++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackActivity.java
@@ -74,8 +74,10 @@ public class DvrPlaybackActivity extends Activity implements OnPinCheckedListene
private Intent createProgramIntent(Intent intent) {
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
- long recordedProgramId = ContentUris.parseId(uri);
- intent.putExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, recordedProgramId);
+ if (uri != null) {
+ long recordedProgramId = ContentUris.parseId(uri);
+ intent.putExtra(Utils.EXTRA_KEY_RECORDED_PROGRAM_ID, recordedProgramId);
+ }
}
return intent;
}
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java
index 59c90d11..791d26bb 100644
--- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java
+++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackControlHelper.java
@@ -39,6 +39,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
+import android.view.ViewGroup;
import com.android.tv.R;
import com.android.tv.util.TimeShiftUtils;
import java.util.ArrayList;
@@ -53,10 +54,13 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
private static final boolean DEBUG = false;
private static final int AUDIO_ACTION_ID = 1001;
+ private static final long INVALID_TIME = -1;
private int mPlaybackState = PlaybackState.STATE_NONE;
private int mPlaybackSpeedLevel;
private int mPlaybackSpeedId;
+ private long mProgramStartTimeMs = INVALID_TIME;
+ private boolean mEnableBuffering = false;
private boolean mReadyToControl;
private final DvrPlaybackOverlayFragment mFragment;
@@ -67,6 +71,8 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
private final MultiAction mClosedCaptioningAction;
private final MultiAction mMultiAudioAction;
private ArrayObjectAdapter mSecondaryActionsAdapter;
+ private PlaybackControlsRow mPlaybackControlsRow;
+ @Nullable private View mPlayPauseButton;
DvrPlaybackControlHelper(Activity activity, DvrPlaybackOverlayFragment overlayFragment) {
super(activity, new int[TimeShiftUtils.MAX_SPEED_LEVEL + 1]);
@@ -79,13 +85,18 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
.getDimensionPixelOffset(R.dimen.dvr_playback_controls_extra_padding_top);
mClosedCaptioningAction = new ClosedCaptioningAction(activity);
mMultiAudioAction = new MultiAudioAction(activity);
+ mProgramStartTimeMs = overlayFragment.getProgramStartTimeMs();
+ if (mProgramStartTimeMs != INVALID_TIME) {
+ mEnableBuffering = true;
+ }
createControlsRowPresenter();
}
void createControlsRow() {
- PlaybackControlsRow controlsRow = new PlaybackControlsRow(this);
- setControlsRow(controlsRow);
- mSecondaryActionsAdapter = (ArrayObjectAdapter) controlsRow.getSecondaryActionsAdapter();
+ mPlaybackControlsRow = new PlaybackControlsRow(this);
+ setControlsRow(mPlaybackControlsRow);
+ mSecondaryActionsAdapter =
+ (ArrayObjectAdapter) mPlaybackControlsRow.getSecondaryActionsAdapter();
}
private void createControlsRowPresenter() {
@@ -118,6 +129,8 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
super.onBindRowViewHolder(vh, item);
vh.setOnKeyListener(DvrPlaybackControlHelper.this);
+ ViewGroup controlBar = (ViewGroup) vh.view.findViewById(R.id.control_bar);
+ mPlayPauseButton = controlBar.getChildAt(1);
}
@Override
@@ -265,6 +278,13 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
getHost().notifyPlaybackRowChanged();
}
+ /** Update the focus to play pause button. */
+ public void onPlaybackResume() {
+ if (mPlayPauseButton != null) {
+ mPlayPauseButton.requestFocus();
+ }
+ }
+
@Nullable
Boolean hasSecondaryRow() {
if (mSecondaryActionsAdapter == null) {
@@ -292,6 +312,15 @@ class DvrPlaybackControlHelper extends PlaybackControlGlue {
mTransportControls.pause();
}
+ @Override
+ public void updateProgress() {
+ if (mEnableBuffering) {
+ super.updateProgress();
+ long bufferedTimeMs = System.currentTimeMillis() - mProgramStartTimeMs;
+ mPlaybackControlsRow.setBufferedPosition(bufferedTimeMs);
+ }
+ }
+
/** Notifies closed caption being enabled/disabled to update related UI. */
void onSubtitleTrackStateChanged(boolean enabled) {
mClosedCaptioningAction.setIndex(
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java
index bef036eb..81abb8e4 100644
--- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java
+++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackMediaSessionHelper.java
@@ -39,9 +39,6 @@ import com.android.tv.util.Utils;
import com.android.tv.util.images.ImageLoader;
class DvrPlaybackMediaSessionHelper {
- private static final String TAG = "DvrPlaybackMediaSessionHelper";
- private static final boolean DEBUG = false;
-
private int mNowPlayingCardWidth;
private int mNowPlayingCardHeight;
private int mSpeedLevel;
@@ -73,6 +70,9 @@ class DvrPlaybackMediaSessionHelper {
@Override
public void onPlaybackPositionChanged(long positionMs) {
updateMediaSessionPlaybackState();
+ if (getProgram().isPartial()) {
+ overlayFragment.updateProgress();
+ }
if (mDvrPlayer.isPlaybackPrepared()) {
mDvrWatchedPositionManager.setWatchedPosition(
mDvrPlayer.getProgram().getId(), positionMs);
@@ -94,6 +94,11 @@ class DvrPlaybackMediaSessionHelper {
mActivity.startActivity(intent);
}
}
+
+ @Override
+ public void onPlaybackResume() {
+ overlayFragment.onPlaybackResume();
+ }
});
initializeMediaSession(mediaSessionTag);
}
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java b/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java
index d3374cfa..1059e852 100644
--- a/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java
+++ b/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java
@@ -25,7 +25,6 @@ import android.media.session.PlaybackState;
import android.media.tv.TvContentRating;
import android.media.tv.TvInputManager;
import android.media.tv.TvTrackInfo;
-import android.media.tv.TvView;
import android.os.Bundle;
import android.support.v17.leanback.app.PlaybackFragment;
import android.support.v17.leanback.app.PlaybackFragmentGlueHost;
@@ -52,7 +51,7 @@ 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.ui.browse.RecordingCardView;
-import com.android.tv.parental.ContentRatingsManager;
+import com.android.tv.ui.AppLayerTvView;
import com.android.tv.util.TvSettings;
import com.android.tv.util.TvTrackInfoUtils;
import com.android.tv.util.Utils;
@@ -66,6 +65,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
private static final String MEDIA_SESSION_TAG = "com.android.tv.dvr.mediasession";
private static final float DISPLAY_ASPECT_RATIO_EPSILON = 0.01f;
+ private static final long INVALID_TIME = -1;
// mProgram is only used to store program from intent. Don't use it elsewhere.
private RecordedProgram mProgram;
@@ -76,8 +76,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
private SortedArrayAdapter<BaseProgram> mRelatedRecordingsRowAdapter;
private DvrPlaybackCardPresenter mRelatedRecordingCardPresenter;
private DvrDataManager mDvrDataManager;
- private ContentRatingsManager mContentRatingsManager;
- private TvView mTvView;
+ private AppLayerTvView mTvView;
private View mBlockScreenView;
private ListRow mRelatedRecordingsRow;
private int mVerticalPaddingBase;
@@ -117,10 +116,6 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
.getDimensionPixelOffset(
R.dimen.dvr_playback_overlay_padding_top_no_secondary_row);
mDvrDataManager = TvSingletons.getSingletons(getActivity()).getDvrDataManager();
- mContentRatingsManager =
- TvSingletons.getSingletons(getContext())
- .getTvInputManagerHelper()
- .getContentRatingsManager();
if (!mDvrDataManager.isRecordedProgramLoadFinished()) {
mDvrDataManager.addRecordedProgramLoadFinishedListener(
new DvrDataManager.OnRecordedProgramLoadFinishedListener() {
@@ -157,9 +152,9 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mTvView = (TvView) getActivity().findViewById(R.id.dvr_tv_view);
+ mTvView = getActivity().findViewById(R.id.dvr_tv_view);
mBlockScreenView = getActivity().findViewById(R.id.block_screen);
- mDvrPlayer = new DvrPlayer(mTvView);
+ mDvrPlayer = new DvrPlayer(mTvView, getActivity());
mMediaSessionHelper =
new DvrPlaybackMediaSessionHelper(
getActivity(), MEDIA_SESSION_TAG, mDvrPlayer, this);
@@ -279,6 +274,7 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
mPlaybackControlHelper.unregisterCallback();
mMediaSessionHelper.release();
mRelatedRecordingCardPresenter.unbindAllViewHolders();
+ mDvrPlayer.release();
super.onDestroy();
}
@@ -503,6 +499,20 @@ public class DvrPlaybackOverlayFragment extends PlaybackFragment {
}
}
+ public void onPlaybackResume() {
+ mPlaybackControlHelper.onPlaybackResume();
+ }
+
+ public long getProgramStartTimeMs() {
+ return (mProgram != null && mProgram.isPartial())
+ ? mProgram.getStartTimeUtcMillis()
+ : INVALID_TIME;
+ }
+
+ public void updateProgress() {
+ mPlaybackControlHelper.updateProgress();
+ }
+
private class RelatedRecordingsAdapter extends SortedArrayAdapter<BaseProgram> {
RelatedRecordingsAdapter(DvrPlaybackCardPresenter presenter) {
super(new SinglePresenterSelector(presenter), BaseProgram.EPISODE_COMPARATOR);
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlayer.java b/src/com/android/tv/dvr/ui/playback/DvrPlayer.java
index 85bb31b2..d14646b8 100644
--- a/src/com/android/tv/dvr/ui/playback/DvrPlayer.java
+++ b/src/com/android/tv/dvr/ui/playback/DvrPlayer.java
@@ -16,6 +16,7 @@
package com.android.tv.dvr.ui.playback;
+import android.content.Context;
import android.media.PlaybackParams;
import android.media.session.PlaybackState;
import android.media.tv.TvContentRating;
@@ -24,12 +25,16 @@ import android.media.tv.TvTrackInfo;
import android.media.tv.TvView;
import android.text.TextUtils;
import android.util.Log;
+import com.android.tv.common.compat.TvViewCompat.TvInputCallbackCompat;
+import com.android.tv.dvr.DvrTvView;
import com.android.tv.dvr.data.RecordedProgram;
+import com.android.tv.ui.AppLayerTvView;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
-class DvrPlayer {
+/** Player for recorded programs. */
+public class DvrPlayer {
private static final String TAG = "DvrPlayer";
private static final boolean DEBUG = false;
@@ -40,10 +45,11 @@ class DvrPlayer {
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 static final long FORWARD_POSITION_MARGIN_MS = TimeUnit.SECONDS.toMillis(5);
private RecordedProgram mProgram;
private long mInitialSeekPositionMs;
- private final TvView mTvView;
+ private final DvrTvView mTvView;
private DvrPlayerCallback mCallback;
private OnAspectRatioChangedListener mOnAspectRatioChangedListener;
private OnContentBlockedListener mOnContentBlockedListener;
@@ -63,6 +69,7 @@ class DvrPlayer {
private long mStartPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
private boolean mTimeShiftPlayAvailable;
+ /** Callback of DVR player. */
public static class DvrPlayerCallback {
/**
* Called when the playback position is changed. The normal updating frequency is around 1
@@ -74,8 +81,11 @@ class DvrPlayer {
public void onPlaybackStateChanged(int playbackState, int playbackSpeed) {}
/** Called when the playback toward the end. */
public void onPlaybackEnded() {}
+ /** Called when the playback is resumed to live position. */
+ public void onPlaybackResume() {}
}
+ /** Listener for aspect ratio changed events. */
public interface OnAspectRatioChangedListener {
/**
* Called when the Video's aspect ratio is changed.
@@ -86,27 +96,32 @@ class DvrPlayer {
void onAspectRatioChanged(float videoAspectRatio);
}
+ /** Listener for content blocked events. */
public interface OnContentBlockedListener {
/** Called when the Video's aspect ratio is changed. */
void onContentBlocked(TvContentRating rating);
}
+ /** Listener for tracks availability changed events */
public interface OnTracksAvailabilityChangedListener {
/** Called when the Video's subtitle or audio tracks are changed. */
void onTracksAvailabilityChanged(boolean hasClosedCaption, boolean hasMultiAudio);
}
+ /** Listener for track selected events */
public interface OnTrackSelectedListener {
/** Called when certain subtitle or audio track is selected. */
void onTrackSelected(String selectedTrackId);
}
- public DvrPlayer(TvView tvView) {
- mTvView = tvView;
+ /** Constructor of DvrPlayer. */
+ public DvrPlayer(AppLayerTvView tvView, Context context) {
+ mTvView = new DvrTvView(context, tvView, this);
mTvView.setCaptionEnabled(true);
mPlaybackParams.setSpeed(1.0f);
setTvViewCallbacks();
setCallback(null);
+ mTvView.init();
}
/**
@@ -333,7 +348,8 @@ class DvrPlayer {
/** Returns the audio tracks of the current playback. */
public ArrayList<TvTrackInfo> getAudioTracks() {
- return new ArrayList<>(mTvView.getTracks(TvTrackInfo.TYPE_AUDIO));
+ List<TvTrackInfo> tracks = mTvView.getTracks(TvTrackInfo.TYPE_AUDIO);
+ return tracks == null ? new ArrayList<>() : new ArrayList<>(tracks);
}
/** Returns the ID of the selected track of the given type. */
@@ -352,6 +368,10 @@ class DvrPlayer {
&& mPlaybackState != PlaybackState.STATE_CONNECTING;
}
+ public void release() {
+ mTvView.release();
+ }
+
/**
* Selects the given track.
*
@@ -426,9 +446,16 @@ class DvrPlayer {
resumeToWatchedPositionIfNeeded();
}
timeMs -= mStartPositionMs;
- if (mPlaybackState == PlaybackState.STATE_REWINDING
- && timeMs <= REWIND_POSITION_MARGIN_MS) {
+ long bufferedTimeMs =
+ System.currentTimeMillis()
+ - mProgram.getStartTimeUtcMillis()
+ - FORWARD_POSITION_MARGIN_MS;
+ if ((mPlaybackState == PlaybackState.STATE_REWINDING
+ && timeMs <= REWIND_POSITION_MARGIN_MS)
+ || (mPlaybackState == PlaybackState.STATE_FAST_FORWARDING
+ && timeMs > bufferedTimeMs)) {
play();
+ mCallback.onPlaybackResume();
} else {
mTimeShiftCurrentPositionMs = getRealSeekPosition(timeMs, 0);
mCallback.onPlaybackPositionChanged(mTimeShiftCurrentPositionMs);
@@ -440,7 +467,7 @@ class DvrPlayer {
}
});
mTvView.setCallback(
- new TvView.TvInputCallback() {
+ new TvInputCallbackCompat() {
@Override
public void onTimeShiftStatusChanged(String inputId, int status) {
if (DEBUG) Log.d(TAG, "onTimeShiftStatusChanged:" + status);