content: package org.adaway.ui.update;
  
  import static android.app.DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR;
  import static android.app.DownloadManager.COLUMN_STATUS;
  import static android.app.DownloadManager.COLUMN_TOTAL_SIZE_BYTES;
  import static android.app.DownloadManager.STATUS_FAILED;
  import static android.app.DownloadManager.STATUS_RUNNING;
  import static android.app.DownloadManager.STATUS_SUCCESSFUL;
  
  import android.app.Application;
  import android.app.DownloadManager;
  import android.database.Cursor;
  
  import androidx.annotation.NonNull;
  import androidx.lifecycle.AndroidViewModel;
  import androidx.lifecycle.LiveData;
  import androidx.lifecycle.MutableLiveData;
  
  import org.adaway.AdAwayApplication;
  import org.adaway.model.update.Manifest;
  import org.adaway.model.update.UpdateModel;
  import org.adaway.ui.adware.AdwareViewModel;
  import org.adaway.util.AppExecutors;
  
  import java.util.concurrent.Executor;
  
  import timber.log.Timber;
  
  /**
   * This class is an {@link AndroidViewModel} for the {@link UpdateActivity} cards.
   *
   * @author Bruce BUJON (bruce.bujon(at)gmail(dot)com)
   */
  public class UpdateViewModel extends AdwareViewModel {
      private static final Executor NETWORK_IO = AppExecutors.getInstance().networkIO();
      private final UpdateModel updateModel;
      private final MutableLiveData<DownloadStatus> downloadProgress;
  
      public UpdateViewModel(@NonNull Application application) {
          super(application);
          this.updateModel = ((AdAwayApplication) application).getUpdateModel();
          this.downloadProgress = new MutableLiveData<>();
      }
  
      public LiveData<Manifest> getAppManifest() {
          return this.updateModel.getManifest();
      }
  
      public void update() {
          long downloadId = this.updateModel.update();
          NETWORK_IO.execute(() -> trackProgress(downloadId));
      }
  
      public MutableLiveData<DownloadStatus> getDownloadProgress() {
          return this.downloadProgress;
      }
  
      private void trackProgress(long downloadId) {
          DownloadManager downloadManager = getApplication().getSystemService(DownloadManager.class);
          DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
          boolean finishDownload = false;
          while (!finishDownload) {
              // Add wait before querying download manager
              try {
                  Thread.sleep(100);
              } catch (InterruptedException e) {
                  Timber.d(e, "Failed to wait before querying download manager.");
                  Thread.currentThread().interrupt();
                  this.downloadProgress.postValue(null);
                  return;
              }
              // Query download manager
              try (Cursor cursor = downloadManager.query(query)) {
                  if (!cursor.moveToFirst()) {
                      Timber.d("Download item was not found");
                      continue;
                  }
                  // Check download status
                  int statusColumnIndex = cursor.getColumnIndex(COLUMN_STATUS);
                  int status = cursor.getInt(statusColumnIndex);
                  switch (status) {
                      case STATUS_FAILED:
                          finishDownload = true;
                          this.downloadProgress.postValue(null);
                          break;
                      case STATUS_RUNNING:
                          int totalSizeColumnIndex = cursor.getColumnIndex(COLUMN_TOTAL_SIZE_BYTES);
                          long total = cursor.getLong(totalSizeColumnIndex);
                          if (total > 0) {
                              int bytesDownloadedColumnIndex = cursor.getColumnIndex(COLUMN_BYTES_DOWNLOADED_SO_FAR);
                              long downloaded = cursor.getLong(bytesDownloadedColumnIndex);
                              this.downloadProgress.postValue(new PendingDownloadStatus(downloaded, total));
                          }
                          break;
                      case STATUS_SUCCESSFUL:
                          this.downloadProgress.postValue(new CompleteDownloadStatus());
                          finishDownload = true;
                          break;
                      default:
                          break;
                  }
              }
          }
      }
  }
  