Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 } | |
| OLD | NEW |