From 66ce116da445cee2a8d9e1e34cc80ac05f3b975c Mon Sep 17 00:00:00 2001 From: Markil3 <75867393+Markil3@users.noreply.github.com> Date: Mon, 26 Jul 2021 20:27:43 -0700 Subject: [PATCH] Added better logging and browser support. We still need to work on the Linux browser a bit. --- .gitignore | 8 +- add-on/background.js | 45 ++-- add-on/intermediary_manifest.json | 7 - add-on/manifest.json | 2 +- addonInter/build.gradle | 54 ++--- .../edu/regis/universeplayer/addon/Main.java | 170 ++++++++++++++- bin/interface | 0 bin/interface.bat | 0 build.gradle | 2 +- interface/build.gradle | 6 +- .../regis/universeplayer/data/LocalSong.java | 15 ++ .../data/LocalSongProvider.java | 199 ++++++++++++++++++ .../regis/universeplayer/data/MP3Song.java | 120 +++++++++++ .../universeplayer/player/Interface.java | 119 ++++++----- 14 files changed, 601 insertions(+), 146 deletions(-) delete mode 100644 add-on/intermediary_manifest.json mode change 100755 => 100644 bin/interface mode change 100755 => 100644 bin/interface.bat create mode 100644 interface/src/main/java/edu/regis/universeplayer/data/LocalSong.java create mode 100644 interface/src/main/java/edu/regis/universeplayer/data/LocalSongProvider.java create mode 100644 interface/src/main/java/edu/regis/universeplayer/data/MP3Song.java diff --git a/.gitignore b/.gitignore index e2e944a..2db29fa 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,4 @@ /*.log /*/bin/ /local.properties -/browser/profile/extensions/universal_music@regis.edu.xpi -/browser/profile/cache2/* -/browser/profile/datareporting/* -/browser/profile/startupCache/* -/browser/profile/sessionstore-backups/* -/browser/profile/lock -/browser/profile/ +/browser/profile/* diff --git a/add-on/background.js b/add-on/background.js index eac4aae..f8ff779 100644 --- a/add-on/background.js +++ b/add-on/background.js @@ -1,29 +1,20 @@ -var port = browser.runtime.connectNative("universal_music") +console.log("Hello from Universal Music addon!") +/* +On startup, connect to the "ping_pong" app. +*/ +var port = browser.runtime.connectNative("universalmusic"); -/** - * Sends a message to the application - */ -function writeMessage(message) -{ - port.postMessage(message) -} +/* +Listen for messages from the app. +*/ +port.onMessage.addListener((response) => { + console.log("Received: " + response); +}); -/** - * Sends a message to the application - */ -function onAppMessage(message) -{ - if ("command" in message) - { - switch (message["command"]) - { - case "ping": - writeMessage({ - "response": "ping" - }); - break - } - } -} - -port.onMessage.addListener(onAppMessage); \ No newline at end of file +/* +On a click on the browser action, send the app a message. +*/ +browser.browserAction.onClicked.addListener(() => { + console.log("Sending: ping"); + port.postMessage("ping"); +}); \ No newline at end of file diff --git a/add-on/intermediary_manifest.json b/add-on/intermediary_manifest.json deleted file mode 100644 index f44638a..0000000 --- a/add-on/intermediary_manifest.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "universal_music", - "description": "A link between the Universal Music Player and the web browser.", - "path": "${LINK_PATH}", - "type": "stdio", - "allowed_extensions": ["universal_music@regis.edu"] -} \ No newline at end of file diff --git a/add-on/manifest.json b/add-on/manifest.json index ab980b9..922cb50 100644 --- a/add-on/manifest.json +++ b/add-on/manifest.json @@ -13,7 +13,7 @@ }, "browser_specific_settings": { "gecko": { - "id": "universal_music@regis.edu", + "id": "universalmusic@regis.edu", "strict_min_version": "55.0" } }, diff --git a/addonInter/build.gradle b/addonInter/build.gradle index 383ebc5..fe5372b 100644 --- a/addonInter/build.gradle +++ b/addonInter/build.gradle @@ -29,6 +29,8 @@ dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.13.3' implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.13.3' + implementation 'com.google.code.gson:gson:2.8.7' + // Declare the dependency for your favourite test framework you want to use in your tests. // TestNG is also supported by the Gradle Test task. Just change the // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add @@ -40,12 +42,14 @@ abstract class BuildManifest extends DefaultTask { @InputDirectory final abstract DirectoryProperty inputFile = project.objects.directoryProperty() @OutputFile - final abstract RegularFileProperty outputFile = project.objects.fileProperty().convention(project.layout.buildDirectory.file("manifest.json")) + final abstract RegularFileProperty outputFile = project.objects.fileProperty().convention(project.layout.buildDirectory.file("universalmusic.json")) @TaskAction void join() { File startupScript - Stream files = Arrays.stream(inputFile.get().asFile.listFiles()) + File binFiles = new File(inputFile.get().asFile, "bin") + print binFiles + Stream files = Arrays.stream(binFiles.listFiles()) if (Os.isFamily(Os.FAMILY_WINDOWS)) { startupScript = files.filter(file -> file.getName().endsWith(".bat")).findFirst().get() } @@ -58,7 +62,7 @@ abstract class BuildManifest extends DefaultTask { outputFile.get().asFile.text = "{\n" + " \"name\": \"universalmusic\",\n" + " \"description\": \"A link between the Universal Music Player and the web browser.\",\n" + - " \"path\": \"" + startupScript.getAbsolutePath() + "\",\n" + + " \"path\": \"" + startupScript.getAbsolutePath().replace("\\", "\\\\") + "\",\n" + " \"type\": \"stdio\",\n" + " \"allowed_extensions\": [\"universalmusic@regis.edu\"]\n" + "}" @@ -66,50 +70,25 @@ abstract class BuildManifest extends DefaultTask { } TaskProvider buildManifestP = tasks.register("buildManifest", BuildManifest) { - inputFile = tasks.named("startScripts", org.gradle.jvm.application.tasks.CreateStartScripts).get().outputDir + inputFile = tasks.named("installDist", Sync).get().destinationDir } -buildManifest.dependsOn(startScripts) +buildManifest.dependsOn(installDist) abstract class InstallAddon extends DefaultTask { @InputFile final abstract RegularFileProperty inputFile = project.objects.fileProperty() - @Internal - final abstract DirectoryProperty build = project.layout.buildDirectory - @InputDirectory - final abstract DirectoryProperty libsDir = project.objects.directoryProperty() @TaskAction void join() { - Path lib = libsDir.get().asFile.toPath() - Path link = new File(build.get().asFile, "lib").toPath() - try { - // This takes up more space than I would like, but Windows doesn't allow symbolic links without superuser privliges - Files.walk(lib).forEach(file -> { - Path fileName = lib.relativize(file) - Path dest = link.resolve(fileName) - println fileName.toString() + ": " + file.toString() + " to " + dest.toString() - if (!Files.exists(dest)) { - try { - Files.copy(file, dest) - } - catch (IOException e) - { - throw new RuntimeException("Could not link " + file.toString() + " to " + dest.toString(), e) - } - } - }) - } - catch (IOException e) - { - throw new RuntimeException("Could not link " + lib.toString() + " to " + link.toString(), e) - } - - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - Runtime.getRuntime() - .exec("REG ADD HKEY_CURRENT_USER\\SOFTWARE\\Mozilla\\NativeMessagingHosts /v universalmusic /d \"" - + inputFile.get().asFile.getAbsolutePath() + "\" ") + Process p = Runtime.getRuntime() + .exec("REG DELETE HKEY_CURRENT_USER\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\universalmusic /f ") + p.waitFor() + p = Runtime.getRuntime() + .exec("REG ADD HKEY_CURRENT_USER\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\universalmusic /ve /d \"" + + inputFile.get().asFile.getAbsolutePath() + "\" /f ") + p.waitFor() } else if (Os.isFamily(Os.FAMILY_MAC)) { Files.copy(inputFile.get().asFile.toPath(), Paths.get(System.getProperty("user.home"), "Library/Application Support/Mozilla/NativeMessagingHosts", inputFile.get().asFile.getName())) @@ -122,7 +101,6 @@ abstract class InstallAddon extends DefaultTask { TaskProvider installAddonP = tasks.register("installAddon", InstallAddon) { inputFile = buildManifestP.get().outputFile - libsDir = jar.destinationDirectory } installAddon.dependsOn(buildManifest) diff --git a/addonInter/src/main/java/edu/regis/universeplayer/addon/Main.java b/addonInter/src/main/java/edu/regis/universeplayer/addon/Main.java index 64f44b7..6f3e48f 100644 --- a/addonInter/src/main/java/edu/regis/universeplayer/addon/Main.java +++ b/addonInter/src/main/java/edu/regis/universeplayer/addon/Main.java @@ -4,33 +4,181 @@ package edu.regis.universeplayer.addon; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; + public class Main { private static Logger logger = LoggerFactory.getLogger(Main.class); + private static final Gson gson = new Gson(); + + private static BufferedInputStream browserIn; + private static BufferedOutputStream browserOut; + public static void main(String[] args) { boolean running = true; - logger.info("This is an INFO level log message!"); - logger.error("This is an ERROR level log message!"); - logger.warn("This could be problimatic"); - logger.info("https://sematext.com/blog/log4j2-tutorial/#toc-log4j-2-configuration-1"); - while (running) + int rawLength, messageLength; + byte[] header = new byte[4]; + byte[] message; + JsonElement messageJson; + JsonPrimitive jsonPrimative; + JsonObject jsonObject; + ByteBuffer headerReader; + + System.err.printf("Working from directory %s\n", System.getProperty("user.dir")); + if (ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN)) + { + logger.info("Incoming messages will be Big-endian"); + } + else + { + logger.info("Incoming messages will be Little-endian"); + } + try + { + browserIn = new BufferedInputStream(System.in); + browserOut = new BufferedOutputStream(System.out); + while (running) + { + try + { + rawLength = browserIn.read(header); + + if (rawLength <= 0) + { + running = false; + break; + } + + headerReader = ByteBuffer.wrap(header, 0, rawLength); + /* + * The browser outputs in whatever the native endian order, + * which may not be what Java does. + */ + headerReader.order(ByteOrder.nativeOrder()); + messageLength = headerReader.getInt(); + logger.debug("Receiving message of {} length", messageLength); + message = new byte[messageLength]; + browserIn.read(message); + logger.debug("Unpacking message {} (length {})", new String(message, "UTF-8"), messageLength); + + messageJson = gson.fromJson(new String(message, StandardCharsets.UTF_8), JsonElement.class); + logger.debug("Reading JSON element {} (type: {})", messageJson, messageJson.getClass()); + + if (messageJson.isJsonPrimitive()) + { + jsonPrimative = ((JsonPrimitive) messageJson); + if (jsonPrimative.isBoolean()) + { + handleMessage(jsonPrimative.getAsBoolean()); + } + else if (jsonPrimative.isNumber()) + { + handleMessage(jsonPrimative.getAsNumber()); + } + else if (jsonPrimative.isString()) + { + handleMessage(jsonPrimative.getAsString()); + } + } + else if (messageJson.isJsonObject()) + { + jsonObject = ((JsonObject) messageJson); + // TODO - Parse the object into Java objects + } + } + catch (IOException e) + { + logger.error("Could not create input stream", e); + // Forward it to the browser via STDERR + e.printStackTrace(); + } + } + } + finally { - logger.info("Waiting1"); - logger.debug("Waiting"); try { - Thread.sleep(500); + browserIn.close(); } - catch (InterruptedException e) + catch (IOException e1) { - logger.trace("Something interrupted us", e); - running = false; + logger.error("Could not close browser input", e1); + } + try + { + browserOut.close(); + } + catch (IOException e1) + { + logger.error("Could not close browser output", e1); } } } + + /** + * A callback for when a message is sent from the browser + * + * @param message - The message sent. + */ + private static void handleMessage(Object message) + { + if (message instanceof String) + { + if (message.equals("ping")) + { + sendMessage("pong"); + } + } + } + + /** + * A callback for when a message is sent from the browser + * + * @param message - The message sent. + */ + public static void sendMessage(Object message) + { + String messageJson; + byte[] messageData; + ByteBuffer headerWriter; + + logger.debug("Sending message {}", message); + + messageJson = gson.toJson(message); + messageData = messageJson.getBytes(StandardCharsets.UTF_8); + headerWriter = ByteBuffer.allocate(4); + /* + * The browser outputs in whatever the native endian order, + * which may not be what Java does. + */ + headerWriter.order(ByteOrder.nativeOrder()); + headerWriter.putInt(messageData.length); + logger.debug("Writing message {} {}", headerWriter.array(), messageJson); + try + { + browserOut.write(headerWriter.array()); + browserOut.write(messageData); + browserOut.flush(); + } + catch (IOException e) + { + logger.error("Could not write message", e); + e.printStackTrace(); + } + } } diff --git a/bin/interface b/bin/interface old mode 100755 new mode 100644 diff --git a/bin/interface.bat b/bin/interface.bat old mode 100755 new mode 100644 diff --git a/build.gradle b/build.gradle index eecf863..e6448f7 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ ext.defaultPackage = "edu.regis.universeplayer" task bundleAddOn(type: Zip) { - archiveName = "universal_music@regis.edu.xpi" + archiveName = "universalmusic@regis.edu.xpi" destinationDir = file("./browser/profile/extensions") from (files("./add-on")) } \ No newline at end of file diff --git a/interface/build.gradle b/interface/build.gradle index 3d328a9..1c22872 100644 --- a/interface/build.gradle +++ b/interface/build.gradle @@ -39,10 +39,10 @@ targetCompatibility = JavaVersion.VERSION_16 mainClassName = defaultPackage + '.player.Interface' //mainClassName = defaultPackage + '.localPlayer.Player' -compileJava.dependsOn rootProject.bundleAddOn - run { systemProperty "java.library.path", file("${project(":player").buildDir}/lib/main/debug").absolutePath } -compileJava.dependsOn ':player:linkDebug' \ No newline at end of file +compileJava.dependsOn rootProject.bundleAddOn +compileJava.dependsOn ':player:linkDebug' +compileJava.dependsOn ':addonInter:installAddon' \ No newline at end of file diff --git a/interface/src/main/java/edu/regis/universeplayer/data/LocalSong.java b/interface/src/main/java/edu/regis/universeplayer/data/LocalSong.java new file mode 100644 index 0000000..6b45fc7 --- /dev/null +++ b/interface/src/main/java/edu/regis/universeplayer/data/LocalSong.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2021 William Hubbard. All Rights Reserved. + */ + +package edu.regis.universeplayer.data; + +import java.io.File; + +/** + * This song represents a song found on the local file system. + */ +public abstract class LocalSong extends Song +{ + public File file; +} diff --git a/interface/src/main/java/edu/regis/universeplayer/data/LocalSongProvider.java b/interface/src/main/java/edu/regis/universeplayer/data/LocalSongProvider.java new file mode 100644 index 0000000..4582786 --- /dev/null +++ b/interface/src/main/java/edu/regis/universeplayer/data/LocalSongProvider.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2021 William Hubbard. All Rights Reserved. + */ + +package edu.regis.universeplayer.data; + +import java.io.File; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; + +public class LocalSongProvider implements SongProvider +{ + private File source; + + private HashMap songs = new HashMap<>(); + + private class SongScanner implements Runnable + { + @Override + public void run() + { + + } + + private void scanFolder(File folder) + { + String type; + LinkedList subFolders = new LinkedList<>(); + for (File file: folder.listFiles()) + { + if (file.isDirectory()) + { + this.scanFolder(file); + } + if (file.getName().lastIndexOf(".") < file.getName().length() - 1) + { + type = file.getName().substring(file.getName().lastIndexOf('.') + 1).toLowerCase(); + switch (type) + { + case "mp3": + + } + } + } + } + } + + public LocalSongProvider(File source) + { + this.source = source; + if (this.source == null || !this.source.isDirectory()) + { + throw new IllegalArgumentException("File source must be existing directory"); + } + + + } + + /** + * Obtains all albums within the collection. + * + * @return A list of albums. + */ + @Override + public Collection getAlbums() + { + return null; + } + + /** + * Obtains all songs within the collection. + * + * @return A list of songs. + */ + @Override + public Collection getSongs() + { + return null; + } + + /** + * Obtains a list of all artists. + * + * @return All artists. + */ + @Override + public Collection getArtists() + { + return null; + } + + /** + * Obtains a list of all album artists. + * + * @return All album artists. + */ + @Override + public Collection getAlbumArtists() + { + return null; + } + + /** + * Obtains a list of all genres. + * + * @return All genres. + */ + @Override + public Collection getGenres() + { + return null; + } + + /** + * Obtains a list of all years that have albums. + * + * @return All years. + */ + @Override + public Collection getYears() + { + return null; + } + + /** + * 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 getSongsFromAlbum(Album album) + { + return null; + } + + /** + * 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 getSongsFromArtist(String artist) + { + return null; + } + + /** + * 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 null; + } + + /** + * 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 getAlbumsFromArtist(String artist) + { + return null; + } + + /** + * Obtains all albums that match a certain genre + * + * @param genre - The genre to search for. + * @return - The collection on matching albums. + */ + @Override + public Collection getAlbumsFromGenre(String genre) + { + return null; + } + + /** + * 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 getAlbumsFromYear(int year) + { + return null; + } +} diff --git a/interface/src/main/java/edu/regis/universeplayer/data/MP3Song.java b/interface/src/main/java/edu/regis/universeplayer/data/MP3Song.java new file mode 100644 index 0000000..6f78d2e --- /dev/null +++ b/interface/src/main/java/edu/regis/universeplayer/data/MP3Song.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2021 William Hubbard. All Rights Reserved. + */ + +package edu.regis.universeplayer.data; + +import com.mpatric.mp3agic.ID3v1; +import com.mpatric.mp3agic.ID3v2; +import com.mpatric.mp3agic.InvalidDataException; +import com.mpatric.mp3agic.Mp3File; +import com.mpatric.mp3agic.UnsupportedTagException; + +import java.io.File; +import java.io.IOException; + +/** + * This will specifically import an MP3 file. + * + * @author William Hubbard + * @version 0.1 + */ +public class MP3Song extends LocalSong +{ + public MP3Song(File file) + { + Mp3File metadata; + ID3v1 tag1; + ID3v2 tag2; + this.file = file; + try + { + metadata = new Mp3File(file); + this.album = new Album(); + if (metadata.hasId3v1Tag()) + { + tag1 = metadata.getId3v1Tag(); + this.title = tag1.getTitle(); + this.artists = tag1.getArtist().split(";"); + /** + * Trim the artists as needed. + */ + for (int i = 0, l = this.artists.length; i < l; i++) + { + this.artists[i] = this.artists[i].trim(); + } + this.trackNum = Integer.parseInt(tag1.getTrack().split("/")[0]); + + this.album.name = tag1.getAlbum(); + this.album.genres = tag1.getGenreDescription().split(";"); + for (int i = 0, l = this.album.genres.length; i < l; i++) + { + this.album.genres[i] = this.album.genres[i].trim(); + } + this.album.year = Integer.parseInt(tag1.getYear()); + } + /** + * Make sure that the v2 tag doesn't contain any contradictory information. + */ + if (metadata.hasId3v2Tag()) + { + tag2 = metadata.getId3v2Tag(); + if (!tag2.getTitle().isEmpty()) + { + this.title = tag2.getTitle(); + } + if (!tag2.getTitle().isEmpty()) + { + this.artists = tag2.getArtist().split(";"); + /** + * Trim the artists as needed. + */ + for (int i = 0, l = this.artists.length; i < l; i++) + { + this.artists[i] = this.artists[i].trim(); + } + } + if (!tag2.getTitle().isEmpty()) + { + this.trackNum = Integer.parseInt(tag2.getTrack().split("/")[0]); + } + + if (!tag2.getAlbum().isEmpty()) + { + this.album.name = tag2.getAlbum(); + } + if (!tag2.getAlbumArtist().isEmpty()) + { + this.album.artists = tag2.getAlbumArtist().split(";"); + /** + * Trim the artists as needed. + */ + for (int i = 0, l = this.album.artists.length; i < l; i++) + { + this.album.artists[i] = this.album.artists[i].trim(); + } + } + if (!tag2.getGenreDescription().isEmpty()) + { + this.album.genres = tag2.getGenreDescription().split(";"); + for (int i = 0, l = this.album.genres.length; i < l; i++) + { + this.album.genres[i] = this.album.genres[i].trim(); + } + } + if (!tag2.getYear().isEmpty()) + { + this.album.year = Integer.parseInt(tag2.getYear()); + } + } + } + catch (IOException e) + { + throw new IllegalArgumentException("Invalid MP3 File", e); + } + catch (UnsupportedTagException | InvalidDataException e) + { + } + + } +} diff --git a/interface/src/main/java/edu/regis/universeplayer/player/Interface.java b/interface/src/main/java/edu/regis/universeplayer/player/Interface.java index f5be022..866158b 100644 --- a/interface/src/main/java/edu/regis/universeplayer/player/Interface.java +++ b/interface/src/main/java/edu/regis/universeplayer/player/Interface.java @@ -21,6 +21,8 @@ import edu.regis.universeplayer.browser.Browser; import edu.regis.universeplayer.browser.MessageManager; import edu.regis.universeplayer.data.CollectionType; import edu.regis.universeplayer.data.Song; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The Interface class serves as the primary GUI that the player interacts with. @@ -30,6 +32,8 @@ import edu.regis.universeplayer.data.Song; */ public class Interface extends JFrame implements SongDisplayListener, ComponentListener, WindowListener, PlaybackCommandListener { + private static Logger logger = LoggerFactory.getLogger(Interface.class); + /** * A reference to the panel containing links to different collection views. */ @@ -46,23 +50,23 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL * 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); - } + /* + * Add this just in case of a crash or something. It won't work if the + * program is forcible terminated by the OS, but it could be helpful + * otherwise. + */ + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + Browser.closeBrowser(); + })); + logger.info("Starting application"); Interface inter = new Interface(); try { @@ -70,13 +74,26 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL } catch (IOException e) { - e.printStackTrace(); + logger.error("Could not open browser communication", e); JOptionPane.showMessageDialog(null, e, "Could not open browser communication", JOptionPane.ERROR_MESSAGE); } inter.pack(); inter.setVisible(true); + + /* + * Launch the browser in the background. + */ + try + { + Browser.launchBrowser(); + } + catch (IOException e) + { + logger.error("Could not open browser background", e); + JOptionPane.showMessageDialog(inter, e, "Could not open browser background", JOptionPane.ERROR_MESSAGE); + } } - + /** * Creates an interface */ @@ -84,93 +101,93 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL { super(); PlayerControls controls; - + this.setTitle("Universal Music Player"); this.getContentPane().setLayout(new BorderLayout()); this.getContentPane() - .add(this.collectionTypes = new Collections(), BorderLayout.LINE_START); + .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 songs) { this.songList.listAlbums(songs); this.centerView.setViewportView(this.songList); this.songList.setPreferredSize(new Dimension(this.centerView.getViewport() - .getExtentSize().width, Integer.MAX_VALUE)); + .getExtentSize().width, Integer.MAX_VALUE)); this.songList.setPreferredSize(new Dimension(this.centerView.getViewport() - .getExtentSize().width, this.songList + .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)); + .getExtentSize().width, Integer.MAX_VALUE)); this.collectionList.setPreferredSize(new Dimension(this.centerView.getViewport() - .getExtentSize().width, this.collectionList + .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)); + .getExtentSize().width, Integer.MAX_VALUE)); this.songList.setPreferredSize(new Dimension(this.centerView.getViewport() - .getExtentSize().width, this.songList + .getExtentSize().width, this.songList .getMinimumSize().height)); this.collectionList.setPreferredSize(new Dimension(this.centerView.getViewport() - .getExtentSize().width, Integer.MAX_VALUE)); + .getExtentSize().width, Integer.MAX_VALUE)); this.collectionList.setPreferredSize(new Dimension(this.centerView.getViewport() - .getExtentSize().width, this.collectionList + .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) { - + logger.info("Interface opened"); } - + @Override public void windowClosing(WindowEvent windowEvent) { @@ -183,40 +200,40 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL } catch (IOException e) { - e.printStackTrace(); + logger.error("Could not close browser", e); } } } - + @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. * @@ -238,7 +255,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL } catch (IOException e) { - e.printStackTrace(); + logger.error("Could not send message to browser", e); JOptionPane.showMessageDialog(this, e, "Could not send message to browser", JOptionPane.ERROR_MESSAGE); } }