package com.jadn.cc.services;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.SortedSet;
import android.content.Context;
import android.util.Log;
import com.jadn.cc.core.Config;
/** Meta information about podcasts. **/
public class MetaHolder {
private Context context;
private Config config;
private List<MetaFile> metas = new ArrayList<MetaFile>();
public MetaHolder(Context context) {
this(context, null);
}
public MetaHolder(Context context, File current) {
this.context = context;
this.config = new Config(context);
loadMeta(current);
}
public void delete(int i) {
metas.get(i).delete();
metas.remove(i);
}
public MetaFile extract(int i) {
return metas.remove(i);
}
public MetaFile get(int current) {
return metas.get(current);
}
public int getSize() {
return metas.size();
}
/* Really a part of the constructor -- assumes "metas" is empty */
private void loadMeta(File current) {
String currentName = current == null ? null : current.getName();
int currentIndex = -1;
boolean priorityFileAddedToOrder = false;
File[] files = config.getPodcastsRoot().listFiles();
File order = config.getPodcastRootPath("podcast-order.txt");
if (files == null)
return;
// Load files in proper order
if (order.exists()) {
try {
DataInputStream dis = new DataInputStream(new FileInputStream(order));
String line = null;
while ((line = dis.readLine()) != null) {
File file = config.getPodcastRootPath(line);
if (file.exists()) {
metas.add(new MetaFile(file));
if ( currentName != null && currentName.equals(file.getName()) )
{
currentIndex = metas.size() - 1;
Log.d("CarCast", "currentIndex: " + currentIndex);
}
}
}
} catch (IOException e) {
Log.e("CarCast", "reading order file", e);
}
}
if ( 0 <= currentIndex && currentIndex < metas.size() )
{
// We've already encountered the currently-playing file;
// currentIndex is its position.
// assert 0 <= currentIndex;
// assert currentIndex < metas.size();
String prev, curr;
do
{
currentIndex += 1;
prev = metas.get(currentIndex - 1).getBaseFilename();
curr = metas.get(currentIndex).getBaseFilename();
} while ( currentIndex < metas.size() && curr.equals(prev) );
// currentIndex is now the index *after* the
// currently-playing podcast file and any following priority
// podcasts.
// assert 1 <= currentIndex;
// assert currentIndex <= metas.size();
}
// Fail safe.
// The code above looks good to me. I've tested it in various ways.
// However, if there are any errors, it could result in incorrect indexing
// into the `metas` array, which would crash the app.
// So, just to be safe for the time being, let's leave some code here that
// catches any problems.
if ( currentIndex < 1 || metas.size() < currentIndex )
// SHOULD NOT HAPPEN!
currentIndex = -1;
// Look for "Found Files" -- not in ordered list... but sitting in the directory
ArrayList<File> foundFiles = new ArrayList<File>();
for (File file : files) {
if (file.length() == 0) {
file.delete();
continue;
}
if (file.getName().endsWith(".mp3") || file.getName().endsWith(".3gp") || file.getName().endsWith(".ogg")) {
if (!alreadyHas(file)) {
if ( 0 <= currentIndex && isPriority(file) )
{
// The currently-playing podcast is in "podcast-order.txt", so insert the new file
// immediately after it.
Log.d("CarCast", "adding: " + currentIndex + " " + file.getName());
metas.add(currentIndex++, new MetaFile(file));
priorityFileAddedToOrder = true;
}
else
// Just append the new file. If this is from a priority podcast, it'll sort
// into the right place anyway.
foundFiles.add(file);
}
}
}
// Order the found files by file name.
Collections.sort(foundFiles, new Comparator<File>() {
@Override
public int compare(File object1, File object2) {
return object1.getName().compareTo(object2.getName());
}
});
Log.i("carcast", "loadMeta found:"+foundFiles.size()+" meta:"+metas.size());
for (File file : foundFiles) {
metas.add(new MetaFile(file));
}
// We need to save the order if any priority files have been inserted into the ordered
// part of the playlist. If we don't, then those priority files may appear to jump around
// in the playlist. For example, if we select a new file for playback and then stop and restart
// the app, then the priority files will jump to after the newly-playing file.
if ( priorityFileAddedToOrder )
saveOrder();
}
boolean alreadyHas(File file) {
for (MetaFile metaFile : metas) {
if (metaFile.getFilename().equals(file.getName())) {
return true;
}
}
return false;
}
public SortedSet<Integer> moveTop(SortedSet<Integer> checkedItems) {
List<MetaFile> tops = new ArrayList<MetaFile>();
Integer[] ciArry = checkedItems.toArray(new Integer[0]);
for (int i = checkedItems.size() - 1; i >= 0; i--) {
tops.add(0, metas.get(ciArry[i]));
metas.remove(metas.get(ciArry[i]));
}
for (MetaFile metaFile : tops) {
metas.add(0, metaFile);
}
checkedItems.clear();
for (MetaFile atop : tops) {
checkedItems.add(metas.indexOf(atop));
}
saveOrder();
return checkedItems;
}
public SortedSet<Integer> moveUp(SortedSet<Integer> checkedItems) {
for (int i = 0; i < metas.size(); i++) {
if (checkedItems.contains(i)) {
if (!checkedItems.contains(i - 1)) {
swapBack(checkedItems, i);
}
}
}
saveOrder();
return checkedItems;
}
private void swapBack(SortedSet<Integer> checkedItems, int i) {
if (i == 0)
return;
checkedItems.remove(i);
checkedItems.add(i - 1);
MetaFile o = metas.remove(i);
metas.add(i - 1, o);
}
public SortedSet<Integer> moveBottom(SortedSet<Integer> checkedItems) {
List<MetaFile> bottoms = new ArrayList<MetaFile>();
Integer[] ciArry = checkedItems.toArray(new Integer[0]);
for (int i = checkedItems.size() - 1; i >= 0; i--) {
bottoms.add(0, metas.get(ciArry[i]));
metas.remove(metas.get(ciArry[i]));
}
for (MetaFile metaFile : bottoms) {
metas.add(metaFile);
}
checkedItems.clear();
for (MetaFile atop : bottoms) {
checkedItems.add(metas.indexOf(atop));
}
saveOrder();
return checkedItems;
}
public SortedSet<Integer> moveDown(SortedSet<Integer> checkedItems) {
for (int i = metas.size() - 2; i >= 0; i--) {
if (checkedItems.contains(i)) {
if (!checkedItems.contains(i + 1)) {
swapForward(checkedItems, i);
}
}
}
saveOrder();
return checkedItems;
}
private void swapForward(SortedSet<Integer> checkedItems, int i) {
checkedItems.remove(i);
checkedItems.add(i + 1);
MetaFile o = metas.remove(i);
metas.add(i + 1, o);
}
public void saveOrder() {
File order = config.getPodcastRootPath("podcast-order.txt");
StringBuilder sb = new StringBuilder();
for (MetaFile metaFile : metas) {
sb.append(metaFile.getFilename());
sb.append('\n');
}
try {
FileOutputStream fos = new FileOutputStream(order);
fos.write(sb.toString().getBytes());
fos.close();
} catch (Exception e) {
Log.e("carcast", "saving order", e);
}
}
// IMPORTANT:
// The regular expression used here *must* match the file naming scheme used in
// DownloadHelper.downloadNewPodCasts().
private boolean isPriority(File file)
{
String pattern = "^\\d+:\\d\\d:\\d+\\..*"; // E.g. "YYYY:00:XXXX.mp3"
boolean priority = file.getName().matches(pattern);
Log.d("CarCast", "priority: " + priority + " " + file.getName());
return priority;
}
}
// vim: set noet ci pi sts=0 sw=4 ts=4