Java tutorial
/* * Copyright (C) 2013 www.yaacc.de * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package de.yaacc.player; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.util.Log; import android.widget.Toast; import javax.xml.datatype.Duration; import de.yaacc.R; import de.yaacc.upnp.SynchronizationInfo; import de.yaacc.upnp.UpnpClient; /** * @author Tobias Schoene (openbit) */ public abstract class AbstractPlayer implements Player { public static final String PROPERTY_ITEM = "item"; private List<PlayableItem> items = new ArrayList<PlayableItem>(); private int previousIndex = 0; private int currentIndex = 0; private Timer playerTimer; private Timer execTimer; private boolean isPlaying = false; private boolean isProcessingCommand = false; private UpnpClient upnpClient; private String name; private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private SynchronizationInfo syncInfo; private boolean paused; private Object loadedItem = null; private int currentLoadedIndex = -1; /** * @param upnpClient */ public AbstractPlayer(UpnpClient upnpClient) { super(); this.upnpClient = upnpClient; } /** * @return the context */ public Context getContext() { return upnpClient.getContext(); } /** * @return the upnpClient */ public UpnpClient getUpnpClient() { return upnpClient; } /* * (non-Javadoc) * * @see de.yaacc.player.Player#next() */ @Override public void next() { if (isProcessingCommand()) { return; } setProcessingCommand(true); paused = false; previousIndex = currentIndex; cancelTimer(); currentIndex++; if (currentIndex > items.size() - 1) { currentIndex = 0; SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); boolean replay = preferences.getBoolean(getContext().getString(R.string.settings_replay_playlist_chkbx), true); if (!replay) { stop(); return; } } Context context = getUpnpClient().getContext(); if (context instanceof Activity) { ((Activity) context).runOnUiThread(new Runnable() { public void run() { Toast toast = Toast.makeText(getContext(), getContext().getResources().getString(R.string.next) + getPositionString(), Toast.LENGTH_SHORT); toast.show(); } }); } setProcessingCommand(false); play(); } // /* * (non-Javadoc) * * @see de.yaacc.player.Player#previous() */ @Override public void previous() { if (isProcessingCommand()) { return; } setProcessingCommand(true); paused = false; previousIndex = currentIndex; cancelTimer(); currentIndex--; if (currentIndex < 0) { if (items.size() > 0) { currentIndex = items.size() - 1; } else { currentIndex = 0; } } Context context = getUpnpClient().getContext(); if (context instanceof Activity) { ((Activity) context).runOnUiThread(new Runnable() { public void run() { Toast toast = Toast.makeText(getContext(), getContext().getResources().getString(R.string.previous) + getPositionString(), Toast.LENGTH_SHORT); toast.show(); } }); } setProcessingCommand(false); play(); } /* * (non-Javadoc) * * @see de.yaacc.player.Player#pause() */ @Override public void pause() { if (isProcessingCommand()) return; setProcessingCommand(true); executeCommand(new TimerTask() { @Override public void run() { cancelTimer(); Context context = getUpnpClient().getContext(); if (context instanceof Activity) { ((Activity) context).runOnUiThread(new Runnable() { public void run() { Toast toast = Toast.makeText(getContext(), getContext().getResources().getString(R.string.pause) + getPositionString(), Toast.LENGTH_SHORT); toast.show(); } }); } isPlaying = false; paused = true; doPause(); setProcessingCommand(false); } }, getExecutionTime()); } /* * (non-Javadoc) * * @see de.yaacc.player.Player#start() */ @Override public void play() { if (isProcessingCommand()) return; setProcessingCommand(true); int possibleNextIndex = currentIndex; if (possibleNextIndex >= 0 && possibleNextIndex < items.size()) { loadItem(possibleNextIndex); } executeCommand(new TimerTask() { @Override public void run() { if (currentIndex < items.size()) { Context context = getUpnpClient().getContext(); if (context instanceof Activity) { ((Activity) context).runOnUiThread(new Runnable() { public void run() { Toast toast = Toast.makeText(getContext(), getContext().getResources().getString(R.string.play) + getPositionString(), Toast.LENGTH_SHORT); toast.show(); } }); } isPlaying = true; if (paused) { doResume(); } else { paused = false; loadItem(previousIndex, currentIndex); } setProcessingCommand(false); } } }, getExecutionTime()); } /* * (non-Javadoc) * * @see de.yaacc.player.Player#stop() */ @Override public void stop() { if (isProcessingCommand()) return; setProcessingCommand(true); currentLoadedIndex = -1; loadedItem = null; executeCommand(new TimerTask() { @Override public void run() { cancelTimer(); currentIndex = 0; Context context = getUpnpClient().getContext(); if (context instanceof Activity) { ((Activity) context).runOnUiThread(new Runnable() { public void run() { Toast toast = Toast.makeText(getContext(), getContext().getResources().getString(R.string.stop) + getPositionString(), Toast.LENGTH_SHORT); toast.show(); } }); } if (items.size() > 0) { stopItem(items.get(currentIndex)); } isPlaying = false; paused = false; setProcessingCommand(false); } }, getExecutionTime()); } /* * (non-Javadoc) * * @see de.yaacc.player.Player#setItems(de.yaacc.player.PlayableItem[]) */ @Override public void setItems(PlayableItem... playableItems) { List<PlayableItem> itemsList = Arrays.asList(playableItems); if (isShufflePlay()) { Collections.shuffle(itemsList); } items.addAll(itemsList); showNotification(); } /** * is shuffle play enabled. * * @return true, if shuffle play is enabled */ protected boolean isShufflePlay() { return false; } /* * (non-Javadoc) * * @see de.yaacc.player.Player#clear() */ @Override public void clear() { items.clear(); } protected void cancelTimer() { if (playerTimer != null) { playerTimer.cancel(); } } public boolean isPlaying() { return isPlaying; } public void setPlaying(boolean isPlaying) { this.isPlaying = isPlaying; } public int getCurrentIndex() { return currentIndex; } public void setCurrentIndex(int currentIndex) { this.currentIndex = currentIndex; } public List<PlayableItem> getItems() { return items; } /** * returns the current item position in the playlist * * @return the position string */ public String getPositionString() { return " (" + (currentIndex + 1) + "/" + items.size() + ")"; } /** * returns the title of the current item * * @return the title */ public String getCurrentItemTitle() { String result = ""; if (currentIndex < items.size()) { result = items.get(currentIndex).getTitle(); } return result; } /** * returns the title of the next current item * * @return the title */ public String getNextItemTitle() { String result = ""; if (currentIndex + 1 < items.size()) { result = items.get(currentIndex + 1).getTitle(); } return result; } protected Object loadItem(int toLoadIndex) { if (toLoadIndex == currentLoadedIndex && loadedItem != null) { Log.d(getClass().getName(), "returning already loaded item"); return loadedItem; } if (toLoadIndex >= 0 && toLoadIndex <= items.size()) { Log.d(getClass().getName(), "loaded item"); currentLoadedIndex = toLoadIndex; loadedItem = loadItem(items.get(toLoadIndex)); return loadedItem; } return null; } protected void loadItem(int previousIndex, int nextIndex) { if (items == null || items.size() == 0) return; PlayableItem playableItem = items.get(nextIndex); Object loadedItem = loadItem(nextIndex); firePropertyChange(PROPERTY_ITEM, items.get(previousIndex), items.get(nextIndex)); startItem(playableItem, loadedItem); if (isPlaying() && items.size() > 1) { if (playableItem.getDuration() > -1) { //Only start timer if automatic track change is active startTimer(playableItem.getDuration() + getSilenceDuration()); } } } protected void doPause() { //default do nothing } protected void doResume() { //default replay current item paused = false; loadItem(currentIndex, currentIndex); } /** * returns the duration between two items * * @return duration in millis */ protected long getSilenceDuration() { return upnpClient.getSilenceDuration(); } /** * Start a timer for the next item change * * @param duration in millis */ public void startTimer(final long duration) { Log.d(getClass().getName(), "Start timer duration: " + duration); if (playerTimer != null) { cancelTimer(); } playerTimer = new Timer(); playerTimer.schedule(new TimerTask() { @Override public void run() { Log.d(getClass().getName(), "TimerEvent" + this); AbstractPlayer.this.next(); } }, duration); } /* * (non-Javadoc) * * @see de.yaacc.player.Player#setName(java.lang.String) */ @Override public void setName(String name) { this.name = name; } /* * (non-Javadoc) * * @see de.yaacc.player.Player#getName() */ @Override public String getName() { return name; } public boolean isProcessingCommand() { return isProcessingCommand; } public void setProcessingCommand(boolean isProcessingCommand) { this.isProcessingCommand = isProcessingCommand; } /* * (non-Javadoc) * * @see de.yaacc.player.Player#exit() */ @Override public void exit() { if (isPlaying()) { stop(); } PlayerFactory.shutdown(this); } /** * Displays the notification. */ private void showNotification() { NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getContext()).setOngoing(false) .setSmallIcon(R.drawable.ic_launcher).setContentTitle("Yaacc player") .setContentText(getName() == null ? "" : getName()); PendingIntent contentIntent = getNotificationIntent(); if (contentIntent != null) { mBuilder.setContentIntent(contentIntent); } NotificationManager mNotificationManager = (NotificationManager) getContext() .getSystemService(Context.NOTIFICATION_SERVICE); // mId allows you to update the notification later on. mNotificationManager.notify(getNotificationId(), mBuilder.build()); } /** * Cancels the notification. */ private void cancleNotification() { NotificationManager mNotificationManager = (NotificationManager) getContext() .getSystemService(Context.NOTIFICATION_SERVICE); // mId allows you to update the notification later on. Log.d(getClass().getName(), "Cancle Notification with ID: " + getNotificationId()); mNotificationManager.cancel(getNotificationId()); } /** * Returns the notification id of the player * * @return */ protected int getNotificationId() { return 0; } /** * Returns the intent which is to be started by pushing the notification * entry * * @return the peneding intent */ public PendingIntent getNotificationIntent() { return null; } protected abstract void stopItem(PlayableItem playableItem); protected abstract Object loadItem(PlayableItem playableItem); protected abstract void startItem(PlayableItem playableItem, Object loadedItem); /* * (non-Javadoc) * * @see de.yaacc.player.Player#onDestroy() */ @Override public void onDestroy() { stop(); cancleNotification(); items.clear(); } /* * (non-Javadoc) * * @see de.yaacc.player.Player#getId() */ @Override public int getId() { return getNotificationId(); } public void addPropertyChangeListener(PropertyChangeListener listener) { this.pcs.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { this.pcs.removePropertyChangeListener(listener); } protected void firePropertyChange(String property, Object oldValue, Object newValue) { this.pcs.firePropertyChange(property, oldValue, newValue); } @Override public String getDuration() { return ""; } @Override public String getElapsedTime() { return ""; } @Override public URI getAlbumArt() { return null; } @Override public void setSyncInfo(SynchronizationInfo syncInfo) { if (syncInfo == null) { syncInfo = new SynchronizationInfo(); } this.syncInfo = syncInfo; } @Override public SynchronizationInfo getSyncInfo() { return syncInfo; } protected Date getExecutionTime() { Calendar execTime = Calendar.getInstance(Locale.getDefault()); execTime.set(Calendar.HOUR_OF_DAY, getSyncInfo().getReferencedPresentationTimeOffset().getHour()); execTime.set(Calendar.MINUTE, getSyncInfo().getReferencedPresentationTimeOffset().getMinute()); execTime.set(Calendar.SECOND, getSyncInfo().getReferencedPresentationTimeOffset().getSecond()); execTime.set(Calendar.MILLISECOND, getSyncInfo().getReferencedPresentationTimeOffset().getMillis()); execTime.add(Calendar.HOUR, getSyncInfo().getOffset().getHour()); execTime.add(Calendar.MINUTE, getSyncInfo().getOffset().getMinute()); execTime.add(Calendar.SECOND, getSyncInfo().getOffset().getSecond()); execTime.add(Calendar.MILLISECOND, getSyncInfo().getOffset().getMillis()); Log.d(getClass().getName(), "ReferencedRepresentationTimeOffset: " + getSyncInfo().getReferencedPresentationTimeOffset()); Log.d(getClass().getName(), "current time: " + new Date().toString() + " get execution time: " + execTime.getTime().toString()); if (execTime.getTime().getTime() <= System.currentTimeMillis()) { Log.d(getClass().getName(), "ExecutionTime is in past!! We will start immediately"); execTime = Calendar.getInstance(Locale.getDefault()); execTime.add(Calendar.MILLISECOND, 100); } return execTime.getTime(); } protected void executeCommand(TimerTask command, Date executionTime) { if (execTimer != null) { execTimer.cancel(); } Timer execTimer = new Timer(); execTimer.schedule(command, executionTime); } public boolean getMute() { return upnpClient.isMute(); } public void setMute(boolean mute) { upnpClient.setMute(mute); } public void setVolume(int volume) { upnpClient.setVolume(volume); } public int getVolume() { return upnpClient.getVolume(); } public int getIconResourceId() { return R.drawable.ic_launcher; } public String getDeviceId() { return UpnpClient.LOCAL_UID; } public abstract void seekTo(long millisecondsFromStart); }