package org.fdroid.fdroid.net; import android.support.annotation.Nullable; import android.util.Log; import org.apache.commons.io.input.BoundedInputStream; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.net.bluetooth.BluetoothClient; import org.fdroid.fdroid.net.bluetooth.BluetoothConnection; import org.fdroid.fdroid.net.bluetooth.FileDetails; import org.fdroid.fdroid.net.bluetooth.httpish.Request; import org.fdroid.fdroid.net.bluetooth.httpish.Response; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; public class BluetoothDownloader extends Downloader { private static final String TAG = "BluetoothDownloader"; private final BluetoothConnection connection; private FileDetails fileDetails; private final String sourcePath; public BluetoothDownloader(String macAddress, URL sourceUrl, File destFile) throws IOException { super(sourceUrl, destFile); this.connection = new BluetoothClient(macAddress).openConnection(); this.sourcePath = sourceUrl.getPath(); } @Override protected InputStream getDownloadersInputStream() throws IOException { Request request = Request.createGET(sourcePath, connection); Response response = request.send(); fileDetails = response.toFileDetails(); // TODO: Manage the dependency which includes this class better? // Right now, I only needed the one class from apache commons. // There are countless classes online which provide this functionality, // including some which are available from the Android SDK - the only // problem is that they have a funky API which doesn't just wrap a // plain old InputStream (the class is ContentLengthInputStream - // whereas this BoundedInputStream is much more generic and useful // to us). BoundedInputStream stream = new BoundedInputStream(response.toContentStream(), fileDetails.getFileSize()); stream.setPropagateClose(false); return stream; } /** * May return null if an error occurred while getting file details. */ @Nullable private FileDetails getFileDetails() { if (fileDetails == null) { Utils.debugLog(TAG, "Going to Bluetooth \"server\" to get file details."); try { fileDetails = Request.createHEAD(sourceUrl.getPath(), connection).send().toFileDetails(); } catch (IOException e) { Log.e(TAG, "Error getting file details from Bluetooth \"server\"", e); } } return fileDetails; } @Override public boolean hasChanged() { FileDetails details = getFileDetails(); return details == null || details.getCacheTag() == null || details.getCacheTag().equals(getCacheTag()); } @Override public int totalDownloadSize() { FileDetails details = getFileDetails(); return details != null ? details.getFileSize() : -1; } @Override public void download() throws IOException, InterruptedException { downloadFromStream(1024, false); connection.closeQuietly(); } @Override public boolean isCached() { FileDetails details = getFileDetails(); return details != null && details.getCacheTag() != null && details.getCacheTag().equals(getCacheTag()); } @Override protected void close() { if (connection != null) { connection.closeQuietly(); } } }