package org.greenstone.android.tipple.base; import java.util.LinkedList; import java.util.List; import android.content.Context; import android.media.MediaPlayer; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class AudioPlayerView extends LinearLayout { TextView text_view_; protected Button buttonPlayPause_; protected Button buttonNext_; protected Button buttonShowHideText_; protected String stopLabel_; protected boolean hasCompletionHandler_; protected TippleActivity.TextAudioModeEnum current_text_audio_mode_; protected List audioPlayerQueue_; protected List idPlayerQueue_; protected List textPlayerQueue_; protected List visitedPlayerQueue_; protected int absolute_head_pos_; protected int absolute_tail_pos_; AudioPlayerView(Context context, TextView text_view) { super(context); text_view_ = text_view; audioPlayerQueue_ = new LinkedList(); idPlayerQueue_ = new LinkedList(); textPlayerQueue_ = new LinkedList(); visitedPlayerQueue_ = new LinkedList(); absolute_head_pos_ = 0; absolute_tail_pos_ = -1; // media buttons - play/pause, and next song/audio as horizontal layout, 50% each stopLabel_ = "Stop"; LayoutParams layout_params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT,1); buttonPlayPause_ = new Button(context); buttonPlayPause_.setLayoutParams(layout_params); buttonPlayPause_.setText("Play"); buttonPlayPause_.setEnabled(false); buttonNext_ = new Button(context); buttonNext_.setLayoutParams(layout_params); buttonNext_.setText(stopLabel_); buttonNext_.setEnabled(false); hasCompletionHandler_ = false; buttonPlayPause_.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (isPlaying()) { // Change to pause TippleActivity.log.optMessage("Manual Pause Audio", textPlayerQueue_.get(0)); pauseAudio(); } else { // Must be paused => resume playing TippleActivity.log.optMessage("Manual Play/Resume Audio", textPlayerQueue_.get(0)); playResumeAudio(); } } }); buttonNext_.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TippleActivity.log.optMessage("Manual Next Audio Location"); nextAudioLocation(); } }); // Optional show text button buttonShowHideText_ = new Button(context); buttonShowHideText_.setLayoutParams(layout_params); buttonShowHideText_.setText("Show Text"); buttonShowHideText_.setEnabled(false); buttonShowHideText_.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { TippleActivity.log.optMessage("Manual Show/Hide Text"); if (text_view_.getVisibility()==View.INVISIBLE) { TippleActivity.log.optMessage("Manual Show Text"); text_view_.setVisibility(View.VISIBLE); buttonShowHideText_.setText("Hide Text"); } else { TippleActivity.log.optMessage("Manual Hide Text"); text_view_.setVisibility(View.INVISIBLE); buttonShowHideText_.setText("Show Text"); } } }); // Horozontal layout is: // Play | OptText | Next addView(buttonPlayPause_); /* current_text_audio_mode_ = TippleActivity.textAudioMode; if (current_text_audio_mode_ == TippleActivity.TextAudioModeEnum.AUDIO_PLUS_TEXT) { addView(buttonShowHideText_); } */ addView(buttonShowHideText_); addView(buttonNext_); } protected boolean audioViewIsActive() { return ((current_text_audio_mode_==TippleActivity.TextAudioModeEnum.AUDIO_ONLY) || (current_text_audio_mode_==TippleActivity.TextAudioModeEnum.AUDIO_PLUS_TEXT) || (current_text_audio_mode_==TippleActivity.TextAudioModeEnum.TEXT_PLUS_AUDIO)); } protected boolean textViewIsActive() { return ((current_text_audio_mode_==TippleActivity.TextAudioModeEnum.TEXT_ONLY) || (current_text_audio_mode_==TippleActivity.TextAudioModeEnum.TEXT_PLUS_AUDIO) || (current_text_audio_mode_==TippleActivity.TextAudioModeEnum.AUDIO_PLUS_TEXT)); } public void refreshView() { if (current_text_audio_mode_ != TippleActivity.textAudioMode) { // There has been a mode change if (TippleActivity.textAudioMode == TippleActivity.TextAudioModeEnum.AUDIO_PLUS_TEXT) { // ensure both play/pause and show/hide removed, then add in order needed if (this.indexOfChild(buttonPlayPause_)>=0) { this.removeView(buttonPlayPause_); } if (this.indexOfChild(buttonShowHideText_)>=0) { this.removeView(buttonShowHideText_); } this.addView(buttonPlayPause_,0); this.addView(buttonShowHideText_,1); stopLabel_ = "Stop"; } else if (TippleActivity.textAudioMode == TippleActivity.TextAudioModeEnum.AUDIO_ONLY) { // ensure show/hide is gone, and only add play/pause if needed if (this.indexOfChild(buttonShowHideText_)>=0) { // Don't want to see a show/hide text button this.removeView(buttonShowHideText_); } if (this.indexOfChild(buttonPlayPause_)<0) { this.addView(buttonPlayPause_); } stopLabel_ = "Stop"; } else if (TippleActivity.textAudioMode == TippleActivity.TextAudioModeEnum.TEXT_PLUS_AUDIO) { // ensure both play/pause and show/hide removed, then add in order needed if (this.indexOfChild(buttonPlayPause_)>=0) { this.removeView(buttonPlayPause_); } if (this.indexOfChild(buttonShowHideText_)>=0) { this.removeView(buttonShowHideText_); } this.addView(buttonShowHideText_,0); this.addView(buttonPlayPause_,1); stopLabel_ = "Stop"; } else if (TippleActivity.textAudioMode == TippleActivity.TextAudioModeEnum.TEXT_ONLY) { // ensure play/pause is gone, and only add show/hide if needed if (this.indexOfChild(buttonPlayPause_)>=0) { // Don't want to see a play button this.removeView(buttonPlayPause_); } if (this.indexOfChild(buttonShowHideText_)<0) { this.addView(buttonShowHideText_,0); } stopLabel_ = "End"; } current_text_audio_mode_ = TippleActivity.textAudioMode; } } synchronized public boolean audioQueueEmpty() { return (audioPlayerQueue_.size()==0); } synchronized public boolean outstandingAudioPending() { return (audioPlayerQueue_.size()>1); } synchronized public int audioQueueLength() { return audioPlayerQueue_.size(); } synchronized public boolean addAudio(String new_id) { // Assume audio should be added, unless an existing match is found boolean add_audio = true; int rev_pos = audioPlayerQueue_.size()-1; for (int i=0; i<3; i++) { if (rev_pos<0) { break; } String id = idPlayerQueue_.get(rev_pos); if (new_id.equals(id)) { add_audio=false; } rev_pos--; } return add_audio; } synchronized public void queueAudio(String audio_filename, String id, String text, boolean previously_visited) { MediaPlayer mp = new MediaPlayer(); try { mp.setDataSource(audio_filename); mp.prepare(); audioPlayerQueue_.add(mp); idPlayerQueue_.add(id); textPlayerQueue_.add(text); visitedPlayerQueue_.add(previously_visited); absolute_tail_pos_++; int audio_queue_len = audioPlayerQueue_.size(); int mod_head_pos = absolute_head_pos_ % 3; if (audio_queue_len==1) { // Nothing to do, not enough data to allow the user to play anything return; } else if (audio_queue_len==2) { // mod_head_pos==0 => title followed by text => ready to play // mod_head_pos==1 => text followed by beep => "stop" // mod_head_pos==2 => beep followed title => "skip to next" if (mod_head_pos==0) { // just queued up a title and text (at the head of the queue) // => make play active if not already so if (!buttonPlayPause_.isEnabled()) { //buttonPlayPause_.setText("Play"); buttonPlayPause_.setEnabled(true); } if (!buttonShowHideText_.isEnabled()) { buttonShowHideText_.setEnabled(true); } buttonNext_.setText(stopLabel_); buttonNext_.setEnabled(true); } else if (mod_head_pos==1) { buttonNext_.setText(stopLabel_); buttonNext_.setEnabled(true); } else { // mod_head_pos==2 buttonNext_.setText("Skip to Next (1)"); buttonNext_.setEnabled(true); } } else { // length >= 3 // mod_head_pos==0 => title at head, spans loc if len >3 // mod_head_pos==1 => text at head, guaranteed to span loc => "skip to next" // mod_head_pos==2 => beep at head, guaranteed to span loc => "skip to next" if ((mod_head_pos==0) && (audio_queue_len==3)) { // stop buttonNext_.setText(stopLabel_); buttonNext_.setEnabled(true); } else { // for everything else, the queue spans to another location int relative_adjustment_pos = mod_head_pos -1; int num_remaining_locations = (audio_queue_len+relative_adjustment_pos)/3; buttonNext_.setText("Skip to Next (" + num_remaining_locations + ")"); buttonNext_.setEnabled(true); } } } catch (Exception e) { e.printStackTrace(); } } synchronized public void queueAudio(String audio_filename, String id, String text) { queueAudio(audio_filename,id,text,false); } synchronized public void removeAudioTrack() { if (audioPlayerQueue_.size()>0) { TippleActivity.log.optMessage("Remove Audio Track","Queue size was: " + audioPlayerQueue_.size()); MediaPlayer mp = audioPlayerQueue_.remove(0); idPlayerQueue_.remove(0); textPlayerQueue_.remove(0); visitedPlayerQueue_.remove(0); absolute_head_pos_++; mp.stop(); mp.release(); hasCompletionHandler_ = false; } else { System.err.println("Error: No audio in queue to remove"); } } synchronized public void removeAudioLocation() { int audio_queue_len = audioPlayerQueue_.size(); if (audio_queue_len>0) { TippleActivity.log.optMessage("Remove Audio Location","Queue size was: " + audioPlayerQueue_.size()); int mod_head_pos = absolute_head_pos_%3; int need_to_skip = 3-mod_head_pos; // remove up to the next absolute multiple of 3 for (int i=0; i1) { text_desc += "\n" + textPlayerQueue_.get(1); } text_view_.setText(text_desc); updated = true; } return updated; } synchronized public void playResumeAudio() { if (audioPlayerQueue_.size()>0) { if ((audioViewIsActive()) && (visitedPlayerQueue_.get(0))) { // Play (synchronously) the "previously visited" message before moving on to // describing the location itself MediaPlayer mp = new MediaPlayer(); try { mp.setDataSource(TipLocationManager.prevVisitedFilename); mp.prepare(); onVisitedAudioCompletion(mp); mp.start(); } catch (Exception e) { e.printStackTrace(); } } else { // go straight on and play the queued audio playResumeAudioInternal(); } } else { System.err.println("Error: No audio in queue to start playing"); } } synchronized public void playResumeAudioInternal() { if (audioPlayerQueue_.size()>0) { TippleActivity.log.optMessage("Play/Resume Audio","Queue size was: " + audioPlayerQueue_.size()); MediaPlayer mp = audioPlayerQueue_.get(0); if (!hasCompletionHandler_) { updateTextViewIfHeadIsNewLocation(); onAudioCompletion(mp); hasCompletionHandler_ = true; } if (audioViewIsActive() || ((absolute_head_pos_%3)==2)) { // allow it to play a "ding" even in text only mode mp.start(); } buttonPlayPause_.setText("Pause"); // next operation } else { System.err.println("Error: No audio in queue to start playing"); } } synchronized public void pauseAudio() { if (audioPlayerQueue_.size()>0) { MediaPlayer mp = audioPlayerQueue_.get(0); mp.pause(); buttonPlayPause_.setText("Play/Resume"); // next operation } else { System.err.println("Error: No audio in queue to pause"); } } synchronized public void nextAudioLocation() { removeAudioLocation(); // stops it if currently playing int remaining_audio_len = audioQueueLength(); if (remaining_audio_len>0) { playResumeAudio(); int mod_head_pos = absolute_head_pos_ % 3; /* int relative_adjustment_pos = 1 - mod_head_pos; int num_remaining_locations = (remaining_audio_len+relative_adjustment_pos)/3; if (num_remaining_locations>=1) { buttonNext_.setText("Skip to Next (" + num_remaining_locations + ")"); buttonNext_.setEnabled(true); } */ int num_remaining_locations = (remaining_audio_len+mod_head_pos)/3; if (num_remaining_locations>1) { buttonNext_.setText("Skip to Next (" + (num_remaining_locations-1) + ")"); buttonNext_.setEnabled(true); } else { buttonNext_.setText(stopLabel_); buttonNext_.setEnabled(true); buttonPlayPause_.setText("Play"); buttonPlayPause_.setEnabled(true); buttonShowHideText_.setText("Show Text"); buttonShowHideText_.setEnabled(true); } } else { buttonNext_.setText(stopLabel_); buttonNext_.setEnabled(false); buttonPlayPause_.setText("Play"); buttonPlayPause_.setEnabled(false); if (text_view_.getVisibility() == View.VISIBLE) { TippleActivity.log.optMessage("Audio Hide Text"); text_view_.setVisibility(View.INVISIBLE); } buttonShowHideText_.setText("Show Text"); buttonShowHideText_.setEnabled(false); } } synchronized public void nextAudioTrack() { removeAudioTrack(); // stops it if currently playing int remaining_audio_len = audioQueueLength(); if (remaining_audio_len>0) { playResumeAudio(); int num_remaining_locations = ((remaining_audio_len-1)/3); if (num_remaining_locations>=1) { buttonNext_.setText("Skip to Next (" + num_remaining_locations + ")"); buttonNext_.setEnabled(true); } else { buttonNext_.setText(stopLabel_); buttonNext_.setEnabled(true); //buttonPlayPause_.setText("Play"); buttonPlayPause_.setEnabled(true); buttonShowHideText_.setEnabled(true); } } else { buttonNext_.setText(stopLabel_); buttonNext_.setEnabled(false); buttonPlayPause_.setText("Play"); buttonPlayPause_.setEnabled(false); if (text_view_.getVisibility() == View.VISIBLE) { TippleActivity.log.optMessage("Audio Hide Text"); text_view_.setVisibility(View.INVISIBLE); } buttonShowHideText_.setText("Show Text"); buttonShowHideText_.setEnabled(false); } } synchronized public boolean isPlaying() { boolean is_playing = false; if (audioPlayerQueue_.size()>0) { is_playing = audioPlayerQueue_.get(0).isPlaying(); } return is_playing; } }