package com.aptoide.amethyst.downloadmanager; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.util.Log; import com.aptoide.amethyst.Aptoide; import com.aptoide.amethyst.analytics.Analytics; import com.aptoide.amethyst.downloadmanager.model.Download; import com.aptoide.amethyst.downloadmanager.state.CompletedState; import java.io.File; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import com.aptoide.amethyst.downloadmanager.model.DownloadModel; import com.aptoide.amethyst.downloadmanager.state.ActiveState; import com.aptoide.amethyst.downloadmanager.state.ErrorState; import com.aptoide.amethyst.downloadmanager.state.InactiveState; import com.aptoide.amethyst.downloadmanager.state.StatusState; import com.aptoide.amethyst.events.BusProvider; import com.aptoide.amethyst.events.OttoEvents; import com.aptoide.amethyst.utils.Logger; /** * Runnable responsible for informing the Notification Bar with the download Progress. * Class renamed from DownloadInfo.java (<= v6) * * User: rmateus * Date: 02-07-2013 * Time: 10:49 * To change this template use File | Settings | File Templates. */ public class DownloadInfoRunnable implements Runnable, Serializable { private static final int UPDATE_INTERVAL_MILLISECONDS = 1000; private double mSpeed; private long id; private long mDownloadedSize; private long mSize; private long mETA; private long mProgress = 0; private boolean isPaused = false; private boolean update; private transient NotificationCompat.Builder mBuilder; private String mDestination; private StatusState mStatusState; private Download download; private List<DownloadModel> mFilesToDownload; private List<DownloadThread> threads = new ArrayList<>(); private DownloadExecutor downloadExecutor; private DownloadManager downloadManager; public DownloadInfoRunnable(DownloadManager manager, long id) { this.id = id; this.downloadManager = manager; this.mStatusState = new InactiveState(this); } @Override public void run() { ExecutorService executor = Executors.newSingleThreadExecutor(); Timer timer = new Timer(); try { for (DownloadModel downloadModel : mFilesToDownload) { DownloadThread thread = new DownloadThread(downloadModel, this); executor.submit(thread); threads.add(thread); } checkDirectorySize(Aptoide.getConfiguration().getPathCacheApks()); mSize = getAllThreadSize(); TimerTask task = new TimerTask() { public long mAvgSpeed; /** How much was downloaded last time. */ private long iMLastDownloadedSize = mDownloadedSize; /** The nanoTime last time. */ private long iMLastTime = System.currentTimeMillis(); private long iMFirstTime = System.currentTimeMillis(); @Override public void run() { long mReaminingSize = getAllSizeRemaining(); mDownloadedSize = getAllDownloadedSize(); mProgress = getAllProgress(); long timeElapsedSinceLastTime = System.currentTimeMillis() - iMLastTime; long timeElapsed = System.currentTimeMillis() - iMFirstTime; iMLastTime = System.currentTimeMillis(); // Difference between last time and this time = how much was downloaded since last run. long downloadedSinceLastTime = mDownloadedSize - iMLastDownloadedSize; iMLastDownloadedSize = mDownloadedSize; if (timeElapsedSinceLastTime > 0 && timeElapsed > 0) { // Speed (bytes per second) = downloaded bytes / time in seconds (nanoseconds / 1000000000) mAvgSpeed = (mDownloadedSize) * 1000 / timeElapsed; mSpeed = downloadedSinceLastTime * 1000 / timeElapsedSinceLastTime; } if (mAvgSpeed > 0) { // ETA (milliseconds) = remaining byte size / bytes per millisecond (bytes per second * 1000) mETA = (mReaminingSize - mDownloadedSize) * 1000 / mAvgSpeed; } Log.d("DownloadManager", "ETA: " + mETA + " Speed: " + mSpeed / 1000 + " Size: " + DownloadUtils.formatBytes(mSize) + " Downloaded: " + DownloadUtils.formatBytes(mDownloadedSize) + " Status: " + mStatusState + " TotalDownloaded: " + DownloadUtils.formatBytes(mProgress) + " " + System.identityHashCode(DownloadInfoRunnable.this)); download.setSpeed(getSpeed()); download.setTimeLeft(mETA); download.setProgress(getPercentDownloaded()); BusProvider.getInstance().post(new OttoEvents.DownloadInProgress(download)); } }; // Schedule above task for every (UPDATE_INTERVAL_MILLISECONDS) milliseconds. timer.schedule(task, 0, UPDATE_INTERVAL_MILLISECONDS); executor.shutdown(); // Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, // or the current thread is interrupted, whichever happens first. executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); timer.cancel(); timer.purge(); mSize = getAllThreadSize(); mProgress = getAllProgress(); Log.d("download-trace", "Downloads done " + mSize + " " + mProgress + " " + mStatusState.getEnumState().name()); download.setSpeed(getSpeed()); download.setProgress(getPercentDownloaded()); if (mStatusState instanceof ActiveState) { changeStatusState(new CompletedState(this)); autoExecute(); Analytics.DownloadComplete.downloadComplete(download); } } catch (Exception e) { changeStatusState(new ErrorState(this, EnumDownloadFailReason.NO_REASON)); e.printStackTrace(); } BusProvider.getInstance().post(new OttoEvents.DownloadEvent(getId(), mStatusState)); downloadManager.updatePendingList(); threads.clear(); mDownloadedSize = 0; mSpeed = 0; mETA = 0; Logger.d("download-trace", "Download Finish??" + download.getName()); } private double getDirSize(File dir) { double size = 0; if (dir.isFile()) { size = dir.length(); } else { File[] subFiles = dir.listFiles(); if (subFiles != null) { for (File file : subFiles) { if (file.isFile()) { size += file.length(); } else { size += this.getDirSize(file); } } } } return size; } private void checkDirectorySize(String dirPath) { File dir = new File(dirPath); if (!dir.exists()) { if (!dir.mkdirs()) { return; } } double size = getDirSize(dir) / 1024 / 1024; SharedPreferences sPref = PreferenceManager.getDefaultSharedPreferences(Aptoide.getContext()); long maxFileCache; try { maxFileCache = Long.parseLong((sPref.getString("maxFileCache", "200"))); } catch (Exception e) { maxFileCache = 50; } if (maxFileCache < 50) maxFileCache = 50; if (maxFileCache > 0 && size > maxFileCache) { File[] files = dir.listFiles(); long latestTime = System.currentTimeMillis(); long currentTime; File fileToDelete = null; for (File file : files) { currentTime = file.lastModified(); if (currentTime < latestTime) { latestTime = currentTime; fileToDelete = file; } } if (fileToDelete != null) { Log.d("download-trace", "Deleting " + fileToDelete.getName()); if (!fileToDelete.delete()) { return; } checkDirectorySize(dirPath); } } } public EnumDownloadFailReason getFailReason() { return ((ErrorState) mStatusState).getErrorMessage(); } public void autoExecute() { if (downloadExecutor != null) { for (DownloadModel file : mFilesToDownload) { if (file.isAutoExecute()) { downloadExecutor.execute(); } } } } private long getAllDownloadedSize() { long sum = 0; for (DownloadThread thread : threads) { sum = sum + thread.getmDownloadedSize(); } return sum; } private long getAllProgress() { long sum = 0; for (DownloadThread thread : threads) { sum = sum + thread.getmProgress(); } return sum; } private long getAllThreadSize() { long sum = 0; for (DownloadThread thread : threads) { sum = sum + thread.getmFullSize(); } return sum; } public void remove(boolean isRemove) { Log.d("download-trace", "Download remove: " + download.getName()); changeStatusState(new CompletedState(this)); if (mFilesToDownload == null) return; if (isRemove) { for (DownloadModel model : mFilesToDownload) { new File(model.getDestination()).delete(); } } mProgress = 0; mSize = 0; mFilesToDownload.clear(); downloadManager.removeDownload(this); BusProvider.getInstance().post(new OttoEvents.DownloadEvent(getId(), mStatusState)); } public void download() { Log.d("download-trace", "download-state: " + download.getDownloadState()); Log.d("download-trace", "Download started at " + download.getProgress()); mProgress = download.getProgress(); BusProvider.getInstance().post(new OttoEvents.DownloadEvent(getId(), mStatusState)); this.mStatusState.download(); } public double getSpeed() { return mSpeed * 8; } public long getEta() { return mETA; } public void changeStatusState(StatusState state) { mStatusState.changeTo(state); } public long getId() { return id; } public long getAllSizeRemaining() { long sum = 0; for (DownloadThread thread : threads) { sum = sum + thread.getmRemainingSize(); // Log.d("DownloadManagerThread", "Size: " + thread.getmRemainingSize()); } return sum; } public boolean isPaused() { return isPaused; } public DownloadManager getDownloadManager() { return downloadManager; } public void setDownloadManager(DownloadManager downloadManager) { this.downloadManager = downloadManager; } public boolean isPaid() { return download.isPaid(); } public boolean isUpdate() { return update; } public void setUpdate(boolean update) { this.update = update; } public DownloadExecutor getDownloadExecutor() { return downloadExecutor; } public void setDownloadExecutor(DownloadExecutor executor) { this.downloadExecutor = executor; } public NotificationCompat.Builder getmBuilder() { return mBuilder; } public void setmBuilder(NotificationCompat.Builder mBuilder) { this.mBuilder = mBuilder; } public Download getDownload() { return download; } public void setDownload(Download download) { this.download = download; this.download.setParent(this); } public void setFilesToDownload(List<DownloadModel> mFilesToDownload) { this.mFilesToDownload = mFilesToDownload; } public int getPercentDownloaded() { if (mSize == 0) { return 0; } return (int) ((mProgress) * 100 / mSize); } public List<DownloadModel> getmFilesToDownload() { return mFilesToDownload; } public String getDestination() { return mDestination; } public void setDestination(String mDestination) { this.mDestination = mDestination; } public StatusState getStatusState() { return this.mStatusState; } public void setStatusState(StatusState statusState) { this.mStatusState = statusState; } }