package org.commcare.views.media; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.Toast; import org.commcare.dalvik.R; import java.io.IOException; /** * @author Phillip Mates (pmates@dimagi.com) */ public abstract class AudioPlaybackButtonBase extends LinearLayout { private final static String TAG = AudioPlaybackButtonBase.class.getSimpleName(); /** * Audio to load when play button pressed. */ private String URI; /** * Audio playback state, used for correctly displaying the button and * dispatching playback logic on button presses. */ private MediaState currentState; /** * Unique ID used to re-attach button to currently playing media */ private ViewId residingViewId; private ImageButton playButton; public AudioPlaybackButtonBase(Context context) { super(context); setupView(context); } /** * Used by media inflater. */ public AudioPlaybackButtonBase(Context context, AttributeSet attrs) { super(context, attrs); setupView(context); } public AudioPlaybackButtonBase(Context context, final String URI, ViewId viewId, boolean visible) { super(context); setupView(context); modifyButtonForNewView(viewId, URI, visible); } protected void setupView(Context context) { LayoutInflater vi = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = vi.inflate(getLayout(), null); addView(view); setupButton(); } private void setupButton() { playButton = (ImageButton)findViewById(R.id.play_button); playButton.setOnClickListener(buildOnClickListener()); } /** * Set button appearance and playback state to 'ready'. Used when another * button is pressed and this one is reset. */ public void resetPlaybackState() { currentState = MediaState.Ready; refreshAppearance(); } /** * Change button appearance to match the playback state. */ private void refreshAppearance() { switch (currentState) { case Ready: resetProgressBar(); playButton.setSelected(false); break; case Playing: startProgressBar( AudioController.INSTANCE.getCurrentPosition(), AudioController.INSTANCE.getDuration()); playButton.setSelected(true); break; case Paused: case PausedForRenewal: pauseProgressBar(); playButton.setSelected(false); } } public void startPlaying() { AudioController.INSTANCE.playCurrentMediaEntity(); currentState = MediaState.Playing; refreshAppearance(); } public void endPlaying() { currentState = MediaState.Ready; refreshAppearance(); } private void pausePlaying() { AudioController.INSTANCE.pauseCurrentMediaEntity(); currentState = MediaState.Paused; refreshAppearance(); } private OnClickListener buildOnClickListener() { return new OnClickListener() { @Override public void onClick(View view) { switch (currentState) { case Ready: try { MediaEntity mediaEntity = new MediaEntity(URI, residingViewId, currentState); AudioController.INSTANCE.setCurrentMediaAndButton( mediaEntity, AudioPlaybackButtonBase.this); startPlaying(); } catch (IOException e) { String errorMsg = getContext().getString(R.string.audio_file_invalid); Log.e(TAG, errorMsg); Toast.makeText(getContext(), errorMsg, Toast.LENGTH_LONG).show(); e.printStackTrace(); } break; case PausedForRenewal: case Paused: startPlaying(); break; case Playing: pausePlaying(); break; default: Log.w(TAG, "Current playback state set to unexpected value"); } } }; } /** * Setup button using the AudioController's state if containing view ids * match-up between the button and the controller. Otherwise, setup the * button using the provided arguments. * * @param viewId Set button's residing view id to this ListAdapter view id. * @param URI audio to load when play button pressed * @param visible Should the button be visible? */ public void modifyButtonForNewView(ViewId viewId, String URI, boolean visible) { if (AudioController.INSTANCE.isMediaLoaded() && AudioController.INSTANCE.getMediaViewId().equals(viewId)) { // The containing view's id of this button and that of the audio // being played by the controller match. Hence, load media info // from the controller into this button. this.URI = AudioController.INSTANCE.getMediaUri(); this.residingViewId = AudioController.INSTANCE.getMediaViewId(); this.currentState = AudioController.INSTANCE.getMediaState(); AudioController.INSTANCE.registerPlaybackButton(this); refreshAppearance(); } else { // the containing view's id of the button doesn't match the audio // controller, so just setup the button normally using the provided // arguments resetButton(URI, viewId, visible); } } /** * Set playback and display state to ready and update media URI. * * @param URI audio to load when play button pressed * @param visible Should the button be visible? */ public void resetButton(String URI, boolean visible) { this.URI = URI; this.currentState = MediaState.Ready; // sets the correct icon for this MediaState refreshAppearance(); // Set not focusable so that list onclick will work playButton.setFocusable(false); playButton.setFocusableInTouchMode(false); if (visible) { this.setVisibility(View.VISIBLE); } else { this.setVisibility(View.INVISIBLE); } } /** * Set playback and display state to ready and update media URI and the id * of the button's containing view. * * @param URI audio to load when play button pressed * @param viewId Set button's residing view id to this ListAdapter view id. * @param visible Should the button be visible? */ private void resetButton(String URI, ViewId viewId, boolean visible) { resetButton(URI, visible); this.residingViewId = viewId; } protected abstract void startProgressBar(int milliPosition, int milliDuration); protected abstract void resetProgressBar(); protected abstract void pauseProgressBar(); protected abstract int getLayout(); }