Sets the main package name to universeplayer.
The extra underscore was causing problems with the JNI.
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
|
||||
/**
|
||||
* A utility class designed to shortcut adding click listeners to AWT objects.
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @version 0.1
|
||||
*/
|
||||
public interface ClickListener extends MouseListener
|
||||
{
|
||||
default void mousePressed(MouseEvent var1)
|
||||
{
|
||||
}
|
||||
|
||||
default void mouseReleased(MouseEvent var1)
|
||||
{
|
||||
}
|
||||
|
||||
default void mouseEntered(MouseEvent var1)
|
||||
{
|
||||
}
|
||||
|
||||
default void mouseExited(MouseEvent var1)
|
||||
{
|
||||
}
|
||||
}
|
||||
75
interface/src/main/java/edu/regis/universeplayer/Player.java
Normal file
75
interface/src/main/java/edu/regis/universeplayer/Player.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer;
|
||||
|
||||
import edu.regis.universeplayer.data.Song;
|
||||
|
||||
/**
|
||||
* This interface serves as the connection to a music player of some sort, whether it be
|
||||
* browser-based or from a file.
|
||||
*
|
||||
* @param <T> - The type of songs supported.
|
||||
* @author William Hubbard
|
||||
* @since 0.1
|
||||
*/
|
||||
public interface Player<T extends Song>
|
||||
{
|
||||
/**
|
||||
* Obtains the song currently playing.
|
||||
*
|
||||
* @return The current song, or null if none is playing.
|
||||
*/
|
||||
Song getCurrentSong();
|
||||
|
||||
/**
|
||||
* Loads up a song
|
||||
*
|
||||
* @param song - The song to load.
|
||||
*/
|
||||
void play(T song);
|
||||
|
||||
/**
|
||||
* Enables playback of the current song, if one is active.
|
||||
*/
|
||||
void play();
|
||||
|
||||
/**
|
||||
* Pauses playback of the current song.
|
||||
*/
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* Toggles between playing and pausing the current song.
|
||||
*/
|
||||
void togglePlayback();
|
||||
|
||||
/**
|
||||
* Sets the current song time to the specified position.
|
||||
*
|
||||
* @param time - The specified time in the song, in seconds.
|
||||
*/
|
||||
void seek(float time);
|
||||
|
||||
/**
|
||||
* Checks to see if the song is paused.
|
||||
*
|
||||
* @return Whether or not the song is paused.
|
||||
*/
|
||||
boolean isPaused();
|
||||
|
||||
/**
|
||||
* Obtains the time we are currently at in the current song.
|
||||
*
|
||||
* @return - The current song position in seconds, or -1 if no song is playing.
|
||||
*/
|
||||
float getCurrentTime();
|
||||
|
||||
/**
|
||||
* Gets the length of the current song.
|
||||
*
|
||||
* @return The song length, in seconds.
|
||||
*/
|
||||
float getLength();
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.browser;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This serves as a central point for controlling the browser process.
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @since 0.1
|
||||
*/
|
||||
public class Browser
|
||||
{
|
||||
private static Process process;
|
||||
|
||||
/**
|
||||
* Utility method for launching a browser instance
|
||||
*
|
||||
* @throws IOException - Thrown if there is a problem launching the browser.
|
||||
*/
|
||||
public static void launchBrowser() throws IOException
|
||||
{
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
String arch = System.getProperty("os.arch").toLowerCase();
|
||||
String args = " -profile browser/profile";
|
||||
if (process != null && process.isAlive())
|
||||
{
|
||||
process.destroy();
|
||||
}
|
||||
|
||||
if (os.contains("windows"))
|
||||
{
|
||||
setupWindows();
|
||||
process = Runtime.getRuntime().exec("browser/windows/FirefoxPortable.exe" + args);
|
||||
}
|
||||
else if (os.contains("linux"))
|
||||
{
|
||||
setupLinux();
|
||||
if (arch.contains("64"))
|
||||
{
|
||||
process = Runtime.getRuntime().exec("./browser/linux64/firefox" + args);
|
||||
}
|
||||
else
|
||||
{
|
||||
process = Runtime.getRuntime().exec("./browser/linux32/firefox" + args);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(os);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs first-time setup for Windows applications.
|
||||
*/
|
||||
private static void setupWindows()
|
||||
{
|
||||
try
|
||||
{
|
||||
Runtime.getRuntime()
|
||||
.exec("REG ADD HKEY_CURRENT_USER\\SOFTWARE\\Mozilla\\NativeMessagingHosts /v universal_music /d \"" + System
|
||||
.getProperty("user.dir") + "\\bin\\interface.bat\" ");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
System.err.println("Could not set Windows registry key.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs first-time setup for OSX applications.
|
||||
*/
|
||||
private static void setupMac()
|
||||
{
|
||||
Gson gson = new Gson();
|
||||
try (JsonWriter writer = gson.newJsonWriter(new FileWriter(new File(System
|
||||
.getProperty("user.home"), "Library/Application Support/Mozilla/NativeMessagingHosts/universal_music.json"))))
|
||||
{
|
||||
writeManifest(writer, System.getProperty("user.dir") + "/bin/interface");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
System.err.println("Could not set Mac application manifest.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs first-time setup for Linux applications.
|
||||
*/
|
||||
private static void setupLinux()
|
||||
{
|
||||
Gson gson = new Gson();
|
||||
try (JsonWriter writer = gson.newJsonWriter(new FileWriter(new File(System
|
||||
.getProperty("user.home"), ".mozilla/native-messaging-hosts/universal_music.json"))))
|
||||
{
|
||||
writeManifest(writer, System.getProperty("user.dir") + "/bin/interface");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
System.err.println("Could not set Linux application manifest.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeManifest(JsonWriter writer, String path) throws IOException
|
||||
{
|
||||
writer.beginObject();
|
||||
|
||||
writer.name("name");
|
||||
writer.value("universal_music");
|
||||
writer.name("description");
|
||||
writer.value("Universal Music Player");
|
||||
writer.name("path");
|
||||
writer.value(path);
|
||||
writer.name("type");
|
||||
writer.value("stdio");
|
||||
|
||||
writer.name("allowed_extensions");
|
||||
writer.beginArray();
|
||||
writer.value("universal_music@regis.edu");
|
||||
writer.endArray();
|
||||
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the browser process to shut down.
|
||||
*/
|
||||
public static void closeBrowser()
|
||||
{
|
||||
if (process != null && process.isAlive())
|
||||
{
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.browser;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
/**
|
||||
* This class will send messages to the
|
||||
*/
|
||||
public class MessageManager implements Closeable
|
||||
{
|
||||
private final JsonReader in;
|
||||
private final JsonWriter out;
|
||||
private final Gson gson;
|
||||
|
||||
public MessageManager() throws IOException
|
||||
{
|
||||
this.gson = new Gson();
|
||||
this.in = this.gson.newJsonReader(new InputStreamReader(System.in));
|
||||
this.out = this.gson.newJsonWriter(new OutputStreamWriter(System.out));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an object from the browser.
|
||||
*
|
||||
* @return - A parsed object. What it is depends on the browser.
|
||||
*/
|
||||
public Object getMessage() throws IOException
|
||||
{
|
||||
Object value = null;
|
||||
this.in.beginObject();
|
||||
while (this.in.hasNext())
|
||||
{
|
||||
switch (this.in.nextName())
|
||||
{
|
||||
case "response":
|
||||
switch (this.in.peek())
|
||||
{
|
||||
case STRING:
|
||||
value = this.in.nextString();
|
||||
break;
|
||||
case NUMBER:
|
||||
value = this.in.nextDouble();
|
||||
break;
|
||||
case BOOLEAN:
|
||||
value = this.in.nextBoolean();
|
||||
break;
|
||||
case NULL:
|
||||
this.in.nextNull();
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unexpected JSON type" + this.in.peek());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.in.endObject();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a string message to the browser.
|
||||
*
|
||||
* @param message - The message to send.
|
||||
*/
|
||||
public void writeMessage(String message)
|
||||
{
|
||||
String value = null;
|
||||
String type = null;
|
||||
try
|
||||
{
|
||||
this.out.beginObject();
|
||||
this.out.name("message");
|
||||
this.out.value(message);
|
||||
this.out.endObject();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pings the browser.
|
||||
*/
|
||||
public void ping()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.out.beginObject();
|
||||
this.out.name("command");
|
||||
this.out.value("ping");
|
||||
this.out.endObject();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
this.in.close();
|
||||
this.out.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.data;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
public class Album implements Comparable<Album>
|
||||
{
|
||||
public int id;
|
||||
public String name;
|
||||
public String[] artists;
|
||||
public ImageIcon art;
|
||||
public int year;
|
||||
public String[] genres;
|
||||
public int totalTracks;
|
||||
|
||||
@Override
|
||||
public int compareTo(Album o)
|
||||
{
|
||||
if (o != null)
|
||||
{
|
||||
return this.name.compareTo(o.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.data;
|
||||
|
||||
/**
|
||||
* Lists all of the type of collections possible.
|
||||
*/
|
||||
public enum CollectionType
|
||||
{
|
||||
album(Album.class), artist(String.class), albumArtist(String.class), genre(String.class), year(Integer.class);
|
||||
|
||||
/**
|
||||
* The object class for this type.
|
||||
*/
|
||||
public Class<?> objectType;
|
||||
|
||||
/**
|
||||
* Creates a collection type.
|
||||
*
|
||||
* @param type - The class this type uses.
|
||||
*/
|
||||
CollectionType(Class<?> type)
|
||||
{
|
||||
this.objectType = type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.data;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A song provider that serves as a central point for any and all song providers, caching the results in memory for quick and easy access.
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @version 0.1
|
||||
*/
|
||||
public class CompiledSongProvider implements SongProvider
|
||||
{
|
||||
/**
|
||||
* A set of all providers we pull from
|
||||
*/
|
||||
private HashMap<SongProvider, Set<Song>> providers = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A cache of all albums used.
|
||||
*/
|
||||
private HashMap<Album, Set<Song>> cachedAlbums = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A cache of all album names.
|
||||
*/
|
||||
private HashMap<String, Album> cachedAlbumNames = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A cache of all songs used.
|
||||
*/
|
||||
private HashSet<Song> cachedSongs = new HashSet<>();
|
||||
|
||||
/**
|
||||
* A cache of all song artists used.
|
||||
*/
|
||||
private HashMap<String, Set<Song>> cachedArtists = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A cache of all album artists used.
|
||||
*/
|
||||
private HashMap<String, Set<Album>> cachedAlbumArtists = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A cache of all genres used.
|
||||
*/
|
||||
private HashMap<String, Set<Album>> cachedGenres = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A cache of all years used.
|
||||
*/
|
||||
private HashMap<Integer, Set<Album>> cachedYears = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Creates a new CompiledSongProvider containing a set of existing providers.
|
||||
*
|
||||
* @param providers - Providers to add.
|
||||
*/
|
||||
public CompiledSongProvider(SongProvider... providers)
|
||||
{
|
||||
for (SongProvider provider : providers)
|
||||
{
|
||||
this.addProvider(provider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a provider to the list
|
||||
*
|
||||
* @param provider - The provider to add.
|
||||
*/
|
||||
public void addProvider(SongProvider provider)
|
||||
{
|
||||
if (!this.providers.containsKey(provider))
|
||||
{
|
||||
this.providers.put(provider, new HashSet<>(provider.getSongs()));
|
||||
this.addToCache(provider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches all the songs contained in a provider.
|
||||
*
|
||||
* @param provider - The provider to cache.
|
||||
*/
|
||||
private void addToCache(SongProvider provider)
|
||||
{
|
||||
for (Song song : provider.getSongs())
|
||||
{
|
||||
if (this.cachedSongs.add(song))
|
||||
{
|
||||
this.cachedAlbumNames.put(song.album.name, song.album);
|
||||
if (!this.cachedAlbums.containsKey(song.album))
|
||||
{
|
||||
this.cachedAlbums.put(song.album, new HashSet<>());
|
||||
}
|
||||
this.cachedAlbums.get(song.album).add(song);
|
||||
for (String artist : song.artists)
|
||||
{
|
||||
if (!this.cachedArtists.containsKey(artist))
|
||||
{
|
||||
this.cachedArtists.put(artist, new HashSet<>());
|
||||
}
|
||||
this.cachedArtists.get(artist).add(song);
|
||||
}
|
||||
for (String artist : song.album.artists)
|
||||
{
|
||||
if (!this.cachedAlbumArtists.containsKey(artist))
|
||||
{
|
||||
this.cachedAlbumArtists.put(artist, new HashSet<>());
|
||||
}
|
||||
this.cachedAlbumArtists.get(artist).add(song.album);
|
||||
}
|
||||
for (String genre : song.album.genres)
|
||||
{
|
||||
if (!this.cachedGenres.containsKey(genre))
|
||||
{
|
||||
this.cachedGenres.put(genre, new HashSet<>());
|
||||
}
|
||||
this.cachedGenres.get(genre).add(song.album);
|
||||
}
|
||||
if (!this.cachedYears.containsKey(song.album.year))
|
||||
{
|
||||
this.cachedYears.put(song.album.year, new HashSet<>());
|
||||
}
|
||||
this.cachedYears.get(song.album.year).add(song.album);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* If we couldn't add it, then another provider has provided that song already. We
|
||||
* should remove it from our collection just to ensure that there is no confusion.
|
||||
*/
|
||||
this.providers.get(provider).remove(song);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a provider from the compilation.
|
||||
*
|
||||
* @param provider - The provider to remove.
|
||||
*/
|
||||
public void removeProvider(SongProvider provider)
|
||||
{
|
||||
this.removeFromCache(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all songs from a provider from a cache.
|
||||
*
|
||||
* @param provider - The provider to move out.
|
||||
*/
|
||||
private void removeFromCache(SongProvider provider)
|
||||
{
|
||||
Set<Song> songs = this.providers.remove(provider);
|
||||
if (songs != null)
|
||||
{
|
||||
for (Song song : songs)
|
||||
{
|
||||
this.cachedSongs.remove(song);
|
||||
this.cachedAlbums.get(song.album).remove(song);
|
||||
/*
|
||||
* Remove empty albums
|
||||
*/
|
||||
if (this.cachedAlbums.get(song.album).isEmpty())
|
||||
{
|
||||
this.cachedAlbums.remove(song.album);
|
||||
this.cachedAlbumNames.remove(song.album.name);
|
||||
}
|
||||
for (String artist : song.artists)
|
||||
{
|
||||
this.cachedArtists.get(artist).remove(song);
|
||||
if (this.cachedArtists.get(artist).isEmpty())
|
||||
{
|
||||
this.cachedArtists.remove(artist);
|
||||
}
|
||||
}
|
||||
for (String artist : song.album.artists)
|
||||
{
|
||||
this.cachedAlbumArtists.get(artist).remove(song.album);
|
||||
if (this.cachedAlbumArtists.get(artist).isEmpty())
|
||||
{
|
||||
this.cachedAlbumArtists.remove(artist);
|
||||
}
|
||||
}
|
||||
for (String genre : song.album.genres)
|
||||
{
|
||||
this.cachedGenres.get(genre).remove(song.album);
|
||||
if (this.cachedGenres.get(genre).isEmpty())
|
||||
{
|
||||
this.cachedGenres.remove(genre);
|
||||
}
|
||||
}
|
||||
this.cachedYears.get(song.album.year).remove(song.album);
|
||||
if (this.cachedYears.get(song.album.year).isEmpty())
|
||||
{
|
||||
this.cachedYears.remove(song.album.year);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains all albums within the collection.
|
||||
*
|
||||
* @return A list of albums.
|
||||
*/
|
||||
@Override
|
||||
public Collection<Album> getAlbums()
|
||||
{
|
||||
return this.cachedAlbums.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains all songs within the collection.
|
||||
*
|
||||
* @return A list of songs.
|
||||
*/
|
||||
@Override
|
||||
public Collection<Song> getSongs()
|
||||
{
|
||||
return this.cachedSongs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a list of all artists.
|
||||
*
|
||||
* @return All artists.
|
||||
*/
|
||||
@Override
|
||||
public Collection<String> getArtists()
|
||||
{
|
||||
return this.cachedArtists.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a list of all album artists.
|
||||
*
|
||||
* @return All album artists.
|
||||
*/
|
||||
@Override
|
||||
public Collection<String> getAlbumArtists()
|
||||
{
|
||||
return this.cachedAlbumArtists.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a list of all genres.
|
||||
*
|
||||
* @return All genres.
|
||||
*/
|
||||
@Override
|
||||
public Collection<String> getGenres()
|
||||
{
|
||||
return this.cachedGenres.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a list of all years that have albums.
|
||||
*
|
||||
* @return All years.
|
||||
*/
|
||||
@Override
|
||||
public Collection<Integer> getYears()
|
||||
{
|
||||
return this.cachedYears.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains all songs from an album.
|
||||
*
|
||||
* @param album - The album to obtain
|
||||
* @return All songs from the requested album, or null if that album is not in the database.
|
||||
*/
|
||||
@Override
|
||||
public Collection<Song> getSongsFromAlbum(Album album)
|
||||
{
|
||||
return this.cachedAlbums.get(album);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains all songs written by a given artist.
|
||||
*
|
||||
* @param artist - The artist to search for
|
||||
* @return A list of all songs from the specified artist, or null if that artist is not in the
|
||||
* database.
|
||||
*/
|
||||
@Override
|
||||
public Collection<Song> getSongsFromArtist(String artist)
|
||||
{
|
||||
return this.cachedArtists.get(artist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an album by a specific name.
|
||||
*
|
||||
* @param name - The name to search for.
|
||||
* @return - The first album that matches the given name, or null if that album name is not in
|
||||
* the database.
|
||||
*/
|
||||
@Override
|
||||
public Album getAlbumByName(String name)
|
||||
{
|
||||
return this.cachedAlbumNames.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains all albums that were written by a certain artist.
|
||||
*
|
||||
* @param artist - The artist to search for.
|
||||
* @return - The collection on matching albums.
|
||||
*/
|
||||
@Override
|
||||
public Collection<Album> getAlbumsFromArtist(String artist)
|
||||
{
|
||||
return this.cachedAlbumArtists.get(artist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains all albums that match a certain genre
|
||||
*
|
||||
* @param genre - The genre to search for.
|
||||
* @return - The collection on matching albums.
|
||||
*/
|
||||
@Override
|
||||
public Collection<Album> getAlbumsFromGenre(String genre)
|
||||
{
|
||||
return this.cachedGenres.get(genre);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains all albums that were released a certain year.
|
||||
*
|
||||
* @param year - The year to search for.
|
||||
* @return - The collection on matching albums.
|
||||
*/
|
||||
@Override
|
||||
public Collection<Album> getAlbumsFromYear(int year)
|
||||
{
|
||||
return this.cachedYears.get(year);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A test song database that automatically generates a handful of songs.
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @version 0.1
|
||||
*/
|
||||
public class SimpleSongProvider implements SongProvider
|
||||
{
|
||||
private ArrayList<Album> albums;
|
||||
private ArrayList<Song> songs;
|
||||
|
||||
@Override
|
||||
public Collection<Album> getAlbums()
|
||||
{
|
||||
if (albums == null)
|
||||
{
|
||||
albums = new ArrayList<>();
|
||||
Random random = new Random();
|
||||
StringBuilder builder;
|
||||
String albumName, albumArtist;
|
||||
Album album;
|
||||
for (int albumNum = 0, numAlbums = random
|
||||
.nextInt(20) + 20; albumNum < numAlbums; albumNum++)
|
||||
{
|
||||
builder = new StringBuilder();
|
||||
for (int i = 0, l = random.nextInt(10) + 10; i < l; i++)
|
||||
{
|
||||
builder.append((char) (random.nextInt(26) + 97));
|
||||
}
|
||||
albumName = builder.toString();
|
||||
|
||||
builder = new StringBuilder();
|
||||
for (int i = 0, l = random.nextInt(10) + 10; i < l; i++)
|
||||
{
|
||||
builder.append((char) (random.nextInt(26) + 97));
|
||||
}
|
||||
albumArtist = builder.toString();
|
||||
|
||||
album = new Album()
|
||||
{
|
||||
};
|
||||
|
||||
album.name = albumName;
|
||||
album.artists = new String[]{albumArtist};
|
||||
album.genres = new String[]{"Soundtrack"};
|
||||
album.year = 2019;
|
||||
album.totalTracks = random.nextInt(10) + 10;
|
||||
album.id = albumNum;
|
||||
|
||||
albums.add(album);
|
||||
}
|
||||
}
|
||||
|
||||
return albums;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Song> getSongs()
|
||||
{
|
||||
if (songs == null)
|
||||
{
|
||||
/*
|
||||
* Generate a list of songs
|
||||
*/
|
||||
Random random = new Random();
|
||||
StringBuilder builder;
|
||||
String songTitle;
|
||||
Song song;
|
||||
int songNum, numSongs;
|
||||
songs = new ArrayList<>();
|
||||
for (Album album : this.getAlbums())
|
||||
{
|
||||
for (songNum = 0, numSongs = album.totalTracks; songNum < numSongs; songNum++)
|
||||
{
|
||||
builder = new StringBuilder();
|
||||
for (int i = 0, l = random.nextInt(10) + 10; i < l; i++)
|
||||
{
|
||||
builder.append((char) (random.nextInt(26) + 97));
|
||||
}
|
||||
songTitle = builder.toString();
|
||||
|
||||
song = new Song()
|
||||
{
|
||||
};
|
||||
song.title = songTitle;
|
||||
song.disc = 1;
|
||||
song.trackNum = songNum + 1;
|
||||
song.artists = album.artists.clone();
|
||||
song.album = album;
|
||||
songs.add(song);
|
||||
}
|
||||
album.totalTracks = numSongs;
|
||||
}
|
||||
}
|
||||
return this.songs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getArtists()
|
||||
{
|
||||
return this.songs.stream().flatMap(song -> Arrays.stream(song.artists)).sorted()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getGenres()
|
||||
{
|
||||
return this.songs.stream().flatMap(song -> Arrays.stream(song.album.genres)).sorted()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getAlbumArtists()
|
||||
{
|
||||
return this.albums.stream().flatMap(album -> Arrays.stream(album.artists)).sorted()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Integer> getYears()
|
||||
{
|
||||
return this.albums.stream().map(album -> album.year).sorted()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Song> getSongsFromAlbum(Album album)
|
||||
{
|
||||
return this.songs.stream().filter(song -> song.album == album).sorted()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Song> getSongsFromArtist(String artist)
|
||||
{
|
||||
return this.songs.stream().filter(song -> Arrays.asList(song.artists).contains(artist)).sorted()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Album getAlbumByName(String name)
|
||||
{
|
||||
return this.albums.stream().filter(album -> album.name.equals(name)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Album> getAlbumsFromArtist(String artist)
|
||||
{
|
||||
return this.albums.stream().filter(album -> Arrays.asList(album.artists).contains(artist))
|
||||
.sorted().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Album> getAlbumsFromGenre(String genre)
|
||||
{
|
||||
return this.albums.stream().filter(album -> Arrays.asList(album.genres).contains(genre))
|
||||
.sorted().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Album> getAlbumsFromYear(int year)
|
||||
{
|
||||
return this.albums.stream().filter(album -> album.year == year)
|
||||
.sorted().collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.data;
|
||||
|
||||
/**
|
||||
* Contains data for a song.
|
||||
*/
|
||||
public abstract class Song implements Comparable<Song>
|
||||
{
|
||||
public String title;
|
||||
public String[] artists;
|
||||
public int trackNum;
|
||||
public int disc;
|
||||
/**
|
||||
* A reference to the album this song is part of.
|
||||
*/
|
||||
public Album album;
|
||||
|
||||
@Override
|
||||
public int compareTo(Song o)
|
||||
{
|
||||
if (o != null)
|
||||
{
|
||||
int comp = this.album.compareTo(o.album);
|
||||
if (comp == 0)
|
||||
{
|
||||
comp = Integer.compare(this.disc, o.disc);
|
||||
if (comp == 0)
|
||||
{
|
||||
comp = Integer.compare(this.trackNum, o.trackNum);
|
||||
if (comp == 0)
|
||||
{
|
||||
comp = this.title.compareTo(o.title);
|
||||
}
|
||||
}
|
||||
}
|
||||
return comp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.data;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* The song provider interface serves to give the application access to any form of song database as needed.
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @version 0.1
|
||||
*/
|
||||
public interface SongProvider
|
||||
{
|
||||
/**
|
||||
* A SongProvider instance designed to
|
||||
*/
|
||||
CompiledSongProvider INSTANCE = new CompiledSongProvider(new SimpleSongProvider());
|
||||
|
||||
/**
|
||||
* Obtains all albums within the collection.
|
||||
*
|
||||
* @return A list of albums.
|
||||
*/
|
||||
Collection<Album> getAlbums();
|
||||
|
||||
/**
|
||||
* Obtains all songs within the collection.
|
||||
*
|
||||
* @return A list of songs.
|
||||
*/
|
||||
Collection<Song> getSongs();
|
||||
|
||||
/**
|
||||
* Obtains a list of all artists.
|
||||
*
|
||||
* @return All artists.
|
||||
*/
|
||||
Collection<String> getArtists();
|
||||
|
||||
/**
|
||||
* Obtains a list of all album artists.
|
||||
*
|
||||
* @return All album artists.
|
||||
*/
|
||||
Collection<String> getAlbumArtists();
|
||||
|
||||
/**
|
||||
* Obtains a list of all genres.
|
||||
*
|
||||
* @return All genres.
|
||||
*/
|
||||
Collection<String> getGenres();
|
||||
|
||||
/**
|
||||
* Obtains a list of all years that have albums.
|
||||
*
|
||||
* @return All years.
|
||||
*/
|
||||
Collection<Integer> getYears();
|
||||
|
||||
/**
|
||||
* Obtains all songs from an album.
|
||||
*
|
||||
* @param album - The album to obtain
|
||||
* @return All songs from the requested album, or null if that album is not in the database.
|
||||
*/
|
||||
Collection<Song> getSongsFromAlbum(Album album);
|
||||
|
||||
/**
|
||||
* Obtains all songs written by a given artist.
|
||||
*
|
||||
* @param artist - The artist to search for
|
||||
* @return A list of all songs from the specified artist, or null if that artist is not in the
|
||||
* database.
|
||||
*/
|
||||
Collection<Song> getSongsFromArtist(String artist);
|
||||
|
||||
/**
|
||||
* Obtains an album by a specific name.
|
||||
*
|
||||
* @param name - The name to search for.
|
||||
* @return - The first album that matches the given name, or null if that album name is not in
|
||||
* the database.
|
||||
*/
|
||||
Album getAlbumByName(String name);
|
||||
|
||||
/**
|
||||
* Obtains all albums that were written by a certain artist.
|
||||
*
|
||||
* @param artist - The artist to search for.
|
||||
* @return - The collection on matching albums.
|
||||
*/
|
||||
Collection<Album> getAlbumsFromArtist(String artist);
|
||||
|
||||
/**
|
||||
* Obtains all albums that match a certain genre
|
||||
*
|
||||
* @param genre - The genre to search for.
|
||||
* @return - The collection on matching albums.
|
||||
*/
|
||||
Collection<Album> getAlbumsFromGenre(String genre);
|
||||
|
||||
/**
|
||||
* Obtains all albums that were released a certain year.
|
||||
*
|
||||
* @param year - The year to search for.
|
||||
* @return - The collection on matching albums.
|
||||
*/
|
||||
Collection<Album> getAlbumsFromYear(int year);
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.player;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SpringLayout;
|
||||
|
||||
import edu.regis.universeplayer.data.Album;
|
||||
|
||||
/**
|
||||
* This panel will display information on an album.
|
||||
*/
|
||||
public class AlbumInfo extends JPanel
|
||||
{
|
||||
private Album album;
|
||||
private JLabel artLabel;
|
||||
private JLabel albumName;
|
||||
private JLabel artists;
|
||||
private JLabel genres;
|
||||
private JLabel year;
|
||||
|
||||
public AlbumInfo()
|
||||
{
|
||||
SpringLayout infoLayout = new SpringLayout();
|
||||
this.setLayout(infoLayout);
|
||||
|
||||
this.artLabel = new JLabel();
|
||||
this.add(this.artLabel);
|
||||
this.albumName = new JLabel("Album");
|
||||
this.add(this.albumName);
|
||||
this.artists = new JLabel("Artists");
|
||||
this.add(this.artists);
|
||||
this.genres = new JLabel("Genres");
|
||||
this.add(this.genres);
|
||||
this.year = new JLabel("20XX");
|
||||
this.add(this.year);
|
||||
|
||||
/*
|
||||
* Set the layout information
|
||||
*/
|
||||
infoLayout.putConstraint(SpringLayout.NORTH, artLabel, 5, SpringLayout.NORTH, this);
|
||||
infoLayout.putConstraint(SpringLayout.WEST, artLabel, 5, SpringLayout.WEST, this);
|
||||
infoLayout.putConstraint(SpringLayout.NORTH, albumName, 5, SpringLayout.NORTH, this);
|
||||
infoLayout.putConstraint(SpringLayout.WEST, albumName, 5, SpringLayout.EAST, artLabel);
|
||||
infoLayout.putConstraint(SpringLayout.NORTH, artists, 5, SpringLayout.SOUTH, albumName);
|
||||
infoLayout.putConstraint(SpringLayout.WEST, artists, 5, SpringLayout.EAST, artLabel);
|
||||
infoLayout.putConstraint(SpringLayout.NORTH, genres, 5, SpringLayout.SOUTH, artists);
|
||||
infoLayout.putConstraint(SpringLayout.WEST, genres, 5, SpringLayout.EAST, artLabel);
|
||||
infoLayout.putConstraint(SpringLayout.NORTH, year, 5, SpringLayout.SOUTH, genres);
|
||||
infoLayout.putConstraint(SpringLayout.WEST, year, 5, SpringLayout.EAST, artLabel);
|
||||
// infoLayout.putConstraint(SpringLayout.EAST, this, 0, SpringLayout.EAST, albumName);
|
||||
infoLayout.putConstraint(SpringLayout.EAST, this, 0, SpringLayout.EAST, artists);
|
||||
// infoLayout.putConstraint(SpringLayout.EAST, this, 0, SpringLayout.EAST, genres);
|
||||
// infoLayout.putConstraint(SpringLayout.EAST, this, 0, SpringLayout.EAST, year);
|
||||
infoLayout.putConstraint(SpringLayout.SOUTH, this, 5, SpringLayout.EAST, artLabel);
|
||||
}
|
||||
|
||||
public AlbumInfo(Album album)
|
||||
{
|
||||
this();
|
||||
this.updateInfo(album);
|
||||
}
|
||||
|
||||
public void updateInfo(Album album)
|
||||
{
|
||||
final int ART_SIZE = 128;
|
||||
ImageIcon icon;
|
||||
StringBuilder builder;
|
||||
|
||||
this.album = album;
|
||||
|
||||
if (album.art != null)
|
||||
{
|
||||
icon = album.art;
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = new ImageIcon(this.getClass()
|
||||
.getResource("/gui/icons/defaultart.png"), "Default");
|
||||
}
|
||||
icon.setImage(icon.getImage().getScaledInstance(ART_SIZE, ART_SIZE, 0));
|
||||
this.artLabel.setIcon(icon);
|
||||
|
||||
this.albumName.setText(album.name);
|
||||
|
||||
builder = new StringBuilder();
|
||||
if (album.artists != null && album.artists.length >= 1)
|
||||
{
|
||||
builder.append(album.artists[0]);
|
||||
for (int i = 1, l = album.artists.length - 1; i < l; i++)
|
||||
{
|
||||
builder.append(", ");
|
||||
builder.append(album.artists[i]);
|
||||
}
|
||||
if (album.artists.length > 1)
|
||||
{
|
||||
builder.append(" & ");
|
||||
builder.append(album.artists[album.artists.length - 1]);
|
||||
}
|
||||
}
|
||||
this.artists.setText(builder.toString());
|
||||
|
||||
builder = new StringBuilder();
|
||||
if (album.genres != null && album.genres.length >= 1)
|
||||
{
|
||||
builder.append(album.genres[0]);
|
||||
for (int i = 1, l = album.genres.length - 1; i < l; i++)
|
||||
{
|
||||
builder.append(", ");
|
||||
builder.append(album.genres[i]);
|
||||
}
|
||||
if (album.genres.length > 1)
|
||||
{
|
||||
builder.append(" & ");
|
||||
builder.append(album.genres[album.genres.length - 1]);
|
||||
}
|
||||
}
|
||||
this.genres.setText(builder.toString());
|
||||
|
||||
this.year.setText(String.valueOf(album.year));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.player;
|
||||
|
||||
import java.awt.FlowLayout;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import edu.regis.universeplayer.ClickListener;
|
||||
import edu.regis.universeplayer.data.Album;
|
||||
import edu.regis.universeplayer.data.CollectionType;
|
||||
import edu.regis.universeplayer.data.Song;
|
||||
import edu.regis.universeplayer.data.SongProvider;
|
||||
|
||||
/**
|
||||
* This panel will list all the song collections on display (albums, artists, genres, etc.)
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @version 0.1
|
||||
*/
|
||||
public class CollectionList extends JPanel
|
||||
{
|
||||
/**
|
||||
* The type of collections being displayed.
|
||||
*/
|
||||
private CollectionType type;
|
||||
/**
|
||||
* A link between the JLabel and the object they point towards.
|
||||
*/
|
||||
private Map<JLabel, Object> labelMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A list of all things interested in knowing when we click a collection.
|
||||
*/
|
||||
private LinkedList<SongDisplayListener> listeners = new LinkedList<>();
|
||||
|
||||
/**
|
||||
* Creates a collections list view.
|
||||
*/
|
||||
public CollectionList()
|
||||
{
|
||||
super();
|
||||
|
||||
FlowLayout layout = new FlowLayout();
|
||||
this.setLayout(layout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the collections currently listed, sorted by album.
|
||||
*
|
||||
* @param type - The type of collection we are displaying.
|
||||
* @param objects - The collection to display.
|
||||
*/
|
||||
public void listCollection(CollectionType type, Collection<?> objects)
|
||||
{
|
||||
Class<?> fType = objects.stream().filter(Objects::nonNull).map(Object::getClass).findFirst()
|
||||
.orElse(null);
|
||||
if (objects.isEmpty() || fType == null)
|
||||
{
|
||||
/*
|
||||
* We really don't need error checking here, and we couldn't get it working anyway.
|
||||
*/
|
||||
this.labelMap.clear();
|
||||
this.removeAll();
|
||||
return;
|
||||
}
|
||||
if (!type.objectType.isAssignableFrom(fType))
|
||||
{
|
||||
throw new ClassCastException("Can't assign " + type.objectType
|
||||
.getName() + " from " + fType
|
||||
.getName());
|
||||
}
|
||||
|
||||
this.labelMap.clear();
|
||||
this.removeAll();
|
||||
this.type = type;
|
||||
switch (type)
|
||||
{
|
||||
case album:
|
||||
this.addAlbums(objects.stream().sorted().map(ob -> (Album) ob)
|
||||
.collect(Collectors.toList()));
|
||||
break;
|
||||
case artist:
|
||||
this.addArtists(objects.stream().sorted().map(ob -> (String) ob)
|
||||
.collect(Collectors.toList()), false);
|
||||
break;
|
||||
case albumArtist:
|
||||
this.addArtists(objects.stream().sorted().map(ob -> (String) ob)
|
||||
.collect(Collectors.toList()), true);
|
||||
break;
|
||||
case genre:
|
||||
this.addGenres(objects.stream().sorted().map(ob -> (String) ob)
|
||||
.collect(Collectors.toList()));
|
||||
break;
|
||||
case year:
|
||||
this.addYears(objects.stream().sorted().map(ob -> (Integer) ob)
|
||||
.collect(Collectors.toList()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the display to show a list of artists
|
||||
*
|
||||
* @param artists - The list of artists to display.
|
||||
* @param album - Whether the list should be treated as song artists or album artists.
|
||||
*/
|
||||
private void addArtists(List<String> artists, boolean album)
|
||||
{
|
||||
final int ART_SIZE = 128;
|
||||
|
||||
JLabel artistLabel;
|
||||
ImageIcon icon;
|
||||
|
||||
for (String artist : artists)
|
||||
{
|
||||
artistLabel = new JLabel();
|
||||
// TODO - Maybe add some sort of artist image lookup?
|
||||
// if (album.art != null)
|
||||
// {
|
||||
// icon = album.art;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
icon = new ImageIcon(this.getClass()
|
||||
.getResource("/gui/icons/artist.png"), "Default");
|
||||
// }
|
||||
icon.setImage(icon.getImage().getScaledInstance(ART_SIZE, ART_SIZE, 0));
|
||||
artistLabel.setIcon(icon);
|
||||
artistLabel.setText(artist);
|
||||
artistLabel.setHorizontalTextPosition(JLabel.CENTER);
|
||||
artistLabel.setVerticalTextPosition(JLabel.BOTTOM);
|
||||
artistLabel.addMouseListener((ClickListener) mouseEvent -> {
|
||||
if (album)
|
||||
{
|
||||
this.triggerSongDisplayListeners(SongProvider.INSTANCE
|
||||
.getAlbumsFromArtist(artist).stream()
|
||||
.flatMap(album2 -> SongProvider.INSTANCE.getSongsFromAlbum(album2)
|
||||
.stream())
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.triggerSongDisplayListeners(SongProvider.INSTANCE
|
||||
.getSongsFromArtist(artist));
|
||||
}
|
||||
});
|
||||
this.add(artistLabel);
|
||||
this.labelMap.put(artistLabel, artist);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the display to show a list of albums.
|
||||
*
|
||||
* @param albums - The list of albums to display.
|
||||
*/
|
||||
private void addAlbums(List<Album> albums)
|
||||
{
|
||||
final int ART_SIZE = 128;
|
||||
|
||||
JLabel albumLabel;
|
||||
ImageIcon icon;
|
||||
|
||||
for (Album album : albums)
|
||||
{
|
||||
albumLabel = new JLabel();
|
||||
if (album.art != null)
|
||||
{
|
||||
icon = album.art;
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = new ImageIcon(this.getClass()
|
||||
.getResource("/gui/icons/defaultart.png"), "Default");
|
||||
}
|
||||
icon.setImage(icon.getImage().getScaledInstance(ART_SIZE, ART_SIZE, 0));
|
||||
albumLabel.setIcon(icon);
|
||||
albumLabel.setText(album.name);
|
||||
albumLabel.setHorizontalTextPosition(JLabel.CENTER);
|
||||
albumLabel.setVerticalTextPosition(JLabel.BOTTOM);
|
||||
albumLabel.addMouseListener((ClickListener) mouseEvent -> this.triggerSongDisplayListeners(SongProvider.INSTANCE
|
||||
.getSongsFromAlbum(album)));
|
||||
this.add(albumLabel);
|
||||
this.labelMap.put(albumLabel, album);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the display to show a list of genres.
|
||||
*
|
||||
* @param genres - The list of genres to display.
|
||||
*/
|
||||
private void addGenres(List<String> genres)
|
||||
{
|
||||
// final int ART_SIZE = 128;
|
||||
|
||||
JLabel genreLabel;
|
||||
// ImageIcon icon;
|
||||
|
||||
for (String genre : genres)
|
||||
{
|
||||
genreLabel = new JLabel();
|
||||
// TODO - Maybe add some sort of artist image lookup?
|
||||
// if (album.art != null)
|
||||
// {
|
||||
// icon = album.art;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// icon = new ImageIcon(this.getClass()
|
||||
// .getResource("/gui/icons/defaultart.png"), "Default");
|
||||
// }
|
||||
// icon.setImage(icon.getImage().getScaledInstance(ART_SIZE, ART_SIZE, 0));
|
||||
// albumLabel.setIcon(icon);
|
||||
genreLabel.setText(genre);
|
||||
genreLabel.setHorizontalTextPosition(JLabel.CENTER);
|
||||
genreLabel.setVerticalTextPosition(JLabel.BOTTOM);
|
||||
genreLabel.addMouseListener((ClickListener) mouseEvent -> this.triggerSongDisplayListeners(SongProvider.INSTANCE
|
||||
.getAlbumsFromGenre(genre).stream()
|
||||
.flatMap(album2 -> SongProvider.INSTANCE.getSongsFromAlbum(album2)
|
||||
.stream())
|
||||
.collect(Collectors.toList())));
|
||||
this.add(genreLabel);
|
||||
this.labelMap.put(genreLabel, genre);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the display to show a list of release years.
|
||||
*
|
||||
* @param years - The list of genres to display.
|
||||
*/
|
||||
private void addYears(List<Integer> years)
|
||||
{
|
||||
// final int ART_SIZE = 128;
|
||||
|
||||
JLabel yearLabel;
|
||||
// ImageIcon icon;
|
||||
|
||||
for (Integer year : years)
|
||||
{
|
||||
yearLabel = new JLabel();
|
||||
// TODO - Maybe add some sort of artist image lookup?
|
||||
// if (album.art != null)
|
||||
// {
|
||||
// icon = album.art;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// icon = new ImageIcon(this.getClass()
|
||||
// .getResource("/gui/icons/defaultart.png"), "Default");
|
||||
// }
|
||||
// icon.setImage(icon.getImage().getScaledInstance(ART_SIZE, ART_SIZE, 0));
|
||||
// albumLabel.setIcon(icon);
|
||||
yearLabel.setText(year.toString());
|
||||
yearLabel.setHorizontalTextPosition(JLabel.CENTER);
|
||||
yearLabel.setVerticalTextPosition(JLabel.BOTTOM);
|
||||
yearLabel.addMouseListener((ClickListener) mouseEvent -> this.triggerSongDisplayListeners(SongProvider.INSTANCE
|
||||
.getAlbumsFromYear(year).stream()
|
||||
.flatMap(album2 -> SongProvider.INSTANCE.getSongsFromAlbum(album2)
|
||||
.stream())
|
||||
.collect(Collectors.toList())));
|
||||
this.add(yearLabel);
|
||||
this.labelMap.put(yearLabel, year);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for when the displayed songs should change.
|
||||
*
|
||||
* @param listener - The listener to add.
|
||||
*/
|
||||
public void addSongDisplayListener(SongDisplayListener listener)
|
||||
{
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for when the displayed songs should change.
|
||||
*
|
||||
* @param listener - The listener to add.
|
||||
*/
|
||||
public void removeSongDisplayListener(SongDisplayListener listener)
|
||||
{
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers all the song display listeners.
|
||||
*/
|
||||
protected void triggerSongDisplayListeners(Collection<Song> songs)
|
||||
{
|
||||
for (SongDisplayListener listener : this.listeners)
|
||||
{
|
||||
listener.updateSongs(songs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.player;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import edu.regis.universeplayer.ClickListener;
|
||||
import edu.regis.universeplayer.data.CollectionType;
|
||||
import edu.regis.universeplayer.data.Song;
|
||||
import edu.regis.universeplayer.data.SongProvider;
|
||||
|
||||
/**
|
||||
* This panel links to various song collections the player has set up.
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @version 0.1
|
||||
*/
|
||||
public class Collections extends JPanel
|
||||
{
|
||||
/**
|
||||
* A list of all things interested in knowing when we click a collection.
|
||||
*/
|
||||
private LinkedList<SongDisplayListener> listeners = new LinkedList<>();
|
||||
|
||||
/**
|
||||
* Creates a collections list view.
|
||||
*/
|
||||
public Collections()
|
||||
{
|
||||
JLabel label;
|
||||
BoxLayout layout = new BoxLayout(this, BoxLayout.Y_AXIS);
|
||||
this.setLayout(layout);
|
||||
|
||||
this.add(label = new JLabel("All Songs"));
|
||||
label.setForeground(Color.BLUE);
|
||||
label.addMouseListener((ClickListener) mouseEvent -> this
|
||||
.triggerSongDisplayListeners(SongProvider.INSTANCE.getSongs()));
|
||||
this.add(label = new JLabel("Artists"));
|
||||
label.setForeground(Color.BLUE);
|
||||
label.addMouseListener((ClickListener) mouseEvent -> this
|
||||
.triggerCollectionDisplayListeners(CollectionType.albumArtist, SongProvider.INSTANCE
|
||||
.getAlbumArtists()));
|
||||
this.add(label = new JLabel("Albums"));
|
||||
label.setForeground(Color.BLUE);
|
||||
label.addMouseListener((ClickListener) mouseEvent -> this
|
||||
.triggerCollectionDisplayListeners(CollectionType.album, SongProvider.INSTANCE
|
||||
.getAlbums()));
|
||||
this.add(label = new JLabel("Genres"));
|
||||
label.setForeground(Color.BLUE);
|
||||
label.addMouseListener((ClickListener) mouseEvent -> this
|
||||
.triggerCollectionDisplayListeners(CollectionType.genre, SongProvider.INSTANCE
|
||||
.getGenres()));
|
||||
this.add(label = new JLabel("Years"));
|
||||
label.setForeground(Color.BLUE);
|
||||
label.addMouseListener((ClickListener) mouseEvent -> this
|
||||
.triggerCollectionDisplayListeners(CollectionType.year, SongProvider.INSTANCE
|
||||
.getYears()));
|
||||
this.add(new JLabel("\u23AF\u23AF\u23AF\u23AF\u23AF\u23AF"));
|
||||
this.add(label = new JLabel("Playlists"));
|
||||
label.setForeground(Color.BLUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for when the displayed songs should change.
|
||||
*
|
||||
* @param listener - The listener to add.
|
||||
*/
|
||||
public void addSongDisplayListener(SongDisplayListener listener)
|
||||
{
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for when the displayed songs should change.
|
||||
*
|
||||
* @param listener - The listener to add.
|
||||
*/
|
||||
public void removeSongDisplayListener(SongDisplayListener listener)
|
||||
{
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers all the song display listeners.
|
||||
*/
|
||||
protected void triggerSongDisplayListeners(Collection<Song> songs)
|
||||
{
|
||||
for (SongDisplayListener listener : this.listeners)
|
||||
{
|
||||
listener.updateSongs(songs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers all the song display listeners.
|
||||
*/
|
||||
protected void triggerCollectionDisplayListeners(CollectionType type, Collection<?> collection)
|
||||
{
|
||||
for (SongDisplayListener listener : this.listeners)
|
||||
{
|
||||
listener.updateCollections(type, collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.player;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.ComponentListener;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowListener;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JScrollPane;
|
||||
|
||||
import edu.regis.universeplayer.browser.Browser;
|
||||
import edu.regis.universeplayer.browser.MessageManager;
|
||||
import edu.regis.universeplayer.data.CollectionType;
|
||||
import edu.regis.universeplayer.data.Song;
|
||||
|
||||
/**
|
||||
* The Interface class serves as the primary GUI that the player interacts with.
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @version 0.1
|
||||
*/
|
||||
public class Interface extends JFrame implements SongDisplayListener, ComponentListener, WindowListener, PlaybackCommandListener
|
||||
{
|
||||
/**
|
||||
* A reference to the panel containing links to different collection views.
|
||||
*/
|
||||
private Collections collectionTypes;
|
||||
/**
|
||||
* A reference to the central view showing a list of songs.
|
||||
*/
|
||||
private SongList songList;
|
||||
/**
|
||||
* A reference to the central view showing a list of collections.
|
||||
*/
|
||||
private CollectionList collectionList;
|
||||
/**
|
||||
* A reference to the central view scroll pane.
|
||||
*/
|
||||
private JScrollPane centerView;
|
||||
|
||||
/**
|
||||
* A link to the browser.
|
||||
*/
|
||||
private MessageManager browser;
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
Browser.launchBrowser();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
JOptionPane.showMessageDialog(null, e, "Could not open browser background", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
Interface inter = new Interface();
|
||||
try
|
||||
{
|
||||
inter.browser = new MessageManager();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
JOptionPane.showMessageDialog(null, e, "Could not open browser communication", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
inter.pack();
|
||||
inter.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an interface
|
||||
*/
|
||||
public Interface()
|
||||
{
|
||||
super();
|
||||
PlayerControls controls;
|
||||
|
||||
this.setTitle("Universal Music Player");
|
||||
this.getContentPane().setLayout(new BorderLayout());
|
||||
this.getContentPane()
|
||||
.add(this.collectionTypes = new Collections(), BorderLayout.LINE_START);
|
||||
this.collectionTypes.addSongDisplayListener(this);
|
||||
controls = new PlayerControls();
|
||||
controls.addCommandListener(this);
|
||||
this.getContentPane().add(controls, BorderLayout.PAGE_END);
|
||||
|
||||
this.songList = new SongList();
|
||||
this.collectionList = new CollectionList();
|
||||
this.collectionList.addSongDisplayListener(this);
|
||||
|
||||
this.centerView = new JScrollPane(this.songList);
|
||||
this.getContentPane().add(this.centerView, BorderLayout.CENTER);
|
||||
this.componentResized(null);
|
||||
|
||||
this.addComponentListener(this);
|
||||
this.addWindowListener(this);
|
||||
|
||||
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSongs(Collection<Song> songs)
|
||||
{
|
||||
this.songList.listAlbums(songs);
|
||||
this.centerView.setViewportView(this.songList);
|
||||
this.songList.setPreferredSize(new Dimension(this.centerView.getViewport()
|
||||
.getExtentSize().width, Integer.MAX_VALUE));
|
||||
this.songList.setPreferredSize(new Dimension(this.centerView.getViewport()
|
||||
.getExtentSize().width, this.songList
|
||||
.getMinimumSize().height));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCollections(CollectionType type, Collection<?> collections)
|
||||
{
|
||||
this.collectionList.listCollection(type, collections);
|
||||
this.centerView.setViewportView(this.collectionList);
|
||||
this.collectionList.setPreferredSize(new Dimension(this.centerView.getViewport()
|
||||
.getExtentSize().width, Integer.MAX_VALUE));
|
||||
this.collectionList.setPreferredSize(new Dimension(this.centerView.getViewport()
|
||||
.getExtentSize().width, this.collectionList
|
||||
.getMinimumSize().height));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentResized(ComponentEvent event)
|
||||
{
|
||||
this.songList.setPreferredSize(new Dimension(this.centerView.getViewport()
|
||||
.getExtentSize().width, Integer.MAX_VALUE));
|
||||
this.songList.setPreferredSize(new Dimension(this.centerView.getViewport()
|
||||
.getExtentSize().width, this.songList
|
||||
.getMinimumSize().height));
|
||||
this.collectionList.setPreferredSize(new Dimension(this.centerView.getViewport()
|
||||
.getExtentSize().width, Integer.MAX_VALUE));
|
||||
this.collectionList.setPreferredSize(new Dimension(this.centerView.getViewport()
|
||||
.getExtentSize().width, this.collectionList
|
||||
.getMinimumSize().height));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentMoved(ComponentEvent event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentShown(ComponentEvent event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden(ComponentEvent event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowOpened(WindowEvent windowEvent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosing(WindowEvent windowEvent)
|
||||
{
|
||||
Browser.closeBrowser();
|
||||
if (this.browser != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.browser.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowClosed(WindowEvent windowEvent)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowIconified(WindowEvent windowEvent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowDeiconified(WindowEvent windowEvent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowActivated(WindowEvent windowEvent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windowDeactivated(WindowEvent windowEvent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a playback command is issued.
|
||||
*
|
||||
* @param command - The command issued.
|
||||
* @param data - Additional data relevent to the command.
|
||||
*/
|
||||
@Override
|
||||
public void onCommand(PlaybackCommand command, Object data)
|
||||
{
|
||||
Object message = null;
|
||||
if (this.browser != null)
|
||||
{
|
||||
synchronized (this.browser)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.browser.ping();
|
||||
message = this.browser.getMessage();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
JOptionPane.showMessageDialog(this, e, "Could not send message to browser", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message != null)
|
||||
{
|
||||
if ("ping".equals(message))
|
||||
{
|
||||
JOptionPane.showMessageDialog(this, "Ping received!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.player;
|
||||
|
||||
/**
|
||||
* Contains possible playback commands that the play can send.
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @version 0.1
|
||||
*/
|
||||
public enum PlaybackCommand
|
||||
{
|
||||
PLAY, PAUSE, NEXT, PREVIOUS, SEEK
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.player;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
/**
|
||||
* A listener that allows objects to observe when playback commands are triggered.
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @version 0.1
|
||||
*/
|
||||
public interface PlaybackCommandListener extends EventListener
|
||||
{
|
||||
/**
|
||||
* Called when a playback command is issued.
|
||||
*
|
||||
* @param command - The command issued.
|
||||
* @param data - Additional data relevent to the command.
|
||||
*/
|
||||
void onCommand(PlaybackCommand command, Object data);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.player;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.SpringLayout;
|
||||
|
||||
/**
|
||||
* This panel contains the buttons necessary for controlling the playback of audio.
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @version 0.1
|
||||
*/
|
||||
public class PlayerControls extends JPanel
|
||||
{
|
||||
private JButton playButton;
|
||||
private JButton nextButton;
|
||||
private JButton prevButton;
|
||||
private JSlider progress;
|
||||
|
||||
/**
|
||||
* A list of all things interested in knowing when we trigger a command.
|
||||
*/
|
||||
private LinkedList<PlaybackCommandListener> listeners = new LinkedList<>();
|
||||
|
||||
public PlayerControls()
|
||||
{
|
||||
final Dimension BUTTON_SIZE = new Dimension(32, 32);
|
||||
final Dimension ICON_SIZE = new Dimension(16, 16);
|
||||
ImageIcon icon;
|
||||
JPanel buttonCont, progressCont;
|
||||
FlowLayout buttonLayout;
|
||||
SpringLayout progressLayout;
|
||||
|
||||
SpringLayout layout = new SpringLayout();
|
||||
this.setLayout(layout);
|
||||
|
||||
buttonLayout = new FlowLayout();
|
||||
buttonCont = new JPanel(buttonLayout);
|
||||
this.add(buttonCont);
|
||||
|
||||
this.prevButton = new JButton();
|
||||
icon = new ImageIcon(this.getClass()
|
||||
.getResource("/gui/icons/skipPrev.png"), "Previous Button");
|
||||
icon.setImage(icon.getImage().getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, 0));
|
||||
this.prevButton.setIcon(icon);
|
||||
this.prevButton.setPreferredSize(BUTTON_SIZE);
|
||||
this.prevButton.addActionListener(actionEvent -> {
|
||||
this.triggerCommandListeners(PlaybackCommand.PREVIOUS, null);
|
||||
});
|
||||
buttonCont.add(this.prevButton);
|
||||
|
||||
this.playButton = new JButton();
|
||||
icon = new ImageIcon(this.getClass().getResource("/gui/icons/play.png"), "Play Button");
|
||||
icon.setImage(icon.getImage().getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, 0));
|
||||
this.playButton.setIcon(icon);
|
||||
this.playButton.setPreferredSize(BUTTON_SIZE);
|
||||
this.playButton.addActionListener(actionEvent -> {
|
||||
this.triggerCommandListeners(PlaybackCommand.PLAY, null);
|
||||
});
|
||||
buttonCont.add(this.playButton);
|
||||
|
||||
this.nextButton = new JButton();
|
||||
icon = new ImageIcon(this.getClass().getResource("/gui/icons/skipNext.png"), "Next Button");
|
||||
icon.setImage(icon.getImage().getScaledInstance(ICON_SIZE.width, ICON_SIZE.height, 0));
|
||||
this.nextButton.setIcon(icon);
|
||||
this.nextButton.setPreferredSize(BUTTON_SIZE);
|
||||
this.nextButton.addActionListener(actionEvent -> {
|
||||
this.triggerCommandListeners(PlaybackCommand.NEXT, null);
|
||||
});
|
||||
buttonCont.add(this.nextButton);
|
||||
|
||||
progressLayout = new SpringLayout();
|
||||
progressCont = new JPanel(progressLayout);
|
||||
this.add(progressCont);
|
||||
|
||||
this.progress = new JSlider();
|
||||
this.progress.addChangeListener(changeEvent -> {
|
||||
this.triggerCommandListeners(PlaybackCommand.SEEK, this.progress.getValue());
|
||||
});
|
||||
this.add(this.progress);
|
||||
|
||||
layout.putConstraint(SpringLayout.NORTH, buttonCont, 0, SpringLayout.NORTH, this);
|
||||
layout.putConstraint(SpringLayout.WEST, buttonCont, 0, SpringLayout.WEST, this);
|
||||
layout.putConstraint(SpringLayout.NORTH, this.progress, 5, SpringLayout.SOUTH, buttonCont);
|
||||
layout.putConstraint(SpringLayout.WEST, this.progress, 5, SpringLayout.WEST, this);
|
||||
layout.putConstraint(SpringLayout.EAST, this, 5, SpringLayout.EAST, this.progress);
|
||||
layout.putConstraint(SpringLayout.SOUTH, this, 5, SpringLayout.SOUTH, this.progress);
|
||||
layout.putConstraint(SpringLayout.EAST, buttonCont, 0, SpringLayout.EAST, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for playback commands.
|
||||
*
|
||||
* @param listener - The listener to add.
|
||||
*/
|
||||
public void addCommandListener(PlaybackCommandListener listener)
|
||||
{
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a playback listener.
|
||||
*
|
||||
* @param listener - The listener to remove.
|
||||
*/
|
||||
public void removeCommandListener(PlaybackCommandListener listener)
|
||||
{
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers all the command listeners.
|
||||
*
|
||||
* @param command - The command to trigger.
|
||||
* @param data - Extra command data.
|
||||
*/
|
||||
protected void triggerCommandListeners(PlaybackCommand command, Object data)
|
||||
{
|
||||
for (PlaybackCommandListener listener : this.listeners)
|
||||
{
|
||||
listener.onCommand(command, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.player;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.EventListener;
|
||||
|
||||
import edu.regis.universeplayer.data.CollectionType;
|
||||
import edu.regis.universeplayer.data.Song;
|
||||
|
||||
/**
|
||||
* A callback that is triggered whenever one element believes that the interface center display needs to be updated.
|
||||
*
|
||||
* @author William Hubbard
|
||||
* @version 0.1
|
||||
*/
|
||||
public interface SongDisplayListener extends EventListener
|
||||
{
|
||||
/**
|
||||
* Called to display a list of songs, sorted by album.
|
||||
*
|
||||
* @param songs - The songs to display.
|
||||
*/
|
||||
void updateSongs(Collection<Song> songs);
|
||||
|
||||
/**
|
||||
* Called to display a list of collections
|
||||
*
|
||||
* @param type - The type of collections to display.
|
||||
* @param collections - The collections to display
|
||||
*/
|
||||
void updateCollections(CollectionType type, Collection<?> collections);
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package edu.regis.universeplayer.player;
|
||||
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import edu.regis.universeplayer.data.Album;
|
||||
import edu.regis.universeplayer.data.Song;
|
||||
import edu.regis.universeplayer.data.SongProvider;
|
||||
|
||||
/**
|
||||
* This panel will list all the songs that are to be currently displayed.
|
||||
*/
|
||||
public class SongList extends JPanel
|
||||
{
|
||||
private Map<Album, List<Song>> currentAlbums;
|
||||
private Map<JLabel, Song> labelMap = new HashMap<>();
|
||||
private Map<AlbumInfo, Album> artMap = new HashMap<>();
|
||||
|
||||
public SongList()
|
||||
{
|
||||
super();
|
||||
|
||||
GridBagLayout layout = new GridBagLayout();
|
||||
this.setLayout(layout);
|
||||
|
||||
SongProvider provider = SongProvider.INSTANCE;
|
||||
this.listAlbums(provider.getSongs());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the songs currently listed, sorted by album.
|
||||
*
|
||||
* @param songs - The songs to display.
|
||||
*/
|
||||
public void listAlbums(Collection<Song> songs)
|
||||
{
|
||||
Map<Album, List<Song>> albums = songs.stream().sorted().collect(Collectors
|
||||
.groupingBy(song -> song.album, Collectors
|
||||
.mapping(song -> song, Collectors.toList())));
|
||||
GridBagConstraints c = new GridBagConstraints(), c2 = new GridBagConstraints();
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.insets = new Insets(0, 0, 20, 0);
|
||||
int i = 0, j;
|
||||
AlbumInfo albumInfo;
|
||||
List<Song> songCollection;
|
||||
JPanel songList;
|
||||
JLabel songNum, songTitle;
|
||||
|
||||
this.labelMap.clear();
|
||||
this.artMap.clear();
|
||||
this.removeAll();
|
||||
this.currentAlbums = albums;
|
||||
|
||||
for (Album album : albums.keySet())
|
||||
{
|
||||
songCollection = albums.get(album);
|
||||
|
||||
albumInfo = new AlbumInfo(album);
|
||||
c.gridx = 0;
|
||||
c.gridy = i;
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
this.add(albumInfo, c);
|
||||
this.artMap.put(albumInfo, album);
|
||||
|
||||
songList = new JPanel(new GridBagLayout());
|
||||
j = 0;
|
||||
for (Song song : songCollection)
|
||||
{
|
||||
songNum = new JLabel(String.valueOf(song.trackNum));
|
||||
c2.gridx = 0;
|
||||
c2.gridy = j;
|
||||
c2.anchor = GridBagConstraints.EAST;
|
||||
c2.insets = new Insets(0, 0, 0, 0);
|
||||
songList.add(songNum, c2);
|
||||
this.labelMap.put(songNum, song);
|
||||
|
||||
songTitle = new JLabel(song.title);
|
||||
c2.gridx = 1;
|
||||
c2.gridy = j;
|
||||
c2.anchor = GridBagConstraints.WEST;
|
||||
c2.insets = new Insets(0, 10, 0, 0);
|
||||
songList.add(songTitle, c2);
|
||||
this.labelMap.put(songTitle, song);
|
||||
// TODO - Add song length or something
|
||||
|
||||
j++;
|
||||
}
|
||||
c.gridx = 1;
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
this.add(songList, c);
|
||||
// this.add(new JLabel(new ImageIcon(this.getClass().getResource("/gui/icons/defaultart.png"), "Default")), c);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user