1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.chrome.browser.download;
6 
7 import org.chromium.base.annotations.CalledByNative;
8 import org.chromium.components.download.DownloadState;
9 import org.chromium.components.download.ResumeMode;
10 import org.chromium.components.offline_items_collection.ContentId;
11 import org.chromium.components.offline_items_collection.OfflineItem;
12 import org.chromium.components.offline_items_collection.OfflineItem.Progress;
13 import org.chromium.components.offline_items_collection.OfflineItemFilter;
14 import org.chromium.components.offline_items_collection.OfflineItemState;
15 
16 /**
17  * A generic class representing a download item. The item can be either downloaded through the
18  * Android DownloadManager, or through Chrome's network stack.
19  *
20  * This represents the native DownloadItem at a specific point in time -- the native side
21  * DownloadManager must be queried for the correct status.
22  */
23 public class DownloadItem {
24     private final ContentId mContentId = new ContentId();
25     private boolean mUseAndroidDownloadManager;
26     private DownloadInfo mDownloadInfo;
27     private long mDownloadId = DownloadConstants.INVALID_DOWNLOAD_ID;
28     private long mStartTime;
29     private long mEndTime;
30     private boolean mHasBeenExternallyRemoved;
31 
DownloadItem(boolean useAndroidDownloadManager, DownloadInfo info)32     public DownloadItem(boolean useAndroidDownloadManager, DownloadInfo info) {
33         mUseAndroidDownloadManager = useAndroidDownloadManager;
34         mDownloadInfo = info;
35         if (mDownloadInfo != null) mContentId.namespace = mDownloadInfo.getContentId().namespace;
36         mContentId.id = getId();
37     }
38 
39     /**
40      * Sets the system download ID retrieved from Android DownloadManager.
41      *
42      * @param downloadId ID from the Android DownloadManager.
43      */
setSystemDownloadId(long downloadId)44     public void setSystemDownloadId(long downloadId) {
45         mDownloadId = downloadId;
46 
47         // Update our ContentId in case it changed.
48         mContentId.id = getId();
49     }
50 
51     /**
52      * @return System download ID from the Android DownloadManager.
53      */
getSystemDownloadId()54     public long getSystemDownloadId() {
55         return mDownloadId;
56     }
57 
58     /**
59      * @return A {@link ContentId} that represents this downloaded item.  The id will match
60      *         {@link #getId()}.
61      */
getContentId()62     public ContentId getContentId() {
63         return mContentId;
64     }
65 
66     /**
67      * @return String ID that uniquely identifies the download.
68      */
getId()69     public String getId() {
70         if (mUseAndroidDownloadManager) {
71             return String.valueOf(mDownloadId);
72         }
73         return mDownloadInfo.getDownloadGuid();
74     }
75 
76     /**
77      * @return Info about the download.
78      */
getDownloadInfo()79     public DownloadInfo getDownloadInfo() {
80         return mDownloadInfo;
81     }
82 
83     /**
84      * Sets the system download info.
85      *
86      * @param info Download information.
87      */
setDownloadInfo(DownloadInfo info)88     public void setDownloadInfo(DownloadInfo info) {
89         mDownloadInfo = info;
90     }
91 
92     /**
93      * Sets the download start time.
94      *
95      * @param startTime Download start time from System.currentTimeMillis().
96      */
setStartTime(long startTime)97     public void setStartTime(long startTime) {
98         mStartTime = startTime;
99     }
100 
101     /**
102      * Gets the download start time.
103      *
104      * @return Download start time from System.currentTimeMillis().
105      */
getStartTime()106     public long getStartTime() {
107         return mStartTime;
108     }
109 
110     /**
111      * Sets the download end time.
112      *
113      * @param endTime Download end time from System.currentTimeMillis().
114      */
setEndTime(long endTime)115     public void setEndTime(long endTime) {
116         mEndTime = endTime;
117     }
118 
119     /**
120      * Gets the download end time.
121      *
122      * @return Download end time from System.currentTimeMillis().
123      */
getEndTime()124     public long getEndTime() {
125         return mEndTime;
126     }
127 
128     /**
129      * Sets whether the file associated with this item has been removed through an external
130      * action.
131      *
132      * @param hasBeenExternallyRemoved Whether the file associated with this item has been removed
133      *                                 from the file system through a means other than the browser
134      *                                 download ui.
135      */
setHasBeenExternallyRemoved(boolean hasBeenExternallyRemoved)136     public void setHasBeenExternallyRemoved(boolean hasBeenExternallyRemoved) {
137         mHasBeenExternallyRemoved = hasBeenExternallyRemoved;
138     }
139 
140     /**
141      * @return Whether the file associated with this item has been removed from the file system
142      *         through a means other than the browser download ui.
143      */
hasBeenExternallyRemoved()144     public boolean hasBeenExternallyRemoved() {
145         return mHasBeenExternallyRemoved;
146     }
147 
148     /**
149      * Helper method to build an {@link OfflineItem} from a {@link DownloadItem}.
150      * @param item The {@link DownloadItem} to mimic.
151      * @return     A {@link OfflineItem} containing the relevant fields from {@code item}.
152      */
createOfflineItem(DownloadItem item)153     public static OfflineItem createOfflineItem(DownloadItem item) {
154         OfflineItem offlineItem = new OfflineItem();
155         DownloadInfo downloadInfo = item.getDownloadInfo();
156         offlineItem.id = downloadInfo.getContentId();
157         offlineItem.filePath = downloadInfo.getFilePath();
158         offlineItem.title = downloadInfo.getFileName();
159         offlineItem.description = downloadInfo.getDescription();
160         offlineItem.isTransient = downloadInfo.getIsTransient();
161         offlineItem.isAccelerated = downloadInfo.getIsParallelDownload();
162         offlineItem.isSuggested = false;
163         offlineItem.totalSizeBytes = downloadInfo.getBytesTotalSize();
164         offlineItem.receivedBytes = downloadInfo.getBytesReceived();
165         offlineItem.isResumable = downloadInfo.isResumable();
166         offlineItem.pageUrl = downloadInfo.getUrl();
167         offlineItem.originalUrl = downloadInfo.getOriginalUrl();
168         offlineItem.isOffTheRecord = downloadInfo.isOffTheRecord();
169         offlineItem.mimeType = downloadInfo.getMimeType();
170         offlineItem.progress = downloadInfo.getProgress();
171         offlineItem.timeRemainingMs = downloadInfo.getTimeRemainingInMillis();
172         offlineItem.isDangerous = downloadInfo.getIsDangerous();
173         offlineItem.pendingState = downloadInfo.getPendingState();
174         offlineItem.failState = downloadInfo.getFailState();
175         offlineItem.promoteOrigin = downloadInfo.getShouldPromoteOrigin();
176         offlineItem.lastAccessedTimeMs = downloadInfo.getLastAccessTime();
177         offlineItem.creationTimeMs = item.getStartTime();
178         offlineItem.completionTimeMs = item.getEndTime();
179         offlineItem.externallyRemoved = item.hasBeenExternallyRemoved();
180         offlineItem.canRename = item.getDownloadInfo().state() == DownloadState.COMPLETE;
181         offlineItem.schedule = downloadInfo.getOfflineItemSchedule();
182         switch (downloadInfo.state()) {
183             case DownloadState.IN_PROGRESS:
184                 offlineItem.state = downloadInfo.isPaused() ? OfflineItemState.PAUSED
185                                                             : OfflineItemState.IN_PROGRESS;
186                 break;
187             case DownloadState.COMPLETE:
188                 offlineItem.state = downloadInfo.getBytesReceived() == 0
189                         ? OfflineItemState.FAILED
190                         : OfflineItemState.COMPLETE;
191                 break;
192             case DownloadState.CANCELLED:
193                 offlineItem.state = OfflineItemState.CANCELLED;
194                 break;
195             case DownloadState.INTERRUPTED:
196                 @ResumeMode
197                 int resumeMode = DownloadUtils.getResumeMode(
198                         downloadInfo.getUrl(), downloadInfo.getFailState());
199                 if (resumeMode == ResumeMode.INVALID || resumeMode == ResumeMode.USER_RESTART) {
200                     // Fail but can restart from the beginning. The UI should let the user to retry.
201                     offlineItem.state = OfflineItemState.INTERRUPTED;
202                 }
203                 // TODO(xingliu): isDownloadPaused and isDownloadPending rely on isAutoResumable
204                 // is set correctly in {@link DownloadSharedPreferenceEntry}. The states of
205                 // notification UI and download home currently may not match. Also pending is
206                 // related to Java side auto resumption on good network condition.
207                 else if (downloadInfo.isPaused()) {
208                     offlineItem.state = OfflineItemState.PAUSED;
209                 } else if (DownloadUtils.isDownloadPending(item)) {
210                     offlineItem.state = OfflineItemState.PENDING;
211                 } else {
212                     // Unknown failure state.
213                     offlineItem.state = OfflineItemState.FAILED;
214                 }
215                 break;
216             default:
217                 assert false;
218         }
219 
220         switch (DownloadFilter.fromMimeType(downloadInfo.getMimeType())) {
221             case DownloadFilter.Type.PAGE:
222                 offlineItem.filter = OfflineItemFilter.PAGE;
223                 break;
224             case DownloadFilter.Type.VIDEO:
225                 offlineItem.filter = OfflineItemFilter.VIDEO;
226                 break;
227             case DownloadFilter.Type.AUDIO:
228                 offlineItem.filter = OfflineItemFilter.AUDIO;
229                 break;
230             case DownloadFilter.Type.IMAGE:
231                 offlineItem.filter = OfflineItemFilter.IMAGE;
232                 break;
233             case DownloadFilter.Type.DOCUMENT:
234                 offlineItem.filter = OfflineItemFilter.DOCUMENT;
235                 break;
236             case DownloadFilter.Type.OTHER:
237             default:
238                 offlineItem.filter = OfflineItemFilter.OTHER;
239                 break;
240         }
241 
242         return offlineItem;
243     }
244 
245     @CalledByNative
createDownloadItem(DownloadInfo downloadInfo, long startTimestamp, long endTimestamp, boolean hasBeenExternallyRemoved)246     private static DownloadItem createDownloadItem(DownloadInfo downloadInfo, long startTimestamp,
247             long endTimestamp, boolean hasBeenExternallyRemoved) {
248         DownloadItem downloadItem = new DownloadItem(false, downloadInfo);
249         downloadItem.setStartTime(startTimestamp);
250         downloadItem.setEndTime(endTimestamp);
251         downloadItem.setHasBeenExternallyRemoved(hasBeenExternallyRemoved);
252         return downloadItem;
253     }
254 
255     /**
256      * @return Whether or not the download has an indeterminate percentage.
257      */
isIndeterminate()258     public boolean isIndeterminate() {
259         Progress progress = getDownloadInfo().getProgress();
260         return progress == null || progress.isIndeterminate();
261     }
262 }
263