OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser.download.items; | 5 package org.chromium.chrome.browser.download.items; |
6 | 6 |
7 import org.chromium.chrome.browser.download.DownloadInfo; | 7 import org.chromium.chrome.browser.download.DownloadInfo; |
8 import org.chromium.chrome.browser.download.DownloadItem; | 8 import org.chromium.chrome.browser.download.DownloadItem; |
9 import org.chromium.chrome.browser.download.DownloadManagerService; | |
10 import org.chromium.chrome.browser.download.DownloadNotifier; | 9 import org.chromium.chrome.browser.download.DownloadNotifier; |
11 import org.chromium.chrome.browser.download.DownloadServiceDelegate; | 10 import org.chromium.chrome.browser.download.DownloadServiceDelegate; |
12 import org.chromium.components.offline_items_collection.ContentId; | 11 import org.chromium.components.offline_items_collection.ContentId; |
13 import org.chromium.components.offline_items_collection.OfflineContentProvider; | 12 import org.chromium.components.offline_items_collection.OfflineContentProvider; |
14 import org.chromium.components.offline_items_collection.OfflineItem; | 13 import org.chromium.components.offline_items_collection.OfflineItem; |
15 import org.chromium.components.offline_items_collection.OfflineItemState; | 14 import org.chromium.components.offline_items_collection.OfflineItemState; |
| 15 import org.chromium.components.offline_items_collection.OfflineItemVisuals; |
| 16 import org.chromium.components.offline_items_collection.VisualsCallback; |
16 | 17 |
17 import java.util.ArrayList; | 18 import java.util.ArrayList; |
| 19 import java.util.HashMap; |
18 | 20 |
19 /** | 21 /** |
20 * A glue class that bridges the Profile-attached OfflineContentProvider with th
e | 22 * A glue class that bridges the Profile-attached OfflineContentProvider with th
e |
21 * download notification code (SystemDownloadNotifier and DownloadServiceDelegat
e). | 23 * download notification code (SystemDownloadNotifier and DownloadServiceDelegat
e). |
22 */ | 24 */ |
23 public class OfflineContentAggregatorNotificationBridgeUi | 25 public class OfflineContentAggregatorNotificationBridgeUi |
24 implements DownloadServiceDelegate, OfflineContentProvider.Observer { | 26 implements DownloadServiceDelegate, OfflineContentProvider.Observer, Vis
ualsCallback { |
| 27 // TODO(dtrainor): Should this just be part of the OfflineContentProvider ca
llback guarantee? |
| 28 private static final OfflineItemVisuals sEmptyOfflineItemVisuals = new Offli
neItemVisuals(); |
| 29 |
25 private final OfflineContentProvider mProvider; | 30 private final OfflineContentProvider mProvider; |
26 | 31 |
| 32 private final DownloadNotifier mUi; |
| 33 |
| 34 /** Holds a list of {@link OfflineItem} updates that are waiting for visuals
. */ |
| 35 private final HashMap<ContentId, OfflineItem> mOutstandingRequests = new Has
hMap<>(); |
| 36 |
| 37 /** |
| 38 * Holds a list of {@link OfflineItemVisuals} for all {@link OfflineItem}s t
hat are currently in |
| 39 * progress. Once an {@link OfflineItem} is no longer in progress it will b
e removed from this |
| 40 * cache. |
| 41 * TODO(dtrainor): Flush this list aggressively if we get onLowMemory/onTrim
Memory. |
| 42 * TODO(dtrainor): Add periodic clean up in case something goes wrong with t
he underlying |
| 43 * downloads. |
| 44 */ |
| 45 private final HashMap<ContentId, OfflineItemVisuals> mVisualsCache = new Has
hMap<>(); |
| 46 |
27 /** | 47 /** |
28 * Creates a new OfflineContentAggregatorNotificationBridgeUi based on {@cod
e provider}. | 48 * Creates a new OfflineContentAggregatorNotificationBridgeUi based on {@cod
e provider}. |
29 */ | 49 */ |
30 public OfflineContentAggregatorNotificationBridgeUi(OfflineContentProvider p
rovider) { | 50 public OfflineContentAggregatorNotificationBridgeUi( |
| 51 OfflineContentProvider provider, DownloadNotifier notifier) { |
31 mProvider = provider; | 52 mProvider = provider; |
| 53 mUi = notifier; |
32 | 54 |
33 mProvider.addObserver(this); | 55 mProvider.addObserver(this); |
34 } | 56 } |
35 | 57 |
36 /** | 58 /** |
37 * Destroys this class and detaches it from associated objects. | 59 * Destroys this class and detaches it from associated objects. |
38 */ | 60 */ |
39 public void destroy() { | 61 public void destroy() { |
40 mProvider.removeObserver(this); | 62 mProvider.removeObserver(this); |
41 destroyServiceDelegate(); | 63 destroyServiceDelegate(); |
42 } | 64 } |
43 | 65 |
44 /** @see OfflineContentProvider#openItem(ContentId) */ | 66 /** @see OfflineContentProvider#openItem(ContentId) */ |
45 public void openItem(ContentId id) { | 67 public void openItem(ContentId id) { |
46 mProvider.openItem(id); | 68 mProvider.openItem(id); |
47 } | 69 } |
48 | 70 |
49 // OfflineContentProvider.Observer implementation. | 71 // OfflineContentProvider.Observer implementation. |
50 @Override | 72 @Override |
51 public void onItemsAvailable() {} | 73 public void onItemsAvailable() {} |
52 | 74 |
53 @Override | 75 @Override |
54 public void onItemsAdded(ArrayList<OfflineItem> items) { | 76 public void onItemsAdded(ArrayList<OfflineItem> items) { |
55 for (int i = 0; i < items.size(); i++) { | 77 for (int i = 0; i < items.size(); i++) { |
56 OfflineItem item = items.get(i); | 78 OfflineItem item = items.get(i); |
57 | 79 if (shouldPushNewItemToUi(item)) getVisualsAndUpdateItem(item); |
58 // Only update the UI for new OfflineItems that are in progress or p
ending. | |
59 if (item.state == OfflineItemState.IN_PROGRESS | |
60 || item.state == OfflineItemState.PENDING) { | |
61 visuallyUpdateOfflineItem(item); | |
62 } | |
63 } | 80 } |
64 } | 81 } |
65 | 82 |
66 @Override | 83 @Override |
67 public void onItemRemoved(ContentId id) {} | 84 public void onItemRemoved(ContentId id) { |
| 85 mOutstandingRequests.remove(id); |
| 86 mVisualsCache.remove(id); |
| 87 mUi.notifyDownloadCanceled(id); |
| 88 } |
68 | 89 |
69 @Override | 90 @Override |
70 public void onItemUpdated(OfflineItem item) { | 91 public void onItemUpdated(OfflineItem item) { |
71 visuallyUpdateOfflineItem(item); | 92 // Assume that any item sending updates should have them reflected in th
e UI. |
| 93 getVisualsAndUpdateItem(item); |
| 94 } |
| 95 |
| 96 // OfflineContentProvider.VisualsCallback implementation. |
| 97 @Override |
| 98 public void onVisualsAvailable(ContentId id, OfflineItemVisuals visuals) { |
| 99 OfflineItem item = mOutstandingRequests.remove(id); |
| 100 if (item == null) return; |
| 101 |
| 102 if (visuals == null) visuals = sEmptyOfflineItemVisuals; |
| 103 |
| 104 // Only cache the visuals if the update we are about to push is interest
ing and we think we |
| 105 // will need them in the future. |
| 106 if (shouldCacheVisuals(item)) mVisualsCache.put(id, visuals); |
| 107 pushItemToUi(item, visuals); |
72 } | 108 } |
73 | 109 |
74 // DownloadServiceDelegate implementation. | 110 // DownloadServiceDelegate implementation. |
75 @Override | 111 @Override |
76 public void cancelDownload(ContentId id, boolean isOffTheRecord) { | 112 public void cancelDownload(ContentId id, boolean isOffTheRecord) { |
77 mProvider.cancelDownload(id); | 113 mProvider.cancelDownload(id); |
78 } | 114 } |
79 | 115 |
80 @Override | 116 @Override |
81 public void pauseDownload(ContentId id, boolean isOffTheRecord) { | 117 public void pauseDownload(ContentId id, boolean isOffTheRecord) { |
82 mProvider.pauseDownload(id); | 118 mProvider.pauseDownload(id); |
83 } | 119 } |
84 | 120 |
85 @Override | 121 @Override |
86 public void resumeDownload(ContentId id, DownloadItem item, boolean hasUserG
esture) { | 122 public void resumeDownload(ContentId id, DownloadItem item, boolean hasUserG
esture) { |
87 mProvider.resumeDownload(id); | 123 mProvider.resumeDownload(id); |
88 } | 124 } |
89 | 125 |
90 @Override | 126 @Override |
91 public void destroyServiceDelegate() {} | 127 public void destroyServiceDelegate() {} |
92 | 128 |
93 /** | 129 private void getVisualsAndUpdateItem(OfflineItem item) { |
94 * Calls into the proper {@link DownloadNotifier} by converting an {@link Of
flineItem} to a | 130 if (needsVisualsForUi(item)) { |
95 * {@link DownloadInfo}. | 131 if (!mVisualsCache.containsKey(item.id)) { |
96 * @param item The {@link OfflineItem} that needs a UI refresh. | 132 // We don't have any visuals for this item yet. Stash the curre
nt OfflineItem and, |
97 */ | 133 // if we haven't already, queue up a request for the visuals. |
98 private void visuallyUpdateOfflineItem(OfflineItem item) { | 134 // TODO(dtrainor): Check if this delay is too much. If so, just
send the update |
99 DownloadInfo info = DownloadInfo.fromOfflineItem(item); | 135 // through and we can push a new notification when the visuals a
rrive. |
100 DownloadNotifier notifier = | 136 boolean requestVisuals = !mOutstandingRequests.containsKey(item.
id); |
101 DownloadManagerService.getDownloadManagerService().getDownloadNo
tifier(); | 137 mOutstandingRequests.put(item.id, item); |
| 138 if (requestVisuals) mProvider.getVisualsForItem(item.id, this); |
| 139 return; |
| 140 } |
| 141 } else { |
| 142 // We don't need the visuals to show this item at this point. Cance
l any requests. |
| 143 mOutstandingRequests.remove(item.id); |
| 144 mVisualsCache.remove(item.id); |
| 145 } |
| 146 |
| 147 pushItemToUi(item, mVisualsCache.get(item.id)); |
| 148 // We will no longer be needing the visuals for this item after this not
ification. |
| 149 if (!shouldCacheVisuals(item)) mVisualsCache.remove(item.id); |
| 150 } |
| 151 |
| 152 private void pushItemToUi(OfflineItem item, OfflineItemVisuals visuals) { |
| 153 DownloadInfo info = DownloadInfo.fromOfflineItem(item, visuals); |
102 switch (item.state) { | 154 switch (item.state) { |
103 case OfflineItemState.IN_PROGRESS: | 155 case OfflineItemState.IN_PROGRESS: |
104 notifier.notifyDownloadProgress(info, item.creationTimeMs, item.
allowMetered); | 156 mUi.notifyDownloadProgress(info, item.creationTimeMs, item.allow
Metered); |
105 break; | 157 break; |
106 case OfflineItemState.COMPLETE: | 158 case OfflineItemState.COMPLETE: |
107 notifier.notifyDownloadSuccessful(info, -1L, false, false); | 159 mUi.notifyDownloadSuccessful(info, -1L, false, false); |
108 break; | 160 break; |
109 case OfflineItemState.CANCELLED: | 161 case OfflineItemState.CANCELLED: |
110 notifier.notifyDownloadCanceled(item.id); | 162 mUi.notifyDownloadCanceled(item.id); |
111 break; | 163 break; |
112 case OfflineItemState.INTERRUPTED: | 164 case OfflineItemState.INTERRUPTED: |
113 // TODO(dtrainor): Push the correct value for auto resume. | 165 // TODO(dtrainor): Push the correct value for auto resume. |
114 notifier.notifyDownloadInterrupted(info, true); | 166 mUi.notifyDownloadInterrupted(info, true); |
115 break; | 167 break; |
116 case OfflineItemState.PAUSED: | 168 case OfflineItemState.PAUSED: |
117 notifier.notifyDownloadPaused(info); | 169 mUi.notifyDownloadPaused(info); |
| 170 break; |
| 171 case OfflineItemState.FAILED: |
| 172 mUi.notifyDownloadFailed(info); |
| 173 break; |
| 174 case OfflineItemState.PENDING: |
| 175 // Not Implemented. |
118 break; | 176 break; |
119 default: | 177 default: |
120 assert false; | 178 assert false : "Unexpected OfflineItem state."; |
| 179 } |
| 180 } |
| 181 |
| 182 private boolean needsVisualsForUi(OfflineItem item) { |
| 183 switch (item.state) { |
| 184 case OfflineItemState.IN_PROGRESS: |
| 185 case OfflineItemState.PENDING: |
| 186 case OfflineItemState.COMPLETE: |
| 187 case OfflineItemState.INTERRUPTED: |
| 188 case OfflineItemState.FAILED: |
| 189 case OfflineItemState.PAUSED: |
| 190 return true; |
| 191 case OfflineItemState.CANCELLED: |
| 192 default: |
| 193 return false; |
| 194 } |
| 195 } |
| 196 |
| 197 private boolean shouldPushNewItemToUi(OfflineItem item) { |
| 198 switch (item.state) { |
| 199 case OfflineItemState.IN_PROGRESS: |
| 200 return true; |
| 201 case OfflineItemState.PENDING: |
| 202 case OfflineItemState.COMPLETE: |
| 203 case OfflineItemState.INTERRUPTED: |
| 204 case OfflineItemState.FAILED: |
| 205 case OfflineItemState.PAUSED: |
| 206 case OfflineItemState.CANCELLED: |
| 207 default: |
| 208 return false; |
| 209 } |
| 210 } |
| 211 |
| 212 private boolean shouldCacheVisuals(OfflineItem item) { |
| 213 switch (item.state) { |
| 214 case OfflineItemState.IN_PROGRESS: |
| 215 case OfflineItemState.PENDING: |
| 216 case OfflineItemState.INTERRUPTED: |
| 217 case OfflineItemState.PAUSED: |
| 218 return true; |
| 219 case OfflineItemState.FAILED: |
| 220 case OfflineItemState.COMPLETE: |
| 221 case OfflineItemState.CANCELLED: |
| 222 default: |
| 223 return false; |
121 } | 224 } |
122 } | 225 } |
123 } | 226 } |
OLD | NEW |