package us.shandian.giga.get; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.util.Log; import com.google.gson.Gson; import java.io.File; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.HashMap; import us.shandian.giga.util.Utility; import static us.shandian.giga.BuildConfig.DEBUG; public class DownloadMission { private static final String TAG = DownloadMission.class.getSimpleName(); public static interface MissionListener { HashMap<MissionListener, Handler> handlerStore = new HashMap<>(); public void onProgressUpdate(long done, long total); public void onFinish(); public void onError(int errCode); } public static final int ERROR_SERVER_UNSUPPORTED = 206; public static final int ERROR_UNKNOWN = 233; public String name = ""; public String url = ""; public String location = ""; public long blocks = 0; public long length = 0; public long done = 0; public int threadCount = 3; public int finishCount = 0; public ArrayList<Long> threadPositions = new ArrayList<Long>(); public HashMap<Long, Boolean> blockState = new HashMap<Long, Boolean>(); public boolean running = false; public boolean finished = false; public boolean fallback = false; public int errCode = -1; public long timestamp = 0; public transient boolean recovered = false; private transient ArrayList<WeakReference<MissionListener>> mListeners = new ArrayList<WeakReference<MissionListener>>(); private transient boolean mWritingToFile = false; public boolean isBlockPreserved(long block) { return blockState.containsKey(block) ? blockState.get(block) : false; } public void preserveBlock(long block) { synchronized (blockState) { blockState.put(block, true); } } public void setPosition(int id, long position) { threadPositions.set(id, position); } public long getPosition(int id) { return threadPositions.get(id); } public synchronized void notifyProgress(long deltaLen) { if (!running) return; if (recovered) { recovered = false; } done += deltaLen; if (done > length) { done = length; } if (done != length) { writeThisToFile(); } for (WeakReference<MissionListener> ref: mListeners) { final MissionListener listener = ref.get(); if (listener != null) { MissionListener.handlerStore.get(listener).post(new Runnable() { @Override public void run() { listener.onProgressUpdate(done, length); } }); } } } public synchronized void notifyFinished() { if (errCode > 0) return; finishCount++; if (finishCount == threadCount) { onFinish(); } } private void onFinish() { if (errCode > 0) return; if (DEBUG) { Log.d(TAG, "onFinish"); } running = false; finished = true; deleteThisFromFile(); for (WeakReference<MissionListener> ref : mListeners) { final MissionListener listener = ref.get(); if (listener != null) { MissionListener.handlerStore.get(listener).post(new Runnable() { @Override public void run() { listener.onFinish(); } }); } } } public synchronized void notifyError(int err) { errCode = err; writeThisToFile(); for (WeakReference<MissionListener> ref : mListeners) { final MissionListener listener = ref.get(); MissionListener.handlerStore.get(listener).post(new Runnable() { @Override public void run() { listener.onError(errCode); } }); } } public synchronized void addListener(MissionListener listener) { Handler handler = new Handler(Looper.getMainLooper()); MissionListener.handlerStore.put(listener, handler); mListeners.add(new WeakReference<MissionListener>(listener)); } public synchronized void removeListener(MissionListener listener) { for (Iterator<WeakReference<MissionListener>> iterator = mListeners.iterator(); iterator.hasNext(); ) { WeakReference<MissionListener> weakRef = iterator.next(); if (listener!=null && listener == weakRef.get()) { iterator.remove(); } } } public void start() { if (!running && !finished) { running = true; if (!fallback) { for (int i = 0; i < threadCount; i++) { if (threadPositions.size() <= i && !recovered) { threadPositions.add((long) i); } new Thread(new DownloadRunnable(this, i)).start(); } } else { // In fallback mode, resuming is not supported. threadCount = 1; done = 0; blocks = 0; new Thread(new DownloadRunnableFallback(this)).start(); } } } public void pause() { if (running) { running = false; recovered = true; // TODO: Notify & Write state to info file // if (err) } } public void delete() { deleteThisFromFile(); new File(location + "/" + name).delete(); } public void writeThisToFile() { if (!mWritingToFile) { mWritingToFile = true; new Thread() { @Override public void run() { doWriteThisToFile(); mWritingToFile = false; } }.start(); } } private void doWriteThisToFile() { synchronized (blockState) { Utility.writeToFile(location + "/" + name + ".giga", new Gson().toJson(this)); } } private void deleteThisFromFile() { new File(location + "/" + name + ".giga").delete(); } }