package com.atomjack.vcfp.fragments; import android.app.PendingIntent; import android.content.Context; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.speech.RecognizerIntent; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.TextView; import com.atomjack.shared.Intent; import com.atomjack.shared.NewLogger; import com.atomjack.shared.PlayerState; import com.atomjack.shared.Preferences; import com.atomjack.vcfp.R; import com.atomjack.vcfp.VoiceControlForPlexApplication; import com.atomjack.vcfp.interfaces.BitmapHandler; import com.atomjack.vcfp.interfaces.MusicPlayerListener; import com.atomjack.vcfp.interfaces.MusicServiceListener; import com.atomjack.vcfp.model.PlexClient; import com.atomjack.vcfp.model.PlexMedia; import com.atomjack.vcfp.model.PlexTrack; import com.atomjack.vcfp.services.PlexSearchService; import java.math.BigInteger; import java.security.SecureRandom; import java.util.ArrayList; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; public class MusicPlayerFragment extends Fragment implements MusicServiceListener { private NewLogger logger; private PlexTrack track; // the current track private ArrayList<PlexTrack> playlist = new ArrayList<>(); private int currentTrackIndex = 0; private MusicPlayerListener listener; private Handler handler; // These will be set on first load, then used subsequently to resize the cover art image int posterWidth = -1, posterHeight = -1; private boolean doingMic = false; @BindView(R.id.nowPlayingOnClient) TextView nowPlayingOnClient; @BindView(R.id.nowPlayingArtist) TextView nowPlayingArtist; @BindView(R.id.nowPlayingTitle) TextView nowPlayingTitle; @BindView(R.id.nowPlayingAlbum) TextView nowPlayingAlbum; @BindView(R.id.nowPlayingPosterContainer) FrameLayout nowPlayingPosterContainer; @BindView(R.id.nowPlayingPoster) ImageView nowPlayingPoster; // Controller elements @BindView(R.id.currentTimeView) TextView currentTimeView; @BindView(R.id.seekBar) SeekBar seekBar; @BindView(R.id.durationView) TextView durationView; @BindView(R.id.playButton) ImageButton playButton; @BindView(R.id.pauseButton) ImageButton pauseButton; @BindView(R.id.playPauseSpinner) ProgressBar playPauseSpinner; @BindView(R.id.stopButton) ImageButton stopButton; @BindView(R.id.nextButton) ImageButton nextButton; @BindView(R.id.micButton) ImageButton micButton; @BindView(R.id.previousButton) ImageButton previousButton; public MusicPlayerFragment() { logger = new NewLogger(this); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { logger.d("onCreateView"); if(savedInstanceState != null) { track = savedInstanceState.getParcelable(Intent.EXTRA_MEDIA); playlist = savedInstanceState.getParcelableArrayList(Intent.EXTRA_PLAYLIST); } View view = inflater.inflate(R.layout.fragment_music_player, container, false); ButterKnife.bind(this, view); handler = new Handler(); showNowPlaying(); return view; } @Override public void onAttach(Context context) { super.onAttach(context); try { listener = (MusicPlayerListener) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString() + " must implement OnHeadlineSelectedListener"); } } public void init(PlexTrack t, ArrayList<PlexTrack> pl) { track = t; playlist = pl; } private SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if(progress % 10 == 0) logger.d("Setting time to %s", VoiceControlForPlexApplication.secondsToTimecode(progress)); currentTimeView.setText(VoiceControlForPlexApplication.secondsToTimecode(progress)); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { listener.seek(seekBar.getProgress()*1000); } }; @OnClick(R.id.previousButton) public void doPrevious(View v) { currentTrackIndex--; listener.doPrevious(); } @OnClick(R.id.nextButton) public void doNext(View v) { currentTrackIndex++; listener.doNext(); } @OnClick(R.id.playButton) public void doPlay(View v) { listener.doPlay(); } @OnClick(R.id.pauseButton) public void doPause(View v) { listener.doPause(); } @OnClick(R.id.stopButton) public void doStop(View v) { logger.d("doStop"); listener.doStop(); } @OnClick(R.id.micButton) public void doMic(View v) { if(listener.isPlaying()) doingMic = true; listener.doPause(); android.content.Intent serviceIntent = new android.content.Intent(getActivity(), PlexSearchService.class); serviceIntent.putExtra(Intent.EXTRA_SERVER, VoiceControlForPlexApplication.gsonWrite.toJson(track.server)); serviceIntent.putExtra(Intent.EXTRA_CLIENT, VoiceControlForPlexApplication.gsonWrite.toJson(PlexClient.getLocalPlaybackClient())); serviceIntent.putExtra(Intent.EXTRA_FROM_MIC, true); serviceIntent.putExtra(Intent.EXTRA_FROM_LOCAL_PLAYER, true); serviceIntent.putExtra(Intent.PLAYER, Intent.PLAYER_AUDIO); SecureRandom random = new SecureRandom(); serviceIntent.setData(Uri.parse(new BigInteger(130, random).toString(32))); PendingIntent resultsPendingIntent = PendingIntent.getService(getActivity(), 0, serviceIntent, PendingIntent.FLAG_ONE_SHOT); android.content.Intent listenerIntent = new android.content.Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); listenerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); listenerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, "voice.recognition.test"); listenerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 5); listenerIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, resultsPendingIntent); listenerIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, getResources().getString(R.string.voice_prompt)); startActivity(listenerIntent); } @Override public void onResume() { super.onResume(); logger.d("onResume"); if(doingMic) listener.doPlay(); doingMic = false; } private void showNowPlaying() { currentTimeView.setText(track.duration / 1000 < 60*60 ? "00:00" : "00:00:00"); durationView.setText(VoiceControlForPlexApplication.secondsToTimecode(track.duration / 1000)); seekBar.setMax(track.duration / 1000); seekBar.setProgress(0); seekBar.setOnSeekBarChangeListener(seekBarChangeListener); setPlayPauseButtonState(PlayerState.BUFFERING); nowPlayingOnClient.setVisibility(View.GONE); logger.d("Setting artist/album to %s/%s", track.getArtist(), track.getAlbum()); nowPlayingArtist.setText(track.getArtist()); nowPlayingAlbum.setText(track.getAlbum()); nowPlayingTitle.setText(track.title); if(playlist == null || playlist.size() == 1) { previousButton.setVisibility(View.GONE); nextButton.setVisibility(View.GONE); } else { previousButton.setAlpha(currentTrackIndex == 0 ? 0.4f : 1.0f); nextButton.setAlpha(currentTrackIndex+1 == playlist.size() ? 0.4f : 1.0f); } if(nowPlayingPosterContainer != null) { if(posterHeight == -1 || posterWidth == -1) { ViewTreeObserver vto = nowPlayingPosterContainer.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) nowPlayingPosterContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this); posterWidth = nowPlayingPosterContainer.getMeasuredWidth(); posterHeight = nowPlayingPosterContainer.getMeasuredHeight(); if(VoiceControlForPlexApplication.getInstance().prefs.get(Preferences.MUSIC_POSTER_WIDTH, -1) == -1) { VoiceControlForPlexApplication.getInstance().prefs.put(Preferences.MUSIC_POSTER_WIDTH, posterWidth); VoiceControlForPlexApplication.getInstance().prefs.put(Preferences.MUSIC_POSTER_HEIGHT, posterHeight); } setMainImage(); } }); } else { setMainImage(); } } } private void setMainImage() { logger.d("Fetching main image"); VoiceControlForPlexApplication.getInstance().fetchMediaThumb(track, posterWidth, posterHeight, track.thumb != null ? track.thumb : track.grandparentThumb, track.getImageKey(PlexMedia.IMAGE_KEY.MUSIC_THUMB), new BitmapHandler() { @Override public void onSuccess(final Bitmap bitmap) { handler.post(new Runnable() { @Override public void run() { nowPlayingPoster.setImageBitmap(bitmap); } }); } }); } @Override public void onDestroy() { super.onDestroy(); } @Override public void onStart() { super.onStart(); } @Override public void onTimeUpdate(PlayerState state, int time) { // logger.d("got time update, state: %s, time: %d", state, time); currentTimeView.setText(VoiceControlForPlexApplication.secondsToTimecode(time / 1000)); seekBar.setProgress(time / 1000); setPlayPauseButtonState(state); } private void setPlayPauseButtonState(PlayerState state) { if(state == PlayerState.PAUSED) { playButton.setVisibility(View.VISIBLE); pauseButton.setVisibility(View.INVISIBLE); playPauseSpinner.setVisibility(View.INVISIBLE); } else if(state == PlayerState.PLAYING) { pauseButton.setVisibility(View.VISIBLE); playButton.setVisibility(View.INVISIBLE); playPauseSpinner.setVisibility(View.INVISIBLE); } else if(state == PlayerState.BUFFERING) { playButton.setVisibility(View.INVISIBLE); pauseButton.setVisibility(View.INVISIBLE); playPauseSpinner.setVisibility(View.VISIBLE); } } @Override public void onTrackChange(PlexTrack t) { logger.d("onTrackChange: %s", t.getTitle()); track = t; showNowPlaying(); } @Override public void onFinished() { logger.d("onFinished"); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelable(Intent.EXTRA_MEDIA, track); outState.putParcelableArrayList(Intent.EXTRA_PLAYLIST, playlist); } }