Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(341)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotifier.java

Issue 2754363004: OfflineContentProvider changes to start service (Closed)
Patch Set: Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.download.items;
6
7 import org.chromium.components.offline_items_collection.ContentId;
8 import org.chromium.components.offline_items_collection.OfflineContentProvider;
9 import org.chromium.components.offline_items_collection.OfflineItem;
10 import org.chromium.components.offline_items_collection.OfflineItemState;
11
12 import java.util.ArrayList;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.Iterator;
16 import java.util.Map;
17 import java.util.Map.Entry;
18 import java.util.Set;
19
20 /**
21 * A glue class that bridges an OfflineContentProvider with an Android notificat ion UI layer. This
22 * class assumes that the UI layer might not necessarily be ready to receive eve nts (which is true
23 * in the case of a {@link Service}), so it can queue updates until the UI is re ady.
24 */
25 public class OfflineContentAggregatorNotifier implements OfflineContentProvider. Observer {
26 /**
27 * An interface that represents the Android notification UI surface that thi s class will post
28 * events to.
29 */
30 public interface NotifierUi {
31 /**
32 * Called when this {@link OfflineContentAggregatorNotifier} needs the U I to be available
33 * because it has to send an update to it.
34 * @param onReadyEvent A {@link Runnable} that should be run when the UI becomes available.
35 * Should only be called if this method returns {@co de false}.
36 * @return Whether or not the UI is available. If not, {@co de onReadyEvent}
37 * will be notified once the UI is available.
38 */
39 boolean onUiNeeded(Runnable onReadyEvent);
40
41 /**
42 * Called when this {@link OfflineContentAggregatorNotifier} no longer e xpects to send
43 * updates to the UI in the short term and it can shut down or suspend i tself.
44 */
45 void onUiNotNeeded();
46
47 /**
48 * Called when there is an update to {@code item} that needs to be propa gated to the UI.
49 * The item might be new or might be an update to an existing {@link Off lineItem}.
50 * @param item The {@link OfflineItem} to show state for.
51 */
52 void updateItem(OfflineItem item);
53
54 /**
55 * Called when {@code id} has been removed from the underlying data sour ce and any UI should
56 * be removed.
57 * @param id The {@link ContentId} of the {@link OfflineItem} to remove from the UI.
58 */
59 void removeItem(ContentId id);
60 }
61
62 /** Any updates from {@code mProvider} that have not been propagated to {@co de mUi} yet. */
gone 2017/03/20 19:03:36 @link #mUi, etc
David Trainor- moved to gerrit 2017/03/25 03:31:13 Done.
63 private final Set<ContentId> mPendingDeadUpdates = new HashSet<>();
64
65 /** Any removals from {@code mProvider} that have not been propagated to {@c ode mUi} yet. */
66 private final Map<ContentId, OfflineItem> mPendingLiveUpdates = new HashMap< >();
67
68 /**
69 * A list of 'active' {@link OfflineItem}'s as currently known by this class . 'Active' means
gone 2017/03/20 19:03:36 no apostrophe before s
David Trainor- moved to gerrit 2017/03/25 03:31:13 Done.
70 * {@link OfflineItem#state} is {@link OfflineItemState#IN_PROGRESS} or
71 * {@link OfflineItemState#PENDING}.
72 */
73 private final Set<ContentId> mActiveItems = new HashSet<>();
74
75 private final OfflineContentProvider mProvider;
76 private final NotifierUi mUi;
77
78 /** A helper {@link Runnable} that will be called when {@code mUi} is initia lized and ready. */
79 private final Runnable mUiReadyObserver = new Runnable() {
80 @Override
81 public void run() {
82 flushPendingActions();
83 }
84 };
85
86 /**
87 * Creates an instance of {@link OfflineContentAggregatorNotifier} that will glue
88 * {@code provider} to {@code ui}.
89 * @param provider The {@link OfflineContentProvider} to expose to {@code ui }.
90 * @param ui The {@link NotifierUi} that will visually represent {@cod e provider}.
91 */
92 public OfflineContentAggregatorNotifier(OfflineContentProvider provider, Not ifierUi ui) {
93 mProvider = provider;
94 mUi = ui;
95
96 mProvider.addObserver(this);
97 }
98
99 /**
100 * Destroys this {@link OfflineContentAggregatorNotifier}. This will detach from any internal
101 * links to the glued objects specified in the constructor.
102 */
103 public void destroy() {
104 mProvider.removeObserver(this);
105 }
106
107 private void flushPendingActions() {
108 for (Iterator<ContentId> it = mPendingDeadUpdates.iterator(); it.hasNext ();) {
109 ContentId id = it.next();
110 if (!mUi.onUiNeeded(mUiReadyObserver)) break;
gone 2017/03/20 19:03:36 does this really need to happen for every single i
David Trainor- moved to gerrit 2017/03/25 03:31:13 I think the main worry is that the service could d
111
112 removeItemInternal(id);
113 it.remove();
114 }
115
116 for (Iterator<Entry<ContentId, OfflineItem>> it = mPendingLiveUpdates.en trySet().iterator();
117 it.hasNext();) {
118 Entry<ContentId, OfflineItem> item = it.next();
119 if (!mUi.onUiNeeded(mUiReadyObserver)) break;
120
121 updateItemInternal(item.getValue());
122 it.remove();
123 }
124
125 if (mActiveItems.isEmpty()) mUi.onUiNotNeeded();
126 }
127
128 private void processLiveItem(OfflineItem item) {
129 if (mUi.onUiNeeded(mUiReadyObserver)) {
130 updateItemInternal(item);
131 if (mActiveItems.isEmpty()) mUi.onUiNotNeeded();
132 } else {
133 mPendingDeadUpdates.remove(item.id);
134 mPendingLiveUpdates.put(item.id, item);
135 }
136 }
137
138 private void processDeadItem(ContentId id) {
139 if (mUi.onUiNeeded(mUiReadyObserver)) {
140 removeItemInternal(id);
141 if (mActiveItems.isEmpty()) mUi.onUiNotNeeded();
142 } else {
143 mPendingDeadUpdates.add(id);
144 mPendingLiveUpdates.remove(id);
145 }
146 }
147
148 private void updateItemInternal(OfflineItem item) {
149 switch (item.state) {
150 case OfflineItemState.IN_PROGRESS:
151 mActiveItems.add(item.id);
152 break;
153 case OfflineItemState.PENDING:
154 case OfflineItemState.COMPLETE:
155 case OfflineItemState.CANCELLED:
156 case OfflineItemState.INTERRUPTED:
157 case OfflineItemState.FAILED:
158 case OfflineItemState.PAUSED:
159 mActiveItems.remove(item.id);
160 break;
161 }
162 mUi.updateItem(item);
163 }
164
165 private void removeItemInternal(ContentId id) {
166 mActiveItems.remove(id);
167 mUi.removeItem(id);
168 }
169
170 // OfflineContentProvider.Observer implementation.
171 @Override
172 public void onItemsAvailable() {
173 // TODO(dtrainor): Query all items and push the current state to notific ations?
174 }
175
176 @Override
177 public void onItemsAdded(ArrayList<OfflineItem> items) {
178 for (int i = 0; i < items.size(); i++) {
179 OfflineItem item = items.get(i);
180
181 // Only update the UI for new OfflineItems that are in progress or p ending.
182 if (item.state == OfflineItemState.IN_PROGRESS
183 || item.state == OfflineItemState.PENDING) {
184 processLiveItem(item);
185 }
186 }
187 }
188
189 @Override
190 public void onItemRemoved(ContentId id) {
191 processDeadItem(id);
192 }
193
194 @Override
195 public void onItemUpdated(OfflineItem item) {
196 processLiveItem(item);
197 }
198 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698