content: package allen.town.podcast.core.storage;
  
  import android.content.Context;
  import android.util.Log;
  
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.List;
  
  import allen.town.podcast.core.service.download.DownloadRequest;
  import allen.town.podcast.core.service.download.DownloadRequestCreator;
  import allen.town.podcast.core.service.download.DownloadService;
  import allen.town.podcast.model.feed.FeedItem;
  import allen.town.podcast.model.feed.FeedPreferences;
  import allen.town.podcast.core.pref.Prefs;
  import allen.town.podcast.core.util.FeedItemUtil;
  import allen.town.podcast.core.util.NetworkUtils;
  import allen.town.podcast.core.util.PowerUtils;
  
  /**
   * Implements the automatic download algorithm used by . This class assumes that
   * the client uses the {@link EpisodeCleanupAlgorithm}.
   */
  public class AutomaticDownloadAlgorithm {
      private static final String TAG = "DownloadAlgorithm";
  
      /**
       * Looks for undownloaded episodes in the queue or list of new items and request a download if
       * 1. Network is available
       * 2. The device is charging or the user allows auto download on battery
       * 3. There is free space in the episode cache
       * This method is executed on an internal single thread executor.
       *
       * @param context  Used for accessing the DB.
       * @return A Runnable that will be submitted to an ExecutorService.
       */
      public Runnable autoDownloadUndownloadedItems(final Context context) {
          return () -> {
  
              // true if we should auto download based on network status
              boolean networkShouldAutoDl = NetworkUtils.isAutoDownloadAllowed()
                      && Prefs.isEnableAutodownload();
  
              // true if we should auto download based on power status
              boolean powerShouldAutoDl = PowerUtils.deviceCharging(context)
                      || Prefs.isEnableAutodownloadOnBattery();
  
              // we should only auto download if both network AND power are happy
              if (networkShouldAutoDl && powerShouldAutoDl) {
  
                  Log.d(TAG, "Performing auto-dl of undownloaded episodes");
  
                  List<FeedItem> candidates;
                  final List<FeedItem> queue = DBReader.getQueue();
                  final List<FeedItem> newItems = DBReader.getNewItemsList(0, Integer.MAX_VALUE);
                  candidates = new ArrayList<>(queue.size() + newItems.size());
                  candidates.addAll(queue);
                  for (FeedItem newItem : newItems) {
                      FeedPreferences feedPrefs = newItem.getFeed().getPreferences();
                      if (feedPrefs.getAutoDownload()
                              && !candidates.contains(newItem)
                              && feedPrefs.getFilter().shouldAutoDownload(newItem)) {
                          candidates.add(newItem);
                      }
                  }
  
                  // filter items that are not auto downloadable
                  Iterator<FeedItem> it = candidates.iterator();
                  while (it.hasNext()) {
                      FeedItem item = it.next();
                      if (!item.isAutoDownloadable(System.currentTimeMillis()) || FeedItemUtil.isPlaying(item.getMedia())
                              || item.getFeed().isLocalFeed()) {
                          it.remove();
                      }
                  }
  
                  int autoDownloadableEpisodes = candidates.size();
                  int downloadedEpisodes = DBReader.getNumberOfDownloadedEpisodes();
                  int deletedEpisodes = Prefs.getEpisodeCleanupAlgorithm()
                          .makeRoomForEpisodes(context, autoDownloadableEpisodes);
                  boolean cacheIsUnlimited =
                          Prefs.getEpisodeCacheSize() == Prefs.getEpisodeCacheSizeUnlimited();
                  int episodeCacheSize = Prefs.getEpisodeCacheSize();
  
                  int episodeSpaceLeft;
                  if (cacheIsUnlimited || episodeCacheSize >= downloadedEpisodes + autoDownloadableEpisodes) {
                      episodeSpaceLeft = autoDownloadableEpisodes;
                  } else {
                      episodeSpaceLeft = episodeCacheSize - (downloadedEpisodes - deletedEpisodes);
                  }
  
                  List<FeedItem> itemsToDownload = candidates.subList(0, episodeSpaceLeft);
                  if (itemsToDownload.size() > 0) {
                      Log.d(TAG, "Enqueueing " + itemsToDownload.size() + " items for download");
  
                      List<DownloadRequest> requests = new ArrayList<>();
                      for (FeedItem episode : itemsToDownload) {
                          DownloadRequest.Builder request = DownloadRequestCreator.create(episode.getMedia());
                          request.withInitiatedByUser(false);
                          requests.add(request.build());
                      }
                      DownloadService.download(context, false, requests.toArray(new DownloadRequest[0]));
                  }
              }
          };
      }
  }
  