blob: fcb8073b0eccc81eae53d544d8534e60d3423712 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// 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.home;
import android.text.TextUtils;
import org.chromium.base.Callback;
import org.chromium.base.ObserverList;
import org.chromium.base.task.PostTask;
import org.chromium.chrome.browser.download.DownloadInfo;
import org.chromium.chrome.browser.download.DownloadItem;
import org.chromium.chrome.browser.download.DownloadManagerService;
import org.chromium.chrome.browser.download.DownloadManagerService.DownloadObserver;
import org.chromium.chrome.browser.download.DownloadMetrics;
import org.chromium.chrome.browser.download.DownloadOpenSource;
import org.chromium.chrome.browser.download.DownloadUtils;
import org.chromium.components.offline_items_collection.ContentId;
import org.chromium.components.offline_items_collection.LegacyHelpers;
import org.chromium.components.offline_items_collection.OfflineContentProvider;
import org.chromium.components.offline_items_collection.OfflineContentProvider.Observer;
import org.chromium.components.offline_items_collection.OfflineItem;
import org.chromium.components.offline_items_collection.OfflineItemSchedule;
import org.chromium.components.offline_items_collection.OfflineItemShareInfo;
import org.chromium.components.offline_items_collection.ShareCallback;
import org.chromium.components.offline_items_collection.VisualsCallback;
import org.chromium.content_public.browser.UiThreadTaskTraits;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Glue class responsible for converting downloads from {@link DownloadManagerService} into some
* semblance of {@link OfflineContentProvider}. This class handles (1) all download idiosyncrasies
* that need to happen in Java before hitting the service and (2) converting from
* {@link DownloadItem}s to {@link OfflineItem}s.
*/
class LegacyDownloadProviderImpl implements DownloadObserver, LegacyDownloadProvider {
private final ArrayList<Callback<ArrayList<OfflineItem>>> mRequests = new ArrayList<>();
private final ArrayList<Callback<ArrayList<OfflineItem>>> mOffTheRecordRequests =
new ArrayList<>();
private final ObserverList<OfflineContentProvider.Observer> mObservers = new ObserverList<>();
/** Creates an instance of a {@link LegacyDownloadProvider}. */
public LegacyDownloadProviderImpl() {
DownloadManagerService.getDownloadManagerService().addDownloadObserver(this);
}
// DownloadObserver (OfflineContentProvider.Observer glue) implementation.
/** @see OfflineContentProvider.Observer#onItemsAdded(ArrayList) */
@Override
public void onDownloadItemCreated(DownloadItem item) {
if (!canShowDownloadItem(item)) return;
for (OfflineContentProvider.Observer observer : mObservers) {
observer.onItemsAdded(Collections.singletonList(DownloadItem.createOfflineItem(item)));
}
}
/** @see OfflineContentProvider.Observer#onItemUpdated(OfflineItem) */
@Override
public void onDownloadItemUpdated(DownloadItem item) {
if (!canShowDownloadItem(item)) return;
OfflineItem offlineItem = DownloadItem.createOfflineItem(item);
for (OfflineContentProvider.Observer observer : mObservers) {
observer.onItemUpdated(offlineItem, null);
}
if (offlineItem.externallyRemoved) {
PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> removeItem(offlineItem));
}
}
/** @see OfflineContentProvider.Observer#onItemRemoved(ContentId) */
@Override
public void onDownloadItemRemoved(String guid, boolean isOffTheRecord) {
for (OfflineContentProvider.Observer observer : mObservers) {
observer.onItemRemoved(LegacyHelpers.buildLegacyContentId(false, guid));
}
}
@Override
public void onAllDownloadsRetrieved(List<DownloadItem> items, boolean offTheRecord) {
List<Callback<ArrayList<OfflineItem>>> list =
offTheRecord ? mOffTheRecordRequests : mRequests;
if (list.isEmpty()) return;
ArrayList<OfflineItem> offlineItems = new ArrayList<>();
for (DownloadItem item : items) {
if (!canShowDownloadItem(item)) continue;
offlineItems.add(DownloadItem.createOfflineItem(item));
}
// Copy the list and clear the original in case the callbacks are reentrant.
List<Callback<ArrayList<OfflineItem>>> listCopy = new ArrayList<>(list);
list.clear();
for (Callback<ArrayList<OfflineItem>> callback : listCopy) callback.onResult(offlineItems);
}
@Override
public void onAddOrReplaceDownloadSharedPreferenceEntry(ContentId id) {}
// LegacyDownloadProvider implementation.
@Override
public void addObserver(Observer observer) {
mObservers.addObserver(observer);
}
@Override
public void removeObserver(Observer observer) {
mObservers.removeObserver(observer);
}
@Override
public void destroy() {
DownloadManagerService.getDownloadManagerService().removeDownloadObserver(this);
}
@Override
public void openItem(OfflineItem item) {
// TODO(shaktisahu): May be pass metrics as a param.
DownloadManagerService.getDownloadManagerService().openDownload(
item.id, item.isOffTheRecord, DownloadOpenSource.DOWNLOAD_HOME);
}
@Override
public void removeItem(OfflineItem item) {
DownloadManagerService.getDownloadManagerService().removeDownload(
item.id.id, item.isOffTheRecord, item.externallyRemoved);
FileDeletionQueue.get().delete(item.filePath);
}
@Override
public void cancelDownload(OfflineItem item) {
DownloadMetrics.recordDownloadCancel(DownloadMetrics.CancelFrom.CANCEL_DOWNLOAD_HOME);
DownloadManagerService.getDownloadManagerService().cancelDownload(
item.id, item.isOffTheRecord);
}
@Override
public void pauseDownload(OfflineItem item) {
DownloadManagerService.getDownloadManagerService().pauseDownload(
item.id, item.isOffTheRecord);
}
@Override
public void resumeDownload(OfflineItem item, boolean hasUserGesture) {
DownloadInfo.Builder builder = DownloadInfo.builderFromOfflineItem(item, null);
// This is a temporary hack to work around the assumption that the DownloadItem passed to
// DownloadManagerService#resumeDownload() will not be paused.
builder.setIsPaused(false);
DownloadItem downloadItem =
new DownloadItem(false /* useAndroidDownloadManager */, builder.build());
if (item.isResumable) {
DownloadManagerService.getDownloadManagerService().resumeDownload(
item.id, downloadItem, hasUserGesture);
} else {
DownloadManagerService.getDownloadManagerService().retryDownload(
item.id, downloadItem, hasUserGesture);
}
}
@Override
public void getItemById(ContentId id, Callback<OfflineItem> callback) {
assert false : "Not supported.";
PostTask.postTask(UiThreadTaskTraits.DEFAULT, callback.bind(null));
}
@Override
public void getAllItems(Callback<ArrayList<OfflineItem>> callback, boolean offTheRecord) {
List<Callback<ArrayList<OfflineItem>>> list =
offTheRecord ? mOffTheRecordRequests : mRequests;
list.add(callback);
if (list.size() > 1) return;
DownloadManagerService.getDownloadManagerService().getAllDownloads(offTheRecord);
}
@Override
public void getVisualsForItem(ContentId id, VisualsCallback callback) {
PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> callback.onVisualsAvailable(id, null));
}
@Override
public void getShareInfoForItem(OfflineItem item, ShareCallback callback) {
OfflineItemShareInfo info = new OfflineItemShareInfo();
info.uri = DownloadUtils.getUriForItem(item.filePath);
PostTask.postTask(
UiThreadTaskTraits.DEFAULT, () -> callback.onShareInfoAvailable(item.id, info));
}
@Override
public void renameItem(
OfflineItem item, String name, Callback</*RenameResult*/ Integer> callback) {
DownloadManagerService.getDownloadManagerService().renameDownload(
item.id, name, callback, item.isOffTheRecord);
}
@Override
public void changeSchedule(final OfflineItem item, final OfflineItemSchedule schedule) {
DownloadManagerService.getDownloadManagerService().changeSchedule(
item.id, schedule, item.isOffTheRecord);
}
/**
* There could be some situations where we can't visually represent this download in the UI.
* This should be handled in native/be more generic, but it's here in the glue for now.
* @return Whether or not {@code item} should be shown in the UI.
*/
private static boolean canShowDownloadItem(DownloadItem item) {
if (TextUtils.isEmpty(item.getDownloadInfo().getFilePath())) return false;
if (TextUtils.isEmpty(item.getDownloadInfo().getFileName())) return false;
return true;
}
}