/**
* This work is licensed under the Creative Commons Attribution-NonCommercial-
* NoDerivs 3.0 Unported License. To view a copy of this license, visit
* http://creativecommons.org/licenses/by-nc-nd/3.0/ or send a letter to
* Creative Commons, 444 Castro Street, Suite 900, Mountain View, California,
* 94041, USA.
*
* Use of this work is permitted only in accordance with license rights granted.
* Materials provided "AS IS"; no representations or warranties provided.
*
* Copyright � 2012 Marcus Parkkinen, Aki K�kel�, Fredrik �hs.
**/
package edu.chalmers.dat255.audiobookplayer.view;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import edu.chalmers.dat255.audiobookplayer.R;
import edu.chalmers.dat255.audiobookplayer.constants.Constants;
import edu.chalmers.dat255.audiobookplayer.constants.PlaybackStatus;
import edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents;
import edu.chalmers.dat255.audiobookplayer.util.TextFormatter;
/**
* A graphical UI representing the audio player.
*
* @author Aki K�kel�
* @version 0.6
*
*/
public class PlayerFragment extends Fragment {
private static final String TAG = "PlayerFragment";
private SeekBar bookBar;
private SeekBar trackBar;
private TextView bookTitle;
private TextView trackTitle;
private TextView bookDuration;
private TextView trackDuration;
private TextView bookElapsedTime;
private TextView trackElapsedTime;
private TextView trackCounter;
private IPlayerEvents fragmentOwner;
private ImageButton playPause;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
boolean ownerImplementsEvents = true;
try {
fragmentOwner = (IPlayerEvents) activity;
} catch (ClassCastException e) {
ownerImplementsEvents = false;
}
if (!ownerImplementsEvents) {
throw new ClassCastException(activity.toString()
+ " does not implement " + IPlayerEvents.class.getName());
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater
.inflate(R.layout.fragment_player, container, false);
ImageButton seekLeft = (ImageButton) view.findViewById(R.id.seekLeft);
seekLeft.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
int ev = event.getAction();
if (ev == MotionEvent.ACTION_DOWN) {
Log.i(TAG, "Seeking LEFT (ACTION_DOWN)");
seekLeft(false);
} else if (ev == MotionEvent.ACTION_UP) {
Log.i(TAG, "Stopped seeking LEFT (ACTION_UP)");
} else if (ev == MotionEvent.ACTION_CANCEL) {
Log.i(TAG, "Cancelled seeking LEFT (ACTION_CANCEL)");
}
return false;
}
});
ImageButton seekRight = (ImageButton) view.findViewById(R.id.seekRight);
seekRight.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
int ev = event.getAction();
if (ev == MotionEvent.ACTION_DOWN) {
Log.i(TAG, "Seeking RIGHT (ACTION_DOWN)");
seekRight(true);
} else if (ev == MotionEvent.ACTION_UP) {
Log.i(TAG, "Stopped seeking RIGHT (ACTION_UP)");
} else if (ev == MotionEvent.ACTION_CANCEL) {
Log.i(TAG, "Cancelled seeking RIGHT (ACTION_CANCEL)");
}
return false;
}
});
playPause = (ImageButton) view.findViewById(R.id.playPause);
playPause.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
/*
* if audio is playing when the user presses pause, then pause
* and show the play button.
*/
if (fragmentOwner.isPlaying()) {
// it was playing, so pause
fragmentOwner.pause();
setPlaybackStatus(PlaybackStatus.PAUSED);
} else if (fragmentOwner.isStarted()) {
// was paused, so resume
fragmentOwner.resume();
setPlaybackStatus(PlaybackStatus.PLAYING);
} else {
// it was stopped, keep it stopped
setPlaybackStatus(PlaybackStatus.STOPPED);
}
}
});
ImageButton nextTrack = (ImageButton) view.findViewById(R.id.nextTrack);
nextTrack.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
fragmentOwner.nextTrack();
}
});
ImageButton prevTrack = (ImageButton) view.findViewById(R.id.prevTrack);
prevTrack.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
fragmentOwner.previousTrack();
}
});
bookBar = (SeekBar) view.findViewById(R.id.book_seek_bar);
bookBar.setMax(Constants.Value.NUMBER_OF_SEEK_BAR_ZONES);
bookBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if (fromUser) {
// calculate the seek progress by the max value of the bar
double seekPercentage = (double) progress
* (1.0 / seekBar.getMax());
fragmentOwner.seekToPercentageInBook(seekPercentage);
}
// else do nothing since this was done by code
}
public void onStartTrackingTouch(SeekBar seekBar) {
}// do nothing
public void onStopTrackingTouch(SeekBar seekBar) {
}// do nothing
});
trackBar = (SeekBar) view.findViewById(R.id.track_bar);
trackBar.setMax(Constants.Value.NUMBER_OF_SEEK_BAR_ZONES);
trackBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if (fromUser) {
// calculate the seek progress by the max value of the bar
double seekPercentage = (double) progress
* (1.0 / seekBar.getMax());
fragmentOwner.seekToPercentageInTrack(seekPercentage);
}
// else do nothing since this was done by code
}
public void onStartTrackingTouch(SeekBar seekBar) {
} // do nothing
public void onStopTrackingTouch(SeekBar seekBar) {
} // do nothing
});
trackTitle = (TextView) view.findViewById(R.id.track_title);
bookTitle = (TextView) view.findViewById(R.id.bookTitle);
bookDuration = (TextView) view.findViewById(R.id.book_duration);
trackDuration = (TextView) view.findViewById(R.id.track_duration);
bookElapsedTime = (TextView) view.findViewById(R.id.book_elapsed_time);
trackElapsedTime = (TextView) view
.findViewById(R.id.track_elapsed_time);
trackCounter = (TextView) view.findViewById(R.id.track_counter);
resetComponents();
return view;
}
protected void seekRight(boolean seek) {
fragmentOwner.seekRight(seek);
}
protected void seekLeft(boolean seek) {
fragmentOwner.seekLeft(seek);
}
/*
* Seek bars
*/
/**
* Sets the progress on the book seek bar to the given percentage.
*
* @param percentage
*/
public void updateBookSeekBar(double percentage) {
int progress = (int) ((double) bookBar.getMax() * percentage);
// calls 'onProgressChanged' from code:
bookBar.setProgress(progress);
}
/**
* Sets the progress on the track seek bar to the given percentage.
*
* @param percentage
*/
public void updateTrackSeekBar(double percentage) {
int progress = (int) (trackBar.getMax() * percentage);
// calls 'onProgressChanged' from code:
trackBar.setProgress(progress);
}
/*
* Titles
*/
/**
* Sets the title of the book to the given title.
*
* @param title
*/
public void updateBookTitleLabel(String title) {
if (isBadTitle(title)) {
bookTitle.setText(Constants.Message.NO_BOOK_TITLE);
} else {
bookTitle.setText(title);
}
}
/**
* Sets the title of the track to the given title.
*
* @param title
*/
public void updateTrackTitleLabel(String title) {
if (isBadTitle(title)) {
trackTitle.setText(Constants.Message.NO_TRACK_TITLE);
} else {
trackTitle.setText(title);
}
}
/*
* Elapsed time labels
*/
/**
* Sets the elapsed time label of the book to the given time in
* milliseconds. Formats the time (see {@link #formatTime(int)})
*
* @param ms
*/
public void updateBookElapsedTimeLabel(int ms) {
bookElapsedTime.setText(TextFormatter.formatTimeFromMillis(ms));
}
/**
* Sets the elapsed time label of the track to the given time in
* milliseconds. Formats the time (see {@link #formatTime(int)})
*
* @param ms
*/
public void updateTrackElapsedTimeLabel(int ms) {
trackElapsedTime.setText(TextFormatter.formatTimeFromMillis(ms));
}
/*
* Duration labels
*/
/**
* Sets the duration label of the book to the given time in milliseconds.
* <p>
* Formats the time. (see {@link TextFormatter#formatTimeFromMillis(int)
* formatTimeFromMillis})
*
* @param ms
*/
public void updateBookDurationLabel(int ms) {
bookDuration.setText(TextFormatter.formatTimeFromMillis(ms));
}
/**
* Sets the duration label of the track to the given time in milliseconds.
* <p>
* Formats the time (see {@link TextFormatter#formatTimeFromMillis(int)
* formatTimeFromMillis}).
*
* @param ms
*/
public void updateTrackDurationLabel(int ms) {
trackDuration.setText(TextFormatter.formatTimeFromMillis(ms));
}
/**
* Sets the track counter label to the given track position and limit.
* <p>
* Formats the given input (see
* {@link PlayerFragment#formatTrackCounter(int, int)}).
*
* @param currentTrack
* @param numberOfTracks
*/
public void updateTrackCounterLabel(int currentTrack, int numberOfTracks) {
this.trackCounter.setText(formatTrackCounter(currentTrack,
numberOfTracks));
}
/**
* Checks if a string is null or empty ("").
*
* @param title
* @return
*/
private boolean isBadTitle(String title) {
return title == null || title.equals("")
|| title.length() > Constants.Value.MAX_TITLE_CHARACTER_LENGTH;
}
/**
* Resets the seek bar progresses, titles, elapsed times and durations to
* display their respective default texts.
*/
public void resetComponents() {
// reset progress of seek bars
this.bookBar.setProgress(0);
this.trackBar.setProgress(0);
// reset title texts
this.bookTitle.setText(Constants.Message.NO_BOOK_TITLE);
this.trackTitle.setText(Constants.Message.NO_TRACK_TITLE);
// reset elapsed time texts
this.trackElapsedTime.setText(Constants.Message.NO_TRACK_ELAPSED_TIME);
this.bookElapsedTime.setText(Constants.Message.NO_BOOK_ELAPSED_TIME);
// reset duration texts
this.trackDuration.setText(Constants.Message.NO_TRACK_DURATION);
this.bookDuration.setText(Constants.Message.NO_BOOK_DURATION);
// reset the track counter
this.trackCounter.setText(Constants.Message.NO_TRACKS_FOUND);
// reset play/pause button to stopped
setPlaybackStatus(PlaybackStatus.STOPPED);
}
/**
* Sets the play/pause status.
*
* @param status
* The status to change to.
*/
public void setPlaybackStatus(int status) {
playPause.setBackgroundResource(0);
switch (status) {
case PlaybackStatus.PLAYING:
playPause.setBackgroundResource(R.drawable.pb_pause_default);
return;
case PlaybackStatus.PAUSED:
playPause.setBackgroundResource(R.drawable.pb_play_default);
return;
case PlaybackStatus.STOPPED:
default:
playPause
.setBackgroundResource(R.drawable.pb_play_pause_disabled_default);
}
}
/**
* Formats a given position and limit.
*
* @param currentTrack
* Current position.
* @param numberOfTracks
* Limit.
* @return Formatted text. If a position is between 0 and the limit the
* current position will be displayed.
*/
private String formatTrackCounter(int currentTrack, int numberOfTracks) {
return TextFormatter.formatCounter(currentTrack, numberOfTracks);
}
public void updateTagTimes(int[] tagTimes) {
// Tags not implemented in GUI.
}
}