package com.novoda.downloadmanager.lib; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; /** * Class used to query the batches table. Use {@link BatchQuery.Builder} in order to build it. */ public class BatchQuery { public static final BatchQuery ALL = new BatchQuery(null, null, null); private static final int LOW_END_FAILED_STATUS_CODE = 400; private static final int HIGH_END_FAILED_STATUS_CODE = 600; private final String selection; private final String sortOrder; private final String[] selectionArguments; BatchQuery(String selection, String[] selectionArguments, String sortOrder) { this.selection = selection; this.sortOrder = sortOrder; this.selectionArguments = selectionArguments; } String getSelection() { return selection; } String getSortOrder() { return sortOrder; } String[] getSelectionArguments() { return selectionArguments; } public static class Builder { private final Criteria.Builder builder; private static final String ORDER_BY_LIVENESS = "CASE " + DownloadContract.Batches.COLUMN_STATUS + " " + "WHEN " + DownloadStatus.RUNNING + " THEN 1 " + "WHEN " + DownloadStatus.PENDING + " THEN 2 " + "WHEN " + DownloadStatus.PAUSED_BY_APP + " THEN 3 " + "WHEN " + DownloadStatus.BATCH_FAILED + " THEN 4 " + "WHEN " + DownloadStatus.SUCCESS + " THEN 5 " + "ELSE 6 " + "END, " + DownloadContract.Batches._ID + " ASC"; private Criteria.Builder criteriaIdBuilder; private Criteria.Builder criteriaStatusBuilder; private Criteria.Builder criteriaExtraDataBuilder; private Criteria.Builder criteriaNoDeletionBuilder; public Builder() { builder = new Criteria.Builder(); } /** * Filter by batch id * * @param id id of the batch * @return {@link BatchQuery.Builder} */ public Builder withId(long id) { this.criteriaIdBuilder = new Criteria.Builder(); criteriaIdBuilder .withSelection(DownloadContract.Batches._ID, Criteria.Wildcard.EQUALS) .withArgument(String.valueOf(id)); return this; } /** * Sorting in ascending order by sort column * * @param sortColumn sort column * @return {@link BatchQuery.Builder} */ public Builder withSortAscendingBy(String sortColumn) { builder.sortBy(sortColumn).ascending(); return this; } /** * Sorting in descending order by sort column * * @param sortColumn sort column * @return {@link BatchQuery.Builder} */ public Builder withSortDescendingBy(String sortColumn) { builder.sortBy(sortColumn).descending(); return this; } /** * Sorts batches according to the 'liveness' of the download, i.e. in the order: * Downloading, queued, other, paused, failed, completed * * @return {@link BatchQuery.Builder} */ public Builder withSortByLiveness() { builder.sortBy(ORDER_BY_LIVENESS); return this; } /** * Filter by status * * @param statusFlags status flags that can be combined with "|" * one of {@link DownloadManager#STATUS_PAUSED}, * {@link DownloadManager#STATUS_PAUSING} * {@link DownloadManager#STATUS_FAILED}, * {@link DownloadManager#STATUS_PENDING}, * {@link DownloadManager#STATUS_SUCCESSFUL}, * {@link DownloadManager#STATUS_RUNNING} * {@link DownloadManager#STATUS_DELETING} * <p/> * e.g. withStatusFilter(DownloadManager.STATUS_FAILED | DownloadManager.STATUS_PENDING) * @return {@link BatchQuery.Builder} */ public Builder withStatusFilter(@Status int statusFlags) { this.criteriaStatusBuilder = new Criteria.Builder() .joinWithOr(buildCriteriaListFrom(statusFlags)); return this; } /** * Filter by extra data. * * @return {@link BatchQuery.Builder} */ public Builder withExtraData(String extraData) { this.criteriaExtraDataBuilder = new Criteria.Builder(); criteriaExtraDataBuilder .withSelection(DownloadContract.Batches.COLUMN_EXTRA_DATA, Criteria.Wildcard.EQUALS) .withArgument(extraData); return this; } public Builder withoutDeleted() { this.criteriaNoDeletionBuilder = new Criteria.Builder(); criteriaNoDeletionBuilder .withSelection(DownloadContract.Batches.COLUMN_DELETED, Criteria.Wildcard.NOT_EQUALS) .withArgument(String.valueOf(DownloadContract.Batches.BATCH_DELETED)); return this; } @NonNull private List<Criteria> buildCriteriaListFrom(@Status int statusFlags) { List<Criteria> criteriaList = new ArrayList<>(); if ((statusFlags & DownloadManager.STATUS_PENDING) != 0) { Criteria pendingCriteria = new Criteria.Builder() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.EQUALS) .withArgument(String.valueOf(DownloadStatus.PENDING)) .or() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.EQUALS) .withArgument(String.valueOf(DownloadStatus.SUBMITTED)) .build(); criteriaList.add(pendingCriteria); } if ((statusFlags & DownloadManager.STATUS_RUNNING) != 0) { Criteria runningCriteria = new Criteria.Builder() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.EQUALS) .withArgument(String.valueOf(DownloadStatus.RUNNING)) .build(); criteriaList.add(runningCriteria); } if ((statusFlags & DownloadManager.STATUS_PAUSING) != 0) { Criteria runningCriteria = new Criteria.Builder() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.EQUALS) .withArgument(String.valueOf(DownloadStatus.PAUSING)) .build(); criteriaList.add(runningCriteria); } if ((statusFlags & DownloadManager.STATUS_PAUSED) != 0) { Criteria pausedCriteria = new Criteria.Builder() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.EQUALS) .withArgument(String.valueOf(DownloadStatus.PAUSED_BY_APP)) .or() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.EQUALS) .withArgument(String.valueOf(DownloadStatus.WAITING_TO_RETRY)) .or() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.EQUALS) .withArgument(String.valueOf(DownloadStatus.WAITING_FOR_NETWORK)) .or() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.EQUALS) .withArgument(String.valueOf(DownloadStatus.QUEUED_FOR_WIFI)) .or() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.EQUALS) .withArgument(String.valueOf(DownloadStatus.QUEUED_DUE_CLIENT_RESTRICTIONS)) .build(); criteriaList.add(pausedCriteria); } if ((statusFlags & DownloadManager.STATUS_DELETING) != 0) { Criteria deletingCriteria = new Criteria.Builder() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.EQUALS) .withArgument(String.valueOf(DownloadStatus.DELETING)) .build(); criteriaList.add(deletingCriteria); } if ((statusFlags & DownloadManager.STATUS_SUCCESSFUL) != 0) { Criteria successfulCriteria = new Criteria.Builder() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.EQUALS) .withArgument(String.valueOf(DownloadStatus.SUCCESS)) .build(); criteriaList.add(successfulCriteria); } if ((statusFlags & DownloadManager.STATUS_FAILED) != 0) { Criteria failedCriteria = new Criteria.Builder() .withInnerCriteria( new Criteria.Builder() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.MORE_THAN_EQUAL) .withArgument(String.valueOf(LOW_END_FAILED_STATUS_CODE)) .and() .withSelection(DownloadContract.Batches.COLUMN_STATUS, Criteria.Wildcard.LESS_THAN) .withArgument(String.valueOf(HIGH_END_FAILED_STATUS_CODE)) .build()) .build(); criteriaList.add(failedCriteria); } return criteriaList; } public BatchQuery build() { List<Criteria.Builder> criteriaBuilders = combineCriteriaBuilders(); setCriteriaListFrom(criteriaBuilders); Criteria criteria = builder.build(); String selection = criteria.getSelection(); String sortOrder = criteria.getSort(); String[] selectionArguments = criteria.getSelectionArguments(); return new BatchQuery(selection, selectionArguments, sortOrder); } private void setCriteriaListFrom(List<Criteria.Builder> criteriaBuilders) { for (Criteria.Builder criteriaBuilder : criteriaBuilders) { builder.withInnerCriteria(criteriaBuilder.build()); if (isNotLast(criteriaBuilder, criteriaBuilders)) { builder.and(); } } } private boolean isNotLast(Criteria.Builder criteriaBuilder, List<Criteria.Builder> criteriaBuilders) { int lastIndex = criteriaBuilders.size() - 1; return criteriaBuilders.indexOf(criteriaBuilder) != lastIndex; } private List<Criteria.Builder> combineCriteriaBuilders() { List<Criteria.Builder> criteriaBuilderList = new ArrayList<>(); if (criteriaIdBuilder != null) { criteriaBuilderList.add(criteriaIdBuilder); } if (criteriaStatusBuilder != null) { criteriaBuilderList.add(criteriaStatusBuilder); } if (criteriaExtraDataBuilder != null) { criteriaBuilderList.add(criteriaExtraDataBuilder); } if (criteriaNoDeletionBuilder != null) { criteriaBuilderList.add(criteriaNoDeletionBuilder); } return criteriaBuilderList; } } @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, value = {DownloadManager.STATUS_PENDING, DownloadManager.STATUS_RUNNING, DownloadManager.STATUS_PAUSING, DownloadManager.STATUS_PAUSED, DownloadManager.STATUS_SUCCESSFUL, DownloadManager.STATUS_FAILED, DownloadManager.STATUS_DELETING}) public @interface Status { //marker interface that ensures the annotated fields are in within the above values } }