/*
* 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.sregg.android.tv.spotifyPlayer.fragments;
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.Bundle;
import android.os.Handler;
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.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.text.TextUtils;
import android.util.Log;
import com.sregg.android.tv.spotifyPlayer.Constants;
import com.sregg.android.tv.spotifyPlayer.R;
import com.sregg.android.tv.spotifyPlayer.SpotifyTvApplication;
import com.sregg.android.tv.spotifyPlayer.controllers.SpotifyPlayerController;
import com.sregg.android.tv.spotifyPlayer.presenters.AlbumCardPresenter;
import com.sregg.android.tv.spotifyPlayer.presenters.ArtistCardPresenter;
import com.sregg.android.tv.spotifyPlayer.presenters.PlaylistCardPresenter;
import com.sregg.android.tv.spotifyPlayer.presenters.TrackCardPresenter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import kaaes.spotify.webapi.android.SpotifyService;
import kaaes.spotify.webapi.android.models.AlbumSimple;
import kaaes.spotify.webapi.android.models.AlbumsPager;
import kaaes.spotify.webapi.android.models.Artist;
import kaaes.spotify.webapi.android.models.ArtistsPager;
import kaaes.spotify.webapi.android.models.PlaylistSimple;
import kaaes.spotify.webapi.android.models.PlaylistsPager;
import kaaes.spotify.webapi.android.models.Track;
import kaaes.spotify.webapi.android.models.TrackSimple;
import kaaes.spotify.webapi.android.models.TracksPager;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
/*
* This class demonstrates how to do in-app search
*/
public class SearchFragment extends android.support.v17.leanback.app.SearchFragment
implements android.support.v17.leanback.app.SearchFragment.SearchResultProvider {
private static final String TAG = "SearchFragment";
private static final int SEARCH_DELAY_MS = 300;
public static final int REQUEST_SPEECH = 0;
private static final boolean FINISH_ON_RECOGNIZER_CANCELED = true;
private ArrayObjectAdapter mRowsAdapter;
private Handler mHandler = new Handler();
private SearchRunnable mDelayedLoad;
private ArrayObjectAdapter mTrackRowAdapter;
@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 Track) {
String trackUri = ((Track) item).uri;
SpotifyPlayerController spotifyPlayerController = SpotifyTvApplication.getInstance().getSpotifyPlayerController();
if (spotifyPlayerController.getPlayingState().isCurrentTrack(trackUri)) {
spotifyPlayerController.togglePauseResume();
} else {
// get song and following ones
List<TrackSimple> tracks = new ArrayList<>();
List<String> trackUris = new ArrayList<>();
for (int i = mTrackRowAdapter.indexOf(item); i < mTrackRowAdapter.size() && i < Constants.MAX_SONGS_PLAYED; i++) {
tracks.add((TrackSimple) mTrackRowAdapter.get(i));
trackUris.add(((Track) mTrackRowAdapter.get(i)).uri);
}
spotifyPlayerController.play(trackUri, trackUris, tracks);
}
} else {
SpotifyTvApplication.getInstance().launchDetailScreen(getActivity(), item);
}
}
});
mDelayedLoad = new SearchRunnable();
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() {
Log.v(TAG, "recognizeSpeech");
try {
startActivityForResult(getRecognizerIntent(), REQUEST_SPEECH);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Cannot find activity for speech recognizer", e);
}
}
});
}
}
private boolean hasPermission(final String permission) {
final Context context = getActivity();
return PackageManager.PERMISSION_GRANTED == context.getPackageManager().checkPermission(
permission, context.getPackageName());
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
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()) {
Log.v(TAG, "Delegating BACK press from recognizer");
getActivity().onBackPressed();
}
}
break;
// the rest includes various recognizer errors, see {@link RecognizerIntent}
}
break;
}
}
public boolean hasResults() {
return mRowsAdapter.size() > 0;
}
@Override
public ObjectAdapter getResultsAdapter() {
return mRowsAdapter;
}
private void queryByWords(String words) {
mRowsAdapter.clear();
if (!TextUtils.isEmpty(words)) {
mDelayedLoad.setSearchQuery(words);
mHandler.removeCallbacks(mDelayedLoad);
mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
}
}
@Override
public boolean onQueryTextChange(String newQuery) {
Log.i(TAG, String.format("Search Query Text Change %s", newQuery));
queryByWords(newQuery);
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
Log.i(TAG, String.format("Search Query Text Submit %s", query));
queryByWords(query);
return true;
}
private void loadRows(String query) {
// search artists
searchArtists(query);
// search albums
searchAlbums(query);
// search songs
searchSongs(query);
// search playlists
searchPlaylists(query);
}
private void searchArtists(String query) {
SpotifyTvApplication.getInstance().getSpotifyService().searchArtists(query, getSearchOptions(), new Callback<ArtistsPager>() {
@Override
public void success(ArtistsPager artistsPager, Response response) {
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new ArtistCardPresenter());
for (Artist artist : artistsPager.artists.items) {
listRowAdapter.add(artist);
}
HeaderItem header = new HeaderItem(getString(R.string.artists));
mRowsAdapter.add(new ListRow(header, listRowAdapter));
// TODO next pages ?
}
@Override
public void failure(RetrofitError error) {
}
});
}
private void searchAlbums(String query) {
SpotifyTvApplication.getInstance().getSpotifyService().searchAlbums(query, getSearchOptions(), new Callback<AlbumsPager>() {
@Override
public void success(AlbumsPager albumsPager, Response response) {
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new AlbumCardPresenter());
for (AlbumSimple artist : albumsPager.albums.items) {
listRowAdapter.add(artist);
}
HeaderItem header = new HeaderItem(getString(R.string.albums));
mRowsAdapter.add(new ListRow(header, listRowAdapter));
// TODO next pages ?
}
@Override
public void failure(RetrofitError error) {
}
});
}
private Map<String, Object> getSearchOptions() {
Map<String, Object> options = new HashMap<>();
options.put(SpotifyService.MARKET, Constants.MARKET_FROM_TOKEN);
return options;
}
private void searchSongs(String query) {
SpotifyTvApplication.getInstance().getSpotifyService().searchTracks(query, getSearchOptions(), new Callback<TracksPager>() {
@Override
public void success(TracksPager tracksPager, Response response) {
mTrackRowAdapter = new ArrayObjectAdapter(new TrackCardPresenter());
for (Track track : tracksPager.tracks.items) {
mTrackRowAdapter.add(track);
}
HeaderItem header = new HeaderItem(getString(R.string.songs));
mRowsAdapter.add(new ListRow(header, mTrackRowAdapter));
// TODO next pages ?
}
@Override
public void failure(RetrofitError error) {
}
});
}
private void searchPlaylists(String query) {
SpotifyTvApplication.getInstance().getSpotifyService().searchPlaylists(query, getSearchOptions(), new Callback<PlaylistsPager>() {
@Override
public void success(PlaylistsPager playlistsPager, Response response) {
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new PlaylistCardPresenter());
for (PlaylistSimple playlistSimple : playlistsPager.playlists.items) {
listRowAdapter.add(playlistSimple);
}
HeaderItem header = new HeaderItem(getString(R.string.playlists));
mRowsAdapter.add(new ListRow(header, listRowAdapter));
// TODO next pages ?
}
@Override
public void failure(RetrofitError error) {
}
});
}
private class SearchRunnable implements Runnable {
private volatile String searchQuery;
public SearchRunnable() {
}
public void run() {
loadRows(searchQuery);
}
public void setSearchQuery(String value) {
this.searchQuery = value;
}
}
}