Guest User

downloadqueue

a guest
Aug 30th, 2016
138
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 18.63 KB | None | 0 0
  1. import android.Manifest;
  2. import android.app.IntentService;
  3. import android.content.Intent;
  4. import android.content.Context;
  5. import android.content.pm.PackageManager;
  6. import android.os.AsyncTask;
  7. import android.os.Environment;
  8. import android.os.StatFs;
  9. import android.support.annotation.NonNull;
  10. import android.support.v4.content.ContextCompat;
  11. import android.util.Log;
  12. import android.widget.Toast;
  13.  
  14. import java.io.File;
  15. import java.util.ArrayList;
  16. import java.util.Collections;
  17. import java.util.Iterator;
  18. import java.util.List;
  19.  
  20. import io.realm.Realm;
  21. import io.realm.RealmResults;
  22. import io.realm.Sort;
  23.  
  24. /**
  25.  * An {@link IntentService} subclass for handling asynchronous task requests in
  26.  * a service on a separate handler thread.
  27.  * <p>
  28.  * TODO: Customize class - update intent actions, extra parameters and static
  29.  * helper methods.
  30.  */
  31. public class DownloadIntentService extends IntentService {
  32.     // TODO: Rename actions, choose action names that describe tasks that this
  33.     // IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS
  34.     private static final String ACTION_DOWNLOAD = "com.vibhinna.sreni.action.ADD_DOWNLOAD";
  35.     private static final String ACTION_PAUSE_DOWNLOAD = "com.vibhinna.sreni.action.PAUSE_DOWNLOAD";
  36.  
  37.     // TODO: Rename parameters
  38.     private static final String EXTRA_ID = "com.vibhinna.sreni.extra.ID";
  39.     private static final String EXTRA_URL = "com.vibhinna.sreni.extra.URL";
  40.     private static final String EXTRA_TITLE = "com.vibhinna.sreni.extra.TITLE";
  41.     private static final String EXTRA_VIDEO_ID = "com.vibhinna.sreni.extra.VIDEO_ID";
  42.     private static final String EXTRA_FORMAT_ID = "com.vibhinna.sreni.extra.FORMAT_ID";
  43.     private static final String EXTRA_EXTENSION = "com.vibhinna.sreni.extra.EXTENSION";
  44.     private static final String EXTRA_THUMBNAIL = "com.vibhinna.sreni.extra.THUMBNAIL";
  45.     private static final String EXTRA_PARAM2 = "com.vibhinna.sreni.extra.PARAM2";
  46.     private static final int QUEUE_SIZE = 3;
  47.     private static final String TAG = "DownloadIntentService";
  48.  
  49.     private final List<Downloader> downloadQueue
  50.             = Collections.synchronizedList(new ArrayList<Downloader>());
  51.  
  52.     public DownloadIntentService() {
  53.         super(TAG);
  54.     }
  55.  
  56.     /**
  57.      * Starts this service to perform action Foo with the given parameters. If
  58.      * the service is already performing a task this action will be queued.
  59.      *
  60.      * @see IntentService
  61.      */
  62.     public static void addActionDownload(Context context,
  63.                                          String id,
  64.                                          String url,
  65.                                          String title,
  66.                                          String videoId,
  67.                                          String formatId,
  68.                                          String extension,
  69.                                          String thumbnail) {
  70.         Intent intent = new Intent(context, DownloadIntentService.class);
  71.         intent.setAction(ACTION_DOWNLOAD);
  72.         intent.putExtra(EXTRA_ID, id);
  73.         intent.putExtra(EXTRA_URL, url);
  74.         intent.putExtra(EXTRA_TITLE, title);
  75.         intent.putExtra(EXTRA_VIDEO_ID, videoId);
  76.         intent.putExtra(EXTRA_FORMAT_ID, formatId);
  77.         intent.putExtra(EXTRA_EXTENSION, extension);
  78.         intent.putExtra(EXTRA_THUMBNAIL, thumbnail);
  79.         context.startService(intent);
  80.     }
  81.  
  82.     public static void startActionPauseDownload(Context context, String... ids) {
  83.         Intent intent = new Intent(context, DownloadIntentService.class);
  84.         intent.setAction(ACTION_PAUSE_DOWNLOAD);
  85.         intent.putExtra(EXTRA_ID, ids);
  86.         context.startService(intent);
  87.     }
  88.  
  89.     @Override
  90.     protected void onHandleIntent(Intent intent) {
  91.         if (intent != null) {
  92.             final String action = intent.getAction();
  93.             if (ACTION_DOWNLOAD.equals(action)) {
  94.                 final String id = intent.getStringExtra(EXTRA_ID);
  95.                 final String url = intent.getStringExtra(EXTRA_URL);
  96.                 final String title = intent.getStringExtra(EXTRA_TITLE);
  97.                 final String videoId = intent.getStringExtra(EXTRA_VIDEO_ID);
  98.                 final String formatId = intent.getStringExtra(EXTRA_FORMAT_ID);
  99.                 final String extension = intent.getStringExtra(EXTRA_EXTENSION);
  100.                 final String thumb = intent.getStringExtra(EXTRA_THUMBNAIL);
  101.                 handleActionAddDownload(id, url, title, videoId, formatId, extension, thumb);
  102.             } else if (ACTION_PAUSE_DOWNLOAD.equals(action)) {
  103.                 final String[] ids = intent.getStringArrayExtra(EXTRA_ID);
  104.                 handleActionBaz(ids);
  105.             }
  106.         }
  107.     }
  108.  
  109.     /**
  110.      * Handle action Foo in the provided background thread with the provided
  111.      * parameters.
  112.      */
  113.     private void handleActionAddDownload(final String id,
  114.                                          final String url,
  115.                                          final String title,
  116.                                          final String videoId,
  117.                                          final String formatId,
  118.                                          final String extension,
  119.                                          final String thumb) {
  120.         Realm realm = Realm.getDefaultInstance();
  121.         final RealmResults<Download> resultSet = realm
  122.                 .where(Download.class)
  123.                 .findAll()
  124.                 .sort("position", Sort.DESCENDING);
  125.         final Download old = Utils.getDownloadById(realm, id);
  126.         if (old != null && old.getDestination() != null) {
  127.             // There's and old record with same id.
  128.             File oldFile = new File(old.getDestination());
  129.             if (oldFile.exists() && old.getStatus() == Download.DONE
  130.                     && oldFile.length() != old.getLength()) {
  131.                 // The old download isn't complete.
  132.                 if (oldFile.length() > 0 && oldFile.length() < old.getLength()) {
  133.                     // Partly downloaded?
  134.                     Log.d(TAG, "handleActionAddDownload: Detected partly downloaded file: "
  135.                             + oldFile.getAbsolutePath());
  136.                 } else {
  137.                     if (oldFile.exists()) {
  138.                         // Bigger than remote file. Shouldn't happen. Try to delete.
  139.                         Log.d(TAG, "handleActionAddDownload: Deleting corrupted download: "
  140.                                 + oldFile.getAbsolutePath());
  141.                         if (!oldFile.delete())
  142.                             Log.e(TAG, "handleActionAddDownload: " +
  143.                                     "Couldn't delete corrupted download: "
  144.                                     + oldFile.getAbsolutePath());;
  145.                     } else {
  146.                         Log.d(TAG, "handleActionAddDownload: Missing download: "
  147.                                 + oldFile.getAbsolutePath());
  148.                     }
  149.                 }
  150.             } else if (oldFile.exists()){
  151.                 Log.w(TAG, "handleActionAddDownload: download is probably complete. Cancelling!");
  152.                 // TODO: 24/8/16 Download is probably complete.
  153.                 // TODO Show a dialog perhaps for a force download option?
  154.                 return;
  155.             }
  156.         }
  157.  
  158.         realm.executeTransaction(new Realm.Transaction() {
  159.             @Override
  160.             public void execute(Realm realm) {
  161.                 Download download = new Download(id);
  162.                 download.setUrl(url);
  163.                 // TODO: 24/8/16 This should occur in the UI.
  164.                 if (old == null || (old.getDestination() == null)) {
  165.                     // Try to download to old location. We don't need it probably.
  166.                     download.setDestination(Environment.getExternalStorageDirectory()
  167.                             + "/Download/" + Utils.toValidFileName(title) + "." + id
  168.                             + "." + extension);
  169.                 }
  170.                 download.setTitle(title);
  171.                 download.setVideoId(videoId);
  172.                 download.setFormatId(formatId);
  173.                 download.setExtension(extension);
  174.                 download.setThumbnail(thumb);
  175.                 long position = 0;
  176.                 if (resultSet.size() > 0) {
  177.                     position = resultSet.get(0).getPosition() + 1;
  178.                 }
  179.                 download.setPosition(position);
  180.                 Log.d(TAG, "execute: " + download.toString());
  181.                 realm.copyToRealmOrUpdate(download);
  182.             }
  183.         });
  184.         resumeDownload(id);
  185.         realm.close();
  186.     }
  187.  
  188.     /**
  189.      * Takes a download id, adds it to the queue if possible and resumes the download.
  190.      *
  191.      * @param id The id.
  192.      */
  193.     private void resumeDownload(String id) {
  194.         //noinspection StatementWithEmptyBody
  195.         if (isQueueOpen()) {
  196.             // URL already exists, try to resume it.
  197.             // FIXME: 21/8/16
  198.             if (!isInQueue(id)) {
  199.                 synchronized (downloadQueue) {
  200.                     downloadQueue.add(new Downloader(id));
  201.                 }
  202.             } else {
  203.                 // Download is already there in the queue. Multiple clicks?
  204.                 Log.e(TAG, "resumeDownload: Download is already there in the queue. " +
  205.                         "Multiple clicks?");
  206.  
  207.             }
  208.             if (!ensureSdCard()) {
  209.                 Log.e(TAG, "resumeDownload: " + "Cannot ensure sdcard" );
  210.                 Toast.makeText(this, R.string.sd_not_available,
  211.                         Toast.LENGTH_LONG).show();
  212.                 return;
  213.             }
  214.             checkDownloadQueue();
  215.         } else {
  216.             // do nothing.
  217.         }
  218.     }
  219.  
  220.     private boolean ensureSdCard() {
  221.         if (!(ContextCompat.checkSelfPermission(this,
  222.                 Manifest.permission.WRITE_EXTERNAL_STORAGE)
  223.                 == PackageManager.PERMISSION_GRANTED)) {
  224.             Log.e(TAG, "ensureSdCard: no sdcard write permission!");
  225.             return false;
  226.         }
  227.         if (!(ContextCompat.checkSelfPermission(this,
  228.                 Manifest.permission.READ_EXTERNAL_STORAGE)
  229.                 == PackageManager.PERMISSION_GRANTED)) {
  230.             Log.e(TAG, "ensureSdCard: no sdcard read permission!");
  231.             return false;
  232.         }
  233.         if (!(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))) {
  234.             Log.e(TAG, "ensureSdCard: sdcard not mounted!");
  235.             return false;
  236.         }
  237.         StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
  238.         double sdAvailSize = (double)stat.getAvailableBlocks()
  239.                 * (double)stat.getBlockSize();
  240.         double mbAvailable = sdAvailSize / (1024 * 1024);
  241.         if (mbAvailable < 256) {
  242.             Log.e(TAG, "ensureSdCard: sdcard is running out of space!");
  243.             return false;
  244.         }
  245.         return true;
  246.     }
  247.  
  248.     /**
  249.      * Check the database and clean queue, add downloads to queue if possible, start downloads in
  250.      * queue if not already started.
  251.      */
  252.     private void checkDownloadQueue() {
  253.         Realm realm = Realm.getDefaultInstance();
  254.         // Clean queue
  255.         synchronized (downloadQueue) {
  256.             for (Iterator<Downloader> it = downloadQueue.iterator(); it.hasNext();) {
  257.                 Downloader downloader = it.next();
  258.                 final Download download = Utils.getDownloadById(realm, downloader.getId());
  259.                 switch (download.getStatus()) {
  260.                     case Download.DONE:
  261.                     case Download.FAILED:
  262.                     case Download.PAUSED:
  263.                         // Remove processed downloads
  264.                         if (!downloader.isCancelled()) {
  265.                             downloader.cancel(true);
  266.                         }
  267.                         downloadQueue.remove(downloader);
  268.                         break;
  269.                     case Download.WAITING_FOR_QUEUE:
  270.                         // Start pending downloads.
  271.                         realm.executeTransaction(new Realm.Transaction() {
  272.                             @Override
  273.                             public void execute(Realm realm) {
  274.                                 download.setStatus(Download.DOWNLOADING);
  275.                                 realm.copyToRealmOrUpdate(download);
  276.                             }
  277.                         });
  278.                         startDownloaderInQueue(downloader.getId());
  279.                         break;
  280.                     case Download.DOWNLOADING:
  281.                         if (downloader.getStatus().equals(AsyncTask.Status.PENDING)) {
  282.                             startDownloaderInQueue(downloader.getId());
  283.                         } else if (downloader.getStatus().equals(AsyncTask.Status.FINISHED)) {
  284.                             if (!downloader.isCancelled()) {
  285.                                 downloader.cancel(true);
  286.                             }
  287.                             realm.executeTransaction(new Realm.Transaction() {
  288.                                 @Override
  289.                                 public void execute(Realm realm) {
  290.                                     download.setStatus(Download.FAILED);
  291.                                     realm.copyToRealmOrUpdate(download);
  292.                                 }
  293.                             });
  294.                             downloadQueue.remove(downloader);
  295.                         } else {
  296.                             // Do nothing, task is running.
  297.                         }
  298.                         break;
  299.                 }
  300.             }
  301.  
  302.             // Add new downloads to queue if any.
  303.             if (isQueueOpen()) {
  304.                 int diff = QUEUE_SIZE - downloadQueue.size();
  305.                 RealmResults<Download> downloads = realm
  306.                         .where(Download.class)
  307.                         .findAll()
  308.                         .sort("position", Sort.ASCENDING);
  309.                 for (int i = 0; (diff > 0 && i < downloads.size()); i++) {
  310.                     final Download download = downloads.get(i);
  311.                     if (download.getStatus() == Download.WAITING_FOR_QUEUE) {
  312.                         realm.executeTransaction(new Realm.Transaction() {
  313.                             @Override
  314.                             public void execute(Realm realm) {
  315.                                 download.setStatus(Download.DOWNLOADING);
  316.                                 realm.copyToRealmOrUpdate(download);
  317.                             }
  318.                         });
  319.                         Downloader downloader = new Downloader(download.getId());
  320.                         downloadQueue.add(downloader);
  321.                         startDownloaderInQueue(download.getId());
  322.                         diff--;
  323.                     } else if (download.getStatus() == Download.DOWNLOADING) {
  324.                         if (isInQueue(download.getId())) {
  325.                             Downloader downloader = new Downloader(download.getId());
  326.                             downloadQueue.add(downloader);
  327.                             startDownloaderInQueue(download.getId());
  328.                             diff--;
  329.                         }
  330.                     }
  331.                 }
  332.             }
  333.         }
  334.         realm.close();
  335.     }
  336.  
  337.     /**
  338.      * Start a download in queue
  339.      *
  340.      * @param id an id that exists in download queue.
  341.      */
  342.     private void startDownloaderInQueue(@NonNull String id) {
  343.         if (!isInQueue(id)) throw new IllegalArgumentException("URL is not in queue");
  344.         Downloader downloader = getDownloaderFromQueue(id);
  345.         Log.d(TAG, "startDownloaderInQueue: " + downloader.getStatus());
  346.         if (AsyncTask.Status.PENDING.equals(downloader.getStatus())) {
  347.             downloader.execute();
  348.         } else if (AsyncTask.Status.FINISHED.equals(downloader.getStatus())) {
  349.             throw new IllegalStateException("Downloader already executed");
  350.         }
  351.     }
  352.  
  353.     private Downloader getDownloaderFromQueue(String id) {
  354.         synchronized (downloadQueue) {
  355.             for (Downloader downloader : downloadQueue) {
  356.                 if (id.equals(downloader.getId())) return downloader;
  357.             }
  358.         }
  359.         throw new IllegalArgumentException(id + "wasn't found in queue.");
  360.     }
  361.  
  362.     /**
  363.      * Check if a url is in the queue.
  364.      *
  365.      * @param id The id.
  366.      * @return weather a id is in the queue or not.
  367.      */
  368.     private boolean isInQueue(@NonNull String id) {
  369.         synchronized (downloadQueue) {
  370.             for (Downloader downloader : downloadQueue) {
  371.                 if (id.equals(downloader.getId()))
  372.                     return true;
  373.             }
  374.         }
  375.         return false;
  376.     }
  377.  
  378.     private boolean isQueueOpen() {
  379.         synchronized (downloadQueue) {
  380.             return downloadQueue.size() < QUEUE_SIZE;
  381.         }
  382.     }
  383.  
  384.     /**
  385.      * Handle action Baz in the provided background thread with the provided
  386.      * parameters.
  387.      */
  388.     private void handleActionBaz(String... ids) {
  389.         Realm realm = Realm.getDefaultInstance();
  390.         for (String id : ids) {
  391.             Download download = Utils.getDownloadById(realm, id);
  392.             download.setStatus(Download.PAUSED);
  393.         }
  394.         checkDownloadQueue();
  395.         realm.close();
  396.     }
  397.  
  398.     private class Downloader extends BaseDownloader {
  399.         public Downloader(String id) {
  400.             super(id);
  401.         }
  402.  
  403.         @Override
  404.         protected void onPostExecute(final Integer status) {
  405.             super.onPostExecute(status);
  406.             Realm realm = Realm.getDefaultInstance();
  407.             final Download download = Utils.getDownloadById(realm, getId());
  408.             realm.executeTransaction(new Realm.Transaction() {
  409.                 @Override
  410.                 public void execute(Realm realm) {
  411.                     download.setStatus(status);
  412.                     realm.copyToRealmOrUpdate(download);
  413.                 }
  414.             });
  415.             realm.close();
  416.             checkDownloadQueue();
  417.         }
  418.  
  419.         @Override
  420.         protected void onProgressUpdate(DownloadProgress... values) {
  421.             super.onProgressUpdate(values);
  422.             Realm realm = Realm.getDefaultInstance();
  423.             final DownloadProgress progress = values[0];
  424.             final Download download = Utils.getDownloadById(realm, getId());
  425.             Log.d(TAG, "onProgressUpdate: " + progress);
  426.             realm.executeTransaction(new Realm.Transaction() {
  427.                 @Override
  428.                 public void execute(Realm realm) {
  429.                     download.updateProgress(progress);
  430.                     realm.copyToRealmOrUpdate(download);
  431.                 }
  432.             });
  433.             realm.close();
  434.         }
  435.     }
  436. }
Add Comment
Please, Sign In to add comment