/***************************************************************************** * MainTvActivity.java ***************************************************************************** * Copyright © 2014-2015 VLC authors, VideoLAN and VideoLabs * Author: Geoffrey Métais * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ package org.videolan.vlc.gui.tv; import android.app.Activity; import android.app.FragmentManager; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.v17.leanback.app.BackgroundManager; import android.support.v17.leanback.app.BrowseFragment; import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.HeaderItem; import android.support.v17.leanback.widget.ListRow; import android.support.v17.leanback.widget.ListRowPresenter; import android.support.v17.leanback.widget.OnItemViewClickedListener; import android.support.v17.leanback.widget.OnItemViewSelectedListener; import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.RowPresenter; import android.support.v4.util.ArrayMap; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.widget.ProgressBar; import org.videolan.vlc.MediaDatabase; import org.videolan.vlc.MediaLibrary; import org.videolan.vlc.MediaWrapper; import org.videolan.vlc.PlaybackService; import org.videolan.vlc.R; import org.videolan.vlc.Thumbnailer; import org.videolan.vlc.VLCApplication; import org.videolan.vlc.gui.tv.audioplayer.AudioPlayerActivity; import org.videolan.vlc.gui.tv.browser.BaseTvActivity; import org.videolan.vlc.gui.tv.browser.MusicFragment; import org.videolan.vlc.gui.tv.browser.VerticalGridActivity; import org.videolan.vlc.gui.preferences.PreferencesActivity; import org.videolan.vlc.gui.video.VideoListHandler; import org.videolan.vlc.interfaces.IVideoBrowser; import org.videolan.vlc.util.AndroidDevices; import org.videolan.vlc.util.VLCInstance; import java.util.ArrayList; public class MainTvActivity extends BaseTvActivity implements IVideoBrowser, OnItemViewSelectedListener, OnItemViewClickedListener, OnClickListener { private static final int NUM_ITEMS_PREVIEW = 5; public static final long HEADER_VIDEO = 0; public static final long HEADER_CATEGORIES = 1; public static final long HEADER_NETWORK = 2; public static final long HEADER_MISC = 3; private static final int ACTIVITY_RESULT_PREFERENCES = 1; public static final String BROWSER_TYPE = "browser_type"; public static final String TAG = "VLC/MainTvActivity"; protected BrowseFragment mBrowseFragment; private ProgressBar mProgressBar; private static Thumbnailer sThumbnailer; ArrayObjectAdapter mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); ArrayObjectAdapter mVideoAdapter; ArrayObjectAdapter mCategoriesAdapter; ArrayObjectAdapter mNetworkAdapter; ArrayObjectAdapter mOtherAdapter; View mRootContainer; final ArrayMap<String, Integer> mVideoIndex = new ArrayMap<String, Integer>(); Drawable mDefaultBackground; Activity mContext; private Object mSelectedItem; private AsyncUpdate mUpdateTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (mMediaLibrary.getMediaItems().isEmpty()) { if (mSettings.getBoolean(PreferencesActivity.AUTO_RESCAN, true)) mMediaLibrary.scanMediaItems(false); else mMediaLibrary.loadMedaItems(); } if (!VLCInstance.testCompatibleCPU(this)) { finish(); return; } mContext = this; setContentView(R.layout.tv_main_fragment); mDefaultBackground = getResources().getDrawable(R.drawable.background); final FragmentManager fragmentManager = getFragmentManager(); mBrowseFragment = (BrowseFragment) fragmentManager.findFragmentById( R.id.browse_fragment); mProgressBar = (ProgressBar) findViewById(R.id.tv_main_progress); // Set display parameters for the BrowseFragment mBrowseFragment.setHeadersState(BrowseFragment.HEADERS_ENABLED); mBrowseFragment.setTitle(getString(R.string.app_name)); mBrowseFragment.setBadgeDrawable(getResources().getDrawable(R.drawable.icon)); // add a listener for selected items mBrowseFragment.setOnItemViewClickedListener(this); mBrowseFragment.setOnItemViewSelectedListener(this); if (!Build.MANUFACTURER.equalsIgnoreCase("amazon")) { //Hide search for Amazon Fire TVs mBrowseFragment.setOnSearchClickedListener(this); // set search icon color mBrowseFragment.setSearchAffordanceColor(getResources().getColor(R.color.orange500)); } mRootContainer = mBrowseFragment.getView(); } @Override public void onConnected(PlaybackService service) { super.onConnected(service); /* * skip browser and show directly Audio Player if a song is playing */ if (service.isPlaying() && !service.canSwitchToVideo()) { updateList(); } } @Override protected void onStop() { super.onStop(); Intent recommendationIntent = new Intent(this, RecommendationsService.class); startService(recommendationIntent); } protected void onResume() { super.onResume(); mMediaLibrary.addUpdateHandler(mHandler); if (sThumbnailer != null) sThumbnailer.setVideoBrowser(this); mBrowseFragment.setBrandColor(getResources().getColor(R.color.orange800)); } protected void onPause() { super.onPause(); mMediaLibrary.removeUpdateHandler(mHandler); /* Stop the thumbnailer */ if (sThumbnailer != null) sThumbnailer.setVideoBrowser(null); } @Override protected void onDestroy() { super.onDestroy(); if (sThumbnailer != null) sThumbnailer.clearJobs(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == ACTIVITY_RESULT_PREFERENCES) { if (resultCode == PreferencesActivity.RESULT_RESCAN) MediaLibrary.getInstance().scanMediaItems(true); else if (resultCode == PreferencesActivity.RESULT_RESTART) { Intent intent = getIntent(); finish(); startActivity(intent); } } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_BUTTON_Y) && mSelectedItem instanceof MediaWrapper) { MediaWrapper media = (MediaWrapper) mSelectedItem; if (media.getType() != MediaWrapper.TYPE_DIR) return false; Intent intent = new Intent(this, DetailsActivity.class); // pass the item information intent.putExtra("media", (MediaWrapper) mSelectedItem); intent.putExtra("item", new MediaItemDetails(media.getTitle(), media.getArtist(), media.getAlbum(), media.getLocation())); startActivity(intent); return true; } return super.onKeyDown(keyCode, event); } protected void updateBackground(Drawable drawable) { BackgroundManager.getInstance(this).setDrawable(drawable); } protected void clearBackground() { BackgroundManager.getInstance(this).setDrawable(mDefaultBackground); } public void updateList() { if (mUpdateTask == null || mUpdateTask.getStatus() == AsyncTask.Status.FINISHED) { mUpdateTask = new AsyncUpdate(); mUpdateTask.execute(); } else { mUpdateTask.AskRefresh(); } checkThumbs(); } @Override public void showProgressBar() { // runOnUiThread(new Runnable() { // @Override // public void run() { // mProgressBar.setVisibility(View.VISIBLE); // } // }); } @Override public void hideProgressBar() { // runOnUiThread(new Runnable() { // @Override // public void run() { // mProgressBar.setVisibility(View.GONE); // } // }); } @Override public void clearTextInfo() { //TODO } @Override public void sendTextInfo(String info, int progress, int max) { } @Override public void setItemToUpdate(MediaWrapper item) { mHandler.sendMessage(mHandler.obtainMessage(VideoListHandler.UPDATE_ITEM, item)); } public void updateItem(MediaWrapper item) { if (mVideoAdapter != null && mVideoIndex != null && item != null) { if (mVideoIndex.containsKey(item.getLocation())) { mVideoAdapter.notifyArrayItemRangeChanged(mVideoIndex.get(item.getLocation()).intValue(), 1); } } } private Handler mHandler = new VideoListHandler(this); @Override public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { mSelectedItem = item; } @Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { if (row.getId() == HEADER_CATEGORIES) { if (((CardPresenter.SimpleCard)item).getId() == MusicFragment.CATEGORY_NOW_PLAYING){ //NOW PLAYING CARD startActivity(new Intent(this, AudioPlayerActivity.class)); return; } CardPresenter.SimpleCard card = (CardPresenter.SimpleCard) item; Intent intent = new Intent(mContext, VerticalGridActivity.class); intent.putExtra(BROWSER_TYPE, HEADER_CATEGORIES); intent.putExtra(MusicFragment.AUDIO_CATEGORY, card.getId()); startActivity(intent); } else if (row.getId() == HEADER_VIDEO) TvUtil.openMedia(mContext, item, row); else if (row.getId() == HEADER_MISC) startActivityForResult(new Intent(this, PreferencesActivity.class), ACTIVITY_RESULT_PREFERENCES); else if (row.getId() == HEADER_NETWORK) { TvUtil.openMedia(mContext, item, row); } } @Override public void onClick(View v) { Intent intent = new Intent(mContext, SearchActivity.class); startActivity(intent); } public class AsyncUpdate extends AsyncTask<Void, Void, Void> { private boolean askRefresh = false; ArrayList<MediaWrapper> videoList; public AsyncUpdate() { } public void AskRefresh() { //Ask for refresh while update is ongoing askRefresh = true; } @Override protected void onPreExecute() { mRowsAdapter.clear(); mProgressBar.setVisibility(View.VISIBLE); //Video Section mVideoIndex.clear(); mVideoAdapter = new ArrayObjectAdapter( new CardPresenter(mContext)); final HeaderItem videoHeader = new HeaderItem(HEADER_VIDEO, getString(R.string.video)); // Empty item to launch grid activity mVideoAdapter.add(new CardPresenter.SimpleCard(0, "All videos", R.drawable.ic_video_collection_big)); mRowsAdapter.add(new ListRow(videoHeader, mVideoAdapter)); //Music sections mCategoriesAdapter = new ArrayObjectAdapter(new CardPresenter(mContext)); final HeaderItem musicHeader = new HeaderItem(HEADER_CATEGORIES, getString(R.string.audio)); if (mService != null && mService.hasMedia() && !mService.canSwitchToVideo()) mCategoriesAdapter.add(new CardPresenter.SimpleCard(MusicFragment.CATEGORY_NOW_PLAYING, getString(R.string.music_now_playing), R.drawable.ic_play_circle_big_o)); mCategoriesAdapter.add(new CardPresenter.SimpleCard(MusicFragment.CATEGORY_ARTISTS, getString(R.string.artists), R.drawable.ic_artist_big)); mCategoriesAdapter.add(new CardPresenter.SimpleCard(MusicFragment.CATEGORY_ALBUMS, getString(R.string.albums), R.drawable.ic_album_big)); mCategoriesAdapter.add(new CardPresenter.SimpleCard(MusicFragment.CATEGORY_GENRES, getString(R.string.genres), R.drawable.ic_genre_big)); mCategoriesAdapter.add(new CardPresenter.SimpleCard(MusicFragment.CATEGORY_SONGS, getString(R.string.songs), R.drawable.ic_song_big)); mRowsAdapter.add(new ListRow(musicHeader, mCategoriesAdapter)); if (AndroidDevices.hasLANConnection()) { mNetworkAdapter = new ArrayObjectAdapter(new CardPresenter(mContext)); final ArrayList<MediaWrapper> favs = MediaDatabase.getInstance().getAllNetworkFav(); final HeaderItem networkHeader = new HeaderItem(HEADER_NETWORK, getString(R.string.network_browsing)); mNetworkAdapter.add(new CardPresenter.SimpleCard(0, getString(R.string.network_browsing), R.drawable.ic_menu_network_big)); if (!favs.isEmpty()) { for (MediaWrapper fav : favs) { mNetworkAdapter.add(fav); } } mRowsAdapter.add(new ListRow(networkHeader, mNetworkAdapter)); } mOtherAdapter = new ArrayObjectAdapter(new CardPresenter(mContext)); final HeaderItem miscHeader = new HeaderItem(HEADER_MISC, getString(R.string.other)); mOtherAdapter.add(new CardPresenter.SimpleCard(0, getString(R.string.preferences), R.drawable.ic_menu_preferences_big)); mRowsAdapter.add(new ListRow(miscHeader, mOtherAdapter)); mBrowseFragment.setAdapter(mRowsAdapter); } @Override protected Void doInBackground(Void... params) { videoList = mMediaLibrary.getVideoItems(); return null; } @Override protected void onPostExecute(Void result) { int size; // Update video section if (!videoList.isEmpty()) { size = videoList.size(); if (NUM_ITEMS_PREVIEW < size) size = NUM_ITEMS_PREVIEW; final int total = size; mRootContainer.post(new Runnable() { @Override public void run() { MediaWrapper item; for (int i = 0; i < total; ++i) { item = videoList.get(i); mVideoAdapter.add(item); mVideoIndex.put(item.getLocation(), Integer.valueOf(i)); } } }); } if (!mMediaLibrary.isWorking()) mProgressBar.setVisibility(View.GONE); if (askRefresh) { //in case new event occured while loading view mHandler.sendEmptyMessage(VideoListHandler.MEDIA_ITEMS_UPDATED); } } } private void checkThumbs() { VLCApplication.runBackground(new Runnable() { @Override public void run() { sThumbnailer = new Thumbnailer(mContext, getWindowManager().getDefaultDisplay()); Bitmap picture; ArrayList<MediaWrapper> videoList = mMediaLibrary.getVideoItems(); MediaDatabase mediaDatabase = MediaDatabase.getInstance(); if (sThumbnailer != null && videoList != null && !videoList.isEmpty()) { for (MediaWrapper MediaWrapper : videoList) { picture = mediaDatabase.getPicture(MediaWrapper.getUri()); if (picture == null) sThumbnailer.addJob(MediaWrapper); } if (sThumbnailer.getJobsCount() > 0) sThumbnailer.start((IVideoBrowser) mContext); } } }); } public static Thumbnailer getThumbnailer() { return sThumbnailer; } protected void refresh() { mMediaLibrary.scanMediaItems(true); } }