Java tutorial
/** * 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 Kkel, Fredrik hs. **/ package edu.chalmers.dat255.audiobookplayer.view; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.List; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.view.ViewPager; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.Toast; 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.ctrl.BookshelfController; import edu.chalmers.dat255.audiobookplayer.ctrl.PlayerController; import edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents; import edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfGUIEvents; import edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents; import edu.chalmers.dat255.audiobookplayer.model.Book; import edu.chalmers.dat255.audiobookplayer.model.Bookshelf; import edu.chalmers.dat255.audiobookplayer.util.BookCreator; import edu.chalmers.dat255.audiobookplayer.util.BookshelfHandler; /** * The main activity of the application. * * @author Aki Kkel, Marcus Parkkinen * @version 0.6 * */ public class MainActivity extends FragmentActivity implements IPlayerEvents, IBookshelfEvents, IBookshelfGUIEvents, PropertyChangeListener { private static final String TAG = "MainActivity"; private static final String USERNAME = "Default"; private static final int PLAYER = 0; private static final int BOOKSHELF = 1; // ViewPager private ViewPager pager; // Fragments private PlayerFragment playerFragment = new PlayerFragment(); private BookshelfFragment bookshelfFragment = new BookshelfFragment(); // Controllers private BookshelfController bookshelfController; private PlayerController playerController; /* * (non-Javadoc) * * @see android.support.v4.app.FragmentActivity#onCreate(android.os.Bundle) */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initPager(); // Load a bookshelf specified by the username Bookshelf bs = BookshelfHandler.loadBookshelf(this, USERNAME); // Create controllers with the bookshelf reference playerController = new PlayerController(bs); bookshelfController = new BookshelfController(bs); BookCreator.getInstance().setBookshelf(bs); // Provide a reference to the bookshelf as an argument in the bundle Bundle bsReference = new Bundle(); bsReference.putSerializable(Constants.Reference.BOOKSHELF, bs); bookshelfFragment.setArguments(bsReference); } /** * Initializes the ViewPager and its adapter. */ private void initPager() { // create a list of our fragments List<Fragment> fragments = new ArrayList<Fragment>(); fragments.add(playerFragment); fragments.add(bookshelfFragment); // create the adapter // NOTE: it is a local variable since it will not be used. ViewPagerAdapter adapter = new ViewPagerAdapter(super.getSupportFragmentManager(), fragments); // create the view pager and set the adapter pager = (ViewPager) super.findViewById(R.id.twopanelviewpager); pager.setAdapter(adapter); // default selected screen pager.setCurrentItem(BOOKSHELF); } /* * (non-Javadoc) * * @see android.support.v4.app.FragmentActivity#onStop() */ @Override protected void onStop() { Log.d(TAG, "onStop()"); super.onStop(); // Disable updates bookshelfController.removeListeners(); } /* * (non-Javadoc) * * @see android.support.v4.app.FragmentActivity#onResume() */ @Override protected void onResume() { Log.d(TAG, "onResume()"); super.onResume(); /* * if the application is stopped, it will go through onStart() and then * onResume(), so just start the updates again here. */ bookshelfController.addPropertyChangeListener(this); /* * This method is run every time the application is created, starts or * resumes. The updates should only start when returning from a paused * or stopped state. */ } /* * (non-Javadoc) * * @see android.support.v4.app.FragmentActivity#onStart() */ @Override protected void onStart() { Log.d(TAG, "onStart()"); super.onStart(); } /* * (non-Javadoc) * * @see android.support.v4.app.FragmentActivity#onDestroy() */ @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy()"); // Stop the audio player playerController.stop(); /* * Whenever the application is about to quit, save a bookmark. */ save(); } /* * (non-Javadoc) * * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) */ @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater menuInflater = getMenuInflater(); menuInflater.inflate(R.menu.main_menu, menu); return true; } /** * Saves a bookmark (the bookshelf). */ private boolean save() { return bookshelfController.saveBookshelf(this, USERNAME); } /* * Handle the menu item selection. Every item has a unique id. * * (non-Javadoc) * * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem) */ @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); switch (id) { case R.id.menu_save: // save a bookmark and show whether it was successful if (save()) { Toast.makeText(this, "Saved", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "Saving failed", Toast.LENGTH_SHORT); } return true; default: Toast.makeText(this, "Unknown", Toast.LENGTH_SHORT).show(); return super.onOptionsItemSelected(item); } } /* IPlayerEvents */ /* * The methods below relay user-initiated events from the fragments to * relevant controller methods. */ /* * (non-Javadoc) * * @see * edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents#previousTrack * () */ public void previousTrack() { playerController.previousTrack(); } /* * (non-Javadoc) * * @see * edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents#resume() */ public void resume() { playerController.resume(); } /* * (non-Javadoc) * * @see edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents#pause() */ public void pause() { playerController.pause(); } /* * (non-Javadoc) * * @see * edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents#nextTrack() */ public void nextTrack() { playerController.nextTrack(); } /* * (non-Javadoc) * * @see * edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents#seekLeft * (boolean) */ public void seekLeft(boolean seek) { playerController.seekLeft(seek); } /* * (non-Javadoc) * * @see * edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents#seekRight * (boolean) */ public void seekRight(boolean seek) { playerController.seekRight(seek); } /* * (non-Javadoc) * * @see edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents# * seekToPercentageInBook(double) */ public void seekToPercentageInBook(double percentage) { playerController.seekToPercentageInBook(percentage); } /* * (non-Javadoc) * * @see edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents# * seekToPercentageInTrack(double) */ public void seekToPercentageInTrack(double percentage) { playerController.seekToPercentageInTrack(percentage); } /* * (non-Javadoc) * * @see * edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents#isPlaying() */ public boolean isPlaying() { return playerController.isPlaying(); } /* * (non-Javadoc) * * @see * edu.chalmers.dat255.audiobookplayer.interfaces.IPlayerEvents#isStarted() */ public boolean isStarted() { return playerController.isStarted(); } /* End IPlayerEvents */ /* BookshelfUIListener */ public void setSelectedBook(int index) { // set the selected book to the new index bookshelfController.setSelectedBook(index); } /* * (non-Javadoc) * * @see edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfGUIEvents# * addBookButtonPressed() */ public void addBookButtonPressed() { Intent intent = new Intent(MainActivity.this, BrowserActivity.class); startActivity(intent); } /* End BookshelfUIListener */ /* * (non-Javadoc) * * @see android.support.v4.app.FragmentActivity#onBackPressed() */ @Override public void onBackPressed() { super.onBackPressed(); Log.i(TAG, "Back pressed in Main Activity"); Intent intent = new Intent(Intent.ACTION_MAIN); // move to the home screen when pressing back intent.addCategory(Intent.CATEGORY_HOME); // start a new task intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } /** * Whenever the model component changes, an event is received here. The new * value property of the event contains a reference to a copy of the model. * * @param event * Event object that contains information about the change. */ public void propertyChange(PropertyChangeEvent event) { if (event.getNewValue() instanceof Bookshelf) { // Get the model copy from the update Bookshelf bs = (Bookshelf) event.getNewValue(); // Update the fragments updateFragments(event.getPropertyName(), bs); } } /** * Handles the updates of the fragments given a model to represent * graphically. * * @param eventName * The property name of the fired event. * @param bs * Model copy. * @precondition The bookshelf instance must not be null. */ private void updateFragments(String eventName, Bookshelf bs) { /* * Check which event was fired, and do relevant updates in the * fragments. */ if (eventName.equals(Constants.Event.BOOK_LIST_CHANGED)) { // update the bookshelf GUI bookshelfFragment.bookshelfUpdated(bs); } else if (eventName.equals(Constants.Event.BOOK_SELECTED)) { if (bs.getSelectedBookIndex() != Constants.Value.NO_BOOK_SELECTED) { Book b = bs.getSelectedBook(); // reset the player controls to standard values playerFragment.resetComponents(); // update the player GUI components updatePlayerGUI(b); // make sure the player GUI knows it is playing playerFragment.setPlaybackStatus(PlaybackStatus.PLAYING); // show the player UI pager.setCurrentItem(PLAYER); // if a track is selected, start it if (bs.getSelectedTrackIndex() != Constants.Value.NO_TRACK_SELECTED) { // start at the saved time playerController.setStartPosition(b.getSelectedTrackElapsedTime()); // select the stored track to start playing in bookshelfController.setSelectedTrack(bs.getSelectedBookIndex(), bs.getSelectedTrackIndex()); } else { // if there is no track selected, start from the beginning. playerController.setStartPosition(0); bookshelfController.setSelectedTrack(bs.getSelectedBookIndex(), 0); } } } else if (eventName.equals(Constants.Event.ELAPSED_TIME_CHANGED)) { Book b = bs.getSelectedBook(); // Bookshelf updateSelectedBookElapsedTime(b); // Player // recalculate the track seekbar updateTrackSeekbar(b); // recalculate the book seekbar updateBookSeekbar(b); // update time labels updateElapsedTimeLabels(b); } else if (eventName.equals(Constants.Event.TRACK_LIST_CHANGED)) { Book b = bs.getSelectedBook(); // Update the bookshelf GUI. bookshelfFragment.bookshelfUpdated(bs); // Player // update book duration label updateBookDurationLabel(b); // show the correct number of tracks updateTrackCounterLabel(b); // Update book duration label updateBookDurationLabel(b); } else if (eventName.equals(Constants.Event.TRACK_INDEX_CHANGED)) { pager.setCurrentItem(PLAYER); if (bs.getSelectedTrackIndex() == Constants.Value.NO_TRACK_SELECTED) { /* * Do not play audio. */ // reset the controls to 'stopped' playerFragment.resetComponents(); // stop the audio player playerController.stop(); } else { /* * Play audio. */ // restart the player playerController.start(); // set the status playerFragment.setPlaybackStatus(PlaybackStatus.PLAYING); } Book b = bs.getSelectedBook(); // Player // update track title label updateTrackTitleLabel(b); // update track book duration label updateTrackDurationLabel(b); // show the correct number of tracks, illegal track index or not updateTrackCounterLabel(b); } else if (eventName.equals(Constants.Event.BOOK_TITLE_CHANGED)) { Book b = bs.getSelectedBook(); // Bookshelf // Player // update the book title label updateBookTitleLabel(b); } else if (eventName.equals(Constants.Event.BOOKSHELF_UPDATED)) { bookshelfFragment.bookshelfUpdated(bs); } /* * Eventually tag updates would have been handled here as well, calling * the private method updateTags. Tags are, however, not implemented in * the GUI so they will not be written here, either. */ } /** * Updates the book title/duration, track title/duration and track counter * (i.e. no changes to seek bars). * <p> * Does nothing if the given book is null. * * @param b * Book given with the data to be represented by the Player UI. */ private void updatePlayerGUI(Book b) { if (b != null) { // show the title of the book updateBookTitleLabel(b); // display its duration updateBookDurationLabel(b); // show the title of the track updateTrackTitleLabel(b); // and display its duration updateTrackDurationLabel(b); // update the track counter updateTrackCounterLabel(b); } } /** * Updates the tags in the player UI (unimplemented in GUI). * * @param selectedBook */ @SuppressWarnings("unused") private void updateTags(final Book selectedBook) { playerFragment.getActivity().runOnUiThread(new Runnable() { public void run() { playerFragment.updateTagTimes(selectedBook.getTagTimes()); } }); } /** * Updates the count of the tracks in the player UI. * * @param b */ private void updateTrackCounterLabel(final Book b) { playerFragment.getActivity().runOnUiThread(new Runnable() { public void run() { int currentTrack = b.getSelectedTrackIndex(); int numberOfTracks = b.getNumberOfTracks(); playerFragment.updateTrackCounterLabel(currentTrack, numberOfTracks); } }); } /* * Titles */ /** * UI mutator method that updates the book title label in the player * fragment. * * @param b * Book that specifies the change */ private void updateBookTitleLabel(final Book b) { playerFragment.getActivity().runOnUiThread(new Runnable() { public void run() { playerFragment.updateBookTitleLabel(b.getSelectedBookTitle()); } }); } /** * UI mutator method that updates the track title in the player fragment. * * @param b * Book that specifies the change */ private void updateTrackTitleLabel(final Book b) { playerFragment.getActivity().runOnUiThread(new Runnable() { public void run() { if (b.getSelectedTrackIndex() != -1) { playerFragment.updateTrackTitleLabel(b.getTrackTitle()); } } }); } /* * Duration times */ /** * UI mutator method that updates the book duration label in the player * fragment. * * @param b * Book that specifies the change */ private void updateBookDurationLabel(final Book b) { playerFragment.getActivity().runOnUiThread(new Runnable() { public void run() { playerFragment.updateBookDurationLabel(b.getDuration()); } }); } /** * UI mutator method that updates the track duration label in the player * fragment. * * @param b * Book that specifies the change */ private void updateTrackDurationLabel(final Book b) { playerFragment.getActivity().runOnUiThread(new Runnable() { public void run() { if (b.getSelectedTrackIndex() != -1) { int trackDur = 0; try { trackDur = b.getSelectedTrackDuration(); } catch (IllegalArgumentException e) { Log.e(TAG, "No track selected when trying to update " + "track duration label. " + "Setting duration to 0."); } playerFragment.updateTrackDurationLabel(trackDur); } } }); } /* * Elapsed times */ /** * UI mutator method that updates the track duration label in the player * fragment. * * @param b * Book that specifies the change */ private void updateElapsedTimeLabels(final Book b) { if (playerFragment.getActivity() != null) { playerFragment.getActivity().runOnUiThread(new Runnable() { public void run() { if (b.getSelectedTrackIndex() != -1) { playerFragment.updateTrackElapsedTimeLabel(b.getSelectedTrackElapsedTime()); playerFragment.updateBookElapsedTimeLabel(b.getBookElapsedTime()); } } }); } } /** * UI mutator method that updates the book position of the selected book in * bookshelf fragment * * @param b * The selected book. */ private void updateSelectedBookElapsedTime(final Book b) { if (b.getSelectedTrackIndex() != -1 && bookshelfFragment.getActivity() != null) { bookshelfFragment.getActivity().runOnUiThread(new Runnable() { public void run() { // update bookshelfFragment.selectedBookElapsedTimeUpdated(b.getBookElapsedTime()); } }); } } /** * UI mutator method that updates the track seekbar in the player fragment. * * @param b * Book that specifies the change */ private void updateTrackSeekbar(Book b) { if (b.getSelectedTrackIndex() != -1) { // elapsed time int trackElapsedTime = b.getSelectedTrackElapsedTime(); // total duration int trackDuration = b.getSelectedTrackDuration(); double progress = getProgress(trackElapsedTime, trackDuration); playerFragment.updateTrackSeekBar(progress); } } /** * UI mutator method that updates the book seekbar in the player fragment. * * @param b * Book that specifies the change */ private void updateBookSeekbar(Book b) { // elapsed time int bookElapsedTime = b.getBookElapsedTime(); // total duration int bookDuration = b.getDuration(); double progress = getProgress(bookElapsedTime, bookDuration); playerFragment.updateBookSeekBar(progress); } /** * Private utility method that calculates the progress of a book or track. * * @param int Elapsed time of the book or track in milliseconds. * @param int Duration of the book or track in milliseconds. * @return The ratio between the elapsed time and the duration. */ private double getProgress(int elapsedTime, int duration) { return ((double) elapsedTime) / duration; } /* * (non-Javadoc) * * @see edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents# * setSelectedTrack(int, int) */ public void setSelectedTrack(int bookIndex, int trackIndex) { bookshelfController.setSelectedTrack(bookIndex, trackIndex); // restart the player playerController.start(); } /* * (non-Javadoc) * * @see * edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents#removeBook * (int) */ public void removeBook(int bookIndex) { bookshelfController.removeBook(bookIndex); } /* * (non-Javadoc) * * @see * edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents#removeTrack * (int, int) */ public void removeTrack(int bookIndex, int trackIndex) { bookshelfController.removeTrack(bookIndex, trackIndex); } /* * (non-Javadoc) * * @see edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents# * setBookTitleAt(int, java.lang.String) */ public void setBookTitleAt(int bookIndex, String newTitle) { bookshelfController.setBookTitleAt(bookIndex, newTitle); } /* * (non-Javadoc) * * @see * edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents#removeTrack * (int) */ public void removeTrack(int trackIndex) { bookshelfController.removeTrack(trackIndex); } /* * (non-Javadoc) * * @see * edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents#moveTrack * (int, int, int) */ public void moveTrack(int bookIndex, int trackIndex, int offset) { bookshelfController.moveTrack(bookIndex, trackIndex, offset); } }