/*
* Created by Angel Leon (@gubatron), Alden Torres (aldenml)
* Copyright (c) 2011-2013, FrostWire(R). All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.bt.download.android.gui.transfers;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import com.bt.download.android.R;
import com.bt.download.android.core.ConfigurationManager;
import com.bt.download.android.core.Constants;
import com.bt.download.android.core.FileDescriptor;
import com.bt.download.android.gui.NetworkManager;
import com.bt.download.android.gui.Peer;
import com.bt.download.android.gui.services.Engine;
import com.bt.download.android.gui.util.UIUtils;
import com.frostwire.bittorrent.BTDownload;
import com.frostwire.bittorrent.BTEngine;
import com.frostwire.bittorrent.BTEngineAdapter;
import com.frostwire.logging.Logger;
import com.frostwire.search.HttpSearchResult;
import com.frostwire.search.SearchResult;
import com.frostwire.search.soundcloud.SoundcloudSearchResult;
import com.frostwire.search.torrent.TorrentCrawledSearchResult;
import com.frostwire.search.torrent.TorrentSearchResult;
import com.frostwire.search.youtube.YouTubeCrawledSearchResult;
import com.frostwire.util.StringUtils;
import com.frostwire.uxstats.UXAction;
import com.frostwire.uxstats.UXStats;
/**
* @author gubatron
* @author aldenml
*
*/
public final class TransferManager {
private static final Logger LOG = Logger.getLogger(TransferManager.class);
private final List<DownloadTransfer> downloads;
private final List<UploadTransfer> uploads;
private final List<BittorrentDownload> bittorrentDownloads;
private int downloadsToReview;
private final Object alreadyDownloadingMonitor = new Object();
private volatile static TransferManager instance;
private OnSharedPreferenceChangeListener preferenceListener;
public static TransferManager instance() {
if (instance == null) {
instance = new TransferManager();
}
return instance;
}
private TransferManager() {
registerPreferencesChangeListener();
this.downloads = new CopyOnWriteArrayList<DownloadTransfer>();
this.uploads = new CopyOnWriteArrayList<UploadTransfer>();
this.bittorrentDownloads = new CopyOnWriteArrayList<BittorrentDownload>();
this.downloadsToReview = 0;
loadTorrents();
}
public List<Transfer> getTransfers() {
List<Transfer> transfers = new ArrayList<Transfer>();
if (downloads != null) {
transfers.addAll(downloads);
}
if (uploads != null) {
transfers.addAll(uploads);
}
if (bittorrentDownloads != null) {
transfers.addAll(bittorrentDownloads);
}
return transfers;
}
private boolean alreadyDownloading(String detailsUrl) {
synchronized (alreadyDownloadingMonitor) {
for (DownloadTransfer dt : downloads) {
if (dt.isDownloading()) {
if (dt.getDetailsUrl() != null && dt.getDetailsUrl().equals(detailsUrl)) {
return true;
}
}
}
}
return false;
}
private boolean isDownloadingTorrentByUri(String uri) {
synchronized (alreadyDownloadingMonitor) {
for (DownloadTransfer dt : downloads) {
if (dt instanceof TorrentFetcherDownload) {
String torrentUri = ((TorrentFetcherDownload) dt).getTorrentUri();
if (torrentUri != null && torrentUri.equals(uri)) {
return true;
}
}
}
}
return false;
}
public DownloadTransfer download(SearchResult sr) {
DownloadTransfer transfer = null;
if (alreadyDownloading(sr.getDetailsUrl())) {
transfer = new ExistingDownload();
}
if (sr instanceof TorrentSearchResult) {
transfer = newBittorrentDownload((TorrentSearchResult) sr);
} else if (sr instanceof HttpSlideSearchResult) {
transfer = newHttpDownload((HttpSlideSearchResult) sr);
} else if (sr instanceof YouTubeCrawledSearchResult) {
transfer = newYouTubeDownload((YouTubeCrawledSearchResult) sr);
} else if (sr instanceof SoundcloudSearchResult) {
transfer = newSoundcloudDownload((SoundcloudSearchResult) sr);
} else if (sr instanceof HttpSearchResult) {
transfer = newHttpDownload((HttpSearchResult) sr);
}
return transfer;
}
public DownloadTransfer download(Peer peer, FileDescriptor fd) {
PeerHttpDownload download = new PeerHttpDownload(this, peer, fd);
if (alreadyDownloading(download.getDetailsUrl())) {
return new ExistingDownload();
}
downloads.add(download);
download.start();
UXStats.instance().log(UXAction.WIFI_SHARING_DOWNLOAD);
return download;
}
public PeerHttpUpload upload(FileDescriptor fd) {
PeerHttpUpload upload = new PeerHttpUpload(this, fd);
uploads.add(upload);
return upload;
}
public void clearComplete() {
List<Transfer> transfers = getTransfers();
for (Transfer transfer : transfers) {
if (transfer != null && transfer.isComplete()) {
if (transfer instanceof BittorrentDownload) {
BittorrentDownload bd = (BittorrentDownload) transfer;
if (bd != null && bd.isResumable()) {
bd.cancel();
}
} else {
transfer.cancel();
}
}
}
}
public int getActiveDownloads() {
int count = 0;
for (BittorrentDownload d : bittorrentDownloads) {
if (!d.isComplete() && d.isDownloading()) {
count++;
}
}
for (DownloadTransfer d : downloads) {
if (!d.isComplete() && d.isDownloading()) {
count++;
}
}
return count;
}
public int getActiveUploads() {
int count = 0;
for (BittorrentDownload d : bittorrentDownloads) {
if (!d.isComplete() && d.isSeeding()) {
count++;
}
}
for (UploadTransfer u : uploads) {
if (!u.isComplete() && u.isUploading()) {
count++;
}
}
return count;
}
public long getDownloadsBandwidth() {
long torrentDownloadsBandwidth = BTEngine.getInstance().getDownloadRate();
long peerDownloadsBandwidth = 0;
for (DownloadTransfer d : downloads) {
peerDownloadsBandwidth += d.getDownloadSpeed() / 1000;
}
return torrentDownloadsBandwidth + peerDownloadsBandwidth;
}
public double getUploadsBandwidth() {
long torrentUploadsBandwidth = BTEngine.getInstance().getUploadRate();
long peerUploadsBandwidth = 0;
for (UploadTransfer u : uploads) {
peerUploadsBandwidth += u.getUploadSpeed() / 1000;
}
return torrentUploadsBandwidth + peerUploadsBandwidth;
}
public int getDownloadsToReview() {
return downloadsToReview;
}
public void incrementDownloadsToReview() {
downloadsToReview++;
}
public void clearDownloadsToReview() {
downloadsToReview = 0;
}
public void stopSeedingTorrents() {
for (BittorrentDownload d : bittorrentDownloads) {
if (d.isSeeding() || d.isComplete()) {
d.pause();
}
}
}
public void loadTorrents() {
bittorrentDownloads.clear();
BTEngine engine = BTEngine.getInstance();
engine.setListener(new BTEngineAdapter() {
@Override
public void downloadAdded(BTEngine engine, BTDownload dl) {
String name = dl.getName();
if (name != null && name.contains("fetchMagnet - ")) {
return;
}
bittorrentDownloads.add(new UIBittorrentDownload(TransferManager.this, dl));
}
});
engine.restoreDownloads();
}
boolean remove(Transfer transfer) {
if (transfer instanceof BittorrentDownload) {
return bittorrentDownloads.remove(transfer);
} else if (transfer instanceof DownloadTransfer) {
return downloads.remove(transfer);
} else if (transfer instanceof UploadTransfer) {
return uploads.remove(transfer);
}
return false;
}
public void pauseTorrents() {
for (BittorrentDownload d : bittorrentDownloads) {
d.pause();
}
}
public BittorrentDownload downloadTorrent(String uri) {
String url = uri.trim();
try {
if (url.contains("urn%3Abtih%3A")) {
//fixes issue #129: over-encoded url coming from intent
url = url.replace("urn%3Abtih%3A", "urn:btih:");
}
URI u = URI.create(url);
BittorrentDownload download = null;
if (u.getScheme().equalsIgnoreCase("file")) {
BTEngine.getInstance().download(new File(u.getPath()), null);
} else if (u.getScheme().equalsIgnoreCase("http") || u.getScheme().equalsIgnoreCase("magnet")) {
if (!isDownloadingTorrentByUri(url)) {
download = new TorrentFetcherDownload(this, new TorrentUrlInfo(u.toString()));
bittorrentDownloads.add(download);
}
} else {
download = new InvalidBittorrentDownload(R.string.torrent_scheme_download_not_supported);
}
return download;
} catch (Throwable e) {
LOG.warn("Error creating download from uri: " + url);
return new InvalidBittorrentDownload(R.string.empty_string);
}
}
private static BittorrentDownload createBittorrentDownload(TransferManager manager, TorrentSearchResult sr) {
if (sr instanceof TorrentCrawledSearchResult) {
BTEngine.getInstance().download((TorrentCrawledSearchResult) sr, null);
} else if (sr.getTorrentUrl() != null) {
return new TorrentFetcherDownload(manager, new TorrentSearchResultInfo(sr));
}
return null;
}
private BittorrentDownload newBittorrentDownload(TorrentSearchResult sr) {
try {
createBittorrentDownload(this, sr);
return null;
} catch (Throwable e) {
LOG.warn("Error creating download from search result: " + sr);
return new InvalidBittorrentDownload(R.string.empty_string);
}
}
private HttpDownload newHttpDownload(HttpSlideSearchResult sr) {
HttpDownload download = new HttpDownload(this, sr.getDownloadLink());
downloads.add(download);
download.start();
return download;
}
private DownloadTransfer newYouTubeDownload(YouTubeCrawledSearchResult sr) {
YouTubeDownload download = new YouTubeDownload(this, sr);
downloads.add(download);
download.start();
return download;
}
private DownloadTransfer newSoundcloudDownload(SoundcloudSearchResult sr) {
SoundcloudDownload download = new SoundcloudDownload(this, sr);
downloads.add(download);
download.start();
return download;
}
private DownloadTransfer newHttpDownload(HttpSearchResult sr) {
HttpDownload download = new HttpDownload(this, new HttpSearchResultDownloadLink(sr));
downloads.add(download);
download.start();
return download;
}
private boolean isBittorrentDownload(DownloadTransfer transfer) {
return transfer instanceof UIBittorrentDownload || transfer instanceof TorrentFetcherDownload;
}
public boolean isBittorrentDownloadAndMobileDataSavingsOn(DownloadTransfer transfer) {
return isBittorrentDownload(transfer) &&
NetworkManager.instance().isDataMobileUp() &&
!ConfigurationManager.instance().getBoolean(Constants.PREF_KEY_NETWORK_USE_MOBILE_DATA);
}
public boolean isBittorrentDownloadAndMobileDataSavingsOff(DownloadTransfer transfer) {
return isBittorrentDownload(transfer) &&
NetworkManager.instance().isDataMobileUp() &&
ConfigurationManager.instance().getBoolean(Constants.PREF_KEY_NETWORK_USE_MOBILE_DATA);
}
public boolean isBittorrentDisconnected(){
return Engine.instance().isStopped() || Engine.instance().isStopping() || Engine.instance().isDisconnected();
}
public void resumeResumableTransfers() {
List<Transfer> transfers = getTransfers();
for (Transfer t : transfers) {
if (t instanceof BittorrentDownload) {
BittorrentDownload bt = (BittorrentDownload) t;
if (bt.isResumable()) {
bt.resume();
}
}
}
}
/** Stops all HttpDownloads (Cloud and Wi-Fi) */
public void stopHttpTransfers() {
List<Transfer> transfers = new ArrayList<Transfer>();
transfers.addAll(downloads);
transfers.addAll(uploads);
for (Transfer t : transfers) {
if (t instanceof DownloadTransfer) {
DownloadTransfer d = (DownloadTransfer) t;
if (!d.isComplete() && d.isDownloading()) {
d.cancel();
}
} else if (t instanceof UploadTransfer) {
UploadTransfer u = (UploadTransfer) t;
if (!u.isComplete() && u.isUploading()) {
u.cancel();
}
}
}
}
private void registerPreferencesChangeListener() {
preferenceListener = new OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
BTEngine e = BTEngine.getInstance();
if (key.equals(Constants.PREF_KEY_TORRENT_MAX_DOWNLOAD_SPEED)) {
e.setDownloadSpeedLimit((int) ConfigurationManager.instance().getLong(key));
} else if (key.equals(Constants.PREF_KEY_TORRENT_MAX_UPLOAD_SPEED)) {
e.setUploadSpeedLimit((int) ConfigurationManager.instance().getLong(key));
} else if (key.equals(Constants.PREF_KEY_TORRENT_MAX_DOWNLOADS)) {
e.setMaxActiveDownloads((int) ConfigurationManager.instance().getLong(key));
} else if (key.equals(Constants.PREF_KEY_TORRENT_MAX_UPLOADS)) {
e.setMaxActiveSeeds((int) ConfigurationManager.instance().getLong(key));
} else if (key.equals(Constants.PREF_KEY_TORRENT_MAX_TOTAL_CONNECTIONS)) {
e.setMaxConnections((int) ConfigurationManager.instance().getLong(key));
} else if (key.equals(Constants.PREF_KEY_TORRENT_MAX_PEERS)) {
e.setMaxPeers((int) ConfigurationManager.instance().getLong(key));
}
}
};
ConfigurationManager.instance().registerOnPreferenceChange(preferenceListener);
}
}