/**
*
*/
package org.odk.collect.android.views.media;
import java.io.File;
import java.io.IOException;
import org.javarosa.core.reference.InvalidReferenceException;
import org.javarosa.core.reference.ReferenceManager;
import org.odk.collect.android.R;
import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.Toast;
/**
* @author ctsims
* @author carlhartung
* @author amstone326
*/
public class AudioButton extends ImageButton implements OnClickListener {
private final static String t = "AudioButton";
private String URI;
private MediaState currentState;
private AudioController controller;
private Object residingViewId;
/*
* Constructor for if not explicitly using an AudioController
*/
public AudioButton(Context context, final String URI, boolean visible) {
super(context);
resetButton(URI, visible);
//default implementation of controller if none is passed in
this.controller = new AudioController() {
private MediaPlayer mp;
@Override
public MediaEntity getCurrMedia() {
return null;
}
@Override
public void setCurrent(MediaEntity newEntity) {
this.mp = newEntity.getPlayer();
}
@Override
public void setCurrent(MediaEntity newEntity, AudioButton newButton) {
setCurrent(newEntity);
}
@Override
public void releaseCurrentMediaEntity() {
if (mp != null) {
mp.reset();
mp.release();
}
}
@Override
public Object getMediaEntityId() {
return residingViewId;
}
@Override
public void refreshCurrentAudioButton(AudioButton clicked) {
return;
}
@Override
public void saveEntityStateAndClear() {
return;
}
@Override
public void setMediaEntityState(MediaState state) {
return;
}
@Override
public void playCurrentMediaEntity() {
mp.start();
}
@Override
public void pauseCurrentMediaEntity() {
mp.pause();
}
@Override
public void setCurrentAudioButton(AudioButton b) {
return;
}
@Override
public void removeCurrentMediaEntity() {
return;
}
@Override
public void attemptSetStateToPauseForRenewal() {
return;
}
};
}
/*
* Constructor for if an AudioController is being used
*/
public AudioButton(Context context, String URI, Object id, AudioController controller,
boolean visible) {
this(context, URI, visible);
this.controller = controller;
this.residingViewId = id;
}
public void resetButton(String URI, boolean visible) {
this.URI = URI;
this.currentState = MediaState.Ready;
this.setImageResource(R.drawable.ic_media_btn_play);
setFocusable(false);
setFocusableInTouchMode(false);
this.setOnClickListener(this);
if (visible) {
this.setVisibility(View.VISIBLE);
}
else {
this.setVisibility(View.INVISIBLE);
}
}
public void resetButton(String URI, Object id, boolean visible) {
resetButton(URI, visible);
this.residingViewId = id;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
/*As soon as this button is attached to the Window we want it to "grab" the handle
to the currently playing media. This will have the side effect of dropping the handle
from anything else that was currently holding it. Only one View at a time should
be in control of the media handle.*/
attachToMedia();
}
private void attachToMedia() {
/*
* Check if the button in this view had media assigned to
* it in a previously-existing app (before rotation, etc.)
*/
MediaEntity currEntity = controller.getCurrMedia();
if (currEntity != null) {
Object oldId = currEntity.getId();
if (oldId.equals(residingViewId)) {
controller.setCurrentAudioButton(this);
restoreButtonFromEntity(currEntity);
}
}
}
public void restoreButtonFromEntity(MediaEntity currentEntity) {
this.URI = currentEntity.getSource();
this.residingViewId = currentEntity.getId();
this.currentState = currentEntity.getState();
refreshAppearance();
}
public Object getViewId() {
return residingViewId;
}
public String getSource() {
return URI;
}
public void modifyButtonForNewView(Object newViewId, String audioResource, boolean visible) {
MediaEntity currentEntity = controller.getCurrMedia();
if (currentEntity == null) {
resetButton(audioResource, newViewId, visible);
return;
}
Object activeId = currentEntity.getId();
if (activeId.equals(newViewId)) {
restoreButtonFromEntity(currentEntity);
}
else {
resetButton(audioResource, newViewId, visible);
}
}
public void setStateToReady() {
currentState = MediaState.Ready;
refreshAppearance();
}
public void setStateToPlaying() {
currentState = MediaState.Playing;
refreshAppearance();
}
public void setStateToPaused() {
currentState = MediaState.Paused;
refreshAppearance();
}
public void refreshAppearance() {
switch(currentState) {
case Ready:
this.setImageResource(R.drawable.ic_media_btn_play);
break;
case Playing:
this.setImageResource(R.drawable.ic_media_pause);
break;
case Paused:
case PausedForRenewal:
this.setImageResource(R.drawable.ic_media_btn_continue);
}
}
/*
* Derives the audio source filename from the URI. Returns the
* filename if successful, and an empty string otherwise
*/
private String preprocessClick() {
if (URI == null) {
// No audio file specified
Log.e(t, "No audio file was specified");
Toast.makeText(getContext(), getContext().getString(R.string.audio_file_error),
Toast.LENGTH_LONG).show();
return "";
}
String audioFilename = "";
try {
audioFilename = ReferenceManager._().DeriveReference(URI).getLocalURI();
} catch (InvalidReferenceException e) {
Log.e(t, "Invalid reference exception");
e.printStackTrace();
return "";
}
File audioFile = new File(audioFilename);
if (!audioFile.exists()) {
// We should have an audio clip, but the file doesn't exist.
String errorMsg = getContext().getString(R.string.file_missing, audioFile);
Log.e(t, errorMsg);
Toast.makeText(getContext(), errorMsg, Toast.LENGTH_LONG).show();
return "";
}
return audioFilename;
}
@Override
public void onClick(View v) {
String audioFilename = preprocessClick();
if ("".equals(audioFilename)) return;
switch(currentState) {
case Ready:
MediaPlayer player = new MediaPlayer();
try {
player.setDataSource(audioFilename);
player.prepare();
player.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
endPlaying();
}
});
controller.setCurrent(new MediaEntity(URI, player, residingViewId,
currentState), this);
startPlaying();
} catch (IOException e) {
String errorMsg = getContext().getString(R.string.audio_file_invalid);
Log.e(t, errorMsg);
Toast.makeText(getContext(), errorMsg, Toast.LENGTH_LONG).show();
e.printStackTrace();
}
break;
case PausedForRenewal:
case Paused:
startPlaying();
break;
case Playing:
pausePlaying();
break;
}
}
public MediaState getMediaState() {
return currentState;
}
public void startPlaying() {
controller.playCurrentMediaEntity();
setStateToPlaying();
}
public void endPlaying() {
if (!currentState.equals(MediaState.Playing)) {
controller.releaseCurrentMediaEntity();
setStateToReady();
}
}
public void pausePlaying() {
controller.pauseCurrentMediaEntity();
setStateToPaused();
}
}