chromium/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadItem.java

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.download;

import org.jni_zero.CalledByNative;

import org.chromium.chrome.browser.profiles.OTRProfileID;
import org.chromium.components.download.DownloadState;
import org.chromium.components.download.ResumeMode;
import org.chromium.components.offline_items_collection.ContentId;
import org.chromium.components.offline_items_collection.OfflineItem;
import org.chromium.components.offline_items_collection.OfflineItem.Progress;
import org.chromium.components.offline_items_collection.OfflineItemFilter;
import org.chromium.components.offline_items_collection.OfflineItemState;

/**
 * A generic class representing a download item. The item can be either downloaded through the
 * Android DownloadManager, or through Chrome's network stack.
 *
 * This represents the native DownloadItem at a specific point in time -- the native side
 * DownloadManager must be queried for the correct status.
 */
public class DownloadItem {
    private final ContentId mContentId = new ContentId();
    private boolean mUseAndroidDownloadManager;
    private DownloadInfo mDownloadInfo;
    private long mDownloadId = DownloadConstants.INVALID_DOWNLOAD_ID;
    private long mStartTime;
    private long mEndTime;
    private boolean mHasBeenExternallyRemoved;

    public DownloadItem(boolean useAndroidDownloadManager, DownloadInfo info) {
        mUseAndroidDownloadManager = useAndroidDownloadManager;
        mDownloadInfo = info;
        if (mDownloadInfo != null) mContentId.namespace = mDownloadInfo.getContentId().namespace;
        mContentId.id = getId();
    }

    /**
     * Sets the system download ID retrieved from Android DownloadManager.
     *
     * @param downloadId ID from the Android DownloadManager.
     */
    public void setSystemDownloadId(long downloadId) {
        mDownloadId = downloadId;

        // Update our ContentId in case it changed.
        mContentId.id = getId();
    }

    /**
     * @return System download ID from the Android DownloadManager.
     */
    public long getSystemDownloadId() {
        return mDownloadId;
    }

    /**
     * @return A {@link ContentId} that represents this downloaded item.  The id will match
     *         {@link #getId()}.
     */
    public ContentId getContentId() {
        return mContentId;
    }

    /**
     * @return String ID that uniquely identifies the download.
     */
    public String getId() {
        if (mUseAndroidDownloadManager) {
            return String.valueOf(mDownloadId);
        }
        return mDownloadInfo.getDownloadGuid();
    }

    /**
     * @return Info about the download.
     */
    public DownloadInfo getDownloadInfo() {
        return mDownloadInfo;
    }

    /**
     * Sets the system download info.
     *
     * @param info Download information.
     */
    public void setDownloadInfo(DownloadInfo info) {
        mDownloadInfo = info;
    }

    /**
     * Sets the download start time.
     *
     * @param startTime Download start time from System.currentTimeMillis().
     */
    public void setStartTime(long startTime) {
        mStartTime = startTime;
    }

    /**
     * Gets the download start time.
     *
     * @return Download start time from System.currentTimeMillis().
     */
    public long getStartTime() {
        return mStartTime;
    }

    /**
     * Sets the download end time.
     *
     * @param endTime Download end time from System.currentTimeMillis().
     */
    public void setEndTime(long endTime) {
        mEndTime = endTime;
    }

    /**
     * Gets the download end time.
     *
     * @return Download end time from System.currentTimeMillis().
     */
    public long getEndTime() {
        return mEndTime;
    }

    /**
     * Sets whether the file associated with this item has been removed through an external
     * action.
     *
     * @param hasBeenExternallyRemoved Whether the file associated with this item has been removed
     *                                 from the file system through a means other than the browser
     *                                 download ui.
     */
    public void setHasBeenExternallyRemoved(boolean hasBeenExternallyRemoved) {
        mHasBeenExternallyRemoved = hasBeenExternallyRemoved;
    }

    /**
     * @return Whether the file associated with this item has been removed from the file system
     *         through a means other than the browser download ui.
     */
    public boolean hasBeenExternallyRemoved() {
        return mHasBeenExternallyRemoved;
    }

    /**
     * Helper method to build an {@link OfflineItem} from a {@link DownloadItem}.
     * @param item The {@link DownloadItem} to mimic.
     * @return     A {@link OfflineItem} containing the relevant fields from {@code item}.
     */
    public static OfflineItem createOfflineItem(DownloadItem item) {
        OfflineItem offlineItem = new OfflineItem();
        DownloadInfo downloadInfo = item.getDownloadInfo();
        offlineItem.id = downloadInfo.getContentId();
        offlineItem.filePath = downloadInfo.getFilePath();
        offlineItem.title = downloadInfo.getFileName();
        offlineItem.description = downloadInfo.getDescription();
        offlineItem.isTransient = downloadInfo.getIsTransient();
        offlineItem.isAccelerated = downloadInfo.getIsParallelDownload();
        offlineItem.isSuggested = false;
        offlineItem.totalSizeBytes = downloadInfo.getBytesTotalSize();
        offlineItem.receivedBytes = downloadInfo.getBytesReceived();
        offlineItem.isResumable = downloadInfo.isResumable();
        offlineItem.url = downloadInfo.getUrl();
        offlineItem.originalUrl = downloadInfo.getOriginalUrl();
        offlineItem.isOffTheRecord = downloadInfo.isOffTheRecord();
        offlineItem.otrProfileId = OTRProfileID.serialize(downloadInfo.getOTRProfileId());
        offlineItem.mimeType = downloadInfo.getMimeType();
        offlineItem.progress = downloadInfo.getProgress();
        offlineItem.timeRemainingMs = downloadInfo.getTimeRemainingInMillis();
        offlineItem.isDangerous = downloadInfo.getIsDangerous();
        offlineItem.pendingState = downloadInfo.getPendingState();
        offlineItem.failState = downloadInfo.getFailState();
        offlineItem.promoteOrigin = downloadInfo.getShouldPromoteOrigin();
        offlineItem.lastAccessedTimeMs = downloadInfo.getLastAccessTime();
        offlineItem.creationTimeMs = item.getStartTime();
        offlineItem.completionTimeMs = item.getEndTime();
        offlineItem.externallyRemoved = item.hasBeenExternallyRemoved();
        offlineItem.canRename = item.getDownloadInfo().state() == DownloadState.COMPLETE;
        switch (downloadInfo.state()) {
            case DownloadState.IN_PROGRESS:
                offlineItem.state =
                        downloadInfo.isPaused()
                                ? OfflineItemState.PAUSED
                                : OfflineItemState.IN_PROGRESS;
                break;
            case DownloadState.COMPLETE:
                offlineItem.state =
                        downloadInfo.getBytesReceived() == 0
                                ? OfflineItemState.FAILED
                                : OfflineItemState.COMPLETE;
                break;
            case DownloadState.CANCELLED:
                offlineItem.state = OfflineItemState.CANCELLED;
                break;
            case DownloadState.INTERRUPTED:
                @ResumeMode
                int resumeMode =
                        DownloadUtils.getResumeMode(
                                downloadInfo.getUrl().getSpec(), downloadInfo.getFailState());
                if (resumeMode == ResumeMode.INVALID || resumeMode == ResumeMode.USER_RESTART) {
                    // Fail but can restart from the beginning. The UI should let the user to retry.
                    offlineItem.state = OfflineItemState.INTERRUPTED;
                }
                // TODO(xingliu): isDownloadPaused and isDownloadPending rely on isAutoResumable
                // is set correctly in {@link DownloadSharedPreferenceEntry}. The states of
                // notification UI and download home currently may not match. Also pending is
                // related to Java side auto resumption on good network condition.
                else if (downloadInfo.isPaused()) {
                    offlineItem.state = OfflineItemState.PAUSED;
                } else if (DownloadUtils.isDownloadPending(item)) {
                    offlineItem.state = OfflineItemState.PENDING;
                } else {
                    // Unknown failure state.
                    offlineItem.state = OfflineItemState.FAILED;
                }
                break;
            default:
                assert false;
        }

        switch (DownloadFilter.fromMimeType(downloadInfo.getMimeType())) {
            case DownloadFilter.Type.PAGE:
                offlineItem.filter = OfflineItemFilter.PAGE;
                break;
            case DownloadFilter.Type.VIDEO:
                offlineItem.filter = OfflineItemFilter.VIDEO;
                break;
            case DownloadFilter.Type.AUDIO:
                offlineItem.filter = OfflineItemFilter.AUDIO;
                break;
            case DownloadFilter.Type.IMAGE:
                offlineItem.filter = OfflineItemFilter.IMAGE;
                break;
            case DownloadFilter.Type.DOCUMENT:
                offlineItem.filter = OfflineItemFilter.DOCUMENT;
                break;
            case DownloadFilter.Type.OTHER:
            default:
                offlineItem.filter = OfflineItemFilter.OTHER;
                break;
        }

        return offlineItem;
    }

    @CalledByNative
    private static DownloadItem createDownloadItem(
            DownloadInfo downloadInfo,
            long startTimestamp,
            long endTimestamp,
            boolean hasBeenExternallyRemoved) {
        DownloadItem downloadItem = new DownloadItem(false, downloadInfo);
        downloadItem.setStartTime(startTimestamp);
        downloadItem.setEndTime(endTimestamp);
        downloadItem.setHasBeenExternallyRemoved(hasBeenExternallyRemoved);
        return downloadItem;
    }

    /**
     * @return Whether or not the download has an indeterminate percentage.
     */
    public boolean isIndeterminate() {
        Progress progress = getDownloadInfo().getProgress();
        return progress == null || progress.isIndeterminate();
    }
}