Download File
Download Project
Settings
Line Wrap
Themes
default
ambiance
bespin
dracula
eclipse
material
mbo
mdn-like
neat
solarized dark
ttcn
zenburn
LiveDataDetailViewWithVideoBackgroundFragment.java
/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.v17.leanback.supportleanbackshowcase.app.room.controller.detail; import android.arch.lifecycle.LifecycleOwner; import android.arch.lifecycle.Observer; import android.arch.lifecycle.ViewModelProvider; import android.arch.lifecycle.ViewModelProviders; import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v17.leanback.app.BackgroundManager; import android.support.v17.leanback.app.DetailsSupportFragment; import android.support.v17.leanback.app.DetailsSupportFragmentBackgroundController; import android.support.v17.leanback.media.MediaPlayerGlue; import android.support.v17.leanback.supportleanbackshowcase.R; import android.support.v17.leanback.supportleanbackshowcase.app.room.adapter.ListAdapter; import android.support.v17.leanback.supportleanbackshowcase.app.room.config.AppConfiguration; import android.support.v17.leanback.supportleanbackshowcase.app.room.controller.app.SampleApplication; import android.support.v17.leanback.supportleanbackshowcase.app.room.db.entity.VideoEntity; import android.support.v17.leanback.supportleanbackshowcase.app.room.di.action.qualifier.LoadingActionQualifier; import android.support.v17.leanback.supportleanbackshowcase.app.room.di.action.qualifier.PlayActionQualifier; import android.support.v17.leanback.supportleanbackshowcase.app.room.di.action.qualifier.PreviewActionQualifier; import android.support.v17.leanback.supportleanbackshowcase.app.room.di.action.qualifier.RentActionQualifier; import android.support.v17.leanback.supportleanbackshowcase.app.room.di.adapter.qualifier.DetailFragmentArrayObjectAdapterForActionsQualifier; import android.support.v17.leanback.supportleanbackshowcase.app.room.di.adapter.qualifier.DetailFragmentArrayObjectAdapterForRowsQualifier; import android.support.v17.leanback.supportleanbackshowcase.app.room.di.adapter.qualifier.ListAdapterForRelatedRowQualifier; import android.support.v17.leanback.supportleanbackshowcase.app.room.di.subcomponentinjection.DaggerApplicationComponent; import android.support.v17.leanback.supportleanbackshowcase.app.room.network.NetworkLiveData; import android.support.v17.leanback.supportleanbackshowcase.app.room.viewmodel.VideosViewModel; import android.support.v17.leanback.widget.Action; import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.DetailsOverviewRow; import android.support.v17.leanback.widget.FullWidthDetailsOverviewSharedElementHelper; import android.support.v17.leanback.widget.ListRow; import android.support.v17.leanback.widget.OnActionClickedListener; import android.support.v17.leanback.widget.OnItemViewClickedListener; import android.support.v17.leanback.widget.PresenterSelector; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.widget.Toast; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.SimpleTarget; import com.bumptech.glide.request.transition.Transition; import java.util.Comparator; import java.util.List; import java.util.Map; import javax.inject.Inject; public class LiveDataDetailViewWithVideoBackgroundFragment extends DetailsSupportFragment { // For debugging purpose. private static final Boolean DEBUG = true; private static final String TAG = "leanback.DetailsFrag"; // Resource category private static final String BACKGROUND = "background"; private static final String CARD = "card"; private static final String VIDEO = "video"; private static final String TRAILER = "trailer"; private static final String TRAILER_VIDEO = " (Trailer)"; private static final String RENTED_VIDEO = " (Rented)"; private static final String RENTED = "rented"; @Inject DetailsSupportFragmentBackgroundController mDetailsBgController; @Inject @DetailFragmentArrayObjectAdapterForRowsQualifier ArrayObjectAdapter mRowsAdapter; @Inject DetailsOverviewRow mDescriptionOverviewRow; @Inject MediaPlayerGlue mVideoGlue; @Inject @DetailFragmentArrayObjectAdapterForActionsQualifier ArrayObjectAdapter mActionAdapter; @Inject BackgroundManager mBackgroundManager; @Inject FullWidthDetailsOverviewSharedElementHelper mHelper; @Inject DisplayMetrics mMetrics; @Inject ListRow mRelatedRow; @Inject @ListAdapterForRelatedRowQualifier ListAdapter
mRelatedRowAdapter; @Inject PresenterSelector mPs; @Inject @PlayActionQualifier Action mActionPlay; @Inject @PreviewActionQualifier Action mActionPreview; @Inject @RentActionQualifier Action mActionRent; @Inject @LoadingActionQualifier Action mActionLoading; @Inject ViewModelProvider.Factory viewModelFactory; @Inject Map
viewOnClickListenerMap; @Inject RequestOptions mDefaultPlaceHolder; private VideoEntity mObservedVideo; private long mSelectedVideoId; private VideosViewModel mViewModel; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // As we have stated in the readme, we have used three different approach to implement the // dependency injection on the app level. For LiveDataDetailViewWithVideoBackgroundFragment // .java, we use the traditionaly subcomponent method to implement the DI DaggerApplicationComponent.builder().application(SampleApplication.getInstance()) .build().presenterBuilder().activity(this.getActivity()) .detailsSupportFragment(this).actionClickedListener(new ActionClickedListener()) .build().inject(this); mSelectedVideoId = getActivity().getIntent().getLongExtra( LiveDataDetailActivity.VIDEO_ID, -1L); mObservedVideo = getActivity().getIntent().getParcelableExtra( LiveDataDetailActivity.CACHED_CONTENT); setSelectedPosition(0, false); setAdapter(mRowsAdapter); mBackgroundManager.attach(getActivity().getWindow()); mHelper.startPostponedEnterTransition(); setOnItemViewClickedListener(viewOnClickListenerMap.get(this.getClass())); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mDetailsBgController.enableParallax(); // First loading "cached" data to finish transition animation mActionAdapter.clear(); mActionAdapter.add(mActionLoading); mDescriptionOverviewRow.setItem(mObservedVideo); loadAndSetVideoCardImage(); mViewModel = ViewModelProviders.of(getActivity(), viewModelFactory) .get(VideosViewModel.class); subscribeToModel(mViewModel); subscribeToNetworkLiveData(); } private void subscribeToModel(final VideosViewModel model) { // Update video Id (live data) and the ui will be updated automatically. model.setVideoId(mSelectedVideoId); model.getVideoById().observe((LifecycleOwner) getActivity(), new Observer
() { @Override public void onChanged(@Nullable VideoEntity videoEntity) { if (videoEntity != null) { mObservedVideo = videoEntity; mDetailsBgController.setupVideoPlayback(mVideoGlue); // different loading strategy based on whether the video is rented or not if (!videoEntity.isRented()) { mActionAdapter.clear(); mActionAdapter.add(mActionRent); mActionAdapter.add(mActionPreview); mVideoGlue.setTitle(mObservedVideo.getTitle().concat(TRAILER_VIDEO)); mVideoGlue.setVideoUrl(findLocalContentUriOrNetworkUrl(TRAILER)); } else { getActivity().findViewById(R.id.renting_progressbar) .setVisibility(View.GONE); getActivity().findViewById(R.id.loading_renting).setVisibility(View.GONE); mActionAdapter.clear(); mActionAdapter.add(mActionPlay); mVideoGlue.setTitle(mObservedVideo.getTitle().concat(RENTED_VIDEO)); mVideoGlue.setVideoUrl(findLocalContentUriOrNetworkUrl(VIDEO)); } mDescriptionOverviewRow.setItem(mObservedVideo); loadAndSetVideoCardImage(); model.setCategory(mObservedVideo.getCategory()); } } }); model.getVideosInSameCategory() .observe((LifecycleOwner) getActivity(), new Observer
>() { @Override public void onChanged(@Nullable List
videoEntities) { if (videoEntities != null) { mRelatedRowAdapter .setItems(videoEntities, new Comparator
() { @Override public int compare(VideoEntity o1, VideoEntity o2) { return o1.getId() == o2.getId() ? 0 : -1; } }, new Comparator
() { @Override public int compare(VideoEntity o1, VideoEntity o2) { return o1.equals(o2) ? 0 : -1; } }); } } }); } private void subscribeToNetworkLiveData() { NetworkLiveData.sync(getActivity()) .observe((LifecycleOwner) getActivity(), new Observer
() { @Override public void onChanged(@Nullable Boolean aBoolean) { if (aBoolean) { getActivity().findViewById(R.id.no_internet_detail) .setVisibility(View.GONE); } else { getActivity().findViewById(R.id.no_internet_detail) .setVisibility(View.VISIBLE); } } }); } // Action Listener is dependent on Runtime information. Cannot be injected in the Listener's // module private final class ActionClickedListener implements OnActionClickedListener { @Override public void onActionClicked(Action action) { if (action == mActionRent) { new AsyncTask
() { @Override protected Void doInBackground(Void... voids) { if (AppConfiguration.IS_RENTING_OPERATION_DELAY_ENABLED) { addDelay(2000L); } // update the database with rented field mViewModel.updateDatabase(mObservedVideo, RENTED, ""); return null; } }.execute(); getActivity().findViewById(R.id.renting_progressbar).setVisibility(View.VISIBLE); getActivity().findViewById(R.id.loading_renting).setVisibility(View.VISIBLE); } else if (action == mActionPlay) { mDetailsBgController.switchToVideo(); } else if (action == mActionPreview) { mDetailsBgController.switchToVideo(); } else if (action == mActionLoading) { Toast.makeText(getActivity(), "Loading...", Toast.LENGTH_SHORT).show(); } } } private void loadAndSetVideoCardImage() { String loadedUri = findLocalContentUriOrNetworkUrl(CARD); Glide.with(this) .asBitmap() .load(loadedUri) .apply(mDefaultPlaceHolder) .into(new SimpleTarget
() { @Override public void onResourceReady(final Bitmap resource, Transition super Bitmap> glideAnimation) { mDescriptionOverviewRow.setImageBitmap(getActivity(), resource); } }); setSelectedPosition(0, false); } private String findLocalContentUriOrNetworkUrl(String type) { String loadedUri; switch (type) { case BACKGROUND: if (!mObservedVideo.getVideoBgImageLocalStorageUrl().isEmpty()) { loadedUri = mObservedVideo.getVideoBgImageLocalStorageUrl(); } else { loadedUri = mObservedVideo.getBgImageUrl(); } break; case CARD: if (!mObservedVideo.getVideoCardImageLocalStorageUrl().isEmpty()) { loadedUri = mObservedVideo.getVideoCardImageLocalStorageUrl(); } else { loadedUri = mObservedVideo.getCardImageUrl(); } break; case VIDEO: if (!mObservedVideo.getVideoLocalStorageUrl().isEmpty()) { loadedUri = mObservedVideo.getVideoLocalStorageUrl(); } else { loadedUri = mObservedVideo.getVideoUrl(); } break; case TRAILER: loadedUri = mObservedVideo.getTrailerVideoUrl(); break; default: if (DEBUG) { Log.d(TAG, "Not valid image resource type"); } return ""; } return loadedUri; } private void addDelay(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { if (DEBUG) { Log.e(TAG, "addDelay: ", e); } } } }