aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2014-02-17 16:13:14 -0800
committerScott Jackson <daneren2005@gmail.com>2014-02-17 16:13:14 -0800
commit0fd52d5642d0d0ccf59c1a94ed6e4004024d4d30 (patch)
treed70a788c7931e33e4a0f9e6fa4ba3d2bf11af75e /src
parent9ac411cec462c068622f4c40503686ba1984ca39 (diff)
downloaddsub-0fd52d5642d0d0ccf59c1a94ed6e4004024d4d30.tar.gz
dsub-0fd52d5642d0d0ccf59c1a94ed6e4004024d4d30.tar.bz2
dsub-0fd52d5642d0d0ccf59c1a94ed6e4004024d4d30.zip
There is no reason for DownloadService to exist as a interface, just causes pointless extra work
Diffstat (limited to 'src')
-rw-r--r--src/github/daneren2005/dsub/activity/SettingsActivity.java7
-rw-r--r--src/github/daneren2005/dsub/activity/SubsonicActivity.java11
-rw-r--r--src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java3
-rw-r--r--src/github/daneren2005/dsub/fragments/EqualizerFragment.java7
-rw-r--r--src/github/daneren2005/dsub/fragments/SubsonicFragment.java3
-rw-r--r--src/github/daneren2005/dsub/provider/DSubWidgetProvider.java7
-rw-r--r--src/github/daneren2005/dsub/receiver/A2dpIntentReceiver.java3
-rw-r--r--src/github/daneren2005/dsub/receiver/BluetoothIntentReceiver.java5
-rw-r--r--src/github/daneren2005/dsub/receiver/MediaButtonIntentReceiver.java5
-rw-r--r--src/github/daneren2005/dsub/service/ChromeCastController.java3
-rw-r--r--src/github/daneren2005/dsub/service/DownloadFile.java6
-rw-r--r--src/github/daneren2005/dsub/service/DownloadService.java1722
-rw-r--r--src/github/daneren2005/dsub/service/DownloadServiceImpl.java1780
-rw-r--r--src/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java34
-rw-r--r--src/github/daneren2005/dsub/service/JukeboxController.java4
-rw-r--r--src/github/daneren2005/dsub/service/OfflineMusicService.java2
-rw-r--r--src/github/daneren2005/dsub/service/RemoteController.java2
-rw-r--r--src/github/daneren2005/dsub/util/MediaRouteManager.java7
-rw-r--r--src/github/daneren2005/dsub/util/Util.java28
-rw-r--r--src/github/daneren2005/dsub/util/compat/CastCompat.java4
-rw-r--r--src/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java8
-rw-r--r--src/github/daneren2005/dsub/view/SongView.java6
-rw-r--r--src/github/daneren2005/dsub/view/VisualizerView.java6
23 files changed, 1714 insertions, 1949 deletions
diff --git a/src/github/daneren2005/dsub/activity/SettingsActivity.java b/src/github/daneren2005/dsub/activity/SettingsActivity.java
index d496a201..87719e5b 100644
--- a/src/github/daneren2005/dsub/activity/SettingsActivity.java
+++ b/src/github/daneren2005/dsub/activity/SettingsActivity.java
@@ -35,15 +35,12 @@ import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
-import android.provider.SearchRecentSuggestions;
import android.text.InputType;
import android.util.Log;
import android.view.MenuItem;
import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.provider.DSubSearchProvider;
import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.util.Constants;
@@ -257,7 +254,7 @@ public class SettingsActivity extends PreferenceActivity implements SharedPrefer
setCacheLocation(sharedPreferences.getString(key, ""));
}
else if (Constants.PREFERENCES_KEY_SLEEP_TIMER_DURATION.equals(key)){
- DownloadService downloadService = DownloadServiceImpl.getInstance();
+ DownloadService downloadService = DownloadService.getInstance();
downloadService.setSleepTimerDuration(Integer.parseInt(sharedPreferences.getString(key, "60")));
}
else if(Constants.PREFERENCES_KEY_SYNC_MOST_RECENT.equals(key)) {
@@ -521,7 +518,7 @@ public class SettingsActivity extends PreferenceActivity implements SharedPrefer
}
// Clear download queue.
- DownloadService downloadService = DownloadServiceImpl.getInstance();
+ DownloadService downloadService = DownloadService.getInstance();
downloadService.clear();
}
}
diff --git a/src/github/daneren2005/dsub/activity/SubsonicActivity.java b/src/github/daneren2005/dsub/activity/SubsonicActivity.java
index d15f3d7b..95bed957 100644
--- a/src/github/daneren2005/dsub/activity/SubsonicActivity.java
+++ b/src/github/daneren2005/dsub/activity/SubsonicActivity.java
@@ -53,7 +53,6 @@ import android.widget.Spinner;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.fragments.SubsonicFragment;
import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.ImageLoader;
import github.daneren2005.dsub.util.Util;
@@ -97,7 +96,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
applyTheme();
super.onCreate(bundle);
applyFullscreen();
- startService(new Intent(this, DownloadServiceImpl.class));
+ startService(new Intent(this, DownloadService.class));
setVolumeControlStream(AudioManager.STREAM_MUSIC);
View actionbar = getLayoutInflater().inflate(R.layout.actionbar_spinner, null);
@@ -461,7 +460,7 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
Util.startActivityWithoutTransition(this, intent);
} else {
finished = true;
- this.stopService(new Intent(this, DownloadServiceImpl.class));
+ this.stopService(new Intent(this, DownloadService.class));
this.finish();
}
}
@@ -675,15 +674,15 @@ public class SubsonicActivity extends ActionBarActivity implements OnItemSelecte
// If service is not available, request it to start and wait for it.
for (int i = 0; i < 5; i++) {
- DownloadService downloadService = DownloadServiceImpl.getInstance();
+ DownloadService downloadService = DownloadService.getInstance();
if (downloadService != null) {
return downloadService;
}
Log.w(TAG, "DownloadService not running. Attempting to start it.");
- startService(new Intent(this, DownloadServiceImpl.class));
+ startService(new Intent(this, DownloadService.class));
Util.sleepQuietly(50L);
}
- return DownloadServiceImpl.getInstance();
+ return DownloadService.getInstance();
}
public static String getThemeName() {
diff --git a/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java b/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
index d22bc3ab..9c8bcaa3 100644
--- a/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
+++ b/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
@@ -54,7 +54,6 @@ import github.daneren2005.dsub.fragments.SelectShareFragment;
import github.daneren2005.dsub.fragments.SubsonicFragment;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
import github.daneren2005.dsub.updates.Updater;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.FileUtil;
@@ -80,7 +79,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_EXIT)) {
- stopService(new Intent(this, DownloadServiceImpl.class));
+ stopService(new Intent(this, DownloadService.class));
finish();
} else if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD)) {
DownloadService service = getDownloadService();
diff --git a/src/github/daneren2005/dsub/fragments/EqualizerFragment.java b/src/github/daneren2005/dsub/fragments/EqualizerFragment.java
index bdc41579..71067d7c 100644
--- a/src/github/daneren2005/dsub/fragments/EqualizerFragment.java
+++ b/src/github/daneren2005/dsub/fragments/EqualizerFragment.java
@@ -21,7 +21,6 @@ package github.daneren2005.dsub.fragments;
import android.content.SharedPreferences;
import android.media.audiofx.Equalizer;
import android.os.Bundle;
-import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
@@ -38,7 +37,7 @@ import java.util.Map;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.audiofx.EqualizerController;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
+import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.Util;
@@ -59,7 +58,7 @@ public class EqualizerFragment extends SubsonicFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
rootView = inflater.inflate(R.layout.equalizer, container, false);
- equalizerController = DownloadServiceImpl.getInstance().getEqualizerController();
+ equalizerController = DownloadService.getInstance().getEqualizerController();
equalizer = equalizerController.getEqualizer();
try {
@@ -105,7 +104,7 @@ public class EqualizerFragment extends SubsonicFragment {
@Override
public void onResume() {
super.onResume();
- equalizerController = DownloadServiceImpl.getInstance().getEqualizerController();
+ equalizerController = DownloadService.getInstance().getEqualizerController();
equalizer = equalizerController.getEqualizer();
}
diff --git a/src/github/daneren2005/dsub/fragments/SubsonicFragment.java b/src/github/daneren2005/dsub/fragments/SubsonicFragment.java
index 1669ad1f..6b63e48d 100644
--- a/src/github/daneren2005/dsub/fragments/SubsonicFragment.java
+++ b/src/github/daneren2005/dsub/fragments/SubsonicFragment.java
@@ -53,7 +53,6 @@ import github.daneren2005.dsub.domain.PodcastEpisode;
import github.daneren2005.dsub.domain.Share;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
import github.daneren2005.dsub.service.MusicService;
import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.service.OfflineException;
@@ -413,7 +412,7 @@ public class SubsonicFragment extends Fragment {
intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true);
Util.startActivityWithoutTransition(context, intent);
} else {
- context.stopService(new Intent(context, DownloadServiceImpl.class));
+ context.stopService(new Intent(context, DownloadService.class));
context.finish();
}
}
diff --git a/src/github/daneren2005/dsub/provider/DSubWidgetProvider.java b/src/github/daneren2005/dsub/provider/DSubWidgetProvider.java
index cfa993ec..04c43dbd 100644
--- a/src/github/daneren2005/dsub/provider/DSubWidgetProvider.java
+++ b/src/github/daneren2005/dsub/provider/DSubWidgetProvider.java
@@ -44,7 +44,6 @@ import github.daneren2005.dsub.activity.DownloadActivity;
import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.Util;
@@ -267,19 +266,19 @@ public class DSubWidgetProvider extends AppWidgetProvider {
// Emulate media button clicks.
intent = new Intent("DSub.PLAY_PAUSE");
- intent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
+ intent.setComponent(new ComponentName(context, DownloadService.class));
intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.control_play, pendingIntent);
intent = new Intent("DSub.NEXT"); // Use a unique action name to ensure a different PendingIntent to be created.
- intent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
+ intent.setComponent(new ComponentName(context, DownloadService.class));
intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT));
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.control_next, pendingIntent);
intent = new Intent("DSub.PREVIOUS"); // Use a unique action name to ensure a different PendingIntent to be created.
- intent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
+ intent.setComponent(new ComponentName(context, DownloadService.class));
intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PREVIOUS));
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.control_previous, pendingIntent);
diff --git a/src/github/daneren2005/dsub/receiver/A2dpIntentReceiver.java b/src/github/daneren2005/dsub/receiver/A2dpIntentReceiver.java
index c8c3a1f9..f1837fd7 100644
--- a/src/github/daneren2005/dsub/receiver/A2dpIntentReceiver.java
+++ b/src/github/daneren2005/dsub/receiver/A2dpIntentReceiver.java
@@ -5,7 +5,6 @@ import android.content.Context;
import android.content.Intent;
import android.util.Log;
import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
public class A2dpIntentReceiver extends BroadcastReceiver {
private static final String PLAYSTATUS_RESPONSE = "com.android.music.playstatusresponse";
@@ -15,7 +14,7 @@ public class A2dpIntentReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "GOT INTENT " + intent);
- DownloadService downloadService = DownloadServiceImpl.getInstance();
+ DownloadService downloadService = DownloadService.getInstance();
if (downloadService != null){
diff --git a/src/github/daneren2005/dsub/receiver/BluetoothIntentReceiver.java b/src/github/daneren2005/dsub/receiver/BluetoothIntentReceiver.java
index ab46c784..13de4d86 100644
--- a/src/github/daneren2005/dsub/receiver/BluetoothIntentReceiver.java
+++ b/src/github/daneren2005/dsub/receiver/BluetoothIntentReceiver.java
@@ -24,7 +24,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
+
+import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.Util;
@@ -49,7 +50,7 @@ public class BluetoothIntentReceiver extends BroadcastReceiver {
SharedPreferences prefs = Util.getPreferences(context);
int pausePref = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_PAUSE_DISCONNECT, "0"));
if(pausePref == 0 || pausePref == 2) {
- context.sendBroadcast(new Intent(DownloadServiceImpl.CMD_PAUSE));
+ context.sendBroadcast(new Intent(DownloadService.CMD_PAUSE));
}
}
}
diff --git a/src/github/daneren2005/dsub/receiver/MediaButtonIntentReceiver.java b/src/github/daneren2005/dsub/receiver/MediaButtonIntentReceiver.java
index 9ea04474..89a4a87b 100644
--- a/src/github/daneren2005/dsub/receiver/MediaButtonIntentReceiver.java
+++ b/src/github/daneren2005/dsub/receiver/MediaButtonIntentReceiver.java
@@ -23,7 +23,8 @@ import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.KeyEvent;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
+
+import github.daneren2005.dsub.service.DownloadService;
/**
* @author Sindre Mehus
@@ -37,7 +38,7 @@ public class MediaButtonIntentReceiver extends BroadcastReceiver {
KeyEvent event = (KeyEvent) intent.getExtras().get(Intent.EXTRA_KEY_EVENT);
Log.i(TAG, "Got MEDIA_BUTTON key event: " + event);
- Intent serviceIntent = new Intent(context, DownloadServiceImpl.class);
+ Intent serviceIntent = new Intent(context, DownloadService.class);
serviceIntent.putExtra(Intent.EXTRA_KEY_EVENT, event);
context.startService(serviceIntent);
if (isOrderedBroadcast())
diff --git a/src/github/daneren2005/dsub/service/ChromeCastController.java b/src/github/daneren2005/dsub/service/ChromeCastController.java
index c25805c2..591d23b9 100644
--- a/src/github/daneren2005/dsub/service/ChromeCastController.java
+++ b/src/github/daneren2005/dsub/service/ChromeCastController.java
@@ -27,7 +27,6 @@ import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.cast.MediaStatus;
import com.google.android.gms.cast.RemoteMediaPlayer;
import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
@@ -61,7 +60,7 @@ public class ChromeCastController extends RemoteController {
private RemoteMediaPlayer mediaPlayer;
private double gain = 0.5;
- public ChromeCastController(DownloadServiceImpl downloadService, CastDevice castDevice) {
+ public ChromeCastController(DownloadService downloadService, CastDevice castDevice) {
downloadService.setPlayerState(PlayerState.PREPARING);
this.downloadService = downloadService;
this.castDevice = castDevice;
diff --git a/src/github/daneren2005/dsub/service/DownloadFile.java b/src/github/daneren2005/dsub/service/DownloadFile.java
index d25d2dc9..9977c69a 100644
--- a/src/github/daneren2005/dsub/service/DownloadFile.java
+++ b/src/github/daneren2005/dsub/service/DownloadFile.java
@@ -414,9 +414,9 @@ public class DownloadFile {
if (wifiLock != null) {
wifiLock.release();
}
- new CacheCleaner(context, DownloadServiceImpl.getInstance()).cleanSpace();
- if(DownloadServiceImpl.getInstance() != null) {
- ((DownloadServiceImpl)DownloadServiceImpl.getInstance()).checkDownloads();
+ new CacheCleaner(context, DownloadService.getInstance()).cleanSpace();
+ if(DownloadService.getInstance() != null) {
+ ((DownloadService) DownloadService.getInstance()).checkDownloads();
}
}
}
diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java
index 471b6d5d..999e825d 100644
--- a/src/github/daneren2005/dsub/service/DownloadService.java
+++ b/src/github/daneren2005/dsub/service/DownloadService.java
@@ -18,136 +18,1706 @@
*/
package github.daneren2005.dsub.service;
-import android.support.v7.media.MediaRouteSelector;
-
-import java.util.List;
-
+import static android.support.v7.media.MediaRouter.RouteInfo;
+import static github.daneren2005.dsub.domain.PlayerState.COMPLETED;
+import static github.daneren2005.dsub.domain.PlayerState.DOWNLOADING;
+import static github.daneren2005.dsub.domain.PlayerState.IDLE;
+import static github.daneren2005.dsub.domain.PlayerState.PAUSED;
+import static github.daneren2005.dsub.domain.PlayerState.PREPARED;
+import static github.daneren2005.dsub.domain.PlayerState.PREPARING;
+import static github.daneren2005.dsub.domain.PlayerState.STARTED;
+import static github.daneren2005.dsub.domain.PlayerState.STOPPED;
import github.daneren2005.dsub.audiofx.EqualizerController;
import github.daneren2005.dsub.audiofx.VisualizerController;
import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PlayerState;
+import github.daneren2005.dsub.domain.PodcastEpisode;
import github.daneren2005.dsub.domain.RemoteControlState;
import github.daneren2005.dsub.domain.RepeatMode;
+import github.daneren2005.dsub.receiver.MediaButtonIntentReceiver;
+import github.daneren2005.dsub.util.CancellableTask;
+import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.MediaRouteManager;
+import github.daneren2005.dsub.util.ShufflePlayBuffer;
+import github.daneren2005.dsub.util.SimpleServiceBinder;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.util.compat.RemoteControlClientHelper;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.audiofx.AudioEffect;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.support.v7.media.MediaRouteSelector;
+import android.util.Log;
+import android.support.v4.util.LruCache;
+import java.net.URLEncoder;
/**
* @author Sindre Mehus
* @version $Id$
*/
-public interface DownloadService {
+public class DownloadService extends Service {
+
+ private static final String TAG = DownloadService.class.getSimpleName();
+
+ public static final String CMD_PLAY = "github.daneren2005.dsub.CMD_PLAY";
+ public static final String CMD_TOGGLEPAUSE = "github.daneren2005.dsub.CMD_TOGGLEPAUSE";
+ public static final String CMD_PAUSE = "github.daneren2005.dsub.CMD_PAUSE";
+ public static final String CMD_STOP = "github.daneren2005.dsub.CMD_STOP";
+ public static final String CMD_PREVIOUS = "github.daneren2005.dsub.CMD_PREVIOUS";
+ public static final String CMD_NEXT = "github.daneren2005.dsub.CMD_NEXT";
+ public static final String CANCEL_DOWNLOADS = "github.daneren2005.dsub.CANCEL_DOWNLOADS";
+
+ private RemoteControlClientHelper mRemoteControl;
+
+ private final IBinder binder = new SimpleServiceBinder<DownloadService>(this);
+ private Looper mediaPlayerLooper;
+ private MediaPlayer mediaPlayer;
+ private MediaPlayer nextMediaPlayer;
+ private boolean nextSetup = false;
+ private boolean isPartial = true;
+ private final List<DownloadFile> downloadList = new ArrayList<DownloadFile>();
+ private final List<DownloadFile> backgroundDownloadList = new ArrayList<DownloadFile>();
+ private final List<DownloadFile> toDelete = new ArrayList<DownloadFile>();
+ private final Handler handler = new Handler();
+ private Handler mediaPlayerHandler;
+ private final DownloadServiceLifecycleSupport lifecycleSupport = new DownloadServiceLifecycleSupport(this);
+ private final ShufflePlayBuffer shufflePlayBuffer = new ShufflePlayBuffer(this);
+
+ private final LruCache<MusicDirectory.Entry, DownloadFile> downloadFileCache = new LruCache<MusicDirectory.Entry, DownloadFile>(100);
+ private final List<DownloadFile> cleanupCandidates = new ArrayList<DownloadFile>();
+ private final Scrobbler scrobbler = new Scrobbler();
+ private RemoteController remoteController;
+ private DownloadFile currentPlaying;
+ private int currentPlayingIndex = -1;
+ private DownloadFile nextPlaying;
+ private DownloadFile currentDownloading;
+ private CancellableTask bufferTask;
+ private CancellableTask nextPlayingTask;
+ private PlayerState playerState = IDLE;
+ private PlayerState nextPlayerState = IDLE;
+ private boolean shufflePlay;
+ private long revision;
+ private static DownloadService instance;
+ private String suggestedPlaylistName;
+ private String suggestedPlaylistId;
+ private PowerManager.WakeLock wakeLock;
+ private boolean keepScreenOn;
+ private int cachedPosition = 0;
+ private long downloadRevision;
+ private boolean downloadOngoing = false;
+ private DownloadFile lastDownloaded = null;
+
+ private static boolean equalizerAvailable;
+ private static boolean visualizerAvailable;
+ private EqualizerController equalizerController;
+ private VisualizerController visualizerController;
+ private boolean showVisualization;
+ private RemoteControlState remoteState = RemoteControlState.LOCAL;
+ private PositionCache positionCache;
+ private StreamProxy proxy;
+
+ private Timer sleepTimer;
+ private int timerDuration;
+ private boolean autoPlayStart = false;
+
+ private MediaRouteManager mediaRouter;
+
+ static {
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
+ equalizerAvailable = true;
+ visualizerAvailable = true;
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ new Thread(new Runnable() {
+ public void run() {
+ Looper.prepare();
+
+ mediaPlayer = new MediaPlayer();
+ mediaPlayer.setWakeMode(DownloadService.this, PowerManager.PARTIAL_WAKE_LOCK);
+
+ mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+ @Override
+ public boolean onError(MediaPlayer mediaPlayer, int what, int more) {
+ handleError(new Exception("MediaPlayer error: " + what + " (" + more + ")"));
+ return false;
+ }
+ });
+
+ try {
+ Intent i = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
+ i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mediaPlayer.getAudioSessionId());
+ i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
+ sendBroadcast(i);
+ } catch(Throwable e) {
+ // Froyo or lower
+ }
+
+ mediaPlayerLooper = Looper.myLooper();
+ mediaPlayerHandler = new Handler(mediaPlayerLooper);
+ Looper.loop();
+ }
+ }).start();
+
+ Util.registerMediaButtonEventReceiver(this);
+
+ if (mRemoteControl == null) {
+ // Use the remote control APIs (if available) to set the playback state
+ mRemoteControl = RemoteControlClientHelper.createInstance();
+ ComponentName mediaButtonReceiverComponent = new ComponentName(getPackageName(), MediaButtonIntentReceiver.class.getName());
+ mRemoteControl.register(this, mediaButtonReceiverComponent);
+ }
+
+ PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
+ wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName());
+ wakeLock.setReferenceCounted(false);
+
+ SharedPreferences prefs = Util.getPreferences(this);
+ try {
+ timerDuration = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_SLEEP_TIMER_DURATION, "5"));
+ } catch(Throwable e) {
+ timerDuration = 5;
+ }
+ sleepTimer = null;
+
+ keepScreenOn = prefs.getBoolean(Constants.PREFERENCES_KEY_KEEP_SCREEN_ON, false);
+
+ mediaRouter = new MediaRouteManager(this);
+
+ instance = this;
+ lifecycleSupport.onCreate();
+
+ if(prefs.getBoolean(Constants.PREFERENCES_EQUALIZER_ON, false)) {
+ getEqualizerController();
+ }
+ if(prefs.getBoolean(Constants.PREFERENCES_VISUALIZER_ON, false)) {
+ getVisualizerController();
+ showVisualization = true;
+ }
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ super.onStartCommand(intent, flags, startId);
+ lifecycleSupport.onStart(intent);
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ instance = null;
+
+ if(currentPlaying != null) currentPlaying.setPlaying(false);
+ if(sleepTimer != null){
+ sleepTimer.cancel();
+ sleepTimer.purge();
+ }
+ lifecycleSupport.onDestroy();
+
+ try {
+ Intent i = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
+ i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mediaPlayer.getAudioSessionId());
+ i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
+ sendBroadcast(i);
+ } catch(Throwable e) {
+ // Froyo or lower
+ }
+
+ mediaPlayer.release();
+ if(nextMediaPlayer != null) {
+ nextMediaPlayer.release();
+ }
+ mediaPlayerLooper.quit();
+ shufflePlayBuffer.shutdown();
+ if (equalizerController != null) {
+ equalizerController.release();
+ }
+ if (visualizerController != null) {
+ visualizerController.release();
+ }
+ if (mRemoteControl != null) {
+ mRemoteControl.unregister(this);
+ mRemoteControl = null;
+ }
+
+ if(bufferTask != null) {
+ bufferTask.cancel();
+ }
+ if(nextPlayingTask != null) {
+ nextPlayingTask.cancel();
+ }
+ if(remoteController != null) {
+ remoteController.stop();
+ remoteController.shutdown();
+ }
+ mediaRouter.destroy();
+ Util.hidePlayingNotification(this, this, handler);
+ Util.hideDownloadingNotification(this);
+ }
+
+ public static DownloadService getInstance() {
+ return instance;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return binder;
+ }
+
+ public synchronized void download(Bookmark bookmark) {
+ clear();
+ DownloadFile downloadFile = new DownloadFile(this, bookmark.getEntry(), false);
+ downloadList.add(downloadFile);
+ revision++;
+ updateJukeboxPlaylist();
+ play(0, true, bookmark.getPosition());
+ lifecycleSupport.serializeDownloadQueue();
+ }
+
+ public synchronized void download(List<MusicDirectory.Entry> songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle) {
+ setShufflePlayEnabled(false);
+ int offset = 1;
+
+ if (songs.isEmpty()) {
+ return;
+ }
+ if (playNext) {
+ if (autoplay && getCurrentPlayingIndex() >= 0) {
+ offset = 0;
+ }
+ for (MusicDirectory.Entry song : songs) {
+ if(song != null) {
+ DownloadFile downloadFile = new DownloadFile(this, song, save);
+ downloadList.add(getCurrentPlayingIndex() + offset, downloadFile);
+ offset++;
+ }
+ }
+ setNextPlaying();
+ revision++;
+ } else {
+ int size = size();
+ int index = getCurrentPlayingIndex();
+ for (MusicDirectory.Entry song : songs) {
+ DownloadFile downloadFile = new DownloadFile(this, song, save);
+ downloadList.add(downloadFile);
+ }
+ if(!autoplay && (size - 1) == index) {
+ setNextPlaying();
+ }
+ revision++;
+ }
+ updateJukeboxPlaylist();
+
+ if(shuffle) {
+ shuffle();
+ }
+
+ if (autoplay) {
+ play(0);
+ } else {
+ if (currentPlaying == null) {
+ currentPlaying = downloadList.get(0);
+ currentPlayingIndex = 0;
+ currentPlaying.setPlaying(true);
+ }
+ checkDownloads();
+ }
+ lifecycleSupport.serializeDownloadQueue();
+ }
+ public synchronized void downloadBackground(List<MusicDirectory.Entry> songs, boolean save) {
+ for (MusicDirectory.Entry song : songs) {
+ DownloadFile downloadFile = new DownloadFile(this, song, save);
+ if(!downloadFile.isWorkDone() || (downloadFile.shouldSave() && !downloadFile.isSaved())) {
+ // Only add to list if there is work to be done
+ backgroundDownloadList.add(downloadFile);
+ }
+ }
+ revision++;
+
+ checkDownloads();
+ lifecycleSupport.serializeDownloadQueue();
+ }
+
+ private void updateJukeboxPlaylist() {
+ if (remoteState != RemoteControlState.LOCAL) {
+ remoteController.updatePlaylist();
+ }
+ }
+
+ public void restore(List<MusicDirectory.Entry> songs, List<MusicDirectory.Entry> toDelete, int currentPlayingIndex, int currentPlayingPosition) {
+ SharedPreferences prefs = Util.getPreferences(this);
+ remoteState = RemoteControlState.values()[prefs.getInt(Constants.PREFERENCES_KEY_CONTROL_MODE, 0)];
+ if(remoteState != RemoteControlState.LOCAL) {
+ String id = prefs.getString(Constants.PREFERENCES_KEY_CONTROL_ID, null);
+ setRemoteState(remoteState, null, id);
+ }
+ boolean startShufflePlay = prefs.getBoolean(Constants.PREFERENCES_KEY_SHUFFLE_MODE, false);
+ download(songs, false, false, false, false);
+ if(startShufflePlay) {
+ shufflePlay = true;
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(Constants.PREFERENCES_KEY_SHUFFLE_MODE, true);
+ editor.commit();
+ }
+ if (currentPlayingIndex != -1) {
+ while(mediaPlayer == null) {
+ Util.sleepQuietly(50L);
+ }
+
+ play(currentPlayingIndex, false);
+ if (currentPlaying != null && currentPlaying.isCompleteFileAvailable() && remoteState == RemoteControlState.LOCAL) {
+ doPlay(currentPlaying, currentPlayingPosition, autoPlayStart);
+ }
+ autoPlayStart = false;
+ }
+
+ if(toDelete != null) {
+ for(MusicDirectory.Entry entry: toDelete) {
+ this.toDelete.add(forSong(entry));
+ }
+ }
+ }
+
+ public synchronized void setShufflePlayEnabled(boolean enabled) {
+ shufflePlay = enabled;
+ if (shufflePlay) {
+ clear();
+ checkDownloads();
+ }
+ SharedPreferences.Editor editor = Util.getPreferences(this).edit();
+ editor.putBoolean(Constants.PREFERENCES_KEY_SHUFFLE_MODE, enabled);
+ editor.commit();
+ }
+
+ public boolean isShufflePlayEnabled() {
+ return shufflePlay;
+ }
+
+ public synchronized void shuffle() {
+ Collections.shuffle(downloadList);
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ if (currentPlaying != null) {
+ downloadList.remove(getCurrentPlayingIndex());
+ downloadList.add(0, currentPlaying);
+ currentPlayingIndex = 0;
+ }
+ revision++;
+ lifecycleSupport.serializeDownloadQueue();
+ updateJukeboxPlaylist();
+ setNextPlaying();
+ }
+
+ public RepeatMode getRepeatMode() {
+ return Util.getRepeatMode(this);
+ }
+
+ public void setRepeatMode(RepeatMode repeatMode) {
+ Util.setRepeatMode(this, repeatMode);
+ setNextPlaying();
+ }
+
+ public boolean getKeepScreenOn() {
+ return keepScreenOn;
+ }
+
+ public void setKeepScreenOn(boolean keepScreenOn) {
+ this.keepScreenOn = keepScreenOn;
+
+ SharedPreferences prefs = Util.getPreferences(this);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(Constants.PREFERENCES_KEY_KEEP_SCREEN_ON, keepScreenOn);
+ editor.commit();
+ }
+
+ public boolean getShowVisualization() {
+ return showVisualization;
+ }
+
+ public void setShowVisualization(boolean showVisualization) {
+ this.showVisualization = showVisualization;
+ SharedPreferences.Editor editor = Util.getPreferences(this).edit();
+ editor.putBoolean(Constants.PREFERENCES_VISUALIZER_ON, showVisualization);
+ editor.commit();
+ }
+
+ public synchronized DownloadFile forSong(MusicDirectory.Entry song) {
+ DownloadFile returnFile = null;
+ for (DownloadFile downloadFile : downloadList) {
+ if (downloadFile.getSong().equals(song)) {
+ if(((downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && downloadFile.getPartialFile().exists()) || downloadFile.isWorkDone())) {
+ // If downloading, return immediately
+ return downloadFile;
+ } else {
+ // Otherwise, check to make sure there isn't a background download going on first
+ returnFile = downloadFile;
+ }
+ }
+ }
+ for (DownloadFile downloadFile : backgroundDownloadList) {
+ if (downloadFile.getSong().equals(song)) {
+ return downloadFile;
+ }
+ }
+
+ if(returnFile != null) {
+ return returnFile;
+ }
+
+ DownloadFile downloadFile = downloadFileCache.get(song);
+ if (downloadFile == null) {
+ downloadFile = new DownloadFile(this, song, false);
+ downloadFileCache.put(song, downloadFile);
+ }
+ return downloadFile;
+ }
+
+ public synchronized void clear() {
+ clear(true);
+ }
+
+ public synchronized void clearBackground() {
+ if(currentDownloading != null && backgroundDownloadList.contains(currentDownloading)) {
+ currentDownloading.cancelDownload();
+ currentDownloading = null;
+ }
+ backgroundDownloadList.clear();
+ Util.hideDownloadingNotification(this);
+ }
+
+ public synchronized void clearIncomplete() {
+ reset();
+ Iterator<DownloadFile> iterator = downloadList.iterator();
+ while (iterator.hasNext()) {
+ DownloadFile downloadFile = iterator.next();
+ if (!downloadFile.isCompleteFileAvailable()) {
+ iterator.remove();
+ }
+ }
+ lifecycleSupport.serializeDownloadQueue();
+ updateJukeboxPlaylist();
+ }
+
+ public synchronized int size() {
+ return downloadList.size();
+ }
+
+ public synchronized void clear(boolean serialize) {
+ // Delete podcast if fully listened to
+ if(currentPlaying != null && currentPlaying.getSong() instanceof PodcastEpisode) {
+ int duration = getPlayerDuration();
+
+ // Make sure > 90% of the way through
+ int cutoffPoint = (int)(duration * 0.90);
+ if(duration > 0 && cachedPosition > cutoffPoint) {
+ currentPlaying.delete();
+ }
+ }
+ for(DownloadFile podcast: toDelete) {
+ podcast.delete();
+ }
+ toDelete.clear();
+
+ reset();
+ downloadList.clear();
+ revision++;
+ if (currentDownloading != null) {
+ currentDownloading.cancelDownload();
+ currentDownloading = null;
+ }
+ setCurrentPlaying(null, false);
+
+ if (serialize) {
+ lifecycleSupport.serializeDownloadQueue();
+ }
+ updateJukeboxPlaylist();
+ setNextPlaying();
+ }
+
+ public synchronized void remove(int which) {
+ downloadList.remove(which);
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ }
+
+ public synchronized void remove(DownloadFile downloadFile) {
+ if (downloadFile == currentDownloading) {
+ currentDownloading.cancelDownload();
+ currentDownloading = null;
+ }
+ if (downloadFile == currentPlaying) {
+ reset();
+ setCurrentPlaying(null, false);
+ }
+ downloadList.remove(downloadFile);
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ backgroundDownloadList.remove(downloadFile);
+ revision++;
+ lifecycleSupport.serializeDownloadQueue();
+ updateJukeboxPlaylist();
+ if(downloadFile == nextPlaying) {
+ setNextPlaying();
+ }
+ }
+
+ public synchronized void delete(List<MusicDirectory.Entry> songs) {
+ for (MusicDirectory.Entry song : songs) {
+ forSong(song).delete();
+ }
+ }
+
+ public synchronized void unpin(List<MusicDirectory.Entry> songs) {
+ for (MusicDirectory.Entry song : songs) {
+ forSong(song).unpin();
+ }
+ }
+
+ synchronized void setCurrentPlaying(int currentPlayingIndex, boolean showNotification) {
+ try {
+ setCurrentPlaying(downloadList.get(currentPlayingIndex), showNotification);
+ } catch (IndexOutOfBoundsException x) {
+ // Ignored
+ }
+ }
+
+ synchronized void setCurrentPlaying(DownloadFile currentPlaying, boolean showNotification) {
+ if(this.currentPlaying != null) {
+ this.currentPlaying.setPlaying(false);
+ }
+ this.currentPlaying = currentPlaying;
+ if(currentPlaying == null) {
+ currentPlayingIndex = -1;
+ } else {
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
+ }
+
+ if (currentPlaying != null) {
+ Util.broadcastNewTrackInfo(this, currentPlaying.getSong());
+ mRemoteControl.updateMetadata(this, currentPlaying.getSong());
+ } else {
+ Util.broadcastNewTrackInfo(this, null);
+ Util.hidePlayingNotification(this, this, handler);
+ }
+ }
+
+ synchronized void setNextPlaying() {
+ SharedPreferences prefs = Util.getPreferences(DownloadService.this);
+ boolean gaplessPlayback = prefs.getBoolean(Constants.PREFERENCES_KEY_GAPLESS_PLAYBACK, true);
+ if(!gaplessPlayback) {
+ nextPlaying = null;
+ nextPlayerState = IDLE;
+ return;
+ }
+
+ int index = getNextPlayingIndex();
+
+ nextSetup = false;
+ if(nextPlayingTask != null) {
+ nextPlayingTask.cancel();
+ nextPlayingTask = null;
+ }
+ if(index < size() && index != -1) {
+ nextPlaying = downloadList.get(index);
+ nextPlayingTask = new CheckCompletionTask(nextPlaying);
+ nextPlayingTask.start();
+ } else {
+ nextPlaying = null;
+ setNextPlayerState(IDLE);
+ }
+ }
+
+ public int getCurrentPlayingIndex() {
+ return currentPlayingIndex;
+ }
+ private int getNextPlayingIndex() {
+ int index = getCurrentPlayingIndex();
+ if (index != -1) {
+ switch (getRepeatMode()) {
+ case OFF:
+ index = index + 1;
+ break;
+ case ALL:
+ index = (index + 1) % size();
+ break;
+ case SINGLE:
+ break;
+ default:
+ break;
+ }
+ }
+ return index;
+ }
+
+ public DownloadFile getCurrentPlaying() {
+ return currentPlaying;
+ }
+
+ public DownloadFile getCurrentDownloading() {
+ return currentDownloading;
+ }
+
+ public List<DownloadFile> getSongs() {
+ return downloadList;
+ }
+
+ public List<DownloadFile> getToDelete() { return toDelete; }
+
+ public synchronized List<DownloadFile> getDownloads() {
+ List<DownloadFile> temp = new ArrayList<DownloadFile>();
+ temp.addAll(downloadList);
+ temp.addAll(backgroundDownloadList);
+ return temp;
+ }
+
+ public List<DownloadFile> getBackgroundDownloads() {
+ return backgroundDownloadList;
+ }
+
+ /** Plays either the current song (resume) or the first/next one in queue. */
+ public synchronized void play()
+ {
+ int current = getCurrentPlayingIndex();
+ if (current == -1) {
+ play(0);
+ } else {
+ play(current);
+ }
+ }
+
+ public synchronized void play(int index) {
+ play(index, true);
+ }
+ private synchronized void play(int index, boolean start) {
+ play(index, start, 0);
+ }
+ private synchronized void play(int index, boolean start, int position) {
+ if (index < 0 || index >= size()) {
+ reset();
+ setCurrentPlaying(null, false);
+ lifecycleSupport.serializeDownloadQueue();
+ } else {
+ if(nextPlayingTask != null) {
+ nextPlayingTask.cancel();
+ nextPlayingTask = null;
+ }
+ setCurrentPlaying(index, start);
+ if (start) {
+ if (remoteState != RemoteControlState.LOCAL) {
+ remoteController.changeTrack(index, downloadList.get(index));
+ setPlayerState(STARTED);
+ } else {
+ bufferAndPlay(position);
+ }
+ }
+ if (remoteState == RemoteControlState.LOCAL) {
+ checkDownloads();
+ setNextPlaying();
+ }
+ }
+ }
+ private synchronized void playNext() {
+ if(nextPlaying != null && nextPlayerState == PlayerState.PREPARED) {
+ if(!nextSetup) {
+ playNext(true);
+ } else {
+ nextSetup = false;
+ playNext(false);
+ }
+ } else {
+ onSongCompleted();
+ }
+ }
+ private synchronized void playNext(boolean start) {
+ // Swap the media players since nextMediaPlayer is ready to play
+ if(start) {
+ nextMediaPlayer.start();
+ } else if(!nextMediaPlayer.isPlaying()) {
+ Log.w(TAG, "nextSetup lied about it's state!");
+ nextMediaPlayer.start();
+ } else {
+ Log.i(TAG, "nextMediaPlayer already playing");
+ }
+ MediaPlayer tmp = mediaPlayer;
+ mediaPlayer = nextMediaPlayer;
+ nextMediaPlayer = tmp;
+ setCurrentPlaying(nextPlaying, true);
+ setPlayerState(PlayerState.STARTED);
+ setupHandlers(currentPlaying, false);
+ setNextPlaying();
+
+ // Proxy should not be being used here since the next player was already setup to play
+ if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
+ }
+
+ /** Plays or resumes the playback, depending on the current player state. */
+ public synchronized void togglePlayPause() {
+ if (playerState == PAUSED || playerState == COMPLETED || playerState == STOPPED) {
+ start();
+ } else if (playerState == STOPPED || playerState == IDLE) {
+ autoPlayStart = true;
+ play();
+ } else if (playerState == STARTED) {
+ pause();
+ }
+ }
+
+ public synchronized void seekTo(int position) {
+ try {
+ if (remoteState != RemoteControlState.LOCAL) {
+ remoteController.changePosition(position / 1000);
+ } else {
+ mediaPlayer.seekTo(position);
+ cachedPosition = position;
+ }
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
+
+ public synchronized void previous() {
+ int index = getCurrentPlayingIndex();
+ if (index == -1) {
+ return;
+ }
+
+ // Restart song if played more than five seconds.
+ if (getPlayerPosition() > 5000 || (index == 0 && getRepeatMode() != RepeatMode.ALL)) {
+ play(index);
+ } else {
+ if(index == 0) {
+ index = size();
+ }
+
+ play(index - 1);
+ }
+ }
+
+ public synchronized void next() {
+ // Delete podcast if fully listened to
+ if(currentPlaying != null && currentPlaying.getSong() instanceof PodcastEpisode) {
+ int duration = getPlayerDuration();
+
+ // Make sure > 90% of the way through
+ int cutoffPoint = (int)(duration * 0.90);
+ if(duration > 0 && cachedPosition > cutoffPoint) {
+ toDelete.add(currentPlaying);
+ }
+ }
+
+ int index = getCurrentPlayingIndex();
+ int nextPlayingIndex = getNextPlayingIndex();
+ // Make sure to actually go to next when repeat song is on
+ if(index == nextPlayingIndex) {
+ nextPlayingIndex++;
+ }
+ if (index != -1 && nextPlayingIndex < size()) {
+ if(nextPlaying != null && downloadList.get(nextPlayingIndex) == nextPlaying && nextPlayerState == PlayerState.PREPARED && remoteState == RemoteControlState.LOCAL) {
+ if(mediaPlayer.isPlaying()) {
+ mediaPlayer.stop();
+ }
+ mediaPlayer.setOnErrorListener(null);
+ mediaPlayer.setOnCompletionListener(null);
+ mediaPlayer.reset();
+ playNext(true);
+ } else {
+ play(nextPlayingIndex);
+ }
+ }
+ }
+
+ private void onSongCompleted() {
+ play(getNextPlayingIndex());
+ }
+
+ public synchronized void pause() {
+ try {
+ if (playerState == STARTED) {
+ if (remoteState != RemoteControlState.LOCAL) {
+ remoteController.stop();
+ } else {
+ mediaPlayer.pause();
+ }
+ setPlayerState(PAUSED);
+ }
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
+
+ public synchronized void stop() {
+ try {
+ if (playerState == STARTED) {
+ if (remoteState != RemoteControlState.LOCAL) {
+ remoteController.stop();
+ } else {
+ mediaPlayer.pause();
+ }
+ setPlayerState(STOPPED);
+ } else if(playerState == PAUSED) {
+ setPlayerState(STOPPED);
+ }
+ } catch(Exception x) {
+ handleError(x);
+ }
+ }
+
+ public synchronized void start() {
+ try {
+ if (remoteState != RemoteControlState.LOCAL) {
+ remoteController.start();
+ } else {
+ mediaPlayer.start();
+ }
+ setPlayerState(STARTED);
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
+
+ public synchronized void reset() {
+ if (bufferTask != null) {
+ bufferTask.cancel();
+ }
+ try {
+ // Only set to idle if it's not being killed to start RemoteController
+ if(remoteState == RemoteControlState.LOCAL) {
+ setPlayerState(IDLE);
+ }
+ mediaPlayer.setOnErrorListener(null);
+ mediaPlayer.setOnCompletionListener(null);
+ mediaPlayer.reset();
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
+
+ public int getPlayerPosition() {
+ try {
+ if (playerState == IDLE || playerState == DOWNLOADING || playerState == PREPARING) {
+ return 0;
+ }
+ if (remoteState != RemoteControlState.LOCAL) {
+ return remoteController.getRemotePosition() * 1000;
+ } else {
+ return cachedPosition;
+ }
+ } catch (Exception x) {
+ handleError(x);
+ return 0;
+ }
+ }
+
+ public synchronized int getPlayerDuration() {
+ if (currentPlaying != null) {
+ Integer duration = currentPlaying.getSong().getDuration();
+ if (duration != null) {
+ return duration * 1000;
+ }
+ }
+ if (playerState != IDLE && playerState != DOWNLOADING && playerState != PlayerState.PREPARING) {
+ try {
+ return mediaPlayer.getDuration();
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
+ return 0;
+ }
+
+ public PlayerState getPlayerState() {
+ return playerState;
+ }
+
+ public synchronized void setPlayerState(final PlayerState playerState) {
+ Log.i(TAG, this.playerState.name() + " -> " + playerState.name() + " (" + currentPlaying + ")");
+
+ if (playerState == PAUSED) {
+ lifecycleSupport.serializeDownloadQueue();
+ }
+
+ boolean show = playerState == PlayerState.STARTED;
+ boolean pause = playerState == PlayerState.PAUSED;
+ boolean hide = playerState == PlayerState.STOPPED;
+ Util.broadcastPlaybackStatusChange(this, (currentPlaying != null) ? currentPlaying.getSong() : null, playerState);
+
+ this.playerState = playerState;
+
+ if(playerState == STARTED) {
+ Util.requestAudioFocus(this);
+ }
+
+ if (show) {
+ Util.showPlayingNotification(this, this, handler, currentPlaying.getSong());
+ } else if (pause) {
+ SharedPreferences prefs = Util.getPreferences(this);
+ if(prefs.getBoolean(Constants.PREFERENCES_KEY_PERSISTENT_NOTIFICATION, false)) {
+ Util.showPlayingNotification(this, this, handler, currentPlaying.getSong());
+ } else {
+ Util.hidePlayingNotification(this, this, handler);
+ }
+ } else if(hide) {
+ Util.hidePlayingNotification(this, this, handler);
+ }
+ mRemoteControl.setPlaybackState(playerState.getRemoteControlClientPlayState());
+
+ if (playerState == STARTED) {
+ scrobbler.scrobble(this, currentPlaying, false);
+ } else if (playerState == COMPLETED) {
+ scrobbler.scrobble(this, currentPlaying, true);
+ }
+
+ if(playerState == STARTED && positionCache == null) {
+ positionCache = new PositionCache();
+ Thread thread = new Thread(positionCache);
+ thread.start();
+ } else if(playerState != STARTED && positionCache != null) {
+ positionCache.stop();
+ positionCache = null;
+ }
+ }
+
+ private class PositionCache implements Runnable {
+ boolean isRunning = true;
+
+ public void stop() {
+ isRunning = false;
+ }
+
+ @Override
+ public void run() {
+ // Stop checking position before the song reaches completion
+ while(isRunning) {
+ try {
+ if(mediaPlayer != null && playerState == STARTED) {
+ cachedPosition = mediaPlayer.getCurrentPosition();
+ }
+ Thread.sleep(1000L);
+ }
+ catch(Exception e) {
+ Log.w(TAG, "Crashed getting current position", e);
+ isRunning = false;
+ positionCache = null;
+ }
+ }
+ }
+ }
+
+ private void setPlayerStateCompleted() {
+ Log.i(TAG, this.playerState.name() + " -> " + PlayerState.COMPLETED + " (" + currentPlaying + ")");
+ this.playerState = PlayerState.COMPLETED;
+ if(positionCache != null) {
+ positionCache.stop();
+ positionCache = null;
+ }
+ scrobbler.scrobble(this, currentPlaying, true);
+ }
+
+ private synchronized void setNextPlayerState(PlayerState playerState) {
+ Log.i(TAG, "Next: " + this.nextPlayerState.name() + " -> " + playerState.name() + " (" + nextPlaying + ")");
+ this.nextPlayerState = playerState;
+ }
+
+ public void setSuggestedPlaylistName(String name, String id) {
+ this.suggestedPlaylistName = name;
+ this.suggestedPlaylistId = id;
+ }
+
+ public String getSuggestedPlaylistName() {
+ return suggestedPlaylistName;
+ }
+
+ public String getSuggestedPlaylistId() {
+ return suggestedPlaylistId;
+ }
+
+ public boolean getEqualizerAvailable() {
+ return equalizerAvailable;
+ }
+
+ public boolean getVisualizerAvailable() {
+ return visualizerAvailable;
+ }
+
+ public EqualizerController getEqualizerController() {
+ if (equalizerAvailable && equalizerController == null) {
+ equalizerController = new EqualizerController(this, mediaPlayer);
+ if (!equalizerController.isAvailable()) {
+ equalizerController = null;
+ } else {
+ equalizerController.loadSettings();
+ }
+ }
+ return equalizerController;
+ }
+
+ public VisualizerController getVisualizerController() {
+ if (visualizerAvailable && visualizerController == null) {
+ visualizerController = new VisualizerController(this, mediaPlayer);
+ if (!visualizerController.isAvailable()) {
+ visualizerController = null;
+ }
+ }
+ return visualizerController;
+ }
+
+ public MediaRouteSelector getRemoteSelector() {
+ return mediaRouter.getSelector();
+ }
+
+ public boolean isRemoteEnabled() {
+ return remoteState != RemoteControlState.LOCAL;
+ }
+
+ public void setRemoteEnabled(RemoteControlState newState) {
+ if(instance != null) {
+ setRemoteEnabled(newState, null);
+ }
+ }
+ public void setRemoteEnabled(RemoteControlState newState, Object ref) {
+ setRemoteState(newState, ref);
+
+ RouteInfo info = mediaRouter.getSelectedRoute();
+ String routeId = info.getId();
+
+ SharedPreferences.Editor editor = Util.getPreferences(this).edit();
+ editor.putInt(Constants.PREFERENCES_KEY_CONTROL_MODE, newState.getValue());
+ editor.putString(Constants.PREFERENCES_KEY_CONTROL_ID, routeId);
+ editor.commit();
+ }
+ private void setRemoteState(RemoteControlState newState, Object ref) {
+ setRemoteState(newState, ref, null);
+ }
+ private void setRemoteState(final RemoteControlState newState, final Object ref, final String routeId) {
+ if(remoteController != null) {
+ remoteController.stop();
+ setPlayerState(PlayerState.IDLE);
+ remoteController.shutdown();
+ remoteController = null;
+ }
+
+ remoteState = newState;
+ switch(newState) {
+ case JUKEBOX_SERVER:
+ remoteController = new JukeboxController(this, handler);
+ break;
+ case CHROMECAST:
+ // TODO: Fix case where starting up with chromecast set
+ if(ref == null) {
+ remoteState = RemoteControlState.LOCAL;
+ break;
+ }
+ remoteController = (RemoteController) ref;
+ break;
+ case LOCAL: default:
+ break;
+ }
+
+ if (remoteState != RemoteControlState.LOCAL) {
+ reset();
+
+ // Cancel current download, if necessary.
+ if (currentDownloading != null) {
+ currentDownloading.cancelDownload();
+ }
+ }
+
+ SharedPreferences prefs = Util.getPreferences(this);
+ if(currentPlaying != null && prefs.getBoolean(Constants.PREFERENCES_KEY_PERSISTENT_NOTIFICATION, false)) {
+ Util.showPlayingNotification(this, this, handler, currentPlaying.getSong());
+ } else {
+ Util.hidePlayingNotification(this, this, handler);
+ }
+
+ if(routeId != null) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ RouteInfo info = mediaRouter.getRouteForId(routeId);
+ if(info == null) {
+ setRemoteState(RemoteControlState.LOCAL, null);
+ } else if(newState == RemoteControlState.CHROMECAST) {
+ RemoteController controller = mediaRouter.getRemoteController(info);
+ if(controller != null) {
+ setRemoteState(RemoteControlState.CHROMECAST, controller);
+ }
+ }
+ }
+ });
+ }
+ }
+
+ public void setRemoteVolume(boolean up) {
+ remoteController.setVolume(up);
+ }
+
+ public void startRemoteScan() {
+ mediaRouter.startScan();
+ }
+
+ public void stopRemoteScan() {
+ mediaRouter.stopScan();
+ }
+
+ private synchronized void bufferAndPlay() {
+ bufferAndPlay(0);
+ }
+ private synchronized void bufferAndPlay(int position) {
+ if(playerState != PREPARED) {
+ reset();
+
+ bufferTask = new BufferTask(currentPlaying, position);
+ bufferTask.start();
+ } else {
+ doPlay(currentPlaying, position, true);
+ }
+ }
+
+ private synchronized void doPlay(final DownloadFile downloadFile, final int position, final boolean start) {
+ try {
+ downloadFile.setPlaying(true);
+ final File file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile();
+ isPartial = file.equals(downloadFile.getPartialFile());
+ downloadFile.updateModificationDate();
+
+ mediaPlayer.setOnCompletionListener(null);
+ mediaPlayer.reset();
+ setPlayerState(IDLE);
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ String dataSource = file.getPath();
+ if(isPartial) {
+ if (proxy == null) {
+ proxy = new StreamProxy(this);
+ proxy.start();
+ }
+ dataSource = String.format("http://127.0.0.1:%d/%s", proxy.getPort(), URLEncoder.encode(dataSource, Constants.UTF_8));
+ Log.i(TAG, "Data Source: " + dataSource);
+ } else if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
+ mediaPlayer.setDataSource(dataSource);
+ setPlayerState(PREPARING);
+
+ mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
+ public void onBufferingUpdate(MediaPlayer mp, int percent) {
+ Log.i(TAG, "Buffered " + percent + "%");
+ if(percent == 100) {
+ mediaPlayer.setOnBufferingUpdateListener(null);
+ }
+ }
+ });
+
+ mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ public void onPrepared(MediaPlayer mediaPlayer) {
+ try {
+ setPlayerState(PREPARED);
+
+ synchronized (DownloadService.this) {
+ if (position != 0) {
+ Log.i(TAG, "Restarting player from position " + position);
+ mediaPlayer.seekTo(position);
+ }
+ cachedPosition = position;
+
+ if (start) {
+ mediaPlayer.start();
+ setPlayerState(STARTED);
+ } else {
+ setPlayerState(PAUSED);
+ }
+ }
+
+ // Only call when starting, setPlayerState(PAUSED) already calls this
+ if(start) {
+ lifecycleSupport.serializeDownloadQueue();
+ }
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
+ });
+
+ setupHandlers(downloadFile, isPartial);
+
+ mediaPlayer.prepareAsync();
+ } catch (Exception x) {
+ handleError(x);
+ }
+ }
+
+ private synchronized void setupNext(final DownloadFile downloadFile) {
+ try {
+ final File file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile();
+ if(nextMediaPlayer != null) {
+ nextMediaPlayer.setOnCompletionListener(null);
+ nextMediaPlayer.setOnErrorListener(null);
+ nextMediaPlayer.reset();
+ nextMediaPlayer.release();
+ nextMediaPlayer = null;
+ }
+
+ // Exit when using remote controllers
+ if(remoteState != RemoteControlState.LOCAL) {
+ return;
+ }
+
+ nextMediaPlayer = new MediaPlayer();
+ nextMediaPlayer.setWakeMode(DownloadService.this, PowerManager.PARTIAL_WAKE_LOCK);
+ try {
+ nextMediaPlayer.setAudioSessionId(mediaPlayer.getAudioSessionId());
+ } catch(Throwable e) {
+ nextMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ }
+ nextMediaPlayer.setDataSource(file.getPath());
+ setNextPlayerState(PREPARING);
+
+ nextMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ public void onPrepared(MediaPlayer mp) {
+ try {
+ setNextPlayerState(PREPARED);
+
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && (playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED)) {
+ mediaPlayer.setNextMediaPlayer(nextMediaPlayer);
+ nextSetup = true;
+ }
+ } catch (Exception x) {
+ handleErrorNext(x);
+ }
+ }
+ });
+
+ nextMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+ public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
+ Log.w(TAG, "Error on playing next " + "(" + what + ", " + extra + "): " + downloadFile);
+ return true;
+ }
+ });
+
+ nextMediaPlayer.prepareAsync();
+ } catch (Exception x) {
+ handleErrorNext(x);
+ }
+ }
+
+ private void setupHandlers(final DownloadFile downloadFile, final boolean isPartial) {
+ final int duration = downloadFile.getSong().getDuration() == null ? 0 : downloadFile.getSong().getDuration() * 1000;
+ mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+ public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
+ Log.w(TAG, "Error on playing file " + "(" + what + ", " + extra + "): " + downloadFile);
+ int pos = cachedPosition;
+ reset();
+ if (!isPartial || (downloadFile.isWorkDone() && (Math.abs(duration - pos) < 10000))) {
+ playNext();
+ } else {
+ downloadFile.setPlaying(false);
+ doPlay(downloadFile, pos, true);
+ downloadFile.setPlaying(true);
+ }
+ return true;
+ }
+ });
+
+ mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mediaPlayer) {
+ // Acquire a temporary wakelock, since when we return from
+ // this callback the MediaPlayer will release its wakelock
+ // and allow the device to go to sleep.
+ wakeLock.acquire(60000);
+
+ setPlayerStateCompleted();
+
+ int pos = cachedPosition;
+ Log.i(TAG, "Ending position " + pos + " of " + duration);
+ if (!isPartial || (downloadFile.isWorkDone() && (Math.abs(duration - pos) < 10000))) {
+ playNext();
+
+ // Finished loading, delete when list is cleared
+ if(downloadFile.getSong() instanceof PodcastEpisode) {
+ toDelete.add(downloadFile);
+ }
+ } else {
+ // If file is not completely downloaded, restart the playback from the current position.
+ synchronized (DownloadService.this) {
+ if(downloadFile.isWorkDone()) {
+ // Complete was called early even though file is fully buffered
+ Log.i(TAG, "Requesting restart from " + pos + " of " + duration);
+ reset();
+ downloadFile.setPlaying(false);
+ doPlay(downloadFile, pos, true);
+ downloadFile.setPlaying(true);
+ } else {
+ Log.i(TAG, "Requesting restart from " + pos + " of " + duration);
+ reset();
+ bufferTask = new BufferTask(downloadFile, pos);
+ bufferTask.start();
+ }
+ }
+ }
+ }
+ });
+ }
+
+ public void setSleepTimerDuration(int duration){
+ timerDuration = duration;
+ }
+
+ public void startSleepTimer(){
+ if(sleepTimer != null){
+ sleepTimer.cancel();
+ sleepTimer.purge();
+ }
+
+ sleepTimer = new Timer();
+
+ sleepTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ pause();
+ sleepTimer.cancel();
+ sleepTimer.purge();
+ sleepTimer = null;
+ }
+
+ }, timerDuration * 60 * 1000);
+ }
+
+ public void stopSleepTimer() {
+ if(sleepTimer != null){
+ sleepTimer.cancel();
+ sleepTimer.purge();
+ }
+ sleepTimer = null;
+ }
+
+ public boolean getSleepTimer() {
+ return sleepTimer != null;
+ }
- void download(Bookmark bookmark);
- void download(List<MusicDirectory.Entry> songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle);
- void downloadBackground(List<MusicDirectory.Entry> songs, boolean save);
+ public void setVolume(float volume) {
+ if(mediaPlayer != null) {
+ mediaPlayer.setVolume(volume, volume);
+ }
+ }
- void setShufflePlayEnabled(boolean enabled);
+ public synchronized void swap(boolean mainList, int from, int to) {
+ List<DownloadFile> list = mainList ? downloadList : backgroundDownloadList;
+ int max = list.size();
+ if(to >= max) {
+ to = max - 1;
+ }
+ else if(to < 0) {
+ to = 0;
+ }
- boolean isShufflePlayEnabled();
+ int currentPlayingIndex = getCurrentPlayingIndex();
+ DownloadFile movedSong = list.remove(from);
+ list.add(to, movedSong);
+ if(remoteState != RemoteControlState.LOCAL && mainList) {
+ updateJukeboxPlaylist();
+ } else if(mainList && (movedSong == nextPlaying || (currentPlayingIndex + 1) == to)) {
+ // Moving next playing or moving a song to be next playing
+ setNextPlaying();
+ }
+ }
- void shuffle();
+ private void handleError(Exception x) {
+ Log.w(TAG, "Media player error: " + x, x);
+ if(mediaPlayer != null) {
+ mediaPlayer.reset();
+ }
+ setPlayerState(IDLE);
+ }
+ private void handleErrorNext(Exception x) {
+ Log.w(TAG, "Next Media player error: " + x, x);
+ nextMediaPlayer.reset();
+ setNextPlayerState(IDLE);
+ }
- RepeatMode getRepeatMode();
+ protected synchronized void checkDownloads() {
+ if (!Util.isExternalStoragePresent() || !lifecycleSupport.isExternalStorageAvailable()) {
+ return;
+ }
- void setRepeatMode(RepeatMode repeatMode);
+ if (shufflePlay) {
+ checkShufflePlay();
+ }
- boolean getKeepScreenOn();
+ if (remoteState != RemoteControlState.LOCAL || !Util.isNetworkConnected(this) || Util.isOffline(this)) {
+ return;
+ }
- void setKeepScreenOn(boolean screenOn);
+ if (downloadList.isEmpty() && backgroundDownloadList.isEmpty()) {
+ return;
+ }
- boolean getShowVisualization();
+ // Need to download current playing?
+ if (currentPlaying != null && currentPlaying != currentDownloading && !currentPlaying.isWorkDone()) {
+ // Cancel current download, if necessary.
+ if (currentDownloading != null) {
+ currentDownloading.cancelDownload();
+ }
- void setShowVisualization(boolean showVisualization);
+ currentDownloading = currentPlaying;
+ currentDownloading.download();
+ cleanupCandidates.add(currentDownloading);
+ }
- void clear();
-
- void clearBackground();
+ // Find a suitable target for download.
+ else if (currentDownloading == null || currentDownloading.isWorkDone() || currentDownloading.isFailed() && (!downloadList.isEmpty() || !backgroundDownloadList.isEmpty())) {
+ currentDownloading = null;
+ int n = size();
- void clearIncomplete();
+ int preloaded = 0;
- int size();
-
- void remove(int which);
+ if(n != 0) {
+ int start = currentPlaying == null ? 0 : getCurrentPlayingIndex();
+ if(start == -1) {
+ start = 0;
+ }
+ int i = start;
+ do {
+ DownloadFile downloadFile = downloadList.get(i);
+ if (!downloadFile.isWorkDone() && !downloadFile.isFailedMax()) {
+ if (downloadFile.shouldSave() || preloaded < Util.getPreloadCount(this)) {
+ currentDownloading = downloadFile;
+ currentDownloading.download();
+ cleanupCandidates.add(currentDownloading);
+ if(i == (start + 1)) {
+ setNextPlayerState(DOWNLOADING);
+ }
+ break;
+ }
+ } else if (currentPlaying != downloadFile) {
+ preloaded++;
+ }
- void remove(DownloadFile downloadFile);
+ i = (i + 1) % n;
+ } while (i != start);
+ }
- List<DownloadFile> getSongs();
-
- List<DownloadFile> getDownloads();
-
- List<DownloadFile> getBackgroundDownloads();
+ if((preloaded + 1 == n || preloaded >= Util.getPreloadCount(this) || downloadList.isEmpty()) && !backgroundDownloadList.isEmpty()) {
+ for(int i = 0; i < backgroundDownloadList.size(); i++) {
+ DownloadFile downloadFile = backgroundDownloadList.get(i);
+ if(downloadFile.isWorkDone() && (!downloadFile.shouldSave() || downloadFile.isSaved())) {
+ // Don't need to keep list like active song list
+ backgroundDownloadList.remove(i);
+ revision++;
+ i--;
+ } else {
+ if(!downloadFile.isFailedMax()) {
+ currentDownloading = downloadFile;
+ currentDownloading.download();
+ cleanupCandidates.add(currentDownloading);
+ break;
+ }
+ }
+ }
+ }
+ }
- int getCurrentPlayingIndex();
+ if(!backgroundDownloadList.isEmpty()) {
+ DownloadFile speedFile = null;
+ // Updating existing notification
+ if(downloadOngoing) {
+ // Changing download, use speed of last DownloadFile
+ if(revision != downloadRevision && lastDownloaded != null) {
+ speedFile = lastDownloaded;
+ } else {
+ // Updated mid-download
+ speedFile = currentDownloading;
+ }
+ }
+ Util.showDownloadingNotification(this, currentDownloading, backgroundDownloadList.size(), speedFile);
+ downloadRevision = revision;
+ lastDownloaded = currentDownloading;
+ downloadOngoing = true;
+ } else if(backgroundDownloadList.isEmpty() && downloadOngoing) {
+ Util.hideDownloadingNotification(this);
+ downloadOngoing = false;
+ lastDownloaded = null;
+ }
- DownloadFile getCurrentPlaying();
+ // Delete obsolete .partial and .complete files.
+ cleanup();
+ }
- DownloadFile getCurrentDownloading();
+ private synchronized void checkShufflePlay() {
- void play(int index);
+ // Get users desired random playlist size
+ SharedPreferences prefs = Util.getPreferences(this);
+ int listSize = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_RANDOM_SIZE, "20"));
+ boolean wasEmpty = downloadList.isEmpty();
- void seekTo(int position);
+ long revisionBefore = revision;
- void previous();
+ // First, ensure that list is at least 20 songs long.
+ int size = size();
+ if (size < listSize) {
+ for (MusicDirectory.Entry song : shufflePlayBuffer.get(listSize - size)) {
+ DownloadFile downloadFile = new DownloadFile(this, song, false);
+ downloadList.add(downloadFile);
+ revision++;
+ }
+ }
- void next();
+ int currIndex = currentPlaying == null ? 0 : getCurrentPlayingIndex();
- void pause();
-
- void stop();
+ // Only shift playlist if playing song #5 or later.
+ if (currIndex > 4) {
+ int songsToShift = currIndex - 2;
+ for (MusicDirectory.Entry song : shufflePlayBuffer.get(songsToShift)) {
+ downloadList.add(new DownloadFile(this, song, false));
+ downloadList.get(0).cancelDownload();
+ downloadList.remove(0);
+ revision++;
+ }
+ }
+ currentPlayingIndex = downloadList.indexOf(currentPlaying);
- void start();
+ if (revisionBefore != revision) {
+ updateJukeboxPlaylist();
+ }
- void reset();
+ if (wasEmpty && !downloadList.isEmpty()) {
+ play(0);
+ }
+ }
- PlayerState getPlayerState();
+ public long getDownloadListUpdateRevision() {
+ return revision;
+ }
- int getPlayerPosition();
+ private synchronized void cleanup() {
+ Iterator<DownloadFile> iterator = cleanupCandidates.iterator();
+ while (iterator.hasNext()) {
+ DownloadFile downloadFile = iterator.next();
+ if (downloadFile != currentPlaying && downloadFile != currentDownloading) {
+ if (downloadFile.cleanup()) {
+ iterator.remove();
+ }
+ }
+ }
+ }
- int getPlayerDuration();
+ private class BufferTask extends CancellableTask {
+ private final DownloadFile downloadFile;
+ private final int position;
+ private final long expectedFileSize;
+ private final File partialFile;
- void delete(List<MusicDirectory.Entry> songs);
+ public BufferTask(DownloadFile downloadFile, int position) {
+ this.downloadFile = downloadFile;
+ this.position = position;
+ partialFile = downloadFile.getPartialFile();
- void unpin(List<MusicDirectory.Entry> songs);
+ // Calculate roughly how many bytes BUFFER_LENGTH_SECONDS corresponds to.
+ int bitRate = downloadFile.getBitRate();
+ long byteCount = Math.max(100000, bitRate * 1024L / 8L * 5L);
- DownloadFile forSong(MusicDirectory.Entry song);
+ // Find out how large the file should grow before resuming playback.
+ Log.i(TAG, "Buffering from position " + position + " and bitrate " + bitRate);
+ expectedFileSize = (position * bitRate / 8) + byteCount;
+ }
- long getDownloadListUpdateRevision();
+ @Override
+ public void execute() {
+ setPlayerState(DOWNLOADING);
- void setSuggestedPlaylistName(String name, String id);
+ while (!bufferComplete()) {
+ Util.sleepQuietly(1000L);
+ if (isCancelled()) {
+ return;
+ }
+ }
+ doPlay(downloadFile, position, true);
+ }
- String getSuggestedPlaylistName();
-
- String getSuggestedPlaylistId();
-
- boolean getEqualizerAvailable();
+ private boolean bufferComplete() {
+ boolean completeFileAvailable = downloadFile.isWorkDone();
+ long size = partialFile.length();
- boolean getVisualizerAvailable();
+ Log.i(TAG, "Buffering " + partialFile + " (" + size + "/" + expectedFileSize + ", " + completeFileAvailable + ")");
+ return completeFileAvailable || size >= expectedFileSize;
+ }
- EqualizerController getEqualizerController();
+ @Override
+ public String toString() {
+ return "BufferTask (" + downloadFile + ")";
+ }
+ }
- VisualizerController getVisualizerController();
+ private class CheckCompletionTask extends CancellableTask {
+ private final DownloadFile downloadFile;
+ private final File partialFile;
- MediaRouteSelector getRemoteSelector();
+ public CheckCompletionTask(DownloadFile downloadFile) {
+ setNextPlayerState(PlayerState.IDLE);
+ this.downloadFile = downloadFile;
+ if(downloadFile != null) {
+ partialFile = downloadFile.getPartialFile();
+ } else {
+ partialFile = null;
+ }
+ }
- boolean isRemoteEnabled();
+ @Override
+ public void execute() {
+ if(downloadFile == null) {
+ return;
+ }
- void setRemoteEnabled(RemoteControlState newState);
+ // Do an initial sleep so this prepare can't compete with main prepare
+ Util.sleepQuietly(5000L);
+ while (!bufferComplete()) {
+ Util.sleepQuietly(5000L);
+ if (isCancelled()) {
+ return;
+ }
+ }
- void setRemoteVolume(boolean up);
+ // Start the setup of the next media player
+ mediaPlayerHandler.post(new Runnable() {
+ public void run() {
+ setupNext(downloadFile);
+ }
+ });
+ }
- void startRemoteScan();
+ private boolean bufferComplete() {
+ boolean completeFileAvailable = downloadFile.isWorkDone();
+ Log.i(TAG, "Buffering next " + partialFile + " (" + partialFile.length() + ")");
+ return completeFileAvailable && (playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED);
+ }
- void stopRemoteScan();
-
- void setSleepTimerDuration(int duration);
-
- void startSleepTimer();
-
- void stopSleepTimer();
-
- boolean getSleepTimer();
-
- void setVolume(float volume);
-
- void swap(boolean mainList, int from, int to);
+ @Override
+ public String toString() {
+ return "CheckCompletionTask (" + downloadFile + ")";
+ }
+ }
}
diff --git a/src/github/daneren2005/dsub/service/DownloadServiceImpl.java b/src/github/daneren2005/dsub/service/DownloadServiceImpl.java
deleted file mode 100644
index d28c5abf..00000000
--- a/src/github/daneren2005/dsub/service/DownloadServiceImpl.java
+++ /dev/null
@@ -1,1780 +0,0 @@
-/*
- This file is part of Subsonic.
-
- Subsonic 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.
-
- Subsonic 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 Subsonic. If not, see <http://www.gnu.org/licenses/>.
-
- Copyright 2009 (C) Sindre Mehus
- */
-package github.daneren2005.dsub.service;
-
-import static android.support.v7.media.MediaRouter.RouteInfo;
-import static github.daneren2005.dsub.domain.PlayerState.COMPLETED;
-import static github.daneren2005.dsub.domain.PlayerState.DOWNLOADING;
-import static github.daneren2005.dsub.domain.PlayerState.IDLE;
-import static github.daneren2005.dsub.domain.PlayerState.PAUSED;
-import static github.daneren2005.dsub.domain.PlayerState.PREPARED;
-import static github.daneren2005.dsub.domain.PlayerState.PREPARING;
-import static github.daneren2005.dsub.domain.PlayerState.STARTED;
-import static github.daneren2005.dsub.domain.PlayerState.STOPPED;
-import github.daneren2005.dsub.audiofx.EqualizerController;
-import github.daneren2005.dsub.audiofx.VisualizerController;
-import github.daneren2005.dsub.domain.Bookmark;
-import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.domain.PlayerState;
-import github.daneren2005.dsub.domain.PodcastEpisode;
-import github.daneren2005.dsub.domain.RemoteControlState;
-import github.daneren2005.dsub.domain.RepeatMode;
-import github.daneren2005.dsub.receiver.MediaButtonIntentReceiver;
-import github.daneren2005.dsub.util.CancellableTask;
-import github.daneren2005.dsub.util.Constants;
-import github.daneren2005.dsub.util.MediaRouteManager;
-import github.daneren2005.dsub.util.ShufflePlayBuffer;
-import github.daneren2005.dsub.util.SimpleServiceBinder;
-import github.daneren2005.dsub.util.Util;
-import github.daneren2005.dsub.util.compat.RemoteControlClientHelper;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
-
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.media.audiofx.AudioEffect;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.support.v7.media.MediaRouteSelector;
-import android.support.v7.media.MediaRouter;
-import android.util.Log;
-import android.support.v4.util.LruCache;
-import java.net.URLEncoder;
-
-/**
- * @author Sindre Mehus
- * @version $Id$
- */
-public class DownloadServiceImpl extends Service implements DownloadService {
-
- private static final String TAG = DownloadServiceImpl.class.getSimpleName();
-
- public static final String CMD_PLAY = "github.daneren2005.dsub.CMD_PLAY";
- public static final String CMD_TOGGLEPAUSE = "github.daneren2005.dsub.CMD_TOGGLEPAUSE";
- public static final String CMD_PAUSE = "github.daneren2005.dsub.CMD_PAUSE";
- public static final String CMD_STOP = "github.daneren2005.dsub.CMD_STOP";
- public static final String CMD_PREVIOUS = "github.daneren2005.dsub.CMD_PREVIOUS";
- public static final String CMD_NEXT = "github.daneren2005.dsub.CMD_NEXT";
- public static final String CANCEL_DOWNLOADS = "github.daneren2005.dsub.CANCEL_DOWNLOADS";
-
- private RemoteControlClientHelper mRemoteControl;
-
- private final IBinder binder = new SimpleServiceBinder<DownloadService>(this);
- private Looper mediaPlayerLooper;
- private MediaPlayer mediaPlayer;
- private MediaPlayer nextMediaPlayer;
- private boolean nextSetup = false;
- private boolean isPartial = true;
- private final List<DownloadFile> downloadList = new ArrayList<DownloadFile>();
- private final List<DownloadFile> backgroundDownloadList = new ArrayList<DownloadFile>();
- private final List<DownloadFile> toDelete = new ArrayList<DownloadFile>();
- private final Handler handler = new Handler();
- private Handler mediaPlayerHandler;
- private final DownloadServiceLifecycleSupport lifecycleSupport = new DownloadServiceLifecycleSupport(this);
- private final ShufflePlayBuffer shufflePlayBuffer = new ShufflePlayBuffer(this);
-
- private final LruCache<MusicDirectory.Entry, DownloadFile> downloadFileCache = new LruCache<MusicDirectory.Entry, DownloadFile>(100);
- private final List<DownloadFile> cleanupCandidates = new ArrayList<DownloadFile>();
- private final Scrobbler scrobbler = new Scrobbler();
- private RemoteController remoteController;
- private DownloadFile currentPlaying;
- private int currentPlayingIndex = -1;
- private DownloadFile nextPlaying;
- private DownloadFile currentDownloading;
- private CancellableTask bufferTask;
- private CancellableTask nextPlayingTask;
- private PlayerState playerState = IDLE;
- private PlayerState nextPlayerState = IDLE;
- private boolean shufflePlay;
- private long revision;
- private static DownloadService instance;
- private String suggestedPlaylistName;
- private String suggestedPlaylistId;
- private PowerManager.WakeLock wakeLock;
- private boolean keepScreenOn;
- private int cachedPosition = 0;
- private long downloadRevision;
- private boolean downloadOngoing = false;
- private DownloadFile lastDownloaded = null;
-
- private static boolean equalizerAvailable;
- private static boolean visualizerAvailable;
- private EqualizerController equalizerController;
- private VisualizerController visualizerController;
- private boolean showVisualization;
- private RemoteControlState remoteState = RemoteControlState.LOCAL;
- private PositionCache positionCache;
- private StreamProxy proxy;
-
- private Timer sleepTimer;
- private int timerDuration;
- private boolean autoPlayStart = false;
-
- private MediaRouteManager mediaRouter;
-
- static {
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
- equalizerAvailable = true;
- visualizerAvailable = true;
- }
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
-
- new Thread(new Runnable() {
- public void run() {
- Looper.prepare();
-
- mediaPlayer = new MediaPlayer();
- mediaPlayer.setWakeMode(DownloadServiceImpl.this, PowerManager.PARTIAL_WAKE_LOCK);
-
- mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
- @Override
- public boolean onError(MediaPlayer mediaPlayer, int what, int more) {
- handleError(new Exception("MediaPlayer error: " + what + " (" + more + ")"));
- return false;
- }
- });
-
- try {
- Intent i = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
- i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mediaPlayer.getAudioSessionId());
- i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
- sendBroadcast(i);
- } catch(Throwable e) {
- // Froyo or lower
- }
-
- mediaPlayerLooper = Looper.myLooper();
- mediaPlayerHandler = new Handler(mediaPlayerLooper);
- Looper.loop();
- }
- }).start();
-
- Util.registerMediaButtonEventReceiver(this);
-
- if (mRemoteControl == null) {
- // Use the remote control APIs (if available) to set the playback state
- mRemoteControl = RemoteControlClientHelper.createInstance();
- ComponentName mediaButtonReceiverComponent = new ComponentName(getPackageName(), MediaButtonIntentReceiver.class.getName());
- mRemoteControl.register(this, mediaButtonReceiverComponent);
- }
-
- PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
- wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName());
- wakeLock.setReferenceCounted(false);
-
- SharedPreferences prefs = Util.getPreferences(this);
- try {
- timerDuration = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_SLEEP_TIMER_DURATION, "5"));
- } catch(Throwable e) {
- timerDuration = 5;
- }
- sleepTimer = null;
-
- keepScreenOn = prefs.getBoolean(Constants.PREFERENCES_KEY_KEEP_SCREEN_ON, false);
-
- mediaRouter = new MediaRouteManager(this);
-
- instance = this;
- lifecycleSupport.onCreate();
-
- if(prefs.getBoolean(Constants.PREFERENCES_EQUALIZER_ON, false)) {
- getEqualizerController();
- }
- if(prefs.getBoolean(Constants.PREFERENCES_VISUALIZER_ON, false)) {
- getVisualizerController();
- showVisualization = true;
- }
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- super.onStartCommand(intent, flags, startId);
- lifecycleSupport.onStart(intent);
- return START_NOT_STICKY;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- instance = null;
-
- if(currentPlaying != null) currentPlaying.setPlaying(false);
- if(sleepTimer != null){
- sleepTimer.cancel();
- sleepTimer.purge();
- }
- lifecycleSupport.onDestroy();
-
- try {
- Intent i = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
- i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mediaPlayer.getAudioSessionId());
- i.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getPackageName());
- sendBroadcast(i);
- } catch(Throwable e) {
- // Froyo or lower
- }
-
- mediaPlayer.release();
- if(nextMediaPlayer != null) {
- nextMediaPlayer.release();
- }
- mediaPlayerLooper.quit();
- shufflePlayBuffer.shutdown();
- if (equalizerController != null) {
- equalizerController.release();
- }
- if (visualizerController != null) {
- visualizerController.release();
- }
- if (mRemoteControl != null) {
- mRemoteControl.unregister(this);
- mRemoteControl = null;
- }
-
- if(bufferTask != null) {
- bufferTask.cancel();
- }
- if(nextPlayingTask != null) {
- nextPlayingTask.cancel();
- }
- if(remoteController != null) {
- remoteController.stop();
- remoteController.shutdown();
- }
- mediaRouter.destroy();
- Util.hidePlayingNotification(this, this, handler);
- Util.hideDownloadingNotification(this);
- }
-
- public static DownloadService getInstance() {
- return instance;
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return binder;
- }
-
- @Override
- public synchronized void download(Bookmark bookmark) {
- clear();
- DownloadFile downloadFile = new DownloadFile(this, bookmark.getEntry(), false);
- downloadList.add(downloadFile);
- revision++;
- updateJukeboxPlaylist();
- play(0, true, bookmark.getPosition());
- lifecycleSupport.serializeDownloadQueue();
- }
-
- @Override
- public synchronized void download(List<MusicDirectory.Entry> songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle) {
- setShufflePlayEnabled(false);
- int offset = 1;
-
- if (songs.isEmpty()) {
- return;
- }
- if (playNext) {
- if (autoplay && getCurrentPlayingIndex() >= 0) {
- offset = 0;
- }
- for (MusicDirectory.Entry song : songs) {
- if(song != null) {
- DownloadFile downloadFile = new DownloadFile(this, song, save);
- downloadList.add(getCurrentPlayingIndex() + offset, downloadFile);
- offset++;
- }
- }
- setNextPlaying();
- revision++;
- } else {
- int size = size();
- int index = getCurrentPlayingIndex();
- for (MusicDirectory.Entry song : songs) {
- DownloadFile downloadFile = new DownloadFile(this, song, save);
- downloadList.add(downloadFile);
- }
- if(!autoplay && (size - 1) == index) {
- setNextPlaying();
- }
- revision++;
- }
- updateJukeboxPlaylist();
-
- if(shuffle) {
- shuffle();
- }
-
- if (autoplay) {
- play(0);
- } else {
- if (currentPlaying == null) {
- currentPlaying = downloadList.get(0);
- currentPlayingIndex = 0;
- currentPlaying.setPlaying(true);
- }
- checkDownloads();
- }
- lifecycleSupport.serializeDownloadQueue();
- }
- public synchronized void downloadBackground(List<MusicDirectory.Entry> songs, boolean save) {
- for (MusicDirectory.Entry song : songs) {
- DownloadFile downloadFile = new DownloadFile(this, song, save);
- if(!downloadFile.isWorkDone() || (downloadFile.shouldSave() && !downloadFile.isSaved())) {
- // Only add to list if there is work to be done
- backgroundDownloadList.add(downloadFile);
- }
- }
- revision++;
-
- checkDownloads();
- lifecycleSupport.serializeDownloadQueue();
- }
-
- private void updateJukeboxPlaylist() {
- if (remoteState != RemoteControlState.LOCAL) {
- remoteController.updatePlaylist();
- }
- }
-
- public void restore(List<MusicDirectory.Entry> songs, List<MusicDirectory.Entry> toDelete, int currentPlayingIndex, int currentPlayingPosition) {
- SharedPreferences prefs = Util.getPreferences(this);
- remoteState = RemoteControlState.values()[prefs.getInt(Constants.PREFERENCES_KEY_CONTROL_MODE, 0)];
- if(remoteState != RemoteControlState.LOCAL) {
- String id = prefs.getString(Constants.PREFERENCES_KEY_CONTROL_ID, null);
- setRemoteState(remoteState, null, id);
- }
- boolean startShufflePlay = prefs.getBoolean(Constants.PREFERENCES_KEY_SHUFFLE_MODE, false);
- download(songs, false, false, false, false);
- if(startShufflePlay) {
- shufflePlay = true;
- SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(Constants.PREFERENCES_KEY_SHUFFLE_MODE, true);
- editor.commit();
- }
- if (currentPlayingIndex != -1) {
- while(mediaPlayer == null) {
- Util.sleepQuietly(50L);
- }
-
- play(currentPlayingIndex, false);
- if (currentPlaying != null && currentPlaying.isCompleteFileAvailable() && remoteState == RemoteControlState.LOCAL) {
- doPlay(currentPlaying, currentPlayingPosition, autoPlayStart);
- }
- autoPlayStart = false;
- }
-
- if(toDelete != null) {
- for(MusicDirectory.Entry entry: toDelete) {
- this.toDelete.add(forSong(entry));
- }
- }
- }
-
- @Override
- public synchronized void setShufflePlayEnabled(boolean enabled) {
- shufflePlay = enabled;
- if (shufflePlay) {
- clear();
- checkDownloads();
- }
- SharedPreferences.Editor editor = Util.getPreferences(this).edit();
- editor.putBoolean(Constants.PREFERENCES_KEY_SHUFFLE_MODE, enabled);
- editor.commit();
- }
-
- @Override
- public boolean isShufflePlayEnabled() {
- return shufflePlay;
- }
-
- @Override
- public synchronized void shuffle() {
- Collections.shuffle(downloadList);
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
- if (currentPlaying != null) {
- downloadList.remove(getCurrentPlayingIndex());
- downloadList.add(0, currentPlaying);
- currentPlayingIndex = 0;
- }
- revision++;
- lifecycleSupport.serializeDownloadQueue();
- updateJukeboxPlaylist();
- setNextPlaying();
- }
-
- @Override
- public RepeatMode getRepeatMode() {
- return Util.getRepeatMode(this);
- }
-
- @Override
- public void setRepeatMode(RepeatMode repeatMode) {
- Util.setRepeatMode(this, repeatMode);
- setNextPlaying();
- }
-
- @Override
- public boolean getKeepScreenOn() {
- return keepScreenOn;
- }
-
- @Override
- public void setKeepScreenOn(boolean keepScreenOn) {
- this.keepScreenOn = keepScreenOn;
-
- SharedPreferences prefs = Util.getPreferences(this);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(Constants.PREFERENCES_KEY_KEEP_SCREEN_ON, keepScreenOn);
- editor.commit();
- }
-
- @Override
- public boolean getShowVisualization() {
- return showVisualization;
- }
-
- @Override
- public void setShowVisualization(boolean showVisualization) {
- this.showVisualization = showVisualization;
- SharedPreferences.Editor editor = Util.getPreferences(this).edit();
- editor.putBoolean(Constants.PREFERENCES_VISUALIZER_ON, showVisualization);
- editor.commit();
- }
-
- @Override
- public synchronized DownloadFile forSong(MusicDirectory.Entry song) {
- DownloadFile returnFile = null;
- for (DownloadFile downloadFile : downloadList) {
- if (downloadFile.getSong().equals(song)) {
- if(((downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && downloadFile.getPartialFile().exists()) || downloadFile.isWorkDone())) {
- // If downloading, return immediately
- return downloadFile;
- } else {
- // Otherwise, check to make sure there isn't a background download going on first
- returnFile = downloadFile;
- }
- }
- }
- for (DownloadFile downloadFile : backgroundDownloadList) {
- if (downloadFile.getSong().equals(song)) {
- return downloadFile;
- }
- }
-
- if(returnFile != null) {
- return returnFile;
- }
-
- DownloadFile downloadFile = downloadFileCache.get(song);
- if (downloadFile == null) {
- downloadFile = new DownloadFile(this, song, false);
- downloadFileCache.put(song, downloadFile);
- }
- return downloadFile;
- }
-
- @Override
- public synchronized void clear() {
- clear(true);
- }
-
- @Override
- public synchronized void clearBackground() {
- if(currentDownloading != null && backgroundDownloadList.contains(currentDownloading)) {
- currentDownloading.cancelDownload();
- currentDownloading = null;
- }
- backgroundDownloadList.clear();
- Util.hideDownloadingNotification(this);
- }
-
- @Override
- public synchronized void clearIncomplete() {
- reset();
- Iterator<DownloadFile> iterator = downloadList.iterator();
- while (iterator.hasNext()) {
- DownloadFile downloadFile = iterator.next();
- if (!downloadFile.isCompleteFileAvailable()) {
- iterator.remove();
- }
- }
- lifecycleSupport.serializeDownloadQueue();
- updateJukeboxPlaylist();
- }
-
- @Override
- public synchronized int size() {
- return downloadList.size();
- }
-
- public synchronized void clear(boolean serialize) {
- // Delete podcast if fully listened to
- if(currentPlaying != null && currentPlaying.getSong() instanceof PodcastEpisode) {
- int duration = getPlayerDuration();
-
- // Make sure > 90% of the way through
- int cutoffPoint = (int)(duration * 0.90);
- if(duration > 0 && cachedPosition > cutoffPoint) {
- currentPlaying.delete();
- }
- }
- for(DownloadFile podcast: toDelete) {
- podcast.delete();
- }
- toDelete.clear();
-
- reset();
- downloadList.clear();
- revision++;
- if (currentDownloading != null) {
- currentDownloading.cancelDownload();
- currentDownloading = null;
- }
- setCurrentPlaying(null, false);
-
- if (serialize) {
- lifecycleSupport.serializeDownloadQueue();
- }
- updateJukeboxPlaylist();
- setNextPlaying();
- }
-
- @Override
- public synchronized void remove(int which) {
- downloadList.remove(which);
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
- }
-
- @Override
- public synchronized void remove(DownloadFile downloadFile) {
- if (downloadFile == currentDownloading) {
- currentDownloading.cancelDownload();
- currentDownloading = null;
- }
- if (downloadFile == currentPlaying) {
- reset();
- setCurrentPlaying(null, false);
- }
- downloadList.remove(downloadFile);
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
- backgroundDownloadList.remove(downloadFile);
- revision++;
- lifecycleSupport.serializeDownloadQueue();
- updateJukeboxPlaylist();
- if(downloadFile == nextPlaying) {
- setNextPlaying();
- }
- }
-
- @Override
- public synchronized void delete(List<MusicDirectory.Entry> songs) {
- for (MusicDirectory.Entry song : songs) {
- forSong(song).delete();
- }
- }
-
- @Override
- public synchronized void unpin(List<MusicDirectory.Entry> songs) {
- for (MusicDirectory.Entry song : songs) {
- forSong(song).unpin();
- }
- }
-
- synchronized void setCurrentPlaying(int currentPlayingIndex, boolean showNotification) {
- try {
- setCurrentPlaying(downloadList.get(currentPlayingIndex), showNotification);
- } catch (IndexOutOfBoundsException x) {
- // Ignored
- }
- }
-
- synchronized void setCurrentPlaying(DownloadFile currentPlaying, boolean showNotification) {
- if(this.currentPlaying != null) {
- this.currentPlaying.setPlaying(false);
- }
- this.currentPlaying = currentPlaying;
- if(currentPlaying == null) {
- currentPlayingIndex = -1;
- } else {
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
- }
-
- if (currentPlaying != null) {
- Util.broadcastNewTrackInfo(this, currentPlaying.getSong());
- mRemoteControl.updateMetadata(this, currentPlaying.getSong());
- } else {
- Util.broadcastNewTrackInfo(this, null);
- Util.hidePlayingNotification(this, this, handler);
- }
- }
-
- synchronized void setNextPlaying() {
- SharedPreferences prefs = Util.getPreferences(DownloadServiceImpl.this);
- boolean gaplessPlayback = prefs.getBoolean(Constants.PREFERENCES_KEY_GAPLESS_PLAYBACK, true);
- if(!gaplessPlayback) {
- nextPlaying = null;
- nextPlayerState = IDLE;
- return;
- }
-
- int index = getNextPlayingIndex();
-
- nextSetup = false;
- if(nextPlayingTask != null) {
- nextPlayingTask.cancel();
- nextPlayingTask = null;
- }
- if(index < size() && index != -1) {
- nextPlaying = downloadList.get(index);
- nextPlayingTask = new CheckCompletionTask(nextPlaying);
- nextPlayingTask.start();
- } else {
- nextPlaying = null;
- setNextPlayerState(IDLE);
- }
- }
-
- @Override
- public int getCurrentPlayingIndex() {
- return currentPlayingIndex;
- }
- private int getNextPlayingIndex() {
- int index = getCurrentPlayingIndex();
- if (index != -1) {
- switch (getRepeatMode()) {
- case OFF:
- index = index + 1;
- break;
- case ALL:
- index = (index + 1) % size();
- break;
- case SINGLE:
- break;
- default:
- break;
- }
- }
- return index;
- }
-
- @Override
- public DownloadFile getCurrentPlaying() {
- return currentPlaying;
- }
-
- @Override
- public DownloadFile getCurrentDownloading() {
- return currentDownloading;
- }
-
- @Override
- public List<DownloadFile> getSongs() {
- return downloadList;
- }
-
- public List<DownloadFile> getToDelete() { return toDelete; }
-
- @Override
- public synchronized List<DownloadFile> getDownloads() {
- List<DownloadFile> temp = new ArrayList<DownloadFile>();
- temp.addAll(downloadList);
- temp.addAll(backgroundDownloadList);
- return temp;
- }
-
- @Override
- public List<DownloadFile> getBackgroundDownloads() {
- return backgroundDownloadList;
- }
-
- /** Plays either the current song (resume) or the first/next one in queue. */
- public synchronized void play()
- {
- int current = getCurrentPlayingIndex();
- if (current == -1) {
- play(0);
- } else {
- play(current);
- }
- }
-
- @Override
- public synchronized void play(int index) {
- play(index, true);
- }
- private synchronized void play(int index, boolean start) {
- play(index, start, 0);
- }
- private synchronized void play(int index, boolean start, int position) {
- if (index < 0 || index >= size()) {
- reset();
- setCurrentPlaying(null, false);
- lifecycleSupport.serializeDownloadQueue();
- } else {
- if(nextPlayingTask != null) {
- nextPlayingTask.cancel();
- nextPlayingTask = null;
- }
- setCurrentPlaying(index, start);
- if (start) {
- if (remoteState != RemoteControlState.LOCAL) {
- remoteController.changeTrack(index, downloadList.get(index));
- setPlayerState(STARTED);
- } else {
- bufferAndPlay(position);
- }
- }
- if (remoteState == RemoteControlState.LOCAL) {
- checkDownloads();
- setNextPlaying();
- }
- }
- }
- private synchronized void playNext() {
- if(nextPlaying != null && nextPlayerState == PlayerState.PREPARED) {
- if(!nextSetup) {
- playNext(true);
- } else {
- nextSetup = false;
- playNext(false);
- }
- } else {
- onSongCompleted();
- }
- }
- private synchronized void playNext(boolean start) {
- // Swap the media players since nextMediaPlayer is ready to play
- if(start) {
- nextMediaPlayer.start();
- } else if(!nextMediaPlayer.isPlaying()) {
- Log.w(TAG, "nextSetup lied about it's state!");
- nextMediaPlayer.start();
- } else {
- Log.i(TAG, "nextMediaPlayer already playing");
- }
- MediaPlayer tmp = mediaPlayer;
- mediaPlayer = nextMediaPlayer;
- nextMediaPlayer = tmp;
- setCurrentPlaying(nextPlaying, true);
- setPlayerState(PlayerState.STARTED);
- setupHandlers(currentPlaying, false);
- setNextPlaying();
-
- // Proxy should not be being used here since the next player was already setup to play
- if(proxy != null) {
- proxy.stop();
- proxy = null;
- }
- }
-
- /** Plays or resumes the playback, depending on the current player state. */
- public synchronized void togglePlayPause() {
- if (playerState == PAUSED || playerState == COMPLETED || playerState == STOPPED) {
- start();
- } else if (playerState == STOPPED || playerState == IDLE) {
- autoPlayStart = true;
- play();
- } else if (playerState == STARTED) {
- pause();
- }
- }
-
- @Override
- public synchronized void seekTo(int position) {
- try {
- if (remoteState != RemoteControlState.LOCAL) {
- remoteController.changePosition(position / 1000);
- } else {
- mediaPlayer.seekTo(position);
- cachedPosition = position;
- }
- } catch (Exception x) {
- handleError(x);
- }
- }
-
- @Override
- public synchronized void previous() {
- int index = getCurrentPlayingIndex();
- if (index == -1) {
- return;
- }
-
- // Restart song if played more than five seconds.
- if (getPlayerPosition() > 5000 || (index == 0 && getRepeatMode() != RepeatMode.ALL)) {
- play(index);
- } else {
- if(index == 0) {
- index = size();
- }
-
- play(index - 1);
- }
- }
-
- @Override
- public synchronized void next() {
- // Delete podcast if fully listened to
- if(currentPlaying != null && currentPlaying.getSong() instanceof PodcastEpisode) {
- int duration = getPlayerDuration();
-
- // Make sure > 90% of the way through
- int cutoffPoint = (int)(duration * 0.90);
- if(duration > 0 && cachedPosition > cutoffPoint) {
- toDelete.add(currentPlaying);
- }
- }
-
- int index = getCurrentPlayingIndex();
- int nextPlayingIndex = getNextPlayingIndex();
- // Make sure to actually go to next when repeat song is on
- if(index == nextPlayingIndex) {
- nextPlayingIndex++;
- }
- if (index != -1 && nextPlayingIndex < size()) {
- if(nextPlaying != null && downloadList.get(nextPlayingIndex) == nextPlaying && nextPlayerState == PlayerState.PREPARED && remoteState == RemoteControlState.LOCAL) {
- if(mediaPlayer.isPlaying()) {
- mediaPlayer.stop();
- }
- mediaPlayer.setOnErrorListener(null);
- mediaPlayer.setOnCompletionListener(null);
- mediaPlayer.reset();
- playNext(true);
- } else {
- play(nextPlayingIndex);
- }
- }
- }
-
- private void onSongCompleted() {
- play(getNextPlayingIndex());
- }
-
- @Override
- public synchronized void pause() {
- try {
- if (playerState == STARTED) {
- if (remoteState != RemoteControlState.LOCAL) {
- remoteController.stop();
- } else {
- mediaPlayer.pause();
- }
- setPlayerState(PAUSED);
- }
- } catch (Exception x) {
- handleError(x);
- }
- }
-
- @Override
- public synchronized void stop() {
- try {
- if (playerState == STARTED) {
- if (remoteState != RemoteControlState.LOCAL) {
- remoteController.stop();
- } else {
- mediaPlayer.pause();
- }
- setPlayerState(STOPPED);
- } else if(playerState == PAUSED) {
- setPlayerState(STOPPED);
- }
- } catch(Exception x) {
- handleError(x);
- }
- }
-
- @Override
- public synchronized void start() {
- try {
- if (remoteState != RemoteControlState.LOCAL) {
- remoteController.start();
- } else {
- mediaPlayer.start();
- }
- setPlayerState(STARTED);
- } catch (Exception x) {
- handleError(x);
- }
- }
-
- @Override
- public synchronized void reset() {
- if (bufferTask != null) {
- bufferTask.cancel();
- }
- try {
- // Only set to idle if it's not being killed to start RemoteController
- if(remoteState == RemoteControlState.LOCAL) {
- setPlayerState(IDLE);
- }
- mediaPlayer.setOnErrorListener(null);
- mediaPlayer.setOnCompletionListener(null);
- mediaPlayer.reset();
- } catch (Exception x) {
- handleError(x);
- }
- }
-
- @Override
- public int getPlayerPosition() {
- try {
- if (playerState == IDLE || playerState == DOWNLOADING || playerState == PREPARING) {
- return 0;
- }
- if (remoteState != RemoteControlState.LOCAL) {
- return remoteController.getRemotePosition() * 1000;
- } else {
- return cachedPosition;
- }
- } catch (Exception x) {
- handleError(x);
- return 0;
- }
- }
-
- @Override
- public synchronized int getPlayerDuration() {
- if (currentPlaying != null) {
- Integer duration = currentPlaying.getSong().getDuration();
- if (duration != null) {
- return duration * 1000;
- }
- }
- if (playerState != IDLE && playerState != DOWNLOADING && playerState != PlayerState.PREPARING) {
- try {
- return mediaPlayer.getDuration();
- } catch (Exception x) {
- handleError(x);
- }
- }
- return 0;
- }
-
- @Override
- public PlayerState getPlayerState() {
- return playerState;
- }
-
- public synchronized void setPlayerState(final PlayerState playerState) {
- Log.i(TAG, this.playerState.name() + " -> " + playerState.name() + " (" + currentPlaying + ")");
-
- if (playerState == PAUSED) {
- lifecycleSupport.serializeDownloadQueue();
- }
-
- boolean show = playerState == PlayerState.STARTED;
- boolean pause = playerState == PlayerState.PAUSED;
- boolean hide = playerState == PlayerState.STOPPED;
- Util.broadcastPlaybackStatusChange(this, (currentPlaying != null) ? currentPlaying.getSong() : null, playerState);
-
- this.playerState = playerState;
-
- if(playerState == STARTED) {
- Util.requestAudioFocus(this);
- }
-
- if (show) {
- Util.showPlayingNotification(this, this, handler, currentPlaying.getSong());
- } else if (pause) {
- SharedPreferences prefs = Util.getPreferences(this);
- if(prefs.getBoolean(Constants.PREFERENCES_KEY_PERSISTENT_NOTIFICATION, false)) {
- Util.showPlayingNotification(this, this, handler, currentPlaying.getSong());
- } else {
- Util.hidePlayingNotification(this, this, handler);
- }
- } else if(hide) {
- Util.hidePlayingNotification(this, this, handler);
- }
- mRemoteControl.setPlaybackState(playerState.getRemoteControlClientPlayState());
-
- if (playerState == STARTED) {
- scrobbler.scrobble(this, currentPlaying, false);
- } else if (playerState == COMPLETED) {
- scrobbler.scrobble(this, currentPlaying, true);
- }
-
- if(playerState == STARTED && positionCache == null) {
- positionCache = new PositionCache();
- Thread thread = new Thread(positionCache);
- thread.start();
- } else if(playerState != STARTED && positionCache != null) {
- positionCache.stop();
- positionCache = null;
- }
- }
-
- private class PositionCache implements Runnable {
- boolean isRunning = true;
-
- public void stop() {
- isRunning = false;
- }
-
- @Override
- public void run() {
- // Stop checking position before the song reaches completion
- while(isRunning) {
- try {
- if(mediaPlayer != null && playerState == STARTED) {
- cachedPosition = mediaPlayer.getCurrentPosition();
- }
- Thread.sleep(1000L);
- }
- catch(Exception e) {
- Log.w(TAG, "Crashed getting current position", e);
- isRunning = false;
- positionCache = null;
- }
- }
- }
- }
-
- private void setPlayerStateCompleted() {
- Log.i(TAG, this.playerState.name() + " -> " + PlayerState.COMPLETED + " (" + currentPlaying + ")");
- this.playerState = PlayerState.COMPLETED;
- if(positionCache != null) {
- positionCache.stop();
- positionCache = null;
- }
- scrobbler.scrobble(this, currentPlaying, true);
- }
-
- private synchronized void setNextPlayerState(PlayerState playerState) {
- Log.i(TAG, "Next: " + this.nextPlayerState.name() + " -> " + playerState.name() + " (" + nextPlaying + ")");
- this.nextPlayerState = playerState;
- }
-
- @Override
- public void setSuggestedPlaylistName(String name, String id) {
- this.suggestedPlaylistName = name;
- this.suggestedPlaylistId = id;
- }
-
- @Override
- public String getSuggestedPlaylistName() {
- return suggestedPlaylistName;
- }
-
- @Override
- public String getSuggestedPlaylistId() {
- return suggestedPlaylistId;
- }
-
- @Override
- public boolean getEqualizerAvailable() {
- return equalizerAvailable;
- }
-
- @Override
- public boolean getVisualizerAvailable() {
- return visualizerAvailable;
- }
-
- @Override
- public EqualizerController getEqualizerController() {
- if (equalizerAvailable && equalizerController == null) {
- equalizerController = new EqualizerController(this, mediaPlayer);
- if (!equalizerController.isAvailable()) {
- equalizerController = null;
- } else {
- equalizerController.loadSettings();
- }
- }
- return equalizerController;
- }
-
- @Override
- public VisualizerController getVisualizerController() {
- if (visualizerAvailable && visualizerController == null) {
- visualizerController = new VisualizerController(this, mediaPlayer);
- if (!visualizerController.isAvailable()) {
- visualizerController = null;
- }
- }
- return visualizerController;
- }
-
- @Override
- public MediaRouteSelector getRemoteSelector() {
- return mediaRouter.getSelector();
- }
-
- @Override
- public boolean isRemoteEnabled() {
- return remoteState != RemoteControlState.LOCAL;
- }
-
- @Override
- public void setRemoteEnabled(RemoteControlState newState) {
- if(instance != null) {
- setRemoteEnabled(newState, null);
- }
- }
- public void setRemoteEnabled(RemoteControlState newState, Object ref) {
- setRemoteState(newState, ref);
-
- RouteInfo info = mediaRouter.getSelectedRoute();
- String routeId = info.getId();
-
- SharedPreferences.Editor editor = Util.getPreferences(this).edit();
- editor.putInt(Constants.PREFERENCES_KEY_CONTROL_MODE, newState.getValue());
- editor.putString(Constants.PREFERENCES_KEY_CONTROL_ID, routeId);
- editor.commit();
- }
- private void setRemoteState(RemoteControlState newState, Object ref) {
- setRemoteState(newState, ref, null);
- }
- private void setRemoteState(final RemoteControlState newState, final Object ref, final String routeId) {
- if(remoteController != null) {
- remoteController.stop();
- setPlayerState(PlayerState.IDLE);
- remoteController.shutdown();
- remoteController = null;
- }
-
- remoteState = newState;
- switch(newState) {
- case JUKEBOX_SERVER:
- remoteController = new JukeboxController(this, handler);
- break;
- case CHROMECAST:
- // TODO: Fix case where starting up with chromecast set
- if(ref == null) {
- remoteState = RemoteControlState.LOCAL;
- break;
- }
- remoteController = (RemoteController) ref;
- break;
- case LOCAL: default:
- break;
- }
-
- if (remoteState != RemoteControlState.LOCAL) {
- reset();
-
- // Cancel current download, if necessary.
- if (currentDownloading != null) {
- currentDownloading.cancelDownload();
- }
- }
-
- SharedPreferences prefs = Util.getPreferences(this);
- if(currentPlaying != null && prefs.getBoolean(Constants.PREFERENCES_KEY_PERSISTENT_NOTIFICATION, false)) {
- Util.showPlayingNotification(this, this, handler, currentPlaying.getSong());
- } else {
- Util.hidePlayingNotification(this, this, handler);
- }
-
- if(routeId != null) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- RouteInfo info = mediaRouter.getRouteForId(routeId);
- if(info == null) {
- setRemoteState(RemoteControlState.LOCAL, null);
- } else if(newState == RemoteControlState.CHROMECAST) {
- RemoteController controller = mediaRouter.getRemoteController(info);
- if(controller != null) {
- setRemoteState(RemoteControlState.CHROMECAST, controller);
- }
- }
- }
- });
- }
- }
-
- @Override
- public void setRemoteVolume(boolean up) {
- remoteController.setVolume(up);
- }
-
- @Override
- public void startRemoteScan() {
- mediaRouter.startScan();
- }
-
- @Override
- public void stopRemoteScan() {
- mediaRouter.stopScan();
- }
-
- private synchronized void bufferAndPlay() {
- bufferAndPlay(0);
- }
- private synchronized void bufferAndPlay(int position) {
- if(playerState != PREPARED) {
- reset();
-
- bufferTask = new BufferTask(currentPlaying, position);
- bufferTask.start();
- } else {
- doPlay(currentPlaying, position, true);
- }
- }
-
- private synchronized void doPlay(final DownloadFile downloadFile, final int position, final boolean start) {
- try {
- downloadFile.setPlaying(true);
- final File file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile();
- isPartial = file.equals(downloadFile.getPartialFile());
- downloadFile.updateModificationDate();
-
- mediaPlayer.setOnCompletionListener(null);
- mediaPlayer.reset();
- setPlayerState(IDLE);
- mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- String dataSource = file.getPath();
- if(isPartial) {
- if (proxy == null) {
- proxy = new StreamProxy(this);
- proxy.start();
- }
- dataSource = String.format("http://127.0.0.1:%d/%s", proxy.getPort(), URLEncoder.encode(dataSource, Constants.UTF_8));
- Log.i(TAG, "Data Source: " + dataSource);
- } else if(proxy != null) {
- proxy.stop();
- proxy = null;
- }
- mediaPlayer.setDataSource(dataSource);
- setPlayerState(PREPARING);
-
- mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
- public void onBufferingUpdate(MediaPlayer mp, int percent) {
- Log.i(TAG, "Buffered " + percent + "%");
- if(percent == 100) {
- mediaPlayer.setOnBufferingUpdateListener(null);
- }
- }
- });
-
- mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
- public void onPrepared(MediaPlayer mediaPlayer) {
- try {
- setPlayerState(PREPARED);
-
- synchronized (DownloadServiceImpl.this) {
- if (position != 0) {
- Log.i(TAG, "Restarting player from position " + position);
- mediaPlayer.seekTo(position);
- }
- cachedPosition = position;
-
- if (start) {
- mediaPlayer.start();
- setPlayerState(STARTED);
- } else {
- setPlayerState(PAUSED);
- }
- }
-
- // Only call when starting, setPlayerState(PAUSED) already calls this
- if(start) {
- lifecycleSupport.serializeDownloadQueue();
- }
- } catch (Exception x) {
- handleError(x);
- }
- }
- });
-
- setupHandlers(downloadFile, isPartial);
-
- mediaPlayer.prepareAsync();
- } catch (Exception x) {
- handleError(x);
- }
- }
-
- private synchronized void setupNext(final DownloadFile downloadFile) {
- try {
- final File file = downloadFile.isCompleteFileAvailable() ? downloadFile.getCompleteFile() : downloadFile.getPartialFile();
- if(nextMediaPlayer != null) {
- nextMediaPlayer.setOnCompletionListener(null);
- nextMediaPlayer.setOnErrorListener(null);
- nextMediaPlayer.reset();
- nextMediaPlayer.release();
- nextMediaPlayer = null;
- }
-
- // Exit when using remote controllers
- if(remoteState != RemoteControlState.LOCAL) {
- return;
- }
-
- nextMediaPlayer = new MediaPlayer();
- nextMediaPlayer.setWakeMode(DownloadServiceImpl.this, PowerManager.PARTIAL_WAKE_LOCK);
- try {
- nextMediaPlayer.setAudioSessionId(mediaPlayer.getAudioSessionId());
- } catch(Throwable e) {
- nextMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- }
- nextMediaPlayer.setDataSource(file.getPath());
- setNextPlayerState(PREPARING);
-
- nextMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
- public void onPrepared(MediaPlayer mp) {
- try {
- setNextPlayerState(PREPARED);
-
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && (playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED)) {
- mediaPlayer.setNextMediaPlayer(nextMediaPlayer);
- nextSetup = true;
- }
- } catch (Exception x) {
- handleErrorNext(x);
- }
- }
- });
-
- nextMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
- public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
- Log.w(TAG, "Error on playing next " + "(" + what + ", " + extra + "): " + downloadFile);
- return true;
- }
- });
-
- nextMediaPlayer.prepareAsync();
- } catch (Exception x) {
- handleErrorNext(x);
- }
- }
-
- private void setupHandlers(final DownloadFile downloadFile, final boolean isPartial) {
- final int duration = downloadFile.getSong().getDuration() == null ? 0 : downloadFile.getSong().getDuration() * 1000;
- mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
- public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
- Log.w(TAG, "Error on playing file " + "(" + what + ", " + extra + "): " + downloadFile);
- int pos = cachedPosition;
- reset();
- if (!isPartial || (downloadFile.isWorkDone() && (Math.abs(duration - pos) < 10000))) {
- playNext();
- } else {
- downloadFile.setPlaying(false);
- doPlay(downloadFile, pos, true);
- downloadFile.setPlaying(true);
- }
- return true;
- }
- });
-
- mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
- @Override
- public void onCompletion(MediaPlayer mediaPlayer) {
- // Acquire a temporary wakelock, since when we return from
- // this callback the MediaPlayer will release its wakelock
- // and allow the device to go to sleep.
- wakeLock.acquire(60000);
-
- setPlayerStateCompleted();
-
- int pos = cachedPosition;
- Log.i(TAG, "Ending position " + pos + " of " + duration);
- if (!isPartial || (downloadFile.isWorkDone() && (Math.abs(duration - pos) < 10000))) {
- playNext();
-
- // Finished loading, delete when list is cleared
- if(downloadFile.getSong() instanceof PodcastEpisode) {
- toDelete.add(downloadFile);
- }
- } else {
- // If file is not completely downloaded, restart the playback from the current position.
- synchronized (DownloadServiceImpl.this) {
- if(downloadFile.isWorkDone()) {
- // Complete was called early even though file is fully buffered
- Log.i(TAG, "Requesting restart from " + pos + " of " + duration);
- reset();
- downloadFile.setPlaying(false);
- doPlay(downloadFile, pos, true);
- downloadFile.setPlaying(true);
- } else {
- Log.i(TAG, "Requesting restart from " + pos + " of " + duration);
- reset();
- bufferTask = new BufferTask(downloadFile, pos);
- bufferTask.start();
- }
- }
- }
- }
- });
- }
-
- @Override
- public void setSleepTimerDuration(int duration){
- timerDuration = duration;
- }
-
- @Override
- public void startSleepTimer(){
- if(sleepTimer != null){
- sleepTimer.cancel();
- sleepTimer.purge();
- }
-
- sleepTimer = new Timer();
-
- sleepTimer.schedule(new TimerTask() {
- @Override
- public void run() {
- pause();
- sleepTimer.cancel();
- sleepTimer.purge();
- sleepTimer = null;
- }
-
- }, timerDuration * 60 * 1000);
- }
-
- @Override
- public void stopSleepTimer() {
- if(sleepTimer != null){
- sleepTimer.cancel();
- sleepTimer.purge();
- }
- sleepTimer = null;
- }
-
- @Override
- public boolean getSleepTimer() {
- return sleepTimer != null;
- }
-
- @Override
- public void setVolume(float volume) {
- if(mediaPlayer != null) {
- mediaPlayer.setVolume(volume, volume);
- }
- }
-
- @Override
- public synchronized void swap(boolean mainList, int from, int to) {
- List<DownloadFile> list = mainList ? downloadList : backgroundDownloadList;
- int max = list.size();
- if(to >= max) {
- to = max - 1;
- }
- else if(to < 0) {
- to = 0;
- }
-
- int currentPlayingIndex = getCurrentPlayingIndex();
- DownloadFile movedSong = list.remove(from);
- list.add(to, movedSong);
- if(remoteState != RemoteControlState.LOCAL && mainList) {
- updateJukeboxPlaylist();
- } else if(mainList && (movedSong == nextPlaying || (currentPlayingIndex + 1) == to)) {
- // Moving next playing or moving a song to be next playing
- setNextPlaying();
- }
- }
-
- private void handleError(Exception x) {
- Log.w(TAG, "Media player error: " + x, x);
- if(mediaPlayer != null) {
- mediaPlayer.reset();
- }
- setPlayerState(IDLE);
- }
- private void handleErrorNext(Exception x) {
- Log.w(TAG, "Next Media player error: " + x, x);
- nextMediaPlayer.reset();
- setNextPlayerState(IDLE);
- }
-
- protected synchronized void checkDownloads() {
- if (!Util.isExternalStoragePresent() || !lifecycleSupport.isExternalStorageAvailable()) {
- return;
- }
-
- if (shufflePlay) {
- checkShufflePlay();
- }
-
- if (remoteState != RemoteControlState.LOCAL || !Util.isNetworkConnected(this) || Util.isOffline(this)) {
- return;
- }
-
- if (downloadList.isEmpty() && backgroundDownloadList.isEmpty()) {
- return;
- }
-
- // Need to download current playing?
- if (currentPlaying != null && currentPlaying != currentDownloading && !currentPlaying.isWorkDone()) {
- // Cancel current download, if necessary.
- if (currentDownloading != null) {
- currentDownloading.cancelDownload();
- }
-
- currentDownloading = currentPlaying;
- currentDownloading.download();
- cleanupCandidates.add(currentDownloading);
- }
-
- // Find a suitable target for download.
- else if (currentDownloading == null || currentDownloading.isWorkDone() || currentDownloading.isFailed() && (!downloadList.isEmpty() || !backgroundDownloadList.isEmpty())) {
- currentDownloading = null;
- int n = size();
-
- int preloaded = 0;
-
- if(n != 0) {
- int start = currentPlaying == null ? 0 : getCurrentPlayingIndex();
- if(start == -1) {
- start = 0;
- }
- int i = start;
- do {
- DownloadFile downloadFile = downloadList.get(i);
- if (!downloadFile.isWorkDone() && !downloadFile.isFailedMax()) {
- if (downloadFile.shouldSave() || preloaded < Util.getPreloadCount(this)) {
- currentDownloading = downloadFile;
- currentDownloading.download();
- cleanupCandidates.add(currentDownloading);
- if(i == (start + 1)) {
- setNextPlayerState(DOWNLOADING);
- }
- break;
- }
- } else if (currentPlaying != downloadFile) {
- preloaded++;
- }
-
- i = (i + 1) % n;
- } while (i != start);
- }
-
- if((preloaded + 1 == n || preloaded >= Util.getPreloadCount(this) || downloadList.isEmpty()) && !backgroundDownloadList.isEmpty()) {
- for(int i = 0; i < backgroundDownloadList.size(); i++) {
- DownloadFile downloadFile = backgroundDownloadList.get(i);
- if(downloadFile.isWorkDone() && (!downloadFile.shouldSave() || downloadFile.isSaved())) {
- // Don't need to keep list like active song list
- backgroundDownloadList.remove(i);
- revision++;
- i--;
- } else {
- if(!downloadFile.isFailedMax()) {
- currentDownloading = downloadFile;
- currentDownloading.download();
- cleanupCandidates.add(currentDownloading);
- break;
- }
- }
- }
- }
- }
-
- if(!backgroundDownloadList.isEmpty()) {
- DownloadFile speedFile = null;
- // Updating existing notification
- if(downloadOngoing) {
- // Changing download, use speed of last DownloadFile
- if(revision != downloadRevision && lastDownloaded != null) {
- speedFile = lastDownloaded;
- } else {
- // Updated mid-download
- speedFile = currentDownloading;
- }
- }
- Util.showDownloadingNotification(this, currentDownloading, backgroundDownloadList.size(), speedFile);
- downloadRevision = revision;
- lastDownloaded = currentDownloading;
- downloadOngoing = true;
- } else if(backgroundDownloadList.isEmpty() && downloadOngoing) {
- Util.hideDownloadingNotification(this);
- downloadOngoing = false;
- lastDownloaded = null;
- }
-
- // Delete obsolete .partial and .complete files.
- cleanup();
- }
-
- private synchronized void checkShufflePlay() {
-
- // Get users desired random playlist size
- SharedPreferences prefs = Util.getPreferences(this);
- int listSize = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_RANDOM_SIZE, "20"));
- boolean wasEmpty = downloadList.isEmpty();
-
- long revisionBefore = revision;
-
- // First, ensure that list is at least 20 songs long.
- int size = size();
- if (size < listSize) {
- for (MusicDirectory.Entry song : shufflePlayBuffer.get(listSize - size)) {
- DownloadFile downloadFile = new DownloadFile(this, song, false);
- downloadList.add(downloadFile);
- revision++;
- }
- }
-
- int currIndex = currentPlaying == null ? 0 : getCurrentPlayingIndex();
-
- // Only shift playlist if playing song #5 or later.
- if (currIndex > 4) {
- int songsToShift = currIndex - 2;
- for (MusicDirectory.Entry song : shufflePlayBuffer.get(songsToShift)) {
- downloadList.add(new DownloadFile(this, song, false));
- downloadList.get(0).cancelDownload();
- downloadList.remove(0);
- revision++;
- }
- }
- currentPlayingIndex = downloadList.indexOf(currentPlaying);
-
- if (revisionBefore != revision) {
- updateJukeboxPlaylist();
- }
-
- if (wasEmpty && !downloadList.isEmpty()) {
- play(0);
- }
- }
-
- public long getDownloadListUpdateRevision() {
- return revision;
- }
-
- private synchronized void cleanup() {
- Iterator<DownloadFile> iterator = cleanupCandidates.iterator();
- while (iterator.hasNext()) {
- DownloadFile downloadFile = iterator.next();
- if (downloadFile != currentPlaying && downloadFile != currentDownloading) {
- if (downloadFile.cleanup()) {
- iterator.remove();
- }
- }
- }
- }
-
- private class BufferTask extends CancellableTask {
- private final DownloadFile downloadFile;
- private final int position;
- private final long expectedFileSize;
- private final File partialFile;
-
- public BufferTask(DownloadFile downloadFile, int position) {
- this.downloadFile = downloadFile;
- this.position = position;
- partialFile = downloadFile.getPartialFile();
-
- // Calculate roughly how many bytes BUFFER_LENGTH_SECONDS corresponds to.
- int bitRate = downloadFile.getBitRate();
- long byteCount = Math.max(100000, bitRate * 1024L / 8L * 5L);
-
- // Find out how large the file should grow before resuming playback.
- Log.i(TAG, "Buffering from position " + position + " and bitrate " + bitRate);
- expectedFileSize = (position * bitRate / 8) + byteCount;
- }
-
- @Override
- public void execute() {
- setPlayerState(DOWNLOADING);
-
- while (!bufferComplete()) {
- Util.sleepQuietly(1000L);
- if (isCancelled()) {
- return;
- }
- }
- doPlay(downloadFile, position, true);
- }
-
- private boolean bufferComplete() {
- boolean completeFileAvailable = downloadFile.isWorkDone();
- long size = partialFile.length();
-
- Log.i(TAG, "Buffering " + partialFile + " (" + size + "/" + expectedFileSize + ", " + completeFileAvailable + ")");
- return completeFileAvailable || size >= expectedFileSize;
- }
-
- @Override
- public String toString() {
- return "BufferTask (" + downloadFile + ")";
- }
- }
-
- private class CheckCompletionTask extends CancellableTask {
- private final DownloadFile downloadFile;
- private final File partialFile;
-
- public CheckCompletionTask(DownloadFile downloadFile) {
- setNextPlayerState(PlayerState.IDLE);
- this.downloadFile = downloadFile;
- if(downloadFile != null) {
- partialFile = downloadFile.getPartialFile();
- } else {
- partialFile = null;
- }
- }
-
- @Override
- public void execute() {
- if(downloadFile == null) {
- return;
- }
-
- // Do an initial sleep so this prepare can't compete with main prepare
- Util.sleepQuietly(5000L);
- while (!bufferComplete()) {
- Util.sleepQuietly(5000L);
- if (isCancelled()) {
- return;
- }
- }
-
- // Start the setup of the next media player
- mediaPlayerHandler.post(new Runnable() {
- public void run() {
- setupNext(downloadFile);
- }
- });
- }
-
- private boolean bufferComplete() {
- boolean completeFileAvailable = downloadFile.isWorkDone();
- Log.i(TAG, "Buffering next " + partialFile + " (" + partialFile.length() + ")");
- return completeFileAvailable && (playerState == PlayerState.STARTED || playerState == PlayerState.PAUSED);
- }
-
- @Override
- public String toString() {
- return "CheckCompletionTask (" + downloadFile + ")";
- }
- }
-}
diff --git a/src/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java b/src/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java
index cd876621..1e123ef3 100644
--- a/src/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java
+++ b/src/github/daneren2005/dsub/service/DownloadServiceLifecycleSupport.java
@@ -33,8 +33,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.media.RemoteControlClient;
-import android.os.AsyncTask;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.telephony.PhoneStateListener;
@@ -56,7 +54,7 @@ public class DownloadServiceLifecycleSupport {
private static final String TAG = DownloadServiceLifecycleSupport.class.getSimpleName();
private static final String FILENAME_DOWNLOADS_SER = "downloadstate2.ser";
- private final DownloadServiceImpl downloadService;
+ private final DownloadService downloadService;
private Looper eventLooper;
private Handler eventHandler;
private ScheduledExecutorService executorService;
@@ -79,17 +77,17 @@ public class DownloadServiceLifecycleSupport {
public void run() {
String action = intent.getAction();
Log.i(TAG, "intentReceiver.onReceive: " + action);
- if (DownloadServiceImpl.CMD_PLAY.equals(action)) {
+ if (DownloadService.CMD_PLAY.equals(action)) {
downloadService.play();
- } else if (DownloadServiceImpl.CMD_NEXT.equals(action)) {
+ } else if (DownloadService.CMD_NEXT.equals(action)) {
downloadService.next();
- } else if (DownloadServiceImpl.CMD_PREVIOUS.equals(action)) {
+ } else if (DownloadService.CMD_PREVIOUS.equals(action)) {
downloadService.previous();
- } else if (DownloadServiceImpl.CMD_TOGGLEPAUSE.equals(action)) {
+ } else if (DownloadService.CMD_TOGGLEPAUSE.equals(action)) {
downloadService.togglePlayPause();
- } else if (DownloadServiceImpl.CMD_PAUSE.equals(action)) {
+ } else if (DownloadService.CMD_PAUSE.equals(action)) {
downloadService.pause();
- } else if (DownloadServiceImpl.CMD_STOP.equals(action)) {
+ } else if (DownloadService.CMD_STOP.equals(action)) {
downloadService.pause();
downloadService.seekTo(0);
}
@@ -99,7 +97,7 @@ public class DownloadServiceLifecycleSupport {
};
- public DownloadServiceLifecycleSupport(DownloadServiceImpl downloadService) {
+ public DownloadServiceLifecycleSupport(DownloadService downloadService) {
this.downloadService = downloadService;
}
@@ -189,13 +187,13 @@ public class DownloadServiceLifecycleSupport {
// Register the handler for outside intents.
IntentFilter commandFilter = new IntentFilter();
- commandFilter.addAction(DownloadServiceImpl.CMD_PLAY);
- commandFilter.addAction(DownloadServiceImpl.CMD_TOGGLEPAUSE);
- commandFilter.addAction(DownloadServiceImpl.CMD_PAUSE);
- commandFilter.addAction(DownloadServiceImpl.CMD_STOP);
- commandFilter.addAction(DownloadServiceImpl.CMD_PREVIOUS);
- commandFilter.addAction(DownloadServiceImpl.CMD_NEXT);
- commandFilter.addAction(DownloadServiceImpl.CANCEL_DOWNLOADS);
+ commandFilter.addAction(DownloadService.CMD_PLAY);
+ commandFilter.addAction(DownloadService.CMD_TOGGLEPAUSE);
+ commandFilter.addAction(DownloadService.CMD_PAUSE);
+ commandFilter.addAction(DownloadService.CMD_STOP);
+ commandFilter.addAction(DownloadService.CMD_PREVIOUS);
+ commandFilter.addAction(DownloadService.CMD_NEXT);
+ commandFilter.addAction(DownloadService.CANCEL_DOWNLOADS);
downloadService.registerReceiver(intentReceiver, commandFilter);
new CacheCleaner(downloadService, downloadService).clean();
@@ -219,7 +217,7 @@ public class DownloadServiceLifecycleSupport {
}
} else {
String action = intent.getAction();
- if(DownloadServiceImpl.CANCEL_DOWNLOADS.equals(action)) {
+ if(DownloadService.CANCEL_DOWNLOADS.equals(action)) {
downloadService.clearBackground();
}
}
diff --git a/src/github/daneren2005/dsub/service/JukeboxController.java b/src/github/daneren2005/dsub/service/JukeboxController.java
index faace3ae..eff2da0b 100644
--- a/src/github/daneren2005/dsub/service/JukeboxController.java
+++ b/src/github/daneren2005/dsub/service/JukeboxController.java
@@ -26,10 +26,8 @@ import github.daneren2005.dsub.service.parser.SubsonicRESTException;
import github.daneren2005.dsub.util.Util;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executors;
-import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -48,7 +46,7 @@ public class JukeboxController extends RemoteController {
private RemoteStatus jukeboxStatus;
private float gain = 0.5f;
- public JukeboxController(DownloadServiceImpl downloadService, Handler handler) {
+ public JukeboxController(DownloadService downloadService, Handler handler) {
this.downloadService = downloadService;
this.handler = handler;
new Thread() {
diff --git a/src/github/daneren2005/dsub/service/OfflineMusicService.java b/src/github/daneren2005/dsub/service/OfflineMusicService.java
index a3cdd437..b054eaad 100644
--- a/src/github/daneren2005/dsub/service/OfflineMusicService.java
+++ b/src/github/daneren2005/dsub/service/OfflineMusicService.java
@@ -404,7 +404,7 @@ public class OfflineMusicService extends RESTMusicService {
@Override
public MusicDirectory getPlaylist(boolean refresh, String id, String name, Context context, ProgressListener progressListener) throws Exception {
- DownloadService downloadService = DownloadServiceImpl.getInstance();
+ DownloadService downloadService = DownloadService.getInstance();
if (downloadService == null) {
return new MusicDirectory();
}
diff --git a/src/github/daneren2005/dsub/service/RemoteController.java b/src/github/daneren2005/dsub/service/RemoteController.java
index f2b8f20d..ec50be51 100644
--- a/src/github/daneren2005/dsub/service/RemoteController.java
+++ b/src/github/daneren2005/dsub/service/RemoteController.java
@@ -35,7 +35,7 @@ import github.daneren2005.dsub.domain.RemoteStatus;
public abstract class RemoteController {
private static final String TAG = RemoteController.class.getSimpleName();
- protected DownloadServiceImpl downloadService;
+ protected DownloadService downloadService;
private VolumeToast volumeToast;
public abstract void start();
diff --git a/src/github/daneren2005/dsub/util/MediaRouteManager.java b/src/github/daneren2005/dsub/util/MediaRouteManager.java
index a1bd9b6b..4a124402 100644
--- a/src/github/daneren2005/dsub/util/MediaRouteManager.java
+++ b/src/github/daneren2005/dsub/util/MediaRouteManager.java
@@ -19,14 +19,13 @@ import android.support.v7.media.MediaControlIntent;
import android.support.v7.media.MediaRouteProvider;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
-import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import github.daneren2005.dsub.domain.RemoteControlState;
import github.daneren2005.dsub.provider.JukeboxRouteProvider;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
+import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.RemoteController;
import github.daneren2005.dsub.util.compat.CastCompat;
@@ -39,7 +38,7 @@ public class MediaRouteManager extends MediaRouter.Callback {
private static final String TAG = MediaRouteManager.class.getSimpleName();
private static boolean castAvailable = false;
- private DownloadServiceImpl downloadService;
+ private DownloadService downloadService;
private MediaRouter router;
private MediaRouteSelector selector;
private List<MediaRouteProvider> providers = new ArrayList<MediaRouteProvider>();
@@ -53,7 +52,7 @@ public class MediaRouteManager extends MediaRouter.Callback {
}
}
- public MediaRouteManager(DownloadServiceImpl downloadService) {
+ public MediaRouteManager(DownloadService downloadService) {
this.downloadService = downloadService;
router = MediaRouter.getInstance(downloadService);
addProviders();
diff --git a/src/github/daneren2005/dsub/util/Util.java b/src/github/daneren2005/dsub/util/Util.java
index e2f2d02f..6140444d 100644
--- a/src/github/daneren2005/dsub/util/Util.java
+++ b/src/github/daneren2005/dsub/util/Util.java
@@ -38,7 +38,6 @@ import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
-import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Environment;
@@ -65,7 +64,6 @@ import github.daneren2005.dsub.provider.DSubWidgetProvider;
import github.daneren2005.dsub.receiver.MediaButtonIntentReceiver;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
import org.apache.http.HttpEntity;
@@ -83,8 +81,6 @@ import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -971,7 +967,7 @@ public final class Util {
((TextView)dialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
}
- public static void showPlayingNotification(final Context context, final DownloadServiceImpl downloadService, Handler handler, MusicDirectory.Entry song) {
+ public static void showPlayingNotification(final Context context, final DownloadService downloadService, Handler handler, MusicDirectory.Entry song) {
// Set the icon, scrolling text and timestamp
final Notification notification = new Notification(R.drawable.stat_notify_playing, song.getTitle(), System.currentTimeMillis());
notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
@@ -1059,7 +1055,7 @@ public final class Util {
if(previous > 0) {
Intent prevIntent = new Intent("KEYCODE_MEDIA_PREVIOUS");
- prevIntent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
+ prevIntent.setComponent(new ComponentName(context, DownloadService.class));
prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PREVIOUS));
pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
rv.setOnClickPendingIntent(previous, pendingIntent);
@@ -1067,13 +1063,13 @@ public final class Util {
if(pause > 0) {
if(playing) {
Intent pauseIntent = new Intent("KEYCODE_MEDIA_PLAY_PAUSE");
- pauseIntent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
+ pauseIntent.setComponent(new ComponentName(context, DownloadService.class));
pauseIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
pendingIntent = PendingIntent.getService(context, 0, pauseIntent, 0);
rv.setOnClickPendingIntent(pause, pendingIntent);
} else {
Intent prevIntent = new Intent("KEYCODE_MEDIA_START");
- prevIntent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
+ prevIntent.setComponent(new ComponentName(context, DownloadService.class));
prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY));
pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
rv.setOnClickPendingIntent(pause, pendingIntent);
@@ -1081,21 +1077,21 @@ public final class Util {
}
if(next > 0) {
Intent nextIntent = new Intent("KEYCODE_MEDIA_NEXT");
- nextIntent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
+ nextIntent.setComponent(new ComponentName(context, DownloadService.class));
nextIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT));
pendingIntent = PendingIntent.getService(context, 0, nextIntent, 0);
rv.setOnClickPendingIntent(next, pendingIntent);
}
if(close > 0) {
Intent prevIntent = new Intent("KEYCODE_MEDIA_STOP");
- prevIntent.setComponent(new ComponentName(context, DownloadServiceImpl.class));
+ prevIntent.setComponent(new ComponentName(context, DownloadService.class));
prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_STOP));
pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
rv.setOnClickPendingIntent(close, pendingIntent);
}
}
- public static void hidePlayingNotification(final Context context, final DownloadServiceImpl downloadService, Handler handler) {
+ public static void hidePlayingNotification(final Context context, final DownloadService downloadService, Handler handler) {
// Remove notification and remove the service from the foreground
handler.post(new Runnable() {
@Override
@@ -1109,8 +1105,8 @@ public final class Util {
}
public static void showDownloadingNotification(final Context context, DownloadFile file, int size, DownloadFile speedFile) {
- Intent cancelIntent = new Intent(context, DownloadServiceImpl.class);
- cancelIntent.setAction(DownloadServiceImpl.CANCEL_DOWNLOADS);
+ Intent cancelIntent = new Intent(context, DownloadService.class);
+ cancelIntent.setAction(DownloadService.CANCEL_DOWNLOADS);
PendingIntent cancelPI = PendingIntent.getService(context, 0, cancelIntent, 0);
String currentDownloading, currentSize, speed;
@@ -1236,7 +1232,7 @@ public final class Util {
hasFocus = true;
audioManager.requestAudioFocus(new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
- DownloadServiceImpl downloadService = (DownloadServiceImpl)context;
+ DownloadService downloadService = (DownloadService)context;
if((focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) && !downloadService.isRemoteEnabled()) {
if(downloadService.getPlayerState() == PlayerState.STARTED) {
SharedPreferences prefs = getPreferences(context);
@@ -1271,7 +1267,7 @@ public final class Util {
* <p>Broadcasts the given song info as the new song being played.</p>
*/
public static void broadcastNewTrackInfo(Context context, MusicDirectory.Entry song) {
- DownloadService downloadService = (DownloadServiceImpl)context;
+ DownloadService downloadService = (DownloadService)context;
Intent intent = new Intent(EVENT_META_CHANGED);
Intent avrcpIntent = new Intent(AVRCP_METADATA_CHANGED);
@@ -1331,7 +1327,7 @@ public final class Util {
private static void addTrackInfo(Context context, MusicDirectory.Entry song, Intent intent) {
if (song != null) {
- DownloadService downloadService = (DownloadServiceImpl)context;
+ DownloadService downloadService = (DownloadService)context;
File albumArtFile = FileUtil.getAlbumArtFile(context, song);
intent.putExtra("track", song.getTitle());
diff --git a/src/github/daneren2005/dsub/util/compat/CastCompat.java b/src/github/daneren2005/dsub/util/compat/CastCompat.java
index 31581816..ab64bca9 100644
--- a/src/github/daneren2005/dsub/util/compat/CastCompat.java
+++ b/src/github/daneren2005/dsub/util/compat/CastCompat.java
@@ -21,7 +21,7 @@ import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.CastMediaControlIntent;
import github.daneren2005.dsub.service.ChromeCastController;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
+import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.RemoteController;
/**
@@ -42,7 +42,7 @@ public final class CastCompat {
// Calling here forces class initialization.
}
- public static RemoteController getController(DownloadServiceImpl downloadService, MediaRouter.RouteInfo info) {
+ public static RemoteController getController(DownloadService downloadService, MediaRouter.RouteInfo info) {
CastDevice device = CastDevice.getFromBundle(info.getExtras());
if(device != null) {
return new ChromeCastController(downloadService, device);
diff --git a/src/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java b/src/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java
index 3bf06b82..56378a25 100644
--- a/src/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java
+++ b/src/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java
@@ -1,7 +1,7 @@
package github.daneren2005.dsub.util.compat;
import github.daneren2005.dsub.domain.MusicDirectory;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
+import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.util.ImageLoader;
import android.annotation.TargetApi;
import android.app.PendingIntent;
@@ -11,10 +11,8 @@ import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaMetadataRetriever;
import android.media.RemoteControlClient;
-import android.util.Log;
import github.daneren2005.dsub.activity.SubsonicActivity;
-import github.daneren2005.dsub.service.DownloadService;
@TargetApi(14)
public class RemoteControlClientICS extends RemoteControlClientHelper {
@@ -22,10 +20,10 @@ public class RemoteControlClientICS extends RemoteControlClientHelper {
protected RemoteControlClient mRemoteControl;
protected ImageLoader imageLoader;
- protected DownloadServiceImpl downloadService;
+ protected DownloadService downloadService;
public void register(final Context context, final ComponentName mediaButtonReceiverComponent) {
- downloadService = (DownloadServiceImpl) context;
+ downloadService = (DownloadService) context;
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
// build the PendingIntent for the remote control client
diff --git a/src/github/daneren2005/dsub/view/SongView.java b/src/github/daneren2005/dsub/view/SongView.java
index 3c1ee842..2176b768 100644
--- a/src/github/daneren2005/dsub/view/SongView.java
+++ b/src/github/daneren2005/dsub/view/SongView.java
@@ -20,8 +20,6 @@ package github.daneren2005.dsub.view;
import android.content.Context;
import android.content.res.TypedArray;
-import android.media.MediaMetadataRetriever;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.*;
@@ -29,12 +27,10 @@ import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PodcastEpisode;
import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.util.Util;
import java.io.File;
-import java.text.DateFormat;
/**
* Used to display songs in a {@code ListView}.
@@ -150,7 +146,7 @@ public class SongView extends UpdateView implements Checkable {
@Override
protected void updateBackground() {
if (downloadService == null) {
- downloadService = DownloadServiceImpl.getInstance();
+ downloadService = DownloadService.getInstance();
if(downloadService == null) {
return;
}
diff --git a/src/github/daneren2005/dsub/view/VisualizerView.java b/src/github/daneren2005/dsub/view/VisualizerView.java
index 53ebc2ec..aa921930 100644
--- a/src/github/daneren2005/dsub/view/VisualizerView.java
+++ b/src/github/daneren2005/dsub/view/VisualizerView.java
@@ -23,12 +23,10 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.media.audiofx.Visualizer;
-import android.util.AttributeSet;
import android.view.View;
import github.daneren2005.dsub.audiofx.VisualizerController;
import github.daneren2005.dsub.domain.PlayerState;
import github.daneren2005.dsub.service.DownloadService;
-import github.daneren2005.dsub.service.DownloadServiceImpl;
/**
* A simple class that draws waveform data received from a
@@ -92,7 +90,7 @@ public class VisualizerView extends View {
}
private VisualizerController getVizualiser() {
- DownloadService downloadService = DownloadServiceImpl.getInstance();
+ DownloadService downloadService = DownloadService.getInstance();
VisualizerController visualizerController = downloadService == null ? null : downloadService.getVisualizerController();
return visualizerController;
}
@@ -109,7 +107,7 @@ public class VisualizerView extends View {
if (!active) {
return;
}
- DownloadService downloadService = DownloadServiceImpl.getInstance();
+ DownloadService downloadService = DownloadService.getInstance();
if (downloadService != null && downloadService.getPlayerState() != PlayerState.STARTED) {
return;
}