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

Java tutorial

Introduction

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

Source

/*
 * Copyright (C) 2015 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.Manifest;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v17.leanback.app.SearchFragment;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
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.ObjectAdapter;
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.SpeechRecognitionCallback;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v7.graphics.Palette;
import android.text.TextUtils;
import android.util.Log;

import com.fastbootmobile.encore.app.BuildConfig;
import com.fastbootmobile.encore.app.R;
import com.fastbootmobile.encore.framework.PlaybackProxy;
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 java.util.List;

public class TvSearchFragment extends SearchFragment implements SearchFragment.SearchResultProvider {
    private static final String TAG = "TvSearchFragment";
    private static final boolean DEBUG = BuildConfig.DEBUG;

    private static final int SEARCH_DELAY_MS = 1000;
    private static final boolean FINISH_ON_RECOGNIZER_CANCELED = true;
    private static final int REQUEST_SPEECH = 0x00000010;

    private ArrayObjectAdapter mRowsAdapter;
    private String mQuery;
    private Handler mHandler = new Handler();
    private final Runnable mDelayedLoad = new Runnable() {
        @Override
        public void run() {
            loadRows();
        }
    };
    private final Runnable mUpdateAdapter = new Runnable() {
        @Override
        public void run() {
            final int numSubs = mRowsAdapter.size();
            for (int i = 0; i < numSubs; ++i) {
                ListRow row = (ListRow) mRowsAdapter.get(i);
                ArrayObjectAdapter adapter = (ArrayObjectAdapter) row.getAdapter();
                adapter.notifyArrayItemRangeChanged(0, adapter.size());
            }
        }
    };
    private ILocalCallback mCallback = new SearchLocalCallback();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
        setSearchResultProvider(this);
        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(getActivity(), TvAlbumDetailsActivity.class);
                    intent.putExtra(TvAlbumDetailsActivity.EXTRA_ALBUM, album);
                    intent.putExtra(TvAlbumDetailsActivity.EXTRA_COLOR, color);

                    Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
                            ((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(getActivity(), TvArtistDetailsActivity.class);
                    intent.putExtra(TvArtistDetailsActivity.EXTRA_ARTIST, artist);
                    intent.putExtra(TvArtistDetailsActivity.EXTRA_COLOR, color);

                    Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
                            ((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(getActivity(), TvPlaylistDetailsActivity.class);
                    intent.putExtra(TvPlaylistDetailsActivity.EXTRA_PLAYLIST, playlist);
                    intent.putExtra(TvPlaylistDetailsActivity.EXTRA_COLOR, color);

                    Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
                            ((ImageCardView) itemViewHolder.view).getMainImageView(),
                            TvAlbumDetailsActivity.SHARED_ELEMENT_NAME).toBundle();
                    startActivity(intent, bundle);
                } else if (item instanceof Song) {
                    PlaybackProxy.playSong((Song) item);
                }
            }
        });

        if (!hasPermission(Manifest.permission.RECORD_AUDIO)) {
            // SpeechRecognitionCallback is not required and if not provided recognition will be handled
            // using internal speech recognizer, in which case you must have RECORD_AUDIO permission
            setSpeechRecognitionCallback(new SpeechRecognitionCallback() {
                @Override
                public void recognizeSpeech() {
                    if (DEBUG)
                        Log.v(TAG, "recognizeSpeech");
                    try {
                        startActivityForResult(getRecognizerIntent(), REQUEST_SPEECH);
                    } catch (ActivityNotFoundException e) {
                        Log.e(TAG, "Cannot find activity for speech recognizer", e);
                    }
                }
            });
        }

    }

    @Override
    public void onPause() {
        mHandler.removeCallbacksAndMessages(null);
        ProviderAggregator.getDefault().removeUpdateCallback(mCallback);
        super.onPause();
    }

    @Override
    public void onResume() {
        super.onResume();
        ProviderAggregator.getDefault().addUpdateCallback(mCallback);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (DEBUG)
            Log.v(TAG,
                    "onActivityResult requestCode=" + requestCode + " resultCode=" + resultCode + " data=" + data);
        switch (requestCode) {
        case REQUEST_SPEECH:
            switch (resultCode) {
            case Activity.RESULT_OK:
                setSearchQuery(data, true);
                break;
            case Activity.RESULT_CANCELED:
                // Once recognizer canceled, user expects the current activity to process
                // the same BACK press as user doesn't know about overlay activity.
                // However, you may not want this behaviour as it makes harder to
                // fall back to keyboard input.
                if (FINISH_ON_RECOGNIZER_CANCELED) {
                    if (!hasResults()) {
                        if (DEBUG)
                            Log.v(TAG, "Delegating BACK press from recognizer");
                        getActivity().onBackPressed();
                    }
                }
                break;
            // the rest includes various recognizer errors, see {@link RecognizerIntent}
            }
            break;
        }
    }

    @Override
    public ObjectAdapter getResultsAdapter() {
        return mRowsAdapter;
    }

    @Override
    public boolean onQueryTextChange(String newQuery) {
        Log.i(TAG, String.format("Search Query Text Change %s", newQuery));
        loadQuery(newQuery);
        return true;
    }

    @Override
    public boolean onQueryTextSubmit(String query) {
        Log.i(TAG, String.format("Search Query Text Submit %s", query));
        loadQuery(query);
        return true;
    }

    private void loadRows() {
        // offload processing from the UI thread
        new AsyncTask<String, Void, ListRow>() {
            private final String query = mQuery;

            @Override
            protected void onPreExecute() {
                mRowsAdapter.clear();
            }

            @Override
            protected ListRow doInBackground(String... params) {
                ProviderAggregator.getDefault().startSearch(query);
                return null;
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    private boolean hasPermission(final String permission) {
        final Context context = getActivity();
        return PackageManager.PERMISSION_GRANTED == context.getPackageManager().checkPermission(permission,
                context.getPackageName());
    }

    public boolean hasResults() {
        return mRowsAdapter.size() > 0;
    }

    private void loadQuery(String query) {
        mHandler.removeCallbacks(mDelayedLoad);
        if (!TextUtils.isEmpty(query) && !query.equals("nil")) {
            mQuery = query;
            mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
        }
    }

    private class SearchLocalCallback implements ILocalCallback {

        @Override
        public void onSongUpdate(List<Song> s) {
            if (mRowsAdapter != null) {
                mHandler.post(mUpdateAdapter);
            }
        }

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

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

        @Override
        public void onPlaylistRemoved(String ref) {
        }

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

        @Override
        public void onProviderConnected(IMusicProvider provider) {

        }

        @Override
        public void onSearchResult(List<SearchResult> searchResult) {
            final ArrayObjectAdapter artistRowAdapter = new ArrayObjectAdapter(new CardPresenter());
            final ArrayObjectAdapter albumRowAdapter = new ArrayObjectAdapter(new CardPresenter());
            final ArrayObjectAdapter songsRowAdapter = new ArrayObjectAdapter(new CardPresenter());
            final ArrayObjectAdapter playlistsRowAdapter = new ArrayObjectAdapter(new CardPresenter());

            final ProviderAggregator aggregator = ProviderAggregator.getDefault();

            for (SearchResult result : searchResult) {
                for (String ref : result.getArtistList()) {
                    Artist artist = aggregator.retrieveArtist(ref, result.getIdentifier());
                    if (artist != null) {
                        artistRowAdapter.add(artist);
                    }
                }

                for (String ref : result.getAlbumsList()) {
                    Album album = aggregator.retrieveAlbum(ref, result.getIdentifier());
                    if (album != null) {
                        albumRowAdapter.add(album);
                    }
                }

                for (String ref : result.getSongsList()) {
                    Song song = aggregator.retrieveSong(ref, result.getIdentifier());
                    if (song != null) {
                        songsRowAdapter.add(song);
                    }
                }

                for (String ref : result.getPlaylistList()) {
                    Playlist playlist = aggregator.retrievePlaylist(ref, result.getIdentifier());
                    if (playlist != null) {
                        playlistsRowAdapter.add(playlist);
                    }
                }
            }

            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mRowsAdapter.clear();

                    HeaderItem header = new HeaderItem(getString(R.string.artist));
                    mRowsAdapter.add(new ListRow(header, artistRowAdapter));

                    header = new HeaderItem(getString(R.string.albums));
                    mRowsAdapter.add(new ListRow(header, albumRowAdapter));

                    header = new HeaderItem(getString(R.string.songs));
                    mRowsAdapter.add(new ListRow(header, songsRowAdapter));

                    header = new HeaderItem(getString(R.string.tab_playlists));
                    mRowsAdapter.add(new ListRow(header, playlistsRowAdapter));

                    mRowsAdapter.notifyArrayItemRangeChanged(0, mRowsAdapter.size());
                }
            });
        }
    }
}