source: other-projects/tipple-android/tipple-lib/src/org/greenstone/android/tipple/base/AudioPlayerView.java@ 29186

Last change on this file since 29186 was 29186, checked in by ak19, 10 years ago

Tidy up on how the .loc and .json files are found

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