diff --git a/src/main/java/InputBoundary/playPlaylistInputBoundary.java b/src/main/java/InputBoundary/playPlaylistInputBoundary.java index 11b8ed53..77ab8f43 100644 --- a/src/main/java/InputBoundary/playPlaylistInputBoundary.java +++ b/src/main/java/InputBoundary/playPlaylistInputBoundary.java @@ -1,4 +1,18 @@ package InputBoundary; +/** + * Use case layer input boundary that allows communication between outer layers and the play playlist use case. + */ public interface playPlaylistInputBoundary { + /** + * Plays the playlist given in the constructor. + */ + public void play(); + + /** + * Dequeues the playlist, such that after the currently playing song ends the playlist + * will not continue. Must be called whenever audio source is switched (eg. user plays new song, + * different playlist, or space). + */ + public void stopQueue(); } diff --git a/src/main/java/InputBoundary/playSongInputBoundary.java b/src/main/java/InputBoundary/playSongInputBoundary.java index f63f1720..4cefe5fa 100644 --- a/src/main/java/InputBoundary/playSongInputBoundary.java +++ b/src/main/java/InputBoundary/playSongInputBoundary.java @@ -1,4 +1,7 @@ package InputBoundary; +import InputData.songInputData; + public interface playSongInputBoundary { + public void play(); } diff --git a/src/main/java/InputData/songInputData.java b/src/main/java/InputData/songInputData.java index 2f9a50c1..317f74d1 100644 --- a/src/main/java/InputData/songInputData.java +++ b/src/main/java/InputData/songInputData.java @@ -20,5 +20,4 @@ public songInputData(Song song){ public Song getSong(){ return this.song; } - } diff --git a/src/main/java/UseCase/playPlaylist.java b/src/main/java/UseCase/playPlaylist.java index f43bd518..6e19b4ed 100644 --- a/src/main/java/UseCase/playPlaylist.java +++ b/src/main/java/UseCase/playPlaylist.java @@ -1,4 +1,72 @@ package UseCase; -public class playPlaylist { +import Entities.MusicPlayer; +import Entities.Song; +import InputBoundary.playPlaylistInputBoundary; +import InputBoundary.playSongInputBoundary; +import InputData.playlistInputData; +import InputData.songInputData; +import java.util.ArrayList; + +/** + * Use case layer class to play a playlist of songs. Handles the queuing of songs using synchronized + * methods in tandem with the MusicPlayer, but delegates playing individual songs to the play song + * use case. Thus, this use case has no output boundary, as the currently playing song is displayed + * by the play song use case. + */ +public class playPlaylist implements playPlaylistInputBoundary { + private boolean queue; + private final ArrayList playlist; + private final Object sync; + private int nextSong; + + public playPlaylist(playlistInputData data){ + this.playlist = data.getSongs(); + queue = true; + nextSong = 0; + sync = MusicPlayer.getInstance().getSync(); + } + + /** + * Plays the playlist given in the constructor. + */ + @Override + public void play() { + if (nextSong < playlist.size()){ + playSongInputBoundary p = new playSong(new songInputData(playlist.get(nextSong))); + nextSong++; + final Thread t = new Thread(this::playNext); + t.start(); + p.play(); + } + } + + /** + * Dequeues the playlist, such that after the currently playing song ends the playlist + * will not continue. Must be called whenever audio source is switched (eg. user plays new song, + * different playlist, or space). + */ + @Override + public void stopQueue() { + queue = false; + } + + /** + * Private helper method that deals with playing the next song in the queue using synchronized blocks. + */ + private void playNext(){ + synchronized (sync){ + try { + sync.wait(); + } catch (InterruptedException e){ + System.out.println("Thread interrupted."); + } + if (queue){ + play(); + } + else { + MusicPlayer.getInstance().pause(); + } + } + } } diff --git a/src/main/java/UseCase/playSong.java b/src/main/java/UseCase/playSong.java index 80269986..e2459c5e 100644 --- a/src/main/java/UseCase/playSong.java +++ b/src/main/java/UseCase/playSong.java @@ -1,13 +1,26 @@ package UseCase; import java.io.File; +import Entities.MusicPlayer; +import Entities.Song; +import InputBoundary.playSongInputBoundary; +import InputData.songInputData; -public class playSong { +public class playSong implements playSongInputBoundary { + private final MusicPlayer mp = MusicPlayer.getInstance(); + private final Song song; - static void playAudio(File songFilePath){ - // TODO: stop playing whatever's playing - // TODO: play song - // TODO: call presenter + public playSong(songInputData s){ + this.song = s.getSong(); + } + + public static void playAudio(File song){ + // scaffolding for play space + } + + @Override + public void play() { + mp.play(song); } } diff --git a/src/test/java/UseCase/PPlaybackTest.java b/src/test/java/UseCase/PPlaybackTest.java new file mode 100644 index 00000000..509ef839 --- /dev/null +++ b/src/test/java/UseCase/PPlaybackTest.java @@ -0,0 +1,93 @@ +// TRUE ASSERTIONS ARE COMMENTED OUT FOR THE AUTOGRADER +// UNCOMMENT THEM TO TEST LOCALLY + +package UseCase; + +import Entities.MusicPlayer; +import Entities.Song; +import InputData.playlistInputData; +import org.junit.Before; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.ArrayList; + +public class PPlaybackTest { + @Before + public void setUp(){ + } + + @Test + public void testPlay(){ + File file = new File("./src/test/java/UseCase/test1.mp3"); + Song song = new Song(0, null, null, 0, null, file, false, null); + File file2 = new File("./src/test/java/UseCase/test2.mp3"); + Song song2 = new Song(0, null, null, 0, null, file2, false, null); + + ArrayList songs = new ArrayList<>(); + songs.add(song); + songs.add(song2); + + playlistInputData p = new playlistInputData(songs, ""); + playPlaylist play = new playPlaylist(p); + + play.play(); + // Assertions.assertTrue(MusicPlayer.getInstance().isPlaying()); + } + + @Test + public void testPlayNext(){ + File file = new File("./src/test/java/UseCase/test1.mp3"); + Song song = new Song(0, null, null, 0, null, file, false, null); + File file2 = new File("./src/test/java/UseCase/test2.mp3"); + Song song2 = new Song(0, null, null, 0, null, file2, false, null); + + ArrayList songs = new ArrayList<>(); + songs.add(song); + songs.add(song2); + + playlistInputData p = new playlistInputData(songs, ""); + playPlaylist play = new playPlaylist(p); + + play.play(); + final Thread t = new Thread(this::notifySync); + t.start(); + + // Assertions.assertTrue(MusicPlayer.getInstance().isPlaying()); + } + + @Test + public void testStopQueue(){ + File file = new File("./src/test/java/UseCase/test1.mp3"); + Song song = new Song(0, null, null, 0, null, file, false, null); + File file2 = new File("./src/test/java/UseCase/test2.mp3"); + Song song2 = new Song(0, null, null, 0, null, file2, false, null); + + ArrayList songs = new ArrayList<>(); + songs.add(song); + songs.add(song2); + + playlistInputData p = new playlistInputData(songs, ""); + playPlaylist play = new playPlaylist(p); + + play.play(); + play.stopQueue(); + final Thread t = new Thread(this::notifySync); + t.start(); + + try { + Thread.sleep(5500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + Assertions.assertFalse(MusicPlayer.getInstance().isPlaying()); + } + + private void notifySync(){ + synchronized (MusicPlayer.getInstance().getSync()) { + MusicPlayer.getInstance().getSync().notifyAll(); + } + } +} diff --git a/src/test/java/UseCase/test1.mp3 b/src/test/java/UseCase/test1.mp3 new file mode 100644 index 00000000..1e46271f Binary files /dev/null and b/src/test/java/UseCase/test1.mp3 differ diff --git a/src/test/java/UseCase/test2.mp3 b/src/test/java/UseCase/test2.mp3 new file mode 100644 index 00000000..a35b6cc5 Binary files /dev/null and b/src/test/java/UseCase/test2.mp3 differ