/* * Copyright (c) 2014 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.example.android.tvleanback.ui; import android.app.LoaderManager; import android.app.NotificationManager; import android.content.Context; import android.content.CursorLoader; import android.content.Intent; import android.content.Loader; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.support.v17.leanback.app.BackgroundManager; import android.support.v17.leanback.app.DetailsFragment; import android.support.v17.leanback.widget.Action; import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.ClassPresenterSelector; import android.support.v17.leanback.widget.CursorObjectAdapter; import android.support.v17.leanback.widget.DetailsOverviewLogoPresenter; import android.support.v17.leanback.widget.DetailsOverviewRow; import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter; import android.support.v17.leanback.widget.FullWidthDetailsOverviewSharedElementHelper; import android.support.v17.leanback.widget.HeaderItem; import android.support.v17.leanback.widget.ImageCardView; import android.support.v17.leanback.widget.ListRow; import android.support.v17.leanback.widget.ListRowPresenter; import android.support.v17.leanback.widget.OnActionClickedListener; import android.support.v17.leanback.widget.OnItemViewClickedListener; import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.RowPresenter; import android.support.v17.leanback.widget.SparseArrayObjectAdapter; import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.content.ContextCompat; import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.Toast; import com.bumptech.glide.Glide; import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.SimpleTarget; import com.example.android.tvleanback.R; import com.example.android.tvleanback.data.VideoContract; import com.example.android.tvleanback.model.Video; import com.example.android.tvleanback.model.VideoCursorMapper; import com.example.android.tvleanback.presenter.CardPresenter; import com.example.android.tvleanback.presenter.DetailsDescriptionPresenter; /* * VideoDetailsFragment extends DetailsFragment, a Wrapper fragment for leanback details screens. * It shows a detailed view of video and its metadata plus related videos. */ public class VideoDetailsFragment extends DetailsFragment implements LoaderManager.LoaderCallbacks<Cursor> { private static final int NO_NOTIFICATION = -1; private static final int ACTION_WATCH_TRAILER = 1; private static final int ACTION_RENT = 2; private static final int ACTION_BUY = 3; // ID for loader that loads related videos. private static final int RELATED_VIDEO_LOADER = 1; // ID for loader that loads the video from global search. private int mGlobalSearchVideoId = 2; private Video mSelectedVideo; private ArrayObjectAdapter mAdapter; private ClassPresenterSelector mPresenterSelector; private BackgroundManager mBackgroundManager; private Drawable mDefaultBackground; private DisplayMetrics mMetrics; private CursorObjectAdapter mVideoCursorAdapter; private FullWidthDetailsOverviewSharedElementHelper mHelper; private final VideoCursorMapper mVideoCursorMapper = new VideoCursorMapper(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); prepareBackgroundManager(); mVideoCursorAdapter = new CursorObjectAdapter(new CardPresenter()); mVideoCursorAdapter.setMapper(mVideoCursorMapper); mSelectedVideo = (Video) getActivity().getIntent() .getParcelableExtra(VideoDetailsActivity.VIDEO); if (mSelectedVideo != null || !hasGlobalSearchIntent()) { removeNotification(getActivity().getIntent() .getIntExtra(VideoDetailsActivity.NOTIFICATION_ID, NO_NOTIFICATION)); setupAdapter(); setupDetailsOverviewRow(); setupMovieListRow(); updateBackground(mSelectedVideo.bgImageUrl); // When a Related Video item is clicked. setOnItemViewClickedListener(new ItemViewClickedListener()); } } private void removeNotification(int notificationId) { if (notificationId != NO_NOTIFICATION) { NotificationManager notificationManager = (NotificationManager) getActivity() .getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel(notificationId); } } @Override public void onStop() { mBackgroundManager.release(); super.onStop(); } /** * Check if there is a global search intent. If there is, load that video. */ private boolean hasGlobalSearchIntent() { Intent intent = getActivity().getIntent(); String intentAction = intent.getAction(); String globalSearch = getString(R.string.global_search); if (globalSearch.equalsIgnoreCase(intentAction)) { Uri intentData = intent.getData(); String videoId = intentData.getLastPathSegment(); Bundle args = new Bundle(); args.putString(VideoContract.VideoEntry._ID, videoId); getLoaderManager().initLoader(mGlobalSearchVideoId++, args, this); return true; } return false; } private void prepareBackgroundManager() { mBackgroundManager = BackgroundManager.getInstance(getActivity()); mBackgroundManager.attach(getActivity().getWindow()); mDefaultBackground = getResources().getDrawable(R.drawable.default_background, null); mMetrics = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay().getMetrics(mMetrics); } private void updateBackground(String uri) { Glide.with(this) .load(uri) .asBitmap() .centerCrop() .error(mDefaultBackground) .into(new SimpleTarget<Bitmap>(mMetrics.widthPixels, mMetrics.heightPixels) { @Override public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) { mBackgroundManager.setBitmap(resource); } }); } private void setupAdapter() { // Set detail background and style. FullWidthDetailsOverviewRowPresenter detailsPresenter = new FullWidthDetailsOverviewRowPresenter(new DetailsDescriptionPresenter(), new MovieDetailsOverviewLogoPresenter()); detailsPresenter.setBackgroundColor( ContextCompat.getColor(getActivity(), R.color.selected_background)); detailsPresenter.setInitialState(FullWidthDetailsOverviewRowPresenter.STATE_HALF); // Hook up transition element. mHelper = new FullWidthDetailsOverviewSharedElementHelper(); mHelper.setSharedElementEnterTransition(getActivity(), VideoDetailsActivity.SHARED_ELEMENT_NAME); detailsPresenter.setListener(mHelper); detailsPresenter.setParticipatingEntranceTransition(false); prepareEntranceTransition(); detailsPresenter.setOnActionClickedListener(new OnActionClickedListener() { @Override public void onActionClicked(Action action) { if (action.getId() == ACTION_WATCH_TRAILER) { Intent intent = new Intent(getActivity(), PlaybackOverlayActivity.class); intent.putExtra(VideoDetailsActivity.VIDEO, mSelectedVideo); startActivity(intent); } else { Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show(); } } }); mPresenterSelector = new ClassPresenterSelector(); mPresenterSelector.addClassPresenter(DetailsOverviewRow.class, detailsPresenter); mPresenterSelector.addClassPresenter(ListRow.class, new ListRowPresenter()); mAdapter = new ArrayObjectAdapter(mPresenterSelector); setAdapter(mAdapter); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { switch (id) { case RELATED_VIDEO_LOADER: { String category = args.getString(VideoContract.VideoEntry.COLUMN_CATEGORY); return new CursorLoader( getActivity(), VideoContract.VideoEntry.CONTENT_URI, null, VideoContract.VideoEntry.COLUMN_CATEGORY + " = ?", new String[]{category}, null ); } default: { // Loading video from global search. String videoId = args.getString(VideoContract.VideoEntry._ID); return new CursorLoader( getActivity(), VideoContract.VideoEntry.CONTENT_URI, null, VideoContract.VideoEntry._ID + " = ?", new String[]{videoId}, null ); } } } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { if (cursor != null && cursor.moveToNext()) { switch (loader.getId()) { case RELATED_VIDEO_LOADER: { mVideoCursorAdapter.changeCursor(cursor); break; } default: { // Loading video from global search. mSelectedVideo = (Video) mVideoCursorMapper.convert(cursor); setupAdapter(); setupDetailsOverviewRow(); setupMovieListRow(); updateBackground(mSelectedVideo.bgImageUrl); // When a Related Video item is clicked. setOnItemViewClickedListener(new ItemViewClickedListener()); } } } } @Override public void onLoaderReset(Loader<Cursor> loader) { mVideoCursorAdapter.changeCursor(null); } static class MovieDetailsOverviewLogoPresenter extends DetailsOverviewLogoPresenter { static class ViewHolder extends DetailsOverviewLogoPresenter.ViewHolder { public ViewHolder(View view) { super(view); } public FullWidthDetailsOverviewRowPresenter getParentPresenter() { return mParentPresenter; } public FullWidthDetailsOverviewRowPresenter.ViewHolder getParentViewHolder() { return mParentViewHolder; } } @Override public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) { ImageView imageView = (ImageView) LayoutInflater.from(parent.getContext()) .inflate(R.layout.lb_fullwidth_details_overview_logo, parent, false); Resources res = parent.getResources(); int width = res.getDimensionPixelSize(R.dimen.detail_thumb_width); int height = res.getDimensionPixelSize(R.dimen.detail_thumb_height); imageView.setLayoutParams(new ViewGroup.MarginLayoutParams(width, height)); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); return new ViewHolder(imageView); } @Override public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { DetailsOverviewRow row = (DetailsOverviewRow) item; ImageView imageView = ((ImageView) viewHolder.view); imageView.setImageDrawable(row.getImageDrawable()); if (isBoundToImage((ViewHolder) viewHolder, row)) { MovieDetailsOverviewLogoPresenter.ViewHolder vh = (MovieDetailsOverviewLogoPresenter.ViewHolder) viewHolder; vh.getParentPresenter().notifyOnBindLogo(vh.getParentViewHolder()); } } } private void setupDetailsOverviewRow() { final DetailsOverviewRow row = new DetailsOverviewRow(mSelectedVideo); Glide.with(this) .load(mSelectedVideo.cardImageUrl) .asBitmap() .dontAnimate() .error(R.drawable.default_background) .into(new SimpleTarget<Bitmap>() { @Override public void onResourceReady(final Bitmap resource, GlideAnimation glideAnimation) { row.setImageBitmap(getActivity(), resource); startEntranceTransition(); } }); SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter(); adapter.set(ACTION_WATCH_TRAILER, new Action(ACTION_WATCH_TRAILER, getResources() .getString(R.string.watch_trailer_1), getResources().getString(R.string.watch_trailer_2))); adapter.set(ACTION_RENT, new Action(ACTION_RENT, getResources().getString(R.string.rent_1), getResources().getString(R.string.rent_2))); adapter.set(ACTION_BUY, new Action(ACTION_BUY, getResources().getString(R.string.buy_1), getResources().getString(R.string.buy_2))); row.setActionsAdapter(adapter); mAdapter.add(row); } private void setupMovieListRow() { String subcategories[] = {getString(R.string.related_movies)}; // Generating related video list. String category = mSelectedVideo.category; Bundle args = new Bundle(); args.putString(VideoContract.VideoEntry.COLUMN_CATEGORY, category); getLoaderManager().initLoader(RELATED_VIDEO_LOADER, args, this); HeaderItem header = new HeaderItem(0, subcategories[0]); mAdapter.add(new ListRow(header, mVideoCursorAdapter)); } private final class ItemViewClickedListener implements OnItemViewClickedListener { @Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { if (item instanceof Video) { Video video = (Video) item; Intent intent = new Intent(getActivity(), VideoDetailsActivity.class); intent.putExtra(VideoDetailsActivity.VIDEO, video); Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation( getActivity(), ((ImageCardView) itemViewHolder.view).getMainImageView(), VideoDetailsActivity.SHARED_ELEMENT_NAME).toBundle(); getActivity().startActivity(intent, bundle); } } } }