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

// Copyright 2013 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 android.graphics.Bitmap;

import androidx.annotation.NonNull;

import org.jni_zero.CalledByNative;
import org.jni_zero.JniType;

import org.chromium.chrome.browser.profiles.OTRProfileID;
import org.chromium.components.download.DownloadState;
import org.chromium.components.offline_items_collection.ContentId;
import org.chromium.components.offline_items_collection.FailState;
import org.chromium.components.offline_items_collection.LegacyHelpers;
import org.chromium.components.offline_items_collection.OfflineItem;
import org.chromium.components.offline_items_collection.OfflineItem.Progress;
import org.chromium.components.offline_items_collection.OfflineItemProgressUnit;
import org.chromium.components.offline_items_collection.OfflineItemState;
import org.chromium.components.offline_items_collection.OfflineItemVisuals;
import org.chromium.components.offline_items_collection.PendingState;
import org.chromium.url.GURL;

/** Class representing the state of a single download. */
public final class DownloadInfo {
    private final @NonNull GURL mUrl;
    private final String mUserAgent;
    private final String mMimeType;
    private final String mCookie;
    private final String mFileName;
    private final String mDescription;
    private final String mFilePath;
    private final @NonNull GURL mReferrer;
    private final @NonNull GURL mOriginalUrl;
    private final long mBytesReceived;
    private final long mBytesTotalSize;
    private final String mDownloadGuid;
    private final boolean mHasUserGesture;
    private final String mContentDisposition;
    private final boolean mIsGETRequest;
    private final Progress mProgress;
    private final long mTimeRemainingInMillis;
    private final boolean mIsResumable;
    private final boolean mIsPaused;
    private final boolean mIsOffTheRecord;
    private final OTRProfileID mOTRProfileId;
    private final boolean mIsOfflinePage;
    private final int mState;
    private final long mLastAccessTime;
    private final boolean mIsDangerous;

    // New variables to assist with the migration to OfflineItems.
    private final ContentId mContentId;
    private final boolean mIsOpenable;
    private final boolean mIsTransient;
    private final boolean mIsParallelDownload;
    private final Bitmap mIcon;
    @PendingState private final int mPendingState;
    @FailState private final int mFailState;
    private final boolean mShouldPromoteOrigin;

    private DownloadInfo(Builder builder) {
        mUrl = builder.mUrl == null ? GURL.emptyGURL() : builder.mUrl;
        mUserAgent = builder.mUserAgent;
        mMimeType = builder.mMimeType;
        mCookie = builder.mCookie;
        mFileName = builder.mFileName;
        mDescription = builder.mDescription;
        mFilePath = builder.mFilePath;
        mReferrer = builder.mReferrer == null ? GURL.emptyGURL() : builder.mReferrer;
        mOriginalUrl = builder.mOriginalUrl == null ? GURL.emptyGURL() : builder.mOriginalUrl;
        mBytesReceived = builder.mBytesReceived;
        mBytesTotalSize = builder.mBytesTotalSize;
        mDownloadGuid = builder.mDownloadGuid;
        mHasUserGesture = builder.mHasUserGesture;
        mIsGETRequest = builder.mIsGETRequest;
        mContentDisposition = builder.mContentDisposition;
        mProgress = builder.mProgress;
        mTimeRemainingInMillis = builder.mTimeRemainingInMillis;
        mIsResumable = builder.mIsResumable;
        mIsPaused = builder.mIsPaused;
        mIsOffTheRecord = builder.mIsOffTheRecord;
        mOTRProfileId = builder.mOTRProfileId;
        mIsOfflinePage = builder.mIsOfflinePage;
        mState = builder.mState;
        mLastAccessTime = builder.mLastAccessTime;
        mIsDangerous = builder.mIsDangerous;

        if (builder.mContentId != null) {
            mContentId = builder.mContentId;
        } else {
            mContentId = LegacyHelpers.buildLegacyContentId(mIsOfflinePage, mDownloadGuid);
        }
        mIsOpenable = builder.mIsOpenable;
        mIsTransient = builder.mIsTransient;
        mIsParallelDownload = builder.mIsParallelDownload;
        mIcon = builder.mIcon;
        mPendingState = builder.mPendingState;
        mFailState = builder.mFailState;
        mShouldPromoteOrigin = builder.mShouldPromoteOrigin;
    }

    public @NonNull GURL getUrl() {
        return mUrl;
    }

    public String getUserAgent() {
        return mUserAgent;
    }

    public String getMimeType() {
        return mMimeType;
    }

    public String getCookie() {
        return mCookie;
    }

    public String getFileName() {
        return mFileName;
    }

    public String getDescription() {
        return mDescription;
    }

    public String getFilePath() {
        return mFilePath;
    }

    public @NonNull GURL getReferrer() {
        return mReferrer;
    }

    public @NonNull GURL getOriginalUrl() {
        return mOriginalUrl;
    }

    public long getBytesReceived() {
        return mBytesReceived;
    }

    public long getBytesTotalSize() {
        return mBytesTotalSize;
    }

    public boolean isGETRequest() {
        return mIsGETRequest;
    }

    public String getDownloadGuid() {
        return mDownloadGuid;
    }

    public boolean hasUserGesture() {
        return mHasUserGesture;
    }

    public String getContentDisposition() {
        return mContentDisposition;
    }

    public Progress getProgress() {
        return mProgress;
    }

    /**
     * @return Remaining download time in milliseconds or -1 if it is unknown.
     */
    public long getTimeRemainingInMillis() {
        return mTimeRemainingInMillis;
    }

    public boolean isResumable() {
        return mIsResumable;
    }

    public boolean isPaused() {
        return mIsPaused;
    }

    public boolean isOffTheRecord() {
        return mIsOffTheRecord;
    }

    public OTRProfileID getOTRProfileId() {
        return mOTRProfileId;
    }

    public boolean isOfflinePage() {
        return mIsOfflinePage;
    }

    public int state() {
        return mState;
    }

    public long getLastAccessTime() {
        return mLastAccessTime;
    }

    public boolean getIsDangerous() {
        return mIsDangerous;
    }

    public ContentId getContentId() {
        return mContentId;
    }

    public boolean getIsOpenable() {
        return mIsOpenable;
    }

    public boolean getIsTransient() {
        return mIsTransient;
    }

    public boolean getIsParallelDownload() {
        return mIsParallelDownload;
    }

    public Bitmap getIcon() {
        return mIcon;
    }

    public @PendingState int getPendingState() {
        return mPendingState;
    }

    public @FailState int getFailState() {
        return mFailState;
    }

    public boolean getShouldPromoteOrigin() {
        return mShouldPromoteOrigin;
    }

    /**
     * Helper method to build a {@link DownloadInfo} from an {@link OfflineItem}.
     * @param item    The {@link OfflineItem} to mimic.
     * @param visuals The {@link OfflineItemVisuals} to mimic.
     * @return        A {@link DownloadInfo} containing the relevant fields from {@code item}.
     */
    public static DownloadInfo fromOfflineItem(OfflineItem item, OfflineItemVisuals visuals) {
        return builderFromOfflineItem(item, visuals).build();
    }

    /**
     * Helper method to build a {@link DownloadInfo.Builder} from an {@link OfflineItem}.
     * @param item    The {@link OfflineItem} to mimic.
     * @param visuals The {@link OfflineItemVisuals} to mimic.
     * @return        A {@link DownloadInfo.Builder} containing the relevant fields from
     *                {@code item}.
     */
    public static DownloadInfo.Builder builderFromOfflineItem(
            OfflineItem item, OfflineItemVisuals visuals) {
        int state;
        switch (item.state) {
            case OfflineItemState.COMPLETE:
                state = DownloadState.COMPLETE;
                break;
            case OfflineItemState.CANCELLED:
                state = DownloadState.CANCELLED;
                break;
            case OfflineItemState.INTERRUPTED:
                state = DownloadState.INTERRUPTED;
                break;
            case OfflineItemState.FAILED:
                state = DownloadState.INTERRUPTED; // TODO(dtrainor): Validate what this state is.
                break;
            case OfflineItemState.PENDING: // TODO(dtrainor): Validate what this state is.
            case OfflineItemState.IN_PROGRESS:
            case OfflineItemState.PAUSED: // TODO(dtrainor): Validate what this state is.
            default:
                state = DownloadState.IN_PROGRESS;
                break;
        }

        return new DownloadInfo.Builder()
                .setContentId(item.id)
                .setDownloadGuid(item.id.id)
                .setFileName(item.title)
                .setFilePath(item.filePath)
                .setDescription(item.description)
                .setIsTransient(item.isTransient)
                .setLastAccessTime(item.lastAccessedTimeMs)
                .setIsOpenable(item.isOpenable)
                .setMimeType(item.mimeType)
                .setUrl(item.url)
                .setOriginalUrl(item.originalUrl)
                .setOTRProfileId(OTRProfileID.deserialize(item.otrProfileId))
                .setState(state)
                .setIsPaused(item.state == OfflineItemState.PAUSED)
                .setIsResumable(item.isResumable)
                .setBytesReceived(item.receivedBytes)
                .setBytesTotalSize(item.totalSizeBytes)
                .setProgress(item.progress)
                .setTimeRemainingInMillis(item.timeRemainingMs)
                .setIsDangerous(item.isDangerous)
                .setIsParallelDownload(item.isAccelerated)
                .setIcon(visuals == null ? null : visuals.icon)
                .setPendingState(item.pendingState)
                .setFailState(item.failState)
                .setShouldPromoteOrigin(item.promoteOrigin);
    }

    /** Helper class for building the DownloadInfo object. */
    public static class Builder {
        private GURL mUrl;
        private String mUserAgent;
        private String mMimeType;
        private String mCookie;
        private String mFileName;
        private String mDescription;
        private String mFilePath;
        private GURL mReferrer;
        private GURL mOriginalUrl;
        private long mBytesReceived;
        private long mBytesTotalSize;
        private boolean mIsGETRequest;
        private String mDownloadGuid;
        private boolean mHasUserGesture;
        private String mContentDisposition;
        private Progress mProgress = Progress.createIndeterminateProgress();
        private long mTimeRemainingInMillis;
        private boolean mIsResumable = true;
        private boolean mIsPaused;
        private boolean mIsOffTheRecord;
        private OTRProfileID mOTRProfileId;
        private boolean mIsOfflinePage;
        private int mState = DownloadState.IN_PROGRESS;
        private long mLastAccessTime;
        private boolean mIsDangerous;
        private ContentId mContentId;
        private boolean mIsOpenable = true;
        private boolean mIsTransient;
        private boolean mIsParallelDownload;
        private Bitmap mIcon;
        @PendingState private int mPendingState;
        @FailState private int mFailState;
        private boolean mShouldPromoteOrigin;

        public Builder setUrl(GURL url) {
            mUrl = url;
            return this;
        }

        public Builder setUserAgent(String userAgent) {
            mUserAgent = userAgent;
            return this;
        }

        public Builder setMimeType(String mimeType) {
            mMimeType = mimeType;
            return this;
        }

        public Builder setCookie(String cookie) {
            mCookie = cookie;
            return this;
        }

        public Builder setFileName(String fileName) {
            mFileName = fileName;
            return this;
        }

        public Builder setDescription(String description) {
            mDescription = description;
            return this;
        }

        public Builder setFilePath(String filePath) {
            mFilePath = filePath;
            return this;
        }

        public Builder setReferrer(GURL referer) {
            mReferrer = referer;
            return this;
        }

        public Builder setOriginalUrl(GURL originalUrl) {
            mOriginalUrl = originalUrl;
            return this;
        }

        public Builder setBytesReceived(long bytesReceived) {
            mBytesReceived = bytesReceived;
            return this;
        }

        public Builder setBytesTotalSize(long bytesTotalSize) {
            mBytesTotalSize = bytesTotalSize;
            return this;
        }

        public Builder setIsGETRequest(boolean isGETRequest) {
            mIsGETRequest = isGETRequest;
            return this;
        }

        public Builder setDownloadGuid(String downloadGuid) {
            mDownloadGuid = downloadGuid;
            return this;
        }

        public Builder setHasUserGesture(boolean hasUserGesture) {
            mHasUserGesture = hasUserGesture;
            return this;
        }

        public Builder setContentDisposition(String contentDisposition) {
            mContentDisposition = contentDisposition;
            return this;
        }

        public Builder setProgress(OfflineItem.Progress progress) {
            mProgress = progress;
            return this;
        }

        public Builder setTimeRemainingInMillis(long timeRemainingInMillis) {
            mTimeRemainingInMillis = timeRemainingInMillis;
            return this;
        }

        public Builder setIsResumable(boolean isResumable) {
            mIsResumable = isResumable;
            return this;
        }

        public Builder setIsPaused(boolean isPaused) {
            mIsPaused = isPaused;
            return this;
        }

        public Builder setOTRProfileId(OTRProfileID otrProfileId) {
            mOTRProfileId = otrProfileId;
            mIsOffTheRecord = OTRProfileID.isOffTheRecord(otrProfileId);
            return this;
        }

        public Builder setIsOfflinePage(boolean isOfflinePage) {
            mIsOfflinePage = isOfflinePage;
            return this;
        }

        public Builder setState(int downloadState) {
            mState = downloadState;
            return this;
        }

        public Builder setLastAccessTime(long lastAccessTime) {
            mLastAccessTime = lastAccessTime;
            return this;
        }

        public Builder setIsDangerous(boolean isDangerous) {
            mIsDangerous = isDangerous;
            return this;
        }

        public Builder setContentId(ContentId contentId) {
            mContentId = contentId;
            return this;
        }

        public Builder setIsOpenable(boolean isOpenable) {
            mIsOpenable = isOpenable;
            return this;
        }

        public Builder setIsTransient(boolean isTransient) {
            mIsTransient = isTransient;
            return this;
        }

        public Builder setIsParallelDownload(boolean isParallelDownload) {
            mIsParallelDownload = isParallelDownload;
            return this;
        }

        public Builder setIcon(Bitmap icon) {
            mIcon = icon;
            return this;
        }

        public Builder setPendingState(@PendingState int pendingState) {
            mPendingState = pendingState;
            return this;
        }

        public Builder setFailState(@FailState int failState) {
            mFailState = failState;
            return this;
        }

        public Builder setShouldPromoteOrigin(boolean shouldPromoteOrigin) {
            mShouldPromoteOrigin = shouldPromoteOrigin;
            return this;
        }

        public DownloadInfo build() {
            return new DownloadInfo(this);
        }

        /**
         * Create a builder from the DownloadInfo object.
         * @param downloadInfo DownloadInfo object from which builder fields are populated.
         * @return A builder initialized with fields from downloadInfo object.
         */
        public static Builder fromDownloadInfo(final DownloadInfo downloadInfo) {
            Builder builder = new Builder();
            builder.setUrl(downloadInfo.getUrl())
                    .setUserAgent(downloadInfo.getUserAgent())
                    .setMimeType(downloadInfo.getMimeType())
                    .setCookie(downloadInfo.getCookie())
                    .setFileName(downloadInfo.getFileName())
                    .setDescription(downloadInfo.getDescription())
                    .setFilePath(downloadInfo.getFilePath())
                    .setReferrer(downloadInfo.getReferrer())
                    .setOriginalUrl(downloadInfo.getOriginalUrl())
                    .setBytesReceived(downloadInfo.getBytesReceived())
                    .setBytesTotalSize(downloadInfo.getBytesTotalSize())
                    .setDownloadGuid(downloadInfo.getDownloadGuid())
                    .setHasUserGesture(downloadInfo.hasUserGesture())
                    .setContentDisposition(downloadInfo.getContentDisposition())
                    .setIsGETRequest(downloadInfo.isGETRequest())
                    .setProgress(downloadInfo.getProgress())
                    .setTimeRemainingInMillis(downloadInfo.getTimeRemainingInMillis())
                    .setIsDangerous(downloadInfo.getIsDangerous())
                    .setIsResumable(downloadInfo.isResumable())
                    .setIsPaused(downloadInfo.isPaused())
                    .setOTRProfileId(downloadInfo.getOTRProfileId())
                    .setIsOfflinePage(downloadInfo.isOfflinePage())
                    .setState(downloadInfo.state())
                    .setLastAccessTime(downloadInfo.getLastAccessTime())
                    .setIsTransient(downloadInfo.getIsTransient())
                    .setIsParallelDownload(downloadInfo.getIsParallelDownload())
                    .setIcon(downloadInfo.getIcon())
                    .setPendingState(downloadInfo.getPendingState())
                    .setFailState(downloadInfo.getFailState())
                    .setShouldPromoteOrigin(downloadInfo.getShouldPromoteOrigin());
            return builder;
        }
    }

    @CalledByNative
    private static DownloadInfo createDownloadInfo(
            @JniType("std::string") String downloadGuid,
            @JniType("std::string") String fileName,
            @JniType("std::string") String filePath,
            @JniType("GURL") GURL url,
            @JniType("std::string") String mimeType,
            long bytesReceived,
            long bytesTotalSize,
            OTRProfileID otrProfileId,
            int state,
            int percentCompleted,
            boolean isPaused,
            boolean hasUserGesture,
            boolean isResumable,
            boolean isParallelDownload,
            @JniType("GURL") GURL originalUrl,
            @JniType("GURL") GURL referrerUrl,
            long timeRemainingInMs,
            long lastAccessTime,
            boolean isDangerous,
            @FailState int failState) {
        String remappedMimeType = MimeUtils.remapGenericMimeType(mimeType, url.getSpec(), fileName);

        Progress progress =
                new Progress(
                        bytesReceived,
                        percentCompleted == -1 ? null : bytesTotalSize,
                        OfflineItemProgressUnit.BYTES);
        return new DownloadInfo.Builder()
                .setBytesReceived(bytesReceived)
                .setBytesTotalSize(bytesTotalSize)
                .setDescription(fileName)
                .setDownloadGuid(downloadGuid)
                .setFileName(fileName)
                .setFilePath(filePath)
                .setHasUserGesture(hasUserGesture)
                .setOTRProfileId(otrProfileId)
                .setIsPaused(isPaused)
                .setIsResumable(isResumable)
                .setIsParallelDownload(isParallelDownload)
                .setMimeType(remappedMimeType)
                .setOriginalUrl(originalUrl)
                .setProgress(progress)
                .setReferrer(referrerUrl)
                .setState(state)
                .setTimeRemainingInMillis(timeRemainingInMs)
                .setLastAccessTime(lastAccessTime)
                .setIsDangerous(isDangerous)
                .setUrl(url)
                .setFailState(failState)
                .build();
    }
}