source: other-projects/tipple-android/tipple-ar/tipple-standalone-hpg/src/org/greenstone/android/tipple/base/AudioPlayerView.java@ 26528

Last change on this file since 26528 was 26528, checked in by davidb, 11 years ago

Code developed by the Tipple-AR Smoke and Mirrors team, based on the main tipple trunk

File size: 17.1 KB
Line 
1package org.greenstone.android.tipple.base;
2
3import java.util.LinkedList;
4import java.util.List;
5
6import android.content.Context;
7import android.content.Intent;
8import android.media.MediaPlayer;
9import android.view.View;
10import android.view.ViewGroup;
11import android.widget.Button;
12import android.widget.FrameLayout;
13import android.widget.LinearLayout;
14import android.widget.TextView;
15
16//import org.greenstone.android.tipple.ArCamera;
17
18public class AudioPlayerView extends LinearLayout
19{
20 TextView text_view_;
21
22 protected Button buttonPlayPause_;
23 protected Button buttonNext_;
24 protected Button buttonShowHideText_;
25
26 protected String stopLabel_;
27
28 protected boolean hasCompletionHandler_;
29
30 protected TippleActivity.TextAudioModeEnum current_text_audio_mode_;
31
32 protected List<MediaPlayer> audioPlayerQueue_;
33 protected List<String> idPlayerQueue_;
34 protected List<String> textPlayerQueue_;
35 protected List<Boolean> visitedPlayerQueue_;
36 protected int absolute_head_pos_;
37 protected int absolute_tail_pos_;
38
39
40 AudioPlayerView(Context context, TextView text_view)
41 {
42 super(context);
43
44 text_view_ = text_view;
45
46 audioPlayerQueue_ = new LinkedList<MediaPlayer>();
47 idPlayerQueue_ = new LinkedList<String>();
48 textPlayerQueue_ = new LinkedList<String>();
49 visitedPlayerQueue_ = new LinkedList<Boolean>();
50
51 absolute_head_pos_ = 0;
52 absolute_tail_pos_ = -1;
53
54 // media buttons - play/pause, and next song/audio as horizontal layout, 50% each
55
56 stopLabel_ = "Stop";
57
58 LayoutParams layout_params
59 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
60 ViewGroup.LayoutParams.MATCH_PARENT,1);
61
62 buttonPlayPause_ = new Button(context);
63 buttonPlayPause_.setLayoutParams(layout_params);
64 buttonPlayPause_.setText("Play");
65 buttonPlayPause_.setEnabled(false);
66
67 buttonNext_ = new Button(context);
68 buttonNext_.setLayoutParams(layout_params);
69 buttonNext_.setText(stopLabel_);
70 buttonNext_.setEnabled(false);
71 // buttonNext_.setHeight()
72
73 hasCompletionHandler_ = false;
74
75
76 buttonPlayPause_.setOnClickListener(new View.OnClickListener() {
77 public void onClick(View v) {
78
79 if (isPlaying()) {
80 // Change to pause
81
82 TippleActivity.log.optMessage("Manual Pause Audio", textPlayerQueue_.get(0));
83
84 pauseAudio();
85 }
86 else {
87 // Must be paused => resume playing
88 TippleActivity.log.optMessage("Manual Play/Resume Audio", textPlayerQueue_.get(0));
89
90 playResumeAudio();
91 }
92 }
93 });
94
95 buttonNext_.setOnClickListener(new View.OnClickListener() {
96 @Override
97 public void onClick(View v) {
98 TippleActivity.log.optMessage("Manual Next Audio Location");
99 nextAudioLocation();
100 }
101 });
102
103 // Optional show text button
104
105 buttonShowHideText_ = new Button(context);
106 buttonShowHideText_.setLayoutParams(layout_params);
107 buttonShowHideText_.setText("Show Text");
108 buttonShowHideText_.setEnabled(false);
109
110 buttonShowHideText_.setOnClickListener(new View.OnClickListener() {
111 public void onClick(View v) {
112 TippleActivity.log.optMessage("Manual Show/Hide Text");
113
114 if (text_view_.getVisibility()==View.INVISIBLE) {
115 TippleActivity.log.optMessage("Manual Show Text");
116 text_view_.setVisibility(View.VISIBLE);
117 buttonShowHideText_.setText("Hide Text");
118 }
119 else {
120 TippleActivity.log.optMessage("Manual Hide Text");
121 text_view_.setVisibility(View.INVISIBLE);
122 buttonShowHideText_.setText("Show Text");
123 }
124 }
125 });
126
127 // Horozontal layout is:
128 // Play | OptText | Next
129
130 addView(buttonPlayPause_);
131
132 /*
133 current_text_audio_mode_ = TippleActivity.textAudioMode;
134 if (current_text_audio_mode_ == TippleActivity.TextAudioModeEnum.AUDIO_PLUS_TEXT) {
135 addView(buttonShowHideText_);
136 }
137 */
138 addView(buttonShowHideText_);
139
140 addView(buttonNext_);
141 }
142
143 protected boolean audioViewIsActive()
144 {
145 return ((current_text_audio_mode_==TippleActivity.TextAudioModeEnum.AUDIO_ONLY)
146 || (current_text_audio_mode_==TippleActivity.TextAudioModeEnum.AUDIO_PLUS_TEXT)
147 || (current_text_audio_mode_==TippleActivity.TextAudioModeEnum.TEXT_PLUS_AUDIO));
148
149 }
150
151 protected boolean textViewIsActive()
152 {
153 return ((current_text_audio_mode_==TippleActivity.TextAudioModeEnum.TEXT_ONLY)
154 || (current_text_audio_mode_==TippleActivity.TextAudioModeEnum.TEXT_PLUS_AUDIO)
155 || (current_text_audio_mode_==TippleActivity.TextAudioModeEnum.AUDIO_PLUS_TEXT));
156
157 }
158
159 public void refreshView()
160 {
161 if (current_text_audio_mode_ != TippleActivity.textAudioMode) {
162 // There has been a mode change
163
164 if (TippleActivity.textAudioMode == TippleActivity.TextAudioModeEnum.AUDIO_PLUS_TEXT) {
165 // ensure both play/pause and show/hide removed, then add in order needed
166
167 if (this.indexOfChild(buttonPlayPause_)>=0) {
168 this.removeView(buttonPlayPause_);
169 }
170 if (this.indexOfChild(buttonShowHideText_)>=0) {
171 this.removeView(buttonShowHideText_);
172 }
173 this.addView(buttonPlayPause_,0);
174 this.addView(buttonShowHideText_,1);
175
176 stopLabel_ = "Stop";
177 }
178
179 else if (TippleActivity.textAudioMode == TippleActivity.TextAudioModeEnum.AUDIO_ONLY) {
180 // ensure show/hide is gone, and only add play/pause if needed
181 if (this.indexOfChild(buttonShowHideText_)>=0) {
182 // Don't want to see a show/hide text button
183 this.removeView(buttonShowHideText_);
184 }
185 if (this.indexOfChild(buttonPlayPause_)<0) {
186 this.addView(buttonPlayPause_);
187 }
188 stopLabel_ = "Stop";
189 }
190
191 else if (TippleActivity.textAudioMode == TippleActivity.TextAudioModeEnum.TEXT_PLUS_AUDIO) {
192 // ensure both play/pause and show/hide removed, then add in order needed
193 if (this.indexOfChild(buttonPlayPause_)>=0) {
194 this.removeView(buttonPlayPause_);
195 }
196 if (this.indexOfChild(buttonShowHideText_)>=0) {
197 this.removeView(buttonShowHideText_);
198 }
199
200 this.addView(buttonShowHideText_,0);
201 this.addView(buttonPlayPause_,1);
202
203 stopLabel_ = "Stop";
204 }
205
206 else if (TippleActivity.textAudioMode == TippleActivity.TextAudioModeEnum.TEXT_ONLY) {
207 // ensure play/pause is gone, and only add show/hide if needed
208
209 if (this.indexOfChild(buttonPlayPause_)>=0) {
210 // Don't want to see a play button
211 this.removeView(buttonPlayPause_);
212 }
213
214 if (this.indexOfChild(buttonShowHideText_)<0) {
215 this.addView(buttonShowHideText_,0);
216 }
217 stopLabel_ = "End";
218 }
219
220 current_text_audio_mode_ = TippleActivity.textAudioMode;
221 }
222
223 }
224 synchronized public boolean audioQueueEmpty()
225 {
226 return (audioPlayerQueue_.size()==0);
227 }
228
229 synchronized public boolean outstandingAudioPending()
230 {
231 return (audioPlayerQueue_.size()>1);
232 }
233
234 synchronized public int audioQueueLength()
235 {
236 return audioPlayerQueue_.size();
237 }
238
239 synchronized public boolean addAudio(String new_id)
240 {
241 // Assume audio should be added, unless an existing match is found
242 boolean add_audio = true;
243
244 int rev_pos = audioPlayerQueue_.size()-1;
245 for (int i=0; i<3; i++) {
246 if (rev_pos<0) {
247 break;
248 }
249
250 String id = idPlayerQueue_.get(rev_pos);
251
252 if (new_id.equals(id)) {
253 add_audio=false;
254 }
255
256 rev_pos--;
257 }
258
259 return add_audio;
260 }
261 synchronized public void queueAudio(String audio_filename, String id, String text,
262 boolean previously_visited)
263 {
264 MediaPlayer mp = new MediaPlayer();
265 try {
266
267 mp.setDataSource(audio_filename);
268 mp.prepare();
269 audioPlayerQueue_.add(mp);
270 idPlayerQueue_.add(id);
271 textPlayerQueue_.add(text);
272 visitedPlayerQueue_.add(previously_visited);
273
274 absolute_tail_pos_++;
275
276 int audio_queue_len = audioPlayerQueue_.size();
277 int mod_head_pos = absolute_head_pos_ % 3;
278
279 if (audio_queue_len==1) {
280 // Nothing to do, not enough data to allow the user to play anything
281 return;
282 }
283 else if (audio_queue_len==2) {
284
285 // mod_head_pos==0 => title followed by text => ready to play
286 // mod_head_pos==1 => text followed by beep => "stop"
287 // mod_head_pos==2 => beep followed title => "skip to next"
288
289 if (mod_head_pos==0) {
290 // just queued up a title and text (at the head of the queue)
291 // => make play active if not already so
292 if (!buttonPlayPause_.isEnabled()) {
293 //buttonPlayPause_.setText("Play");
294 buttonPlayPause_.setEnabled(true);
295 }
296 if (!buttonShowHideText_.isEnabled()) {
297 buttonShowHideText_.setEnabled(true);
298
299 }
300 buttonNext_.setText(stopLabel_);
301 buttonNext_.setEnabled(true);
302 }
303 else if (mod_head_pos==1) {
304
305 buttonNext_.setText(stopLabel_);
306 buttonNext_.setEnabled(true);
307 }
308 else {
309 // mod_head_pos==2
310 buttonNext_.setText("Skip to Next (1)");
311 buttonNext_.setEnabled(true);
312 }
313 }
314 else {
315 // length >= 3
316
317 // mod_head_pos==0 => title at head, spans loc if len >3
318 // mod_head_pos==1 => text at head, guaranteed to span loc => "skip to next"
319 // mod_head_pos==2 => beep at head, guaranteed to span loc => "skip to next"
320
321 if ((mod_head_pos==0) && (audio_queue_len==3)) {
322 // stop
323 buttonNext_.setText(stopLabel_);
324 buttonNext_.setEnabled(true);
325 }
326 else {
327 // for everything else, the queue spans to another location
328
329
330 int relative_adjustment_pos = mod_head_pos -1;
331 int num_remaining_locations = (audio_queue_len+relative_adjustment_pos)/3;
332
333 buttonNext_.setText("Skip to Next (" + num_remaining_locations + ")");
334 buttonNext_.setEnabled(true);
335 }
336 }
337 }
338 catch (Exception e) {
339 e.printStackTrace();
340 }
341 }
342
343 synchronized public void queueAudio(String audio_filename, String id, String text)
344 {
345 queueAudio(audio_filename,id,text,false);
346 }
347
348 synchronized public void removeAudioTrack()
349 {
350 if (audioPlayerQueue_.size()>0) {
351
352 TippleActivity.log.optMessage("Remove Audio Track","Queue size was: " + audioPlayerQueue_.size());
353
354 MediaPlayer mp = audioPlayerQueue_.remove(0);
355 idPlayerQueue_.remove(0);
356 textPlayerQueue_.remove(0);
357 visitedPlayerQueue_.remove(0);
358
359 absolute_head_pos_++;
360 mp.stop();
361 mp.release();
362
363 hasCompletionHandler_ = false;
364 }
365 else {
366 System.err.println("Error: No audio in queue to remove");
367 }
368 }
369
370 synchronized public void removeAudioLocation()
371 {
372 int audio_queue_len = audioPlayerQueue_.size();
373
374 if (audio_queue_len>0) {
375
376 TippleActivity.log.optMessage("Remove Audio Location","Queue size was: " + audioPlayerQueue_.size());
377
378 int mod_head_pos = absolute_head_pos_%3;
379 int need_to_skip = 3-mod_head_pos;
380
381 // remove up to the next absolute multiple of 3
382 for (int i=0; i<need_to_skip; i++) {
383 MediaPlayer mp = audioPlayerQueue_.remove(0);
384 idPlayerQueue_.remove(0);
385 textPlayerQueue_.remove(0);
386 visitedPlayerQueue_.remove(0);
387 absolute_head_pos_++;
388
389 mp.stop();
390 mp.release();
391 if (audioPlayerQueue_.size()==0) {
392 // happens if there is no beep on the last location
393 break;
394 }
395 }
396
397 hasCompletionHandler_ = false;
398 }
399 else {
400 System.err.println("Error: No audio in queue to remove");
401 }
402 }
403
404
405 synchronized public void onAudioCompletion(MediaPlayer mp)
406 {
407 mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
408
409 @Override
410 public void onCompletion(MediaPlayer mp) {
411 nextAudioTrack();
412 }
413 });
414 }
415
416 synchronized public void onVisitedAudioCompletion(MediaPlayer mp)
417 {
418 mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
419
420 @Override
421 public void onCompletion(MediaPlayer mp) {
422 playResumeAudioInternal();
423 }
424 });
425 }
426
427 public boolean updateTextViewIfHeadIsNewLocation()
428 {
429 boolean updated = false;
430
431 if ((absolute_head_pos_%3)==0) {
432 // start of a new location
433 String text_desc = textPlayerQueue_.get(0);
434 if (visitedPlayerQueue_.get(0)) {
435 text_desc += "\n(Previously Visited)";
436 }
437 text_desc += "\n------";
438
439 if (textPlayerQueue_.size()>1) {
440 text_desc += "\n" + textPlayerQueue_.get(1);
441 }
442
443 text_view_.setText(text_desc);
444 updated = true;
445 }
446
447 return updated;
448 }
449
450
451 synchronized public void playResumeAudio()
452 {
453 if (audioPlayerQueue_.size()>0) {
454
455 if ((audioViewIsActive()) && (visitedPlayerQueue_.get(0))) {
456 // Play (synchronously) the "previously visited" message before moving on to
457 // describing the location itself
458 MediaPlayer mp = new MediaPlayer();
459
460 try {
461 mp.setDataSource(TipLocationManager.prevVisitedFilename);
462 mp.prepare();
463 onVisitedAudioCompletion(mp);
464 mp.start();
465 }
466 catch (Exception e) {
467 e.printStackTrace();
468 }
469 }
470 else {
471 // go straight on and play the queued audio
472 playResumeAudioInternal();
473 }
474 }
475 else {
476 System.err.println("Error: No audio in queue to start playing");
477
478 }
479
480 }
481
482
483 synchronized public void playResumeAudioInternal()
484 {
485 if (audioPlayerQueue_.size()>0) {
486 TippleActivity.log.optMessage("Play/Resume Audio","Queue size was: " + audioPlayerQueue_.size());
487
488 MediaPlayer mp = audioPlayerQueue_.get(0);
489
490 if (!hasCompletionHandler_) {
491
492 updateTextViewIfHeadIsNewLocation();
493
494 onAudioCompletion(mp);
495 hasCompletionHandler_ = true;
496 }
497
498 if (audioViewIsActive() || ((absolute_head_pos_%3)==2)) {
499 // allow it to play a "ding" even in text only mode
500 mp.start();
501 }
502 buttonPlayPause_.setText("Pause"); // next operation
503 }
504 else {
505 System.err.println("Error: No audio in queue to start playing");
506 }
507
508 }
509
510 synchronized public void pauseAudio()
511 {
512 if (audioPlayerQueue_.size()>0) {
513 MediaPlayer mp = audioPlayerQueue_.get(0);
514 mp.pause();
515 buttonPlayPause_.setText("Play/Resume"); // next operation
516 }
517 else {
518 System.err.println("Error: No audio in queue to pause");
519 }
520 }
521
522 synchronized public void nextAudioLocation()
523 {
524 removeAudioLocation(); // stops it if currently playing
525
526 int remaining_audio_len = audioQueueLength();
527 if (remaining_audio_len>0) {
528 playResumeAudio();
529
530 int mod_head_pos = absolute_head_pos_ % 3;
531 /*
532 int relative_adjustment_pos = 1 - mod_head_pos;
533 int num_remaining_locations = (remaining_audio_len+relative_adjustment_pos)/3;
534
535 if (num_remaining_locations>=1) {
536
537 buttonNext_.setText("Skip to Next (" + num_remaining_locations + ")");
538 buttonNext_.setEnabled(true);
539 }
540 */
541
542 int num_remaining_locations = (remaining_audio_len+mod_head_pos)/3;
543
544 if (num_remaining_locations>1) {
545
546 buttonNext_.setText("Skip to Next (" + (num_remaining_locations-1) + ")");
547 buttonNext_.setEnabled(true);
548 }
549
550 else {
551 buttonNext_.setText(stopLabel_);
552 buttonNext_.setEnabled(true);
553 buttonPlayPause_.setText("Play");
554 buttonPlayPause_.setEnabled(true);
555 buttonShowHideText_.setText("Show Text");
556 buttonShowHideText_.setEnabled(true);
557 }
558 }
559 else {
560 buttonNext_.setText(stopLabel_);
561 buttonNext_.setEnabled(false);
562 buttonPlayPause_.setText("Play");
563 buttonPlayPause_.setEnabled(false);
564
565 if (text_view_.getVisibility() == View.VISIBLE) {
566 TippleActivity.log.optMessage("Audio Hide Text");
567 text_view_.setVisibility(View.INVISIBLE);
568 }
569 buttonShowHideText_.setText("Show Text");
570 buttonShowHideText_.setEnabled(false);
571 }
572 }
573
574 synchronized public void nextAudioTrack()
575 {
576 removeAudioTrack(); // stops it if currently playing
577
578 int remaining_audio_len = audioQueueLength();
579 if (remaining_audio_len>0) {
580 playResumeAudio();
581
582 int num_remaining_locations = ((remaining_audio_len-1)/3);
583
584 if (num_remaining_locations>=1) {
585
586 buttonNext_.setText("Skip to Next (" + num_remaining_locations + ")");
587 buttonNext_.setEnabled(true);
588 }
589 else {
590 buttonNext_.setText(stopLabel_);
591 buttonNext_.setEnabled(true);
592 //buttonPlayPause_.setText("Play");
593 buttonPlayPause_.setEnabled(true);
594 buttonShowHideText_.setEnabled(true);
595 }
596 }
597 else {
598 buttonNext_.setText(stopLabel_);
599 buttonNext_.setEnabled(false);
600 buttonPlayPause_.setText("Play");
601 buttonPlayPause_.setEnabled(false);
602
603 if (text_view_.getVisibility() == View.VISIBLE) {
604 TippleActivity.log.optMessage("Audio Hide Text");
605 text_view_.setVisibility(View.INVISIBLE);
606 }
607 buttonShowHideText_.setText("Show Text");
608 buttonShowHideText_.setEnabled(false);
609
610
611 }
612 }
613
614
615 synchronized public boolean isPlaying()
616 {
617 boolean is_playing = false;
618 if (audioPlayerQueue_.size()>0) {
619 is_playing = audioPlayerQueue_.get(0).isPlaying();
620 }
621 return is_playing;
622 }
623}
Note: See TracBrowser for help on using the repository browser.