source: other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/base/AudioPlayerView.java@ 24807

Last change on this file since 24807 was 24807, checked in by davidb, 12 years ago

Reloating base files into 'base' package

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