com.fastbootmobile.encore.app.tv.TvActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.fastbootmobile.encore.app.tv.TvActivity.java

Source

/*
 * Copyright (C) 2014 Fastboot Mobile, LLC.
 *
 * 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 3 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, see <http://www.gnu.org/licenses>.
 */

package com.fastbootmobile.encore.app.tv;

import android.app.Activity;
import android.app.FragmentManager;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.support.v17.leanback.app.BrowseFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.ClassPresenterSelector;
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.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.v4.app.ActivityOptionsCompat;
import android.support.v7.graphics.Palette;
import android.view.View;

import com.fastbootmobile.encore.api.common.Pair;
import com.fastbootmobile.encore.app.R;
import com.fastbootmobile.encore.app.adapters.HistoryAdapter;
import com.fastbootmobile.encore.framework.ListenLogger;
import com.fastbootmobile.encore.framework.PluginsLookup;
import com.fastbootmobile.encore.model.Album;
import com.fastbootmobile.encore.model.Artist;
import com.fastbootmobile.encore.model.Playlist;
import com.fastbootmobile.encore.model.SearchResult;
import com.fastbootmobile.encore.model.Song;
import com.fastbootmobile.encore.providers.ILocalCallback;
import com.fastbootmobile.encore.providers.IMusicProvider;
import com.fastbootmobile.encore.providers.ProviderAggregator;
import com.fastbootmobile.encore.providers.ProviderConnection;
import com.fastbootmobile.encore.providers.ProviderIdentifier;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

public class TvActivity extends Activity {
    public static final String TAG = "TvActivity";

    private static final int MSG_NOTIFY_CHANGES = 1;

    private static final int TYPE_ALBUM = 0;
    private static final int TYPE_ARTIST = 1;

    private static final int ROW_RECENTS = 0;
    private static final int ROW_RECOMMENDATIONS = 1;
    private static final int ROW_LIBRARY = 2;
    private static final int ROW_PLAYLISTS = 3;
    private static final int ROW_SETTINGS = 4;

    private ArrayObjectAdapter mRowsAdapter;
    private Handler mHandler;
    private int mNumSuggestions;

    protected BrowseFragment mBrowseFragment;
    private ILocalCallback mLocalCallback = new TvLocalCallback();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tv_browsefragment);

        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case MSG_NOTIFY_CHANGES:
                    if (mRowsAdapter != null) {
                        mRowsAdapter.notifyArrayItemRangeChanged(0, 2);
                    }
                    break;
                }
            }
        };

        final FragmentManager fragmentManager = getFragmentManager();
        mBrowseFragment = (BrowseFragment) fragmentManager.findFragmentById(R.id.browse_fragment);

        // Set display parameters for the BrowseFragment
        mBrowseFragment.setHeadersState(BrowseFragment.HEADERS_ENABLED);
        mBrowseFragment.setTitle(getString(R.string.app_name));
        mBrowseFragment.setBadgeDrawable(getResources().getDrawable(R.mipmap.ic_launcher));

        // Set search interface
        mBrowseFragment.setOnSearchClickedListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(TvActivity.this, TvSearchActivity.class);
                startActivity(intent);
            }
        });
        mBrowseFragment.setSearchAffordanceColor(getResources().getColor(R.color.primary_dark));

        // Setup event listeners
        mBrowseFragment.setOnItemViewClickedListener(new OnItemViewClickedListener() {
            @Override
            public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
                    RowPresenter.ViewHolder rowViewHolder, Row row) {
                if (item instanceof Album) {
                    Album album = (Album) item;
                    int color = getResources().getColor(R.color.primary);
                    if (itemViewHolder.view.getTag() != null && itemViewHolder.view.getTag() instanceof Palette) {
                        color = ((Palette) itemViewHolder.view.getTag()).getDarkVibrantColor(color);
                    }

                    Intent intent = new Intent(TvActivity.this, TvAlbumDetailsActivity.class);
                    intent.putExtra(TvAlbumDetailsActivity.EXTRA_ALBUM, album);
                    intent.putExtra(TvAlbumDetailsActivity.EXTRA_COLOR, color);

                    Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(TvActivity.this,
                            ((ImageCardView) itemViewHolder.view).getMainImageView(),
                            TvAlbumDetailsActivity.SHARED_ELEMENT_NAME).toBundle();
                    startActivity(intent, bundle);
                } else if (item instanceof Artist) {
                    Artist artist = (Artist) item;
                    int color = getResources().getColor(R.color.primary);
                    if (itemViewHolder.view.getTag() != null && itemViewHolder.view.getTag() instanceof Palette) {
                        color = ((Palette) itemViewHolder.view.getTag()).getDarkVibrantColor(color);
                    }

                    Intent intent = new Intent(TvActivity.this, TvArtistDetailsActivity.class);
                    intent.putExtra(TvArtistDetailsActivity.EXTRA_ARTIST, artist);
                    intent.putExtra(TvArtistDetailsActivity.EXTRA_COLOR, color);

                    Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(TvActivity.this,
                            ((ImageCardView) itemViewHolder.view).getMainImageView(),
                            TvArtistDetailsActivity.SHARED_ELEMENT_NAME).toBundle();
                    startActivity(intent, bundle);
                } else if (item instanceof Playlist) {
                    Playlist playlist = (Playlist) item;
                    int color = getResources().getColor(R.color.primary);
                    if (itemViewHolder.view.getTag() != null && itemViewHolder.view.getTag() instanceof Palette) {
                        color = ((Palette) itemViewHolder.view.getTag()).getDarkVibrantColor(color);
                    }

                    Intent intent = new Intent(TvActivity.this, TvPlaylistDetailsActivity.class);
                    intent.putExtra(TvPlaylistDetailsActivity.EXTRA_PLAYLIST, playlist);
                    intent.putExtra(TvPlaylistDetailsActivity.EXTRA_COLOR, color);

                    Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(TvActivity.this,
                            ((ImageCardView) itemViewHolder.view).getMainImageView(),
                            TvAlbumDetailsActivity.SHARED_ELEMENT_NAME).toBundle();
                    startActivity(intent, bundle);
                } else if (item instanceof SettingsItem) {
                    SettingsItem sitem = (SettingsItem) item;
                    switch (sitem.getType()) {
                    case SettingsItem.ITEM_PROVIDERS:
                        startActivity(new Intent(TvActivity.this, TvProvidersActivity.class));
                        break;

                    case SettingsItem.ITEM_EFFECTS:
                        Intent intent = new Intent(TvActivity.this, TvProvidersActivity.class);
                        intent.putExtra(TvProvidersActivity.EXTRA_DSP_MODE, true);
                        startActivity(intent);
                        break;
                    }
                } else if (item instanceof MyLibraryItem) {
                    MyLibraryItem libraryItem = (MyLibraryItem) item;
                    Intent intent = new Intent(TvActivity.this, TvEntityGridActivity.class);
                    switch (libraryItem.getType()) {
                    case MyLibraryItem.TYPE_ALBUMS:
                        intent.putExtra(TvEntityGridActivity.EXTRA_MODE, TvEntityGridActivity.MODE_ALBUM);
                        break;

                    case MyLibraryItem.TYPE_ARTISTS:
                        intent.putExtra(TvEntityGridActivity.EXTRA_MODE, TvEntityGridActivity.MODE_ARTIST);
                        break;
                    }

                    startActivity(intent);
                }
            }
        });

        // Build adapter items
        mHandler.postDelayed(new Runnable() {
            public void run() {
                buildRowsAdapter();
            }
        }, 1500);
    }

    @Override
    protected void onResume() {
        super.onResume();
        ProviderAggregator.getDefault().addUpdateCallback(mLocalCallback);

        if (mNumSuggestions > 2) {
            requestAdapterUpdate();
        } else {
            mHandler.postDelayed(new Runnable() {
                public void run() {
                    buildRowsAdapter();
                }
            }, 1500);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        ProviderAggregator.getDefault().removeUpdateCallback(mLocalCallback);
    }

    private void requestAdapterUpdate() {
        mHandler.removeMessages(MSG_NOTIFY_CHANGES);
        mHandler.sendEmptyMessage(MSG_NOTIFY_CHANGES);
    }

    private void buildRowsAdapter() {
        ClassPresenterSelector selector = new ClassPresenterSelector();
        selector.addClassPresenter(ListRow.class, new ListRowPresenter());
        selector.addClassPresenter(ShadowlessListRow.class, ShadowlessListRow.createPresenter(this));

        mRowsAdapter = new ArrayObjectAdapter(selector);

        // Generate rows contents
        generateRecentlyPlayedRow();
        generateRecommendations();
        generateMyLibraryRow();
        generatePlaylistsRow();
        generateSettingsRow();

        mBrowseFragment.setAdapter(mRowsAdapter);
    }

    private void generateRecentlyPlayedRow() {
        final Random rand = new Random();
        final ProviderAggregator aggregator = ProviderAggregator.getDefault();

        // First row: Recently played (10 items, randomly artist or album)
        ListenLogger logger = new ListenLogger(this);
        List<ListenLogger.LogEntry> logEntries = HistoryAdapter.sortByTime(logger.getEntries(25));
        ArrayObjectAdapter logEntriesRowAdapter = new ArrayObjectAdapter(new CardPresenter());
        int entriesCount = 0;

        for (ListenLogger.LogEntry logEntry : logEntries) {
            if (entriesCount == 10)
                break;

            // Get the song information
            Song song = aggregator.retrieveSong(logEntry.getReference(), logEntry.getIdentifier());

            if (song == null) {
                // That song might not exist anymore, keep moving
                continue;
            }

            final int type = rand.nextInt(2);
            if (type == TYPE_ALBUM && song.getAlbum() == null || type == TYPE_ARTIST && song.getArtist() == null) {
                // We don't have the requested track info, so skip it
                continue;
            }

            if (type == TYPE_ALBUM) {
                Album album = aggregator.retrieveAlbum(song.getAlbum(), song.getProvider());
                logEntriesRowAdapter.add(album);
            } else if (type == TYPE_ARTIST) {
                Artist artist = aggregator.retrieveArtist(song.getArtist(), song.getProvider());
                logEntriesRowAdapter.add(artist);
            }
            ++entriesCount;
        }

        // Build Recently Played Leanback item
        HeaderItem header = new HeaderItem(ROW_RECENTS, "Recently played");
        ListRow row = new ListRow(header, logEntriesRowAdapter);
        if (mRowsAdapter.size() > ROW_RECENTS) {
            mRowsAdapter.replace(ROW_RECENTS, row);
        } else {
            mRowsAdapter.add(row);
        }
    }

    private void generateRecommendations() {
        final Random rand = new Random();
        final ProviderAggregator aggregator = ProviderAggregator.getDefault();

        // Get all the available tracks to build Recommendations
        final List<Pair<String, ProviderIdentifier>> availableReferences = new ArrayList<>();
        final List<ProviderConnection> providers = PluginsLookup.getDefault().getAvailableProviders();

        final List<Playlist> playlists = aggregator.getAllPlaylists();
        for (Playlist p : playlists) {
            Iterator<String> it = p.songs();
            while (it.hasNext()) {
                String ref = it.next();
                Pair<String, ProviderIdentifier> pair = Pair.create(ref, p.getProvider());
                if (!availableReferences.contains(pair)) {
                    availableReferences.add(pair);
                }
            }
        }

        for (ProviderConnection provider : providers) {
            IMusicProvider binder = provider.getBinder();
            if (binder != null) {
                int limit = 50;
                int offset = 0;
                boolean goAhead = true;
                try {
                    while (goAhead) {
                        List<Song> songs = binder.getSongs(offset, limit);
                        if (songs == null) {
                            goAhead = false;
                            continue;
                        }

                        if (songs.size() < limit) {
                            goAhead = false;
                        }

                        offset += songs.size();

                        for (Song song : songs) {
                            Pair<String, ProviderIdentifier> pair = Pair.create(song.getRef(), song.getProvider());
                            if (!availableReferences.contains(pair)) {
                                availableReferences.add(pair);
                            }
                        }
                    }
                } catch (RemoteException ignore) {
                }
            }
        }

        // Randomly generate recommendations
        ArrayObjectAdapter recommendedRowAdapter = new ArrayObjectAdapter(new CardPresenter());
        int entriesCount = 0;
        List<String> knownRefs = new ArrayList<>();

        for (Pair<String, ProviderIdentifier> ref : availableReferences) {
            if (entriesCount == 20)
                break;

            // Get the song information
            Song song = aggregator.retrieveSong(ref.first, ref.second);

            if (song == null) {
                // That song might not exist anymore, keep moving
                continue;
            }

            final int type = rand.nextInt(2);
            if (type == TYPE_ALBUM && (song.getAlbum() == null || knownRefs.contains(song.getAlbum()))
                    || type == TYPE_ARTIST && (song.getArtist() == null || knownRefs.contains(song.getArtist()))) {
                // We don't have the requested track info, so skip it
                continue;
            }

            if (type == TYPE_ALBUM) {
                knownRefs.add(song.getAlbum());
                Album album = aggregator.retrieveAlbum(song.getAlbum(), song.getProvider());
                recommendedRowAdapter.add(album);
            } else if (type == TYPE_ARTIST) {
                knownRefs.add(song.getArtist());
                Artist artist = aggregator.retrieveArtist(song.getArtist(), song.getProvider());
                recommendedRowAdapter.add(artist);
            }
            ++entriesCount;
        }

        mNumSuggestions = recommendedRowAdapter.size();

        // Build Recommended Leanback item
        HeaderItem header = new HeaderItem(ROW_RECOMMENDATIONS, "Recommended for you");
        ListRow row = new ListRow(header, recommendedRowAdapter);
        if (mRowsAdapter.size() > ROW_RECOMMENDATIONS) {
            mRowsAdapter.replace(ROW_RECOMMENDATIONS, row);
        } else {
            mRowsAdapter.add(row);
        }
    }

    private void generateMyLibraryRow() {
        // Build My Library item
        ArrayObjectAdapter libraryAdapter = new ArrayObjectAdapter(new CardPresenter());
        libraryAdapter.add(new MyLibraryItem(MyLibraryItem.TYPE_ARTISTS));
        libraryAdapter.add(new MyLibraryItem(MyLibraryItem.TYPE_ALBUMS));

        HeaderItem header = new HeaderItem(ROW_LIBRARY, getString(R.string.title_section_my_songs));
        ListRow row = new ListRow(header, libraryAdapter);
        if (mRowsAdapter.size() > ROW_LIBRARY) {
            mRowsAdapter.replace(ROW_LIBRARY, row);
        } else {
            mRowsAdapter.add(row);
        }
    }

    private void generatePlaylistsRow() {
        // Build Playlists items
        final ProviderAggregator aggregator = ProviderAggregator.getDefault();
        final List<Pair<String, ProviderIdentifier>> availableReferences = new ArrayList<>();
        final List<Playlist> playlists = aggregator.getAllPlaylists();
        for (Playlist p : playlists) {
            Iterator<String> it = p.songs();
            while (it.hasNext()) {
                String ref = it.next();
                Pair<String, ProviderIdentifier> pair = Pair.create(ref, p.getProvider());
                if (!availableReferences.contains(pair)) {
                    availableReferences.add(pair);
                }
            }
        }

        ArrayObjectAdapter playlistsAdapter = new ArrayObjectAdapter(new CardPresenter());
        playlistsAdapter.addAll(0, playlists);

        HeaderItem header = new HeaderItem(ROW_PLAYLISTS, getString(R.string.title_section_playlists));
        ListRow row = new ListRow(header, playlistsAdapter);
        if (mRowsAdapter.size() > ROW_PLAYLISTS) {
            mRowsAdapter.replace(ROW_PLAYLISTS, row);
        } else {
            mRowsAdapter.add(row);
        }
    }

    private void generateSettingsRow() {
        // Build Settings items
        ArrayObjectAdapter settingsAdapter = new ArrayObjectAdapter(new IconPresenter());
        settingsAdapter.add(new SettingsItem(SettingsItem.ITEM_PROVIDERS));
        settingsAdapter.add(new SettingsItem(SettingsItem.ITEM_EFFECTS));
        settingsAdapter.add(new SettingsItem(SettingsItem.ITEM_LICENSES));

        HeaderItem header = new HeaderItem(ROW_SETTINGS, getString(R.string.title_activity_settings));
        ShadowlessListRow row = new ShadowlessListRow(header, settingsAdapter);
        if (mRowsAdapter.size() > ROW_SETTINGS) {
            mRowsAdapter.replace(ROW_SETTINGS, row);
        } else {
            mRowsAdapter.add(row);
        }
    }

    private class TvLocalCallback implements ILocalCallback {
        @Override
        public void onSongUpdate(List<Song> s) {
            if (mRowsAdapter != null) {
                if (mNumSuggestions < 2) {
                    generateRecommendations();
                } else {
                    requestAdapterUpdate();
                }
            }
        }

        @Override
        public void onAlbumUpdate(List<Album> a) {
            if (mRowsAdapter != null) {
                requestAdapterUpdate();
            }
        }

        @Override
        public void onPlaylistUpdate(List<Playlist> p) {
            if (mRowsAdapter != null) {
                generatePlaylistsRow();
            }
        }

        @Override
        public void onPlaylistRemoved(String ref) {

        }

        @Override
        public void onArtistUpdate(List<Artist> a) {
            if (mRowsAdapter != null) {
                requestAdapterUpdate();
            }
        }

        @Override
        public void onProviderConnected(IMusicProvider provider) {
            if (mRowsAdapter != null) {
                requestAdapterUpdate();
            }
        }

        @Override
        public void onSearchResult(List<SearchResult> searchResult) {

        }
    }
}