package com.kenny.openimgur.classes; import android.os.AsyncTask; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import com.kenny.openimgur.activities.SettingsActivity; import com.kenny.openimgur.util.FileUtil; import com.kenny.openimgur.util.ImageUtil; import com.kenny.openimgur.util.LogUtil; import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; /** * Created by kcampagna on 10/9/14. */ public class VideoCache { private static final String TAG = "VideoCache"; private static VideoCache mInstance; private File mCacheDir; private Md5FileNameGenerator mKeyGenerator; public static VideoCache getInstance() { if (mInstance == null) { mInstance = new VideoCache(); } return mInstance; } private VideoCache() { OpengurApp app = OpengurApp.getInstance(); String cacheKey = app.getPreferences().getString(SettingsActivity.KEY_CACHE_LOC, SettingsActivity.CACHE_LOC_INTERNAL); File dir = ImageUtil.getCacheDirectory(app.getApplicationContext(), cacheKey); mCacheDir = new File(dir, "video_cache"); mCacheDir.mkdirs(); mKeyGenerator = new Md5FileNameGenerator(); } public void setCacheDirectory(File dir) { mCacheDir = new File(dir, "video_cache"); mCacheDir.mkdirs(); } /** * Downloads and saves the video file to the cache * * @param url The url of the video * @param listener Optional VideoCacheListener */ public void putVideo(@Nullable String url, @Nullable VideoCacheListener listener) { if (TextUtils.isEmpty(url)) { Exception e = new IllegalArgumentException("Url is null"); LogUtil.e(TAG, "Invalid url", e); if (listener != null) listener.onVideoDownloadFailed(e, url); return; } String key = mKeyGenerator.generate(url); File file = getVideoFile(key); if (FileUtil.isFileValid(file)) { LogUtil.v(TAG, "File already exists, deleting existing file and replacing it"); file.delete(); } try { if (file == null) { String ext = getExtension(url); if (TextUtils.isEmpty(ext)) { if (listener != null) listener.onVideoDownloadFailed(new IllegalArgumentException("Invalid extension for url " + url), url); return; } file = new File(mCacheDir, key + ext); } file.createNewFile(); } catch (IOException e) { LogUtil.e(TAG, "Error creating file", e); if (listener != null) listener.onVideoDownloadFailed(e, url); } if (FileUtil.isFileValid(file)) { new DownloadVideo(key, url, listener).execute(file); } else if (listener != null) { Exception e = new FileNotFoundException("Unable to create file for download"); LogUtil.e(TAG, "Error creating file", e); listener.onVideoDownloadFailed(e, url); } } /** * Returns the cached video file for the given url. NULL if it does not exist * * @param url * @return */ public File getVideoFile(String url) { if (TextUtils.isEmpty(url)) return null; String ext = getExtension(url); if (TextUtils.isEmpty(url)) return null; String key = mKeyGenerator.generate(url); File file = new File(mCacheDir, key + ext); return FileUtil.isFileValid(file) ? file : null; } public void deleteCache() { FileUtil.deleteDirectory(mCacheDir); mCacheDir.mkdirs(); } public long getCacheSize() { return FileUtil.getDirectorySize(mCacheDir); } public interface VideoCacheListener { // Called when the Video download starts void onVideoDownloadStart(String key, String url); // Called when the video download fails void onVideoDownloadFailed(Exception ex, String url); // Called when the video download completes void onVideoDownloadComplete(File file); void onProgress(int downloaded, int total); } private static class DownloadVideo extends AsyncTask<File, Integer, Object> { private String mKey; private VideoCacheListener mListener; private String mUrl; public DownloadVideo(String key, String url, VideoCacheListener listener) { this.mKey = key; this.mListener = listener; this.mUrl = url; if (mUrl.endsWith(".gifv")) mUrl = mUrl.replace(".gifv", ".mp4"); } @Override protected void onPreExecute() { super.onPreExecute(); if (mListener != null) mListener.onVideoDownloadStart(mKey, mUrl); } @Override protected Object doInBackground(File... file) { InputStream in = null; BufferedOutputStream buffer = null; LogUtil.v(TAG, "Downloading video from " + mUrl); File writeFile = null; try { URL url = new URL(mUrl); URLConnection connection = url.openConnection(); connection.connect(); in = connection.getInputStream(); writeFile = file[0]; buffer = new BufferedOutputStream(new FileOutputStream(writeFile)); byte byt[] = new byte[1024]; int i; int total = 0; int size = connection.getContentLength(); for (long l = 0L; (i = in.read(byt)) != -1; l += i) { total += i; buffer.write(byt, 0, i); publishProgress(total, size); } buffer.flush(); return writeFile; } catch (Exception e) { LogUtil.e(TAG, "An error occurred whiling downloading video", e); // Delete the file if an exception occurs to allow download retries if (writeFile != null) writeFile.delete(); return e; } finally { FileUtil.closeStream(in); FileUtil.closeStream(buffer); } } @Override protected void onProgressUpdate(Integer... values) { if (values != null && values.length == 2) { if (mListener != null) mListener.onProgress(values[0], values[1]); } } @Override protected void onPostExecute(Object o) { if (o instanceof File) { LogUtil.v(TAG, "Video downloaded successfully to " + ((File) o).getAbsolutePath()); if (mListener != null) { mListener.onVideoDownloadComplete((File) o); } } else if (mListener != null) { mListener.onVideoDownloadFailed((Exception) o, mUrl); } } } @Nullable private String getExtension(@NonNull String url) { if (url.endsWith(".gifv") || url.endsWith(".mp4")) { return ".mp4"; } else if (url.endsWith(".webm")) { return ".webm"; } return null; } }