/*
* PlayList.java
*
* Copyright � 1998-2011 Research In Motion Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.device.mediakeysdemo.mediaplayerdemo.mediaplayerlib;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Vector;
import javax.microedition.io.Connection;
import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
import net.rim.device.api.io.IOUtilities;
import net.rim.device.api.util.Arrays;
import net.rim.device.api.util.Comparator;
import net.rim.device.api.util.StringComparator;
/**
* This class copies the sound files from the compiled cod file into the device
* file system. This allows for the tracks to be seekable.
*/
public class PlayList {
/**
* The names of the songs stored in the COD file as resources
*/
private static final String[] SONG_FILENAMES = { "BlackBerryBold.mp3",
"BlackBerryCurve.mp3", "BlackBerryPowerPlay.mp3",
"BlackBerryStorm.mp3", };
/**
* Copies the sound files from the COD file to the device file system
*
* @throws IOException
* If the operation fails
*/
private static void init() throws IOException {
final String[] dirURLs = getAudioFileSearchURLs();
// Stores the exceptions thrown from any failed operations
final Throwable[] exceptions = new Throwable[dirURLs.length];
for (int dirURLIndex = 0; dirURLIndex < dirURLs.length; dirURLIndex++) {
final String dirURL = dirURLs[dirURLIndex];
if (dirURL == null) {
continue;
}
Connection con;
try {
con = Connector.open(dirURL);
} catch (final Exception e) {
exceptions[dirURLIndex] = e;
continue;
}
try {
final FileConnection fcon = (FileConnection) con;
if (!fcon.exists()) {
throw new IOException("directory does not exist");
} else if (!fcon.isDirectory()) {
throw new IOException("not a directory");
}
for (int i = 0; i < SONG_FILENAMES.length; i++) {
final String filename = SONG_FILENAMES[i];
final String fileURL = fcon.getURL() + filename;
Connection con2;
try {
con2 = Connector.open(fileURL);
} catch (final Exception e) {
throw new IOException("unable to open connection to "
+ fileURL + ": " + e);
}
try {
final FileConnection fcon2 = (FileConnection) con2;
if (fcon2.exists()) {
continue;
}
try {
fcon2.create();
} catch (final Exception e) {
throw new IOException("unable to create " + fileURL
+ ": " + e);
}
final String resourcePath = "/sounds/" + filename;
copyCODResourceToFileConnection(resourcePath, fcon2);
} finally {
try {
con2.close();
} catch (final Exception e) {
}
}
}
return;
} catch (final Exception e) {
exceptions[dirURLIndex] = e;
continue;
} finally {
try {
con.close();
} catch (final Exception e) {
}
}
}
// Generate a failure message to indicate what went wrong, then throw
// an IOException.
final StringBuffer sb = new StringBuffer();
sb.append("no suitable locations for audio files found: ");
for (int i = 0; i < dirURLs.length; i++) {
if (i > 0) {
sb.append("; ");
}
final String url = dirURLs[i];
final Throwable exception = exceptions[i];
sb.append(url).append(": ");
if (exception instanceof IOException) {
sb.append(exception.getMessage());
} else {
sb.append(exception.toString());
}
}
final String failMessage = sb.toString();
throw new IOException(failMessage);
}
/**
* Copies the contents of a resource of the COD file in which this class is
* located into a file on the filesystem.
*
* @param resourcePath
* The path of the resource to copy - will be specified to
* Class.getResourceAsStream()
* @param con
* The FileConnection representing the file to copy to. The file
* must already exist.
* @throws IOException
* If the operation fails
* @throws NullPointerException
* If any argument is null
*/
private static void copyCODResourceToFileConnection(
final String resourcePath, final FileConnection con)
throws IOException {
if (resourcePath == null) {
throw new NullPointerException("resourcePath==null");
} else if (con == null) {
throw new NullPointerException("con==null");
}
final InputStream in = PlayList.class.getResourceAsStream(resourcePath);
if (in == null) {
throw new IOException("resource not found: " + resourcePath);
}
try {
OutputStream out;
try {
out = con.openOutputStream();
} catch (final Exception e) {
throw new IOException("unable to open output stream to "
+ con.getURL() + ": " + e);
}
try {
final byte[] bytes = IOUtilities.streamToBytes(in);
out.write(bytes);
} catch (final Exception e) {
throw new IOException("unable to copy data from "
+ resourcePath + " to " + con.getURL() + ": " + e);
} finally {
try {
out.close();
} catch (final Exception e) {
}
}
} finally {
try {
in.close();
} catch (final Exception e) {
}
}
}
/**
* Returns the list of file:/// URLs to search for audio files
*
* @return a newly-created array of strings whose values are the URLs of the
* folders on the BlackBerry's filesystem in which to search for
* audio files. The method never returns null but individual
* elements may be null.
*/
private static String[] getAudioFileSearchURLs() {
return new String[] {
System.getProperty("fileconn.dir.memorycard.music"),
System.getProperty("fileconn.dir.music"), };
}
/**
* Searches the filesystem for entries to put in the playlist. This method
* also extracts the audio files included in this COD file into the
* filesystem, if they are not already there.
*
* @return The list of playlist entries - never returns null.
* @throws IOException
* If an error occurs.
*/
public static PlaylistEntry[] getPlaylistEntries() throws IOException {
// Copy the MP3 files from the COD file into the filesystem
init();
// Search the music folders for MP3 files to add to the playlist
final Vector vector = new Vector();
final String[] dirURLs = getAudioFileSearchURLs();
for (int dirURLIndex = 0; dirURLIndex < dirURLs.length; dirURLIndex++) {
final String dirURL = dirURLs[dirURLIndex];
if (dirURL == null) {
continue;
}
// Open a connection to the folder so that we can list its files
Connection con;
try {
con = Connector.open(dirURL);
} catch (final Exception e) {
System.err.println("WARNING: unable to open connection to "
+ dirURL + ": " + e);
continue; // Skip this directory
}
try {
final FileConnection fcon = (FileConnection) con;
if (!fcon.isDirectory()) {
continue; // Not a directory, so skip it
}
// Search for files whose names end with ".mp3" and add them to
// the playlist
Enumeration enumeration;
try {
enumeration = fcon.list();
} catch (final Exception e) {
System.err.println("WARNING: unable to list files in "
+ dirURL + ": " + e);
continue; // Skip this directory
}
while (enumeration.hasMoreElements()) {
final String filename = (String) enumeration.nextElement();
if (filename != null
&& filename.toLowerCase().endsWith(".mp3")) {
final String fileURL = fcon.getURL() + filename;
final String name =
filename.substring(0, filename.length() - 4);
final PlaylistEntry playlistEntry =
new PlaylistEntry(fileURL, name);
vector.addElement(playlistEntry);
}
}
} finally {
try {
con.close();
} catch (final Exception e) {
}
}
}
// Put the PlaylistEntry objects for the discovered MP3 files into an
// array
final PlaylistEntry[] array = new PlaylistEntry[vector.size()];
vector.copyInto(array);
// Sort the array so that the songs are presented in alphabetical order
Arrays.sort(array, PlaylistEntryComparator.INSTANCE);
return array;
}
/**
* A comparator that orders PlaylistEntry objects in ascending alphabetical
* order by name.
*/
private static class PlaylistEntryComparator implements Comparator {
public static PlaylistEntryComparator INSTANCE =
new PlaylistEntryComparator();
private static final Comparator STRING_COMPARATOR = StringComparator
.getInstance(true);
/**
* Compares two PlaylistEntry objects for order, based on their names.
*
* @param o1
* The first PlaylistEntry object to compare
* @param o2
* The second PlaylistEntry object to compare
* @return A negative integer if the first PlaylistEntry should be
* ordered before the second, a positive integer if it should be
* ordered after the second, or 0 if they should be ordered in
* the same position.
*/
public int compare(final Object o1, final Object o2) {
final String name1 = ((PlaylistEntry) o1).getName();
final String name2 = ((PlaylistEntry) o2).getName();
return STRING_COMPARATOR.compare(name1, name2);
}
}
}