Moved externally visible functions to interface, added comments

This commit is contained in:
Nite 2020-06-26 15:18:14 +02:00
parent 0bef3ae417
commit bbe9f39300
No known key found for this signature in database
GPG Key ID: 1D1AD59B1C6386C1
22 changed files with 180 additions and 74 deletions

View File

@ -34,6 +34,7 @@ import org.moire.ultrasonic.R;
import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicDirectory;
import org.moire.ultrasonic.domain.MusicDirectory.Entry; import org.moire.ultrasonic.domain.MusicDirectory.Entry;
import org.moire.ultrasonic.service.DownloadFile; import org.moire.ultrasonic.service.DownloadFile;
import org.moire.ultrasonic.service.MediaPlayerController;
import org.moire.ultrasonic.service.MusicService; import org.moire.ultrasonic.service.MusicService;
import org.moire.ultrasonic.service.MusicServiceFactory; import org.moire.ultrasonic.service.MusicServiceFactory;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
@ -296,7 +297,8 @@ public class BookmarkActivity extends SubsonicTabActivity
private void enableButtons() private void enableButtons()
{ {
if (getMediaPlayerController() == null) MediaPlayerController mediaPlayerController = getMediaPlayerController();
if (mediaPlayerController == null)
{ {
return; return;
} }
@ -310,7 +312,7 @@ public class BookmarkActivity extends SubsonicTabActivity
for (MusicDirectory.Entry song : selection) for (MusicDirectory.Entry song : selection)
{ {
DownloadFile downloadFile = downloader.getValue().getDownloadFileForSong(song); DownloadFile downloadFile = mediaPlayerController.getDownloadFileForSong(song);
if (downloadFile.isWorkDone()) if (downloadFile.isWorkDone())
{ {
deleteEnabled = true; deleteEnabled = true;

View File

@ -293,7 +293,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
@Override @Override
protected Boolean doInBackground() throws Throwable protected Boolean doInBackground() throws Throwable
{ {
if (downloader.getValue().getCurrentPlayingIndex() < downloader.getValue().downloadList.size() - 1) if (getMediaPlayerController().getCurrentPlayingNumberOnPlaylist() < getMediaPlayerController().getPlaylistSize() - 1)
{ {
getMediaPlayerController().next(); getMediaPlayerController().next();
return true; return true;
@ -567,7 +567,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
final MediaPlayerController mediaPlayerController = getMediaPlayerController(); final MediaPlayerController mediaPlayerController = getMediaPlayerController();
if (mediaPlayerController == null || localMediaPlayer.getValue().currentPlaying == null) if (mediaPlayerController == null || mediaPlayerController.getCurrentPlaying() == null)
{ {
playlistFlipper.setDisplayedChild(1); playlistFlipper.setDisplayedChild(1);
} }
@ -632,7 +632,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
} }
} }
final DownloadFile currentDownloading = downloader.getValue().currentDownloading; final DownloadFile currentDownloading = getMediaPlayerController().getCurrentDownloading();
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
if (currentDownloading == playlistView.getItemAtPosition(i)) if (currentDownloading == playlistView.getItemAtPosition(i))
@ -782,7 +782,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
if (mediaPlayerController != null) if (mediaPlayerController != null)
{ {
DownloadFile downloadFile = localMediaPlayer.getValue().currentPlaying; DownloadFile downloadFile = mediaPlayerController.getCurrentPlaying();
if (downloadFile != null) if (downloadFile != null)
{ {
@ -1019,7 +1019,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
onDownloadListChanged(); onDownloadListChanged();
return true; return true;
case R.id.menu_item_save_playlist: case R.id.menu_item_save_playlist:
if (!downloader.getValue().downloadList.isEmpty()) if (getMediaPlayerController().getPlaylistSize() > 0)
{ {
showDialog(DIALOG_SAVE_PLAYLIST); showDialog(DIALOG_SAVE_PLAYLIST);
} }
@ -1142,7 +1142,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
if (mediaPlayerController != null) if (mediaPlayerController != null)
{ {
List<DownloadFile> downloadServiceSongs = downloader.getValue().downloadList; List<DownloadFile> downloadServiceSongs = mediaPlayerController.getPlayList();
if (downloadServiceSongs != null) if (downloadServiceSongs != null)
{ {
@ -1170,17 +1170,18 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
private void update() private void update()
{ {
if (getMediaPlayerController() == null) MediaPlayerController mediaPlayerController = getMediaPlayerController();
if (mediaPlayerController == null)
{ {
return; return;
} }
if (currentRevision != downloader.getValue().getDownloadListUpdateRevision()) if (currentRevision != mediaPlayerController.getPlayListUpdateRevision())
{ {
onDownloadListChanged(); onDownloadListChanged();
} }
if (currentPlaying != localMediaPlayer.getValue().currentPlaying) if (currentPlaying != mediaPlayerController.getCurrentPlaying())
{ {
onCurrentChanged(); onCurrentChanged();
} }
@ -1199,7 +1200,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
protected Void doInBackground() throws Throwable protected Void doInBackground() throws Throwable
{ {
final List<MusicDirectory.Entry> entries = new LinkedList<MusicDirectory.Entry>(); final List<MusicDirectory.Entry> entries = new LinkedList<MusicDirectory.Entry>();
for (final DownloadFile downloadFile : downloader.getValue().downloadList) for (final DownloadFile downloadFile : getMediaPlayerController().getPlayList())
{ {
entries.add(downloadFile.getSong()); entries.add(downloadFile.getSong());
} }
@ -1254,7 +1255,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
{ {
warnIfNetworkOrStorageUnavailable(); warnIfNetworkOrStorageUnavailable();
final int current = downloader.getValue().getCurrentPlayingIndex(); final int current = getMediaPlayerController().getCurrentPlayingNumberOnPlaylist();
if (current == -1) if (current == -1)
{ {
@ -1275,7 +1276,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
return; return;
} }
final List<DownloadFile> list = downloader.getValue().downloadList; final List<DownloadFile> list = mediaPlayerController.getPlayList();
emptyTextView.setText(R.string.download_empty); emptyTextView.setText(R.string.download_empty);
final SongListAdapter adapter = new SongListAdapter(this, list); final SongListAdapter adapter = new SongListAdapter(this, list);
@ -1313,7 +1314,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
return; return;
} }
DownloadFile currentPlaying = localMediaPlayer.getValue().currentPlaying; DownloadFile currentPlaying = mediaPlayerController.getCurrentPlaying();
if (currentPlaying == item) if (currentPlaying == item)
{ {
@ -1333,7 +1334,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
}); });
emptyTextView.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE); emptyTextView.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE);
currentRevision = downloader.getValue().getDownloadListUpdateRevision(); currentRevision = mediaPlayerController.getPlayListUpdateRevision();
switch (mediaPlayerController.getRepeatMode()) switch (mediaPlayerController.getRepeatMode())
{ {
@ -1360,13 +1361,13 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
return; return;
} }
currentPlaying = localMediaPlayer.getValue().currentPlaying; currentPlaying = mediaPlayerController.getCurrentPlaying();
scrollToCurrent(); scrollToCurrent();
long totalDuration = downloader.getValue().getDownloadListDuration(); long totalDuration = mediaPlayerController.getPlayListDuration();
long totalSongs = downloader.getValue().downloadList.size(); long totalSongs = mediaPlayerController.getPlaylistSize();
int currentSongIndex = downloader.getValue().getCurrentPlayingIndex() + 1; int currentSongIndex = mediaPlayerController.getCurrentPlayingNumberOnPlaylist() + 1;
String duration = Util.formatTotalDuration(totalDuration); String duration = Util.formatTotalDuration(totalDuration);
@ -1580,7 +1581,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
if (e1X - e2X > swipeDistance && absX > swipeVelocity) if (e1X - e2X > swipeDistance && absX > swipeVelocity)
{ {
warnIfNetworkOrStorageUnavailable(); warnIfNetworkOrStorageUnavailable();
if (downloader.getValue().getCurrentPlayingIndex() < downloader.getValue().downloadList.size() - 1) if (mediaPlayerController.getCurrentPlayingNumberOnPlaylist() < mediaPlayerController.getPlaylistSize() - 1)
{ {
mediaPlayerController.next(); mediaPlayerController.next();
onCurrentChanged(); onCurrentChanged();

View File

@ -520,7 +520,7 @@ public class SearchActivity extends SubsonicTabActivity
if (autoplay) if (autoplay)
{ {
mediaPlayerController.play(downloader.getValue().downloadList.size() - 1); mediaPlayerController.play(mediaPlayerController.getPlaylistSize() - 1);
} }
Util.toast(SearchActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_added, 1, 1)); Util.toast(SearchActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_added, 1, 1));

View File

@ -40,6 +40,7 @@ import org.moire.ultrasonic.R;
import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicDirectory;
import org.moire.ultrasonic.domain.Share; import org.moire.ultrasonic.domain.Share;
import org.moire.ultrasonic.service.DownloadFile; import org.moire.ultrasonic.service.DownloadFile;
import org.moire.ultrasonic.service.MediaPlayerController;
import org.moire.ultrasonic.service.MusicService; import org.moire.ultrasonic.service.MusicService;
import org.moire.ultrasonic.service.MusicServiceFactory; import org.moire.ultrasonic.service.MusicServiceFactory;
import org.moire.ultrasonic.util.AlbumHeader; import org.moire.ultrasonic.util.AlbumHeader;
@ -1010,7 +1011,8 @@ public class SelectAlbumActivity extends SubsonicTabActivity
private void enableButtons() private void enableButtons()
{ {
if (getMediaPlayerController() == null) MediaPlayerController mediaPlayerController = getMediaPlayerController();
if (mediaPlayerController == null)
{ {
return; return;
} }
@ -1024,7 +1026,7 @@ public class SelectAlbumActivity extends SubsonicTabActivity
for (MusicDirectory.Entry song : selection) for (MusicDirectory.Entry song : selection)
{ {
DownloadFile downloadFile = downloader.getValue().getDownloadFileForSong(song); DownloadFile downloadFile = mediaPlayerController.getDownloadFileForSong(song);
if (downloadFile.isWorkDone()) if (downloadFile.isWorkDone())
{ {
deleteEnabled = true; deleteEnabled = true;

View File

@ -78,9 +78,6 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
private Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class); private Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
private Lazy<MediaPlayerLifecycleSupport> lifecycleSupport = inject(MediaPlayerLifecycleSupport.class); private Lazy<MediaPlayerLifecycleSupport> lifecycleSupport = inject(MediaPlayerLifecycleSupport.class);
protected Lazy<Downloader> downloader = inject(Downloader.class);
protected Lazy<LocalMediaPlayer> localMediaPlayer = inject(LocalMediaPlayer.class);
public MenuDrawer menuDrawer; public MenuDrawer menuDrawer;
private int activePosition = 1; private int activePosition = 1;
@ -269,7 +266,7 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
if (playerState.equals(PlayerState.PAUSED) || playerState.equals(PlayerState.STARTED)) if (playerState.equals(PlayerState.PAUSED) || playerState.equals(PlayerState.STARTED))
{ {
DownloadFile file = localMediaPlayer.getValue().currentPlaying; DownloadFile file = mediaPlayerControllerLazy.getValue().getCurrentPlaying();
if (file != null) if (file != null)
{ {

View File

@ -6,8 +6,6 @@ import android.content.Intent;
import org.moire.ultrasonic.domain.MusicDirectory.Entry; import org.moire.ultrasonic.domain.MusicDirectory.Entry;
import org.moire.ultrasonic.service.MediaPlayerController; import org.moire.ultrasonic.service.MediaPlayerController;
import org.moire.ultrasonic.service.Downloader;
import org.moire.ultrasonic.service.LocalMediaPlayer;
import kotlin.Lazy; import kotlin.Lazy;
@ -17,18 +15,16 @@ public class A2dpIntentReceiver extends BroadcastReceiver
{ {
private static final String PLAYSTATUS_RESPONSE = "com.android.music.playstatusresponse"; private static final String PLAYSTATUS_RESPONSE = "com.android.music.playstatusresponse";
private Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class); private Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
private Lazy<Downloader> downloader = inject(Downloader.class);
protected Lazy<LocalMediaPlayer> localMediaPlayer = inject(LocalMediaPlayer.class);
@Override @Override
public void onReceive(Context context, Intent intent) public void onReceive(Context context, Intent intent)
{ {
if (localMediaPlayer.getValue().currentPlaying == null) if (mediaPlayerControllerLazy.getValue().getCurrentPlaying() == null)
{ {
return; return;
} }
Entry song = localMediaPlayer.getValue().currentPlaying.getSong(); Entry song = mediaPlayerControllerLazy.getValue().getCurrentPlaying().getSong();
if (song == null) if (song == null)
{ {
@ -39,7 +35,7 @@ public class A2dpIntentReceiver extends BroadcastReceiver
Integer duration = song.getDuration(); Integer duration = song.getDuration();
int playerPosition = mediaPlayerControllerLazy.getValue().getPlayerPosition(); int playerPosition = mediaPlayerControllerLazy.getValue().getPlayerPosition();
int listSize = downloader.getValue().getDownloads().size(); int listSize = mediaPlayerControllerLazy.getValue().getPlaylistSize();
if (duration != null) if (duration != null)
{ {

View File

@ -1,5 +1,10 @@
package org.moire.ultrasonic.service; package org.moire.ultrasonic.service;
/**
* Abstract class for consumers with two parameters
* @param <T> The type of the first object to consume
* @param <U> The type of the second object to consume
*/
public abstract class BiConsumer<T, U> public abstract class BiConsumer<T, U>
{ {
public abstract void accept(T t, U u); public abstract void accept(T t, U u);

View File

@ -1,5 +1,9 @@
package org.moire.ultrasonic.service; package org.moire.ultrasonic.service;
/**
* Abstract class for consumers with one parameter
* @param <T> The type of the object to consume
*/
public abstract class Consumer<T> public abstract class Consumer<T>
{ {
public abstract void accept(T t); public abstract void accept(T t);

View File

@ -24,6 +24,7 @@ import android.os.PowerManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import org.jetbrains.annotations.NotNull;
import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicDirectory;
import org.moire.ultrasonic.util.CacheCleaner; import org.moire.ultrasonic.util.CacheCleaner;
import org.moire.ultrasonic.util.CancellableTask; import org.moire.ultrasonic.util.CancellableTask;
@ -51,7 +52,6 @@ import static org.koin.java.standalone.KoinJavaComponent.inject;
*/ */
public class DownloadFile public class DownloadFile
{ {
private static final String TAG = DownloadFile.class.getSimpleName(); private static final String TAG = DownloadFile.class.getSimpleName();
private final Context context; private final Context context;
private final MusicDirectory.Entry song; private final MusicDirectory.Entry song;
@ -70,7 +70,6 @@ public class DownloadFile
private Lazy<Downloader> downloader = inject(Downloader.class); private Lazy<Downloader> downloader = inject(Downloader.class);
public DownloadFile(Context context, MusicDirectory.Entry song, boolean save) public DownloadFile(Context context, MusicDirectory.Entry song, boolean save)
{ {
super(); super();
@ -287,6 +286,7 @@ public class DownloadFile
this.isPlaying = isPlaying; this.isPlaying = isPlaying;
} }
@NotNull
@Override @Override
public String toString() public String toString()
{ {
@ -309,7 +309,7 @@ public class DownloadFile
{ {
PowerManager pm = (PowerManager) context.getSystemService(POWER_SERVICE); PowerManager pm = (PowerManager) context.getSystemService(POWER_SERVICE);
wakeLock = pm.newWakeLock(SCREEN_DIM_WAKE_LOCK | ON_AFTER_RELEASE, toString()); wakeLock = pm.newWakeLock(SCREEN_DIM_WAKE_LOCK | ON_AFTER_RELEASE, toString());
wakeLock.acquire(); wakeLock.acquire(10*60*1000L /*10 minutes*/);
Log.i(TAG, String.format("Acquired wake lock %s", wakeLock)); Log.i(TAG, String.format("Acquired wake lock %s", wakeLock));
} }
@ -450,6 +450,7 @@ public class DownloadFile
} }
} }
@NotNull
@Override @Override
public String toString() public String toString()
{ {

View File

@ -11,6 +11,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
/**
* This class is responsible for the serialization / deserialization
* of the DownloadQueue (playlist) to the filesystem.
* It also serializes the player state e.g. current playing number and play position.
*/
public class DownloadQueueSerializer public class DownloadQueueSerializer
{ {
private static final String TAG = DownloadQueueSerializer.class.getSimpleName(); private static final String TAG = DownloadQueueSerializer.class.getSimpleName();

View File

@ -22,6 +22,10 @@ import static org.koin.java.standalone.KoinJavaComponent.inject;
import static org.moire.ultrasonic.domain.PlayerState.DOWNLOADING; import static org.moire.ultrasonic.domain.PlayerState.DOWNLOADING;
import static org.moire.ultrasonic.domain.PlayerState.STARTED; import static org.moire.ultrasonic.domain.PlayerState.STARTED;
/**
* This class is responsible for maintaining the playlist and downloading
* its items from the network to the filesystem.
*/
public class Downloader public class Downloader
{ {
private static final String TAG = Downloader.class.getSimpleName(); private static final String TAG = Downloader.class.getSimpleName();
@ -85,7 +89,7 @@ public class Downloader
Log.i(TAG, "Downloader destroyed"); Log.i(TAG, "Downloader destroyed");
} }
protected synchronized void checkDownloads() public synchronized void checkDownloads()
{ {
if (!Util.isExternalStoragePresent() || !externalStorageMonitor.isExternalStorageAvailable()) if (!Util.isExternalStoragePresent() || !externalStorageMonitor.isExternalStorageAvailable())
{ {

View File

@ -6,6 +6,9 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.util.Log; import android.util.Log;
/**
* Monitors the state of the mobile's external storage
*/
public class ExternalStorageMonitor public class ExternalStorageMonitor
{ {
private static final String TAG = ExternalStorageMonitor.class.getSimpleName(); private static final String TAG = ExternalStorageMonitor.class.getSimpleName();

View File

@ -43,6 +43,9 @@ import static org.moire.ultrasonic.domain.PlayerState.PREPARED;
import static org.moire.ultrasonic.domain.PlayerState.PREPARING; import static org.moire.ultrasonic.domain.PlayerState.PREPARING;
import static org.moire.ultrasonic.domain.PlayerState.STARTED; import static org.moire.ultrasonic.domain.PlayerState.STARTED;
/**
* Represents a Media Player which uses the mobile's resources for playback
*/
public class LocalMediaPlayer public class LocalMediaPlayer
{ {
private static final String TAG = LocalMediaPlayer.class.getSimpleName(); private static final String TAG = LocalMediaPlayer.class.getSimpleName();
@ -59,6 +62,7 @@ public class LocalMediaPlayer
public PlayerState playerState = IDLE; public PlayerState playerState = IDLE;
public DownloadFile currentPlaying; public DownloadFile currentPlaying;
public DownloadFile nextPlaying; public DownloadFile nextPlaying;
private PlayerState nextPlayerState = IDLE; private PlayerState nextPlayerState = IDLE;
private boolean nextSetup; private boolean nextSetup;
private CancellableTask nextPlayingTask; private CancellableTask nextPlayingTask;

View File

@ -27,12 +27,14 @@ import org.moire.ultrasonic.domain.RepeatMode;
import java.util.List; import java.util.List;
/** /**
* This interface contains all functions which are necessary for the Application UI
* to control the Media Player implementation.
*
* @author Sindre Mehus * @author Sindre Mehus
* @version $Id$ * @version $Id$
*/ */
public interface MediaPlayerController public interface MediaPlayerController
{ {
void download(List<Entry> songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle, boolean newPlaylist); void download(List<Entry> songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle, boolean newPlaylist);
void downloadBackground(List<Entry> songs, boolean save); void downloadBackground(List<Entry> songs, boolean save);
@ -114,4 +116,20 @@ public interface MediaPlayerController
void updateNotification(); void updateNotification();
void setSongRating(final int rating); void setSongRating(final int rating);
DownloadFile getCurrentPlaying();
int getPlaylistSize();
int getCurrentPlayingNumberOnPlaylist();
DownloadFile getCurrentDownloading();
List<DownloadFile> getPlayList();
long getPlayListUpdateRevision();
long getPlayListDuration();
DownloadFile getDownloadFileForSong(Entry song);
} }

View File

@ -43,6 +43,10 @@ import kotlin.Lazy;
import static org.koin.java.standalone.KoinJavaComponent.inject; import static org.koin.java.standalone.KoinJavaComponent.inject;
/** /**
* The implementation of the Media Player Controller.
* This class contains everything that is necessary for the Application UI
* to control the Media Player implementation.
*
* @author Sindre Mehus, Joshua Bahnsen * @author Sindre Mehus, Joshua Bahnsen
* @version $Id$ * @version $Id$
*/ */
@ -58,21 +62,24 @@ public class MediaPlayerControllerImpl implements MediaPlayerController
private Context context; private Context context;
private Lazy<JukeboxMediaPlayer> jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class); private Lazy<JukeboxMediaPlayer> jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class);
private Lazy<DownloadQueueSerializer> downloadQueueSerializer = inject(DownloadQueueSerializer.class); private final DownloadQueueSerializer downloadQueueSerializer;
private Lazy<ExternalStorageMonitor> externalStorageMonitor = inject(ExternalStorageMonitor.class); private final ExternalStorageMonitor externalStorageMonitor;
private final Downloader downloader; private final Downloader downloader;
private final ShufflePlayBuffer shufflePlayBuffer; private final ShufflePlayBuffer shufflePlayBuffer;
private final LocalMediaPlayer localMediaPlayer; private final LocalMediaPlayer localMediaPlayer;
public MediaPlayerControllerImpl(Context context, Downloader downloader, ShufflePlayBuffer shufflePlayBuffer, public MediaPlayerControllerImpl(Context context, DownloadQueueSerializer downloadQueueSerializer,
LocalMediaPlayer localMediaPlayer) ExternalStorageMonitor externalStorageMonitor, Downloader downloader,
ShufflePlayBuffer shufflePlayBuffer, LocalMediaPlayer localMediaPlayer)
{ {
this.context = context; this.context = context;
this.downloadQueueSerializer = downloadQueueSerializer;
this.externalStorageMonitor = externalStorageMonitor;
this.downloader = downloader; this.downloader = downloader;
this.shufflePlayBuffer = shufflePlayBuffer; this.shufflePlayBuffer = shufflePlayBuffer;
this.localMediaPlayer = localMediaPlayer; this.localMediaPlayer = localMediaPlayer;
externalStorageMonitor.getValue().onCreate(new Runnable() { this.externalStorageMonitor.onCreate(new Runnable() {
@Override @Override
public void run() { public void run() {
reset(); reset();
@ -87,7 +94,7 @@ public class MediaPlayerControllerImpl implements MediaPlayerController
public void onDestroy() public void onDestroy()
{ {
externalStorageMonitor.getValue().onDestroy(); externalStorageMonitor.onDestroy();
context.stopService(new Intent(context, MediaPlayerService.class)); context.stopService(new Intent(context, MediaPlayerService.class));
Log.i(TAG, "MediaPlayerControllerImpl destroyed"); Log.i(TAG, "MediaPlayerControllerImpl destroyed");
} }
@ -235,14 +242,14 @@ public class MediaPlayerControllerImpl implements MediaPlayerController
downloader.checkDownloads(); downloader.checkDownloads();
} }
downloadQueueSerializer.getValue().serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition());
} }
@Override @Override
public synchronized void downloadBackground(List<MusicDirectory.Entry> songs, boolean save) public synchronized void downloadBackground(List<MusicDirectory.Entry> songs, boolean save)
{ {
downloader.downloadBackground(songs, save); downloader.downloadBackground(songs, save);
downloadQueueSerializer.getValue().serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition());
} }
public synchronized void setCurrentPlaying(DownloadFile currentPlaying) public synchronized void setCurrentPlaying(DownloadFile currentPlaying)
@ -291,7 +298,7 @@ public class MediaPlayerControllerImpl implements MediaPlayerController
{ {
downloader.shuffle(); downloader.shuffle();
downloadQueueSerializer.getValue().serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition());
jukeboxMediaPlayer.getValue().updatePlaylist(); jukeboxMediaPlayer.getValue().updatePlaylist();
MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance(); MediaPlayerService mediaPlayerService = MediaPlayerService.getRunningInstance();
@ -365,7 +372,7 @@ public class MediaPlayerControllerImpl implements MediaPlayerController
} }
} }
downloadQueueSerializer.getValue().serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition());
jukeboxMediaPlayer.getValue().updatePlaylist(); jukeboxMediaPlayer.getValue().updatePlaylist();
} }
@ -380,7 +387,7 @@ public class MediaPlayerControllerImpl implements MediaPlayerController
downloader.removeDownloadFile(downloadFile); downloader.removeDownloadFile(downloadFile);
downloadQueueSerializer.getValue().serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition());
jukeboxMediaPlayer.getValue().updatePlaylist(); jukeboxMediaPlayer.getValue().updatePlaylist();
if (downloadFile == localMediaPlayer.nextPlaying) if (downloadFile == localMediaPlayer.nextPlaying)
@ -597,4 +604,44 @@ public class MediaPlayerControllerImpl implements MediaPlayerController
updateNotification(); updateNotification();
} }
@Override
public DownloadFile getCurrentPlaying() {
return localMediaPlayer.currentPlaying;
}
@Override
public int getPlaylistSize() {
return downloader.downloadList.size();
}
@Override
public int getCurrentPlayingNumberOnPlaylist() {
return downloader.getCurrentPlayingIndex();
}
@Override
public DownloadFile getCurrentDownloading() {
return downloader.currentDownloading;
}
@Override
public List<DownloadFile> getPlayList() {
return downloader.downloadList;
}
@Override
public long getPlayListUpdateRevision() {
return downloader.getDownloadListUpdateRevision();
}
@Override
public long getPlayListDuration() {
return downloader.getDownloadListDuration();
}
@Override
public DownloadFile getDownloadFileForSong(Entry song) {
return downloader.getDownloadFileForSong(song);
}
} }

View File

@ -35,26 +35,26 @@ import org.moire.ultrasonic.util.CacheCleaner;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
import kotlin.Lazy;
import static org.koin.java.standalone.KoinJavaComponent.inject;
/** /**
* This class is responsible for handling received events for the Media Player implementation
*
* @author Sindre Mehus * @author Sindre Mehus
*/ */
public class MediaPlayerLifecycleSupport public class MediaPlayerLifecycleSupport
{ {
private static final String TAG = MediaPlayerLifecycleSupport.class.getSimpleName(); private static final String TAG = MediaPlayerLifecycleSupport.class.getSimpleName();
private Lazy<DownloadQueueSerializer> downloadQueueSerializer = inject(DownloadQueueSerializer.class); private DownloadQueueSerializer downloadQueueSerializer; // From DI
private final MediaPlayerControllerImpl mediaPlayerController; // From DI private final MediaPlayerControllerImpl mediaPlayerController; // From DI
private final Downloader downloader; // From DI private final Downloader downloader; // From DI
private Context context; private Context context;
private BroadcastReceiver headsetEventReceiver; private BroadcastReceiver headsetEventReceiver;
public MediaPlayerLifecycleSupport(Context context, final MediaPlayerControllerImpl mediaPlayerController, final Downloader downloader) public MediaPlayerLifecycleSupport(Context context, DownloadQueueSerializer downloadQueueSerializer,
final MediaPlayerControllerImpl mediaPlayerController, final Downloader downloader)
{ {
this.downloadQueueSerializer = downloadQueueSerializer;
this.mediaPlayerController = mediaPlayerController; this.mediaPlayerController = mediaPlayerController;
this.context = context; this.context = context;
this.downloader = downloader; this.downloader = downloader;
@ -75,14 +75,14 @@ public class MediaPlayerLifecycleSupport
commandFilter.addAction(Constants.CMD_PROCESS_KEYCODE); commandFilter.addAction(Constants.CMD_PROCESS_KEYCODE);
context.registerReceiver(intentReceiver, commandFilter); context.registerReceiver(intentReceiver, commandFilter);
downloadQueueSerializer.getValue().deserializeDownloadQueue(new Consumer<State>() { this.downloadQueueSerializer.deserializeDownloadQueue(new Consumer<State>() {
@Override @Override
public void accept(State state) { public void accept(State state) {
// TODO: here the autoPlay = false creates problems when Ultrasonic is started by a Play MediaButton as the player won't start this way. // TODO: here the autoPlay = false creates problems when Ultrasonic is started by a Play MediaButton as the player won't start this way.
mediaPlayerController.restore(state.songs, state.currentPlayingIndex, state.currentPlayingPosition, false, false); mediaPlayerController.restore(state.songs, state.currentPlayingIndex, state.currentPlayingPosition, false, false);
// Work-around: Serialize again, as the restore() method creates a serialization without current playing info. // Work-around: Serialize again, as the restore() method creates a serialization without current playing info.
downloadQueueSerializer.getValue().serializeDownloadQueue(downloader.downloadList, MediaPlayerLifecycleSupport.this.downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList,
downloader.getCurrentPlayingIndex(), mediaPlayerController.getPlayerPosition()); downloader.getCurrentPlayingIndex(), mediaPlayerController.getPlayerPosition());
} }
}); });

View File

@ -47,6 +47,10 @@ import static org.moire.ultrasonic.domain.PlayerState.PREPARING;
import static org.moire.ultrasonic.domain.PlayerState.STARTED; import static org.moire.ultrasonic.domain.PlayerState.STARTED;
import static org.moire.ultrasonic.domain.PlayerState.STOPPED; import static org.moire.ultrasonic.domain.PlayerState.STOPPED;
/**
* Android Foreground Service for playing music
* while the rest of the Ultrasonic App is in the background.
*/
public class MediaPlayerService extends Service public class MediaPlayerService extends Service
{ {
private static final String TAG = MediaPlayerService.class.getSimpleName(); private static final String TAG = MediaPlayerService.class.getSimpleName();
@ -61,13 +65,14 @@ public class MediaPlayerService extends Service
private final Scrobbler scrobbler = new Scrobbler(); private final Scrobbler scrobbler = new Scrobbler();
public Lazy<JukeboxMediaPlayer> jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class); public Lazy<JukeboxMediaPlayer> jukeboxMediaPlayer = inject(JukeboxMediaPlayer.class);
private Lazy<DownloadQueueSerializer> downloadQueueSerializer = inject(DownloadQueueSerializer.class); private Lazy<DownloadQueueSerializer> downloadQueueSerializerLazy = inject(DownloadQueueSerializer.class);
private Lazy<ShufflePlayBuffer> shufflePlayBufferLazy = inject(ShufflePlayBuffer.class); private Lazy<ShufflePlayBuffer> shufflePlayBufferLazy = inject(ShufflePlayBuffer.class);
private Lazy<Downloader> downloaderLazy = inject(Downloader.class); private Lazy<Downloader> downloaderLazy = inject(Downloader.class);
private Lazy<LocalMediaPlayer> localMediaPlayerLazy = inject(LocalMediaPlayer.class); private Lazy<LocalMediaPlayer> localMediaPlayerLazy = inject(LocalMediaPlayer.class);
private LocalMediaPlayer localMediaPlayer; private LocalMediaPlayer localMediaPlayer;
private Downloader downloader; private Downloader downloader;
private ShufflePlayBuffer shufflePlayBuffer; private ShufflePlayBuffer shufflePlayBuffer;
private DownloadQueueSerializer downloadQueueSerializer;
private boolean isInForeground = false; private boolean isInForeground = false;
private NotificationCompat.Builder notificationBuilder; private NotificationCompat.Builder notificationBuilder;
@ -129,6 +134,7 @@ public class MediaPlayerService extends Service
downloader = downloaderLazy.getValue(); downloader = downloaderLazy.getValue();
localMediaPlayer = localMediaPlayerLazy.getValue(); localMediaPlayer = localMediaPlayerLazy.getValue();
shufflePlayBuffer = shufflePlayBufferLazy.getValue(); shufflePlayBuffer = shufflePlayBufferLazy.getValue();
downloadQueueSerializer = downloadQueueSerializerLazy.getValue();
downloader.onCreate(); downloader.onCreate();
shufflePlayBuffer.onCreate(); shufflePlayBuffer.onCreate();
@ -140,7 +146,7 @@ public class MediaPlayerService extends Service
localMediaPlayer.onPrepared = new Runnable() { localMediaPlayer.onPrepared = new Runnable() {
@Override @Override
public void run() { public void run() {
downloadQueueSerializer.getValue().serializeDownloadQueue(downloader.downloadList, downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList,
downloader.getCurrentPlayingIndex(), getPlayerPosition()); downloader.getCurrentPlayingIndex(), getPlayerPosition());
} }
}; };
@ -401,7 +407,7 @@ public class MediaPlayerService extends Service
{ {
localMediaPlayer.reset(); localMediaPlayer.reset();
localMediaPlayer.setCurrentPlaying(null); localMediaPlayer.setCurrentPlaying(null);
downloadQueueSerializer.getValue().serializeDownloadQueue(downloader.downloadList, downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList,
downloader.getCurrentPlayingIndex(), getPlayerPosition()); downloader.getCurrentPlayingIndex(), getPlayerPosition());
} }
@ -457,7 +463,7 @@ public class MediaPlayerService extends Service
public void accept(PlayerState playerState, DownloadFile currentPlaying) { public void accept(PlayerState playerState, DownloadFile currentPlaying) {
if (playerState == PAUSED) if (playerState == PAUSED)
{ {
downloadQueueSerializer.getValue().serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition()); downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList, downloader.getCurrentPlayingIndex(), getPlayerPosition());
} }
boolean showWhenPaused = (playerState != PlayerState.STOPPED && Util.isNotificationAlwaysEnabled(MediaPlayerService.this)); boolean showWhenPaused = (playerState != PlayerState.STOPPED && Util.isNotificationAlwaysEnabled(MediaPlayerService.this));
@ -579,7 +585,7 @@ public class MediaPlayerService extends Service
setNextPlaying(); setNextPlaying();
if (serialize) { if (serialize) {
downloadQueueSerializer.getValue().serializeDownloadQueue(downloader.downloadList, downloadQueueSerializer.serializeDownloadQueue(downloader.downloadList,
downloader.getCurrentPlayingIndex(), getPlayerPosition()); downloader.getCurrentPlayingIndex(), getPlayerPosition());
} }
} }

View File

@ -6,6 +6,9 @@ import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/**
* Represents the state of the Media Player implementation
*/
public class State implements Serializable public class State implements Serializable
{ {
public static final long serialVersionUID = -6346438781062572270L; public static final long serialVersionUID = -6346438781062572270L;

View File

@ -1,5 +1,9 @@
package org.moire.ultrasonic.service; package org.moire.ultrasonic.service;
/**
* Abstract class for supplying items to a consumer
* @param <T> The type of the item supplied
*/
public abstract class Supplier<T> public abstract class Supplier<T>
{ {
public abstract T get(); public abstract T get();

View File

@ -27,6 +27,10 @@ import static org.koin.java.standalone.KoinJavaComponent.inject;
* @author Sindre Mehus * @author Sindre Mehus
* @version $Id$ * @version $Id$
*/ */
/**
* Responsible for cleaning up files from the offline download cache on the filesystem
*/
public class CacheCleaner public class CacheCleaner
{ {

View File

@ -37,10 +37,9 @@ import org.moire.ultrasonic.domain.MusicDirectory.Entry;
import org.moire.ultrasonic.featureflags.Feature; import org.moire.ultrasonic.featureflags.Feature;
import org.moire.ultrasonic.featureflags.FeatureStorage; import org.moire.ultrasonic.featureflags.FeatureStorage;
import org.moire.ultrasonic.service.DownloadFile; import org.moire.ultrasonic.service.DownloadFile;
import org.moire.ultrasonic.service.Downloader; import org.moire.ultrasonic.service.MediaPlayerController;
import org.moire.ultrasonic.service.MusicService; import org.moire.ultrasonic.service.MusicService;
import org.moire.ultrasonic.service.MusicServiceFactory; import org.moire.ultrasonic.service.MusicServiceFactory;
import org.moire.ultrasonic.service.LocalMediaPlayer;
import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.util.Util;
import org.moire.ultrasonic.util.VideoPlayerType; import org.moire.ultrasonic.util.VideoPlayerType;
@ -82,8 +81,7 @@ public class SongView extends UpdateView implements Checkable
private boolean maximized = false; private boolean maximized = false;
private boolean useFiveStarRating; private boolean useFiveStarRating;
private Lazy<Downloader> downloader = inject(Downloader.class); private Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
protected Lazy<LocalMediaPlayer> localMediaPlayer = inject(LocalMediaPlayer.class);
public SongView(Context context) public SongView(Context context)
{ {
@ -170,7 +168,7 @@ public class SongView extends UpdateView implements Checkable
this.song = song; this.song = song;
this.downloadFile = downloader.getValue().getDownloadFileForSong(song); this.downloadFile = mediaPlayerControllerLazy.getValue().getDownloadFileForSong(song);
StringBuilder artist = new StringBuilder(60); StringBuilder artist = new StringBuilder(60);
@ -321,7 +319,7 @@ public class SongView extends UpdateView implements Checkable
{ {
updateBackground(); updateBackground();
downloadFile = downloader.getValue().getDownloadFileForSong(this.song); downloadFile = mediaPlayerControllerLazy.getValue().getDownloadFileForSong(this.song);
File partialFile = downloadFile.getPartialFile(); File partialFile = downloadFile.getPartialFile();
if (downloadFile.isWorkDone()) if (downloadFile.isWorkDone())
@ -411,7 +409,7 @@ public class SongView extends UpdateView implements Checkable
viewHolder.fiveStar4.setImageDrawable(rating > 3 ? starDrawable : starHollowDrawable); viewHolder.fiveStar4.setImageDrawable(rating > 3 ? starDrawable : starHollowDrawable);
viewHolder.fiveStar5.setImageDrawable(rating > 4 ? starDrawable : starHollowDrawable); viewHolder.fiveStar5.setImageDrawable(rating > 4 ? starDrawable : starHollowDrawable);
boolean playing = localMediaPlayer.getValue().currentPlaying == downloadFile; boolean playing = mediaPlayerControllerLazy.getValue().getCurrentPlaying() == downloadFile;
if (playing) if (playing)
{ {

View File

@ -113,13 +113,15 @@ val musicServiceModule = module(MUSIC_SERVICE_CONTEXT) {
single { SubsonicImageLoader(getProperty(DiProperties.APP_CONTEXT), get()) } single { SubsonicImageLoader(getProperty(DiProperties.APP_CONTEXT), get()) }
single<MediaPlayerController> { MediaPlayerControllerImpl(androidContext(), get(), get(), get()) } single<MediaPlayerController> { MediaPlayerControllerImpl(androidContext(), get(), get(), get(), get(), get()) }
single { MediaPlayerControllerImpl(androidContext(), get(), get(), get()) }
single { JukeboxMediaPlayer(androidContext(), get()) } single { JukeboxMediaPlayer(androidContext(), get()) }
single { MediaPlayerLifecycleSupport(androidContext(), get(), get()) } single { MediaPlayerLifecycleSupport(androidContext(), get(), get(), get()) }
single { DownloadQueueSerializer(androidContext()) } single { DownloadQueueSerializer(androidContext()) }
single { ExternalStorageMonitor(androidContext()) } single { ExternalStorageMonitor(androidContext()) }
single { ShufflePlayBuffer(androidContext()) } single { ShufflePlayBuffer(androidContext()) }
single { Downloader(androidContext(), get(), get(), get()) } single { Downloader(androidContext(), get(), get(), get()) }
single { LocalMediaPlayer(androidContext()) } single { LocalMediaPlayer(androidContext()) }
// TODO: Ideally this can be cleaned up when all circular references are removed.
single { MediaPlayerControllerImpl(androidContext(), get(), get(), get(), get(), get()) }
} }