/** * Copyright (c) 2015 unfoldingWord * http://creativecommons.org/licenses/MIT/ * See LICENSE file for details. * Contributors: * PJ Fechner <pj@actsmedia.com> */ package singletons; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.media.MediaPlayer; import android.net.Uri; import android.os.AsyncTask; import android.view.KeyEvent; import java.util.ArrayList; import java.util.List; import de.greenrobot.event.EventBus; import eventbusmodels.BiblePagingEvent; import eventbusmodels.StoriesPagingEvent; import model.AudioMarker; import model.DataFileManager; import model.DownloadState; import model.daoModels.AudioChapter; import model.daoModels.StoryPage; import model.parsers.AudioMarkerParser; import model.parsers.MediaType; /** * Created by Fechner on 10/5/15. */ public class UWAudioPlayer { private static final String TAG = "UWAudioPlayer"; private static final int REFRESH_TIME_IN_MILLI = 200; private static UWAudioPlayer ourInstance; public static UWAudioPlayer getInstance(Context context) { if(ourInstance == null){ ourInstance = new UWAudioPlayer(context); } return ourInstance; } private StoryPage currentModel; private Context context; private List<UWAudioPlayerListener> listeners; private MediaPlayer mediaPlayer; private AudioMarker currentMarker; private Uri currentUri; private UWAudioPlayer(Context context) { this.context = context; listeners = new ArrayList<>(); registerEventListeners(); } private void registerEventListeners(){ EventBus.getDefault().register(this, 1); } public void unregisterEventListeners(){ EventBus.getDefault().unregister(this); } public void onEventMainThread(BiblePagingEvent event){ } public void onEvent(StoriesPagingEvent event){ prepareAudio(event.mainStoryPage); } public void addListener(UWAudioPlayerListener listener){ if(!listeners.contains(listener)) { listeners.add(listener); } } public void removeListener(UWAudioPlayerListener listener){ if(listeners.contains(listener)){ listeners.remove(listener); } } public void reset(){ unregisterEventListeners(); resetMediaPLayer(); ourInstance = null; } public boolean isPlaying(){ return mediaPlayer != null && mediaPlayer.isPlaying(); } public void seekTo(int timeInSeconds){ if(mediaPlayer != null){ mediaPlayer.seekTo((int) (timeInSeconds + currentMarker.getStartTime())); updatePlayProgress(false); } } private void resetMediaPLayer(){ if(mediaPlayer != null){ mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; } } public AudioMarker getCurrentMarker() { return currentMarker; } public MediaPlayer getMediaPlayer() { return mediaPlayer; } public void play(){ if(mediaPlayer != null) { mediaPlayer.start(); notifyPlay(); updatePlayProgress(true); } } public void pause(){ if(mediaPlayer != null){ mediaPlayer.pause(); notifyPause(); } } public void updatePlayPause(){ if(isPlaying()){ notifyPause(); } else{ notifyPlay(); } } public void togglePlay(){ if(isPlaying()){ pause(); } else{ play(); } } public int getCurrentTime(){ if(mediaPlayer != null && currentMarker != null){ return mediaPlayer.getCurrentPosition() - (int) currentMarker.getStartTime(); } else { return -1; } } public void prepareAudio(final StoryPage page){ if(currentModel == null || !page.getId().equals(currentModel.getId())) { DataFileManager.getStateOfContent(context, page.getStoriesChapter().getBook().getVersion(), MediaType.MEDIA_TYPE_AUDIO, new DataFileManager.GetDownloadStateResponse() { @Override public void foundDownloadState(DownloadState state) { AudioChapter chapter = page.getStoriesChapter().getAudioForChapter(); if (state == DownloadState.DOWNLOAD_STATE_DOWNLOADED) { // File audioFile = UWFileUtils.loadSourceFile(chapter.getAudioUrl(), context); // Uri uri = Uri.fromFile(audioFile); Uri uri = DataFileManager.getUri(context, page.getStoriesChapter().getBook(), MediaType.MEDIA_TYPE_AUDIO, chapter.getDownloadedAudioUrl(context)); List<AudioMarker> markers = AudioMarkerParser.createAudioMarkers(uri, chapter.getLength() * 1000); currentModel = page; setupAudio(uri, markers.get(Integer.parseInt(page.getNumber()) - 1)); } } }); } } private void setupAudio(Uri audioUri, AudioMarker marker){ boolean wasPlaying = mediaPlayer != null && mediaPlayer.isPlaying(); currentMarker = marker; if(currentModel == null || currentUri == null || !audioUri.getPath().equalsIgnoreCase(currentUri.getPath())) { resetMediaPLayer(); mediaPlayer = MediaPlayer.create(context, audioUri); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { notifyPause(); currentUri = null; prepareAudio(currentModel); } }); currentUri = audioUri; } int currentPosition = mediaPlayer.getCurrentPosition(); // seek to start time if the current time isn't within a second of the start time if(currentPosition >= marker.getStartTime() + 1000 || currentPosition <= marker.getStartTime() - 1000){ mediaPlayer.seekTo((int) marker.getStartTime()); // int currentTime = mediaPlayer.getCurrentPosition(); // Log.i(TAG, "current time: " + currentTime); } if(wasPlaying){ mediaPlayer.start(); } updatePlayProgress(false); } private void updatePlayProgress(boolean autoUpdate){ if(mediaPlayer == null){ return; } long currentPosition = (mediaPlayer.getCurrentPosition()) - currentMarker.getStartTime(); long duration = currentMarker.getDuration(); boolean markerIsComplete = currentPosition >= duration; if(markerIsComplete && autoUpdate){ goToNextPage(); } else { updateListeners(duration, currentPosition); if(isPlaying()) { waitAndUpdatePlayProgress(); } else{ notifyPause(); } } } private void updateListeners(long duration, long currentPosition){ for (UWAudioPlayerListener listener : listeners) { if (listener != null) { listener.update(duration, currentPosition); } } } private void notifyPlay(){ for (UWAudioPlayerListener listener : listeners) { if (listener != null) { listener.started(); } } } private void notifyPause(){ for (UWAudioPlayerListener listener : listeners) { if (listener != null) { listener.paused(); } } } private void goToNextPage(){ StoryPage newPage = currentModel.getNextStoryPage(); if(newPage != null){ StoriesPagingEvent event = StoriesPagingEvent.getStickyEvent(context); EventBus.getDefault().postSticky(new StoriesPagingEvent(newPage, event.secondaryStoryPage)); } } private void waitAndUpdatePlayProgress(){ new AsyncTask<Void, Void, Void>(){ @Override protected Void doInBackground(Void... params) { try { synchronized (this) { wait(REFRESH_TIME_IN_MILLI); } } catch (InterruptedException e){ e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); updatePlayProgress(true); } }.execute(); } public interface UWAudioPlayerListener{ void update(long duration, long progress); void started(); void paused(); } public static class RemoteControlReceiver extends BroadcastReceiver { static boolean clickedHeadPhone = false; public RemoteControlReceiver() { } @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) { KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); if (event != null) { int keyCode = event.getKeyCode(); if(keyCode == KeyEvent.KEYCODE_MEDIA_PLAY || keyCode == KeyEvent.KEYCODE_HEADSETHOOK) { if(keyCode == KeyEvent.KEYCODE_HEADSETHOOK){ clickedHeadPhone = !clickedHeadPhone; if(clickedHeadPhone){ return; } } if(ourInstance != null) { ourInstance.togglePlay(); } // if (player != null) { // if(player.isPlaying()) { // player.pause(); // } else { // player.start(); // } // } } } } } } }