package com.util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.util.DownloadInfo;
import com.util.DownloadObserver;
import com.util.P2pDownloadInfo;
import com.limegroup.gnutella.Downloader;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;
public class DownloadService extends Service {
private static final int POOL_SIZE = 4;
private static final String TAG = "DownloadService";
private ArrayList<DownloadInfo> mDownloads = new ArrayList<DownloadInfo>();
private ArrayList<DownloadObserver> mObservers = new ArrayList<DownloadObserver>();
private ExecutorService mPool;
private ArrayList<MediaScannerNotifier> mScanners = new ArrayList<MediaScannerNotifier>();
public class LocalBinder extends Binder {
public DownloadService getService() {
return DownloadService.this;
}
}
@Override
public void onCreate() {
Log.i(TAG, "Download service started");
mPool = Executors.newFixedThreadPool(POOL_SIZE);
}
public void registerDownloadObserver(DownloadObserver observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
if (mObservers.contains(observer)) {
throw new IllegalStateException("Observer " + observer + " is already registered.");
}
mObservers.add(observer);
}
}
public void unregisterObserver(DownloadObserver observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
int index = mObservers.indexOf(observer);
if (index == -1) {
throw new IllegalStateException("Observer " + observer + " was not registered.");
}
mObservers.remove(index);
}
}
/**
* Remove all registered observer
*/
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
public void notifyChanged() {
synchronized(mObservers) {
for (DownloadObserver o : mObservers) {
o.onChange();
}
}
}
public ArrayList<DownloadInfo> getDownloadInfos() {
synchronized(mDownloads) {
return new ArrayList<DownloadInfo>(mDownloads);
}
}
public boolean fileBeingDownloaded(com.util.SearchResult mp3) {
if (mp3 == null)
return true;
String filename = mp3.getFileName();
if (filename == null)
return false;
synchronized(mDownloads) {
for (DownloadInfo d : mDownloads) {
if (d.getSearchResult() == null)
continue;
if (d.getSearchResult().getFileName() != null &&
filename.equals(d.getSearchResult().getFileName()))
return true;
}
}
return false;
}
public void insertDownload(DownloadInfo info) {
if (info == null || !info.valid())
return;
synchronized(mDownloads) {
for (DownloadInfo d : mDownloads) {
if (d.same(info))
return;
}
mDownloads.add(info);
}
if (info.ableToResume()) {
synchronized(info) {
info.setFailed(false);
if (info.isScheduled()) {
// No re-entrance.
return;
} else {
info.setScheduled(true);
}
}
}
// This should not block.
mPool.execute(new Task(info));
notifyChanged();
}
public void retryDownload(DownloadInfo info) {
if (info == null)
return;
if (info.ableToRetry()) {
mPool.execute(new Task(info));
notifyChanged();
}
}
public void resumeDownload(DownloadInfo info) {
if (info == null)
return;
info.resumeDownload();
if (!info.ableToResume())
retryDownload(info);
}
public void removeDownload(DownloadInfo info) {
if (info == null)
return;
synchronized(mDownloads) {
int index = mDownloads.indexOf(info);
if (index == -1) {
return;
}
mDownloads.remove(index);
}
notifyChanged();
}
public void clearFinished() {
ArrayList<DownloadInfo> downloads = new ArrayList<DownloadInfo>();
boolean changed = false;
synchronized(mDownloads) {
for (DownloadInfo d : mDownloads) {
if (d.getState() != Downloader.COMPLETE) {
downloads.add(d);
} else {
changed = true;
}
}
if (changed) {
mDownloads = downloads;
}
}
if (changed) {
notifyChanged();
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IBinder mBinder = new LocalBinder();
private class Task implements Runnable {
private DownloadInfo mInfo;
private void download() throws IOException, InterruptedException {
if (mInfo == null)
return;
mInfo.download(DownloadService.this);
}
public Task(DownloadInfo download) {
mInfo = download;
}
@Override
public void run() {
if (mInfo == null)
return;
PowerManager.WakeLock wakeLock = null;
try { PowerManager pm = (PowerManager)DownloadService.this.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
wakeLock.acquire();
download();
} catch (Exception e) {
e.printStackTrace();
synchronized(mInfo) {
mInfo.setFailed(true);
mInfo.setError(e.getMessage());
}
} finally {
if (wakeLock != null) {
wakeLock.release();
wakeLock = null;
}
notifyChanged();
com.util.Utils.D("task finished: " + mInfo);
synchronized(mInfo) {
if (mInfo instanceof P2pDownloadInfo)
((P2pDownloadInfo) mInfo).setScheduled(false);
notifyChanged();
}
}
}
}
private class MediaScannerNotifier implements MediaScannerConnectionClient {
private MediaScannerConnection mConnection;
private String mPath;
private String mMimeType;
public MediaScannerNotifier(String path, String mimeType) {
mPath = path;
mMimeType = mimeType;
mConnection = new MediaScannerConnection(DownloadService.this, this);
mConnection.connect();
}
public void onMediaScannerConnected() {
mConnection.scanFile(mPath, mMimeType);
}
public void onScanCompleted(String path, Uri uri) {
if (mPath == null)
return;
if (mPath.equals(path)) {
com.util.Utils.D("File scanned: " + path);
mConnection.disconnect();
synchronized(mScanners) {
mScanners.remove(this);
}
}
}
}
public void ScanMediaFile(final String musicPath) {
synchronized(mScanners) {
mScanners.add(new MediaScannerNotifier(musicPath, "audio/mpeg"));
}
}
}