aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java')
-rw-r--r--src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java307
1 files changed, 307 insertions, 0 deletions
diff --git a/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java b/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java
new file mode 100644
index 00000000..19fb7117
--- /dev/null
+++ b/src/com/android/tv/dvr/ui/browse/DvrDetailsFragment.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.tv.dvr.ui.browse;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.media.tv.TvContentRating;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v17.leanback.app.DetailsFragment;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ClassPresenterSelector;
+import android.support.v17.leanback.widget.DetailsOverviewRow;
+import android.support.v17.leanback.widget.DetailsOverviewRowPresenter;
+import android.support.v17.leanback.widget.OnActionClickedListener;
+import android.support.v17.leanback.widget.PresenterSelector;
+import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
+import android.support.v17.leanback.widget.VerticalGridView;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+import com.android.tv.R;
+import com.android.tv.TvApplication;
+import com.android.tv.common.SoftPreconditions;
+import com.android.tv.data.Channel;
+import com.android.tv.data.ChannelDataManager;
+import com.android.tv.dialog.PinDialogFragment;
+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.util.ImageLoader;
+import com.android.tv.util.ToastUtils;
+import com.android.tv.util.Utils;
+
+import java.io.File;
+
+abstract class DvrDetailsFragment extends DetailsFragment {
+ private static final int LOAD_LOGO_IMAGE = 1;
+ private static final int LOAD_BACKGROUND_IMAGE = 2;
+
+ protected DetailsViewBackgroundHelper mBackgroundHelper;
+ private ArrayObjectAdapter mRowsAdapter;
+ private DetailsOverviewRow mDetailsOverview;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (!onLoadRecordingDetails(getArguments())) {
+ getActivity().finish();
+ return;
+ }
+ mBackgroundHelper = new DetailsViewBackgroundHelper(getActivity());
+ setupAdapter();
+ onCreateInternal();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ // TODO: remove the workaround of b/30401180.
+ VerticalGridView container = (VerticalGridView) getActivity()
+ .findViewById(R.id.container_list);
+ // Need to manually modify offset. Please refer DetailsFragment.setVerticalGridViewLayout.
+ container.setItemAlignmentOffset(0);
+ container.setWindowAlignmentOffset(
+ getResources().getDimensionPixelSize(R.dimen.lb_details_rows_align_top));
+ }
+
+ private void setupAdapter() {
+ DetailsOverviewRowPresenter rowPresenter = new DetailsOverviewRowPresenter(
+ new DetailsContentPresenter(getActivity()));
+ rowPresenter.setBackgroundColor(getResources().getColor(R.color.common_tv_background,
+ null));
+ rowPresenter.setSharedElementEnterTransition(getActivity(),
+ DvrDetailsActivity.SHARED_ELEMENT_NAME);
+ rowPresenter.setOnActionClickedListener(onCreateOnActionClickedListener());
+ mRowsAdapter = new ArrayObjectAdapter(onCreatePresenterSelector(rowPresenter));
+ setAdapter(mRowsAdapter);
+ }
+
+ /**
+ * Returns details views' rows adapter.
+ */
+ protected ArrayObjectAdapter getRowsAdapter() {
+ return mRowsAdapter;
+ }
+
+ /**
+ * Sets details overview.
+ */
+ protected void setDetailsOverviewRow(DetailsContent detailsContent) {
+ mDetailsOverview = new DetailsOverviewRow(detailsContent);
+ mDetailsOverview.setActionsAdapter(onCreateActionsAdapter());
+ mRowsAdapter.add(mDetailsOverview);
+ onLoadLogoAndBackgroundImages(detailsContent);
+ }
+
+ /**
+ * Creates and returns presenter selector will be used by rows adaptor.
+ */
+ protected PresenterSelector onCreatePresenterSelector(
+ DetailsOverviewRowPresenter rowPresenter) {
+ ClassPresenterSelector presenterSelector = new ClassPresenterSelector();
+ presenterSelector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
+ return presenterSelector;
+ }
+
+ /**
+ * Does customized initialization of subclasses. Since {@link #onCreate(Bundle)} might finish
+ * activity early when it cannot fetch valid recordings, subclasses' onCreate method should not
+ * do anything after calling {@link #onCreate(Bundle)}. If there's something subclasses have to
+ * do after the super class did onCreate, it should override this method and put the codes here.
+ */
+ protected void onCreateInternal() { }
+
+ /**
+ * Updates actions of details overview.
+ */
+ protected void updateActions() {
+ mDetailsOverview.setActionsAdapter(onCreateActionsAdapter());
+ }
+
+ /**
+ * Loads recording details according to the arguments the fragment got.
+ *
+ * @return false if cannot find valid recordings, else return true. If the return value
+ * is false, the detail activity and fragment will be ended.
+ */
+ abstract boolean onLoadRecordingDetails(Bundle args);
+
+ /**
+ * Creates actions users can interact with and their adaptor for this fragment.
+ */
+ abstract SparseArrayObjectAdapter onCreateActionsAdapter();
+
+ /**
+ * Creates actions listeners to implement the behavior of the fragment after users click some
+ * action buttons.
+ */
+ abstract OnActionClickedListener onCreateOnActionClickedListener();
+
+ /**
+ * Loads logo and background images for detail fragments.
+ */
+ protected void onLoadLogoAndBackgroundImages(DetailsContent detailsContent) {
+ Drawable logoDrawable = null;
+ Drawable backgroundDrawable = null;
+ if (TextUtils.isEmpty(detailsContent.getLogoImageUri())) {
+ logoDrawable = getContext().getResources()
+ .getDrawable(R.drawable.dvr_default_poster, null);
+ mDetailsOverview.setImageDrawable(logoDrawable);
+ }
+ if (TextUtils.isEmpty(detailsContent.getBackgroundImageUri())) {
+ backgroundDrawable = getContext().getResources()
+ .getDrawable(R.drawable.dvr_default_poster, null);
+ mBackgroundHelper.setBackground(backgroundDrawable);
+ }
+ if (logoDrawable != null && backgroundDrawable != null) {
+ return;
+ }
+ if (logoDrawable == null && backgroundDrawable == null
+ && detailsContent.getLogoImageUri().equals(
+ detailsContent.getBackgroundImageUri())) {
+ ImageLoader.loadBitmap(getContext(), detailsContent.getLogoImageUri(),
+ new MyImageLoaderCallback(this, LOAD_LOGO_IMAGE | LOAD_BACKGROUND_IMAGE,
+ getContext()));
+ return;
+ }
+ if (logoDrawable == null) {
+ int imageWidth = getResources().getDimensionPixelSize(R.dimen.dvr_details_poster_width);
+ int imageHeight = getResources()
+ .getDimensionPixelSize(R.dimen.dvr_details_poster_height);
+ ImageLoader.loadBitmap(getContext(), detailsContent.getLogoImageUri(),
+ imageWidth, imageHeight,
+ new MyImageLoaderCallback(this, LOAD_LOGO_IMAGE, getContext()));
+ }
+ if (backgroundDrawable == null) {
+ ImageLoader.loadBitmap(getContext(), detailsContent.getBackgroundImageUri(),
+ new MyImageLoaderCallback(this, LOAD_BACKGROUND_IMAGE, getContext()));
+ }
+ }
+
+ protected void startPlayback(RecordedProgram recordedProgram, long seekTimeMs) {
+ if (Utils.isInBundledPackageSet(recordedProgram.getPackageName()) &&
+ !isDataUriAccessible(recordedProgram.getDataUri())) {
+ // Since cleaning RecordedProgram from forgotten storage will take some time,
+ // ignore playback until cleaning is finished.
+ ToastUtils.show(getContext(),
+ getContext().getResources().getString(R.string.dvr_toast_recording_deleted),
+ Toast.LENGTH_SHORT);
+ return;
+ }
+ long programId = recordedProgram.getId();
+ ParentalControlSettings parental = TvApplication.getSingletons(getActivity())
+ .getTvInputManagerHelper().getParentalControlSettings();
+ if (!parental.isParentalControlsEnabled()) {
+ DvrUiHelper.startPlaybackActivity(getContext(), programId, seekTimeMs, false);
+ return;
+ }
+ ChannelDataManager channelDataManager =
+ TvApplication.getSingletons(getActivity()).getChannelDataManager();
+ Channel channel = channelDataManager.getChannel(recordedProgram.getChannelId());
+ if (channel != null && channel.isLocked()) {
+ checkPinToPlay(recordedProgram, seekTimeMs);
+ return;
+ }
+ TvContentRating[] ratings = recordedProgram.getContentRatings();
+ TvContentRating blockRatings = parental.getBlockedRating(ratings);
+ if (blockRatings != null) {
+ checkPinToPlay(recordedProgram, seekTimeMs);
+ } else {
+ DvrUiHelper.startPlaybackActivity(getContext(), programId, seekTimeMs, false);
+ }
+ }
+
+ private boolean isDataUriAccessible(Uri dataUri) {
+ if (dataUri == null || dataUri.getPath() == null) {
+ return false;
+ }
+ try {
+ File recordedProgramPath = new File(dataUri.getPath());
+ if (recordedProgramPath.exists()) {
+ return true;
+ }
+ } catch (SecurityException e) {
+ }
+ return false;
+ }
+
+ private void checkPinToPlay(RecordedProgram recordedProgram, long seekTimeMs) {
+ SoftPreconditions.checkState(getActivity() instanceof DvrDetailsActivity);
+ if (getActivity() instanceof DvrDetailsActivity) {
+ ((DvrDetailsActivity) getActivity()).setOnPinCheckListener(new OnPinCheckedListener() {
+ @Override
+ public void onPinChecked(boolean checked, int type, String rating) {
+ ((DvrDetailsActivity) getActivity()).setOnPinCheckListener(null);
+ if (checked && type == PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_PROGRAM) {
+ DvrUiHelper.startPlaybackActivity(getContext(), recordedProgram.getId(),
+ seekTimeMs, true);
+ }
+ }
+ });
+ PinDialogFragment.create(PinDialogFragment.PIN_DIALOG_TYPE_UNLOCK_PROGRAM)
+ .show(getActivity().getFragmentManager(), PinDialogFragment.DIALOG_TAG);
+ }
+ }
+
+ private static class MyImageLoaderCallback extends
+ ImageLoader.ImageLoaderCallback<DvrDetailsFragment> {
+ private final Context mContext;
+ private final int mLoadType;
+
+ public MyImageLoaderCallback(DvrDetailsFragment fragment,
+ int loadType, Context context) {
+ super(fragment);
+ mLoadType = loadType;
+ mContext = context;
+ }
+
+ @Override
+ public void onBitmapLoaded(DvrDetailsFragment fragment,
+ @Nullable Bitmap bitmap) {
+ Drawable drawable;
+ int loadType = mLoadType;
+ if (bitmap == null) {
+ Resources res = mContext.getResources();
+ drawable = res.getDrawable(R.drawable.dvr_default_poster, null);
+ if ((loadType & LOAD_BACKGROUND_IMAGE) != 0 && !fragment.isDetached()) {
+ loadType &= ~LOAD_BACKGROUND_IMAGE;
+ fragment.mBackgroundHelper.setBackgroundColor(
+ res.getColor(R.color.dvr_detail_default_background));
+ fragment.mBackgroundHelper.setScrim(
+ res.getColor(R.color.dvr_detail_default_background_scrim));
+ }
+ } else {
+ drawable = new BitmapDrawable(mContext.getResources(), bitmap);
+ }
+ if (!fragment.isDetached()) {
+ if ((loadType & LOAD_LOGO_IMAGE) != 0) {
+ fragment.mDetailsOverview.setImageDrawable(drawable);
+ }
+ if ((loadType & LOAD_BACKGROUND_IMAGE) != 0) {
+ fragment.mBackgroundHelper.setBackground(drawable);
+ }
+ }
+ }
+ }
+}