This commit is contained in:
Daniel Martí 2015-09-10 18:52:52 -07:00
commit 2159b3d49b
17 changed files with 281 additions and 80 deletions

View File

@ -98,7 +98,9 @@ public class RepoUpdater {
} catch (IOException e) { } catch (IOException e) {
if (downloader != null && downloader.getFile() != null) { if (downloader != null && downloader.getFile() != null) {
downloader.getFile().delete(); downloader.getFile().delete();
downloader.close();
} }
throw new UpdateException(repo, "Error getting index file from " + repo.address, e); throw new UpdateException(repo, "Error getting index file from " + repo.address, e);
} }
return downloader; return downloader;
@ -121,6 +123,8 @@ public class RepoUpdater {
// successful download, then we will have a file ready to use: // successful download, then we will have a file ready to use:
processDownloadedFile(downloader.getFile(), downloader.getCacheTag()); processDownloadedFile(downloader.getFile(), downloader.getCacheTag());
} }
downloader.close();
} }
protected void processDownloadedFile(File downloadedFile, String cacheTag) throws UpdateException { protected void processDownloadedFile(File downloadedFile, String cacheTag) throws UpdateException {
@ -278,7 +282,7 @@ public class RepoUpdater {
* actually in the index.jar itself. If no fingerprint, just store the * actually in the index.jar itself. If no fingerprint, just store the
* signing certificate */ * signing certificate */
boolean trustNewSigningCertificate = false; boolean trustNewSigningCertificate = false;
if (repo.fingerprint == null) { if (repo.fingerprint == null || TextUtils.isEmpty(repo.fingerprint)) {
// no info to check things are valid, so just Trust On First Use // no info to check things are valid, so just Trust On First Use
trustNewSigningCertificate = true; trustNewSigningCertificate = true;
} else { } else {

View File

@ -241,7 +241,7 @@ public class App extends ValueObject implements Comparable<App> {
PackageManager.GET_META_DATA); PackageManager.GET_META_DATA);
installerPackageLabel = installerAppInfo.loadLabel(pm); installerPackageLabel = installerAppInfo.loadLabel(pm);
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Could not get app info", e); Log.w(TAG, "Could not get app info: " + installerPackageName,e);
} }
} }
if (TextUtils.isEmpty(installerPackageLabel)) if (TextUtils.isEmpty(installerPackageLabel))

View File

@ -486,8 +486,12 @@ public class LocalRepoManager {
} }
public void writeIndexJar() throws IOException { public void writeIndexJar() throws IOException {
FileWriter writer;
try { try {
new IndexXmlBuilder(context, apps).build(xmlIndex); writer = new FileWriter(xmlIndex);
new IndexXmlBuilder(context, apps).build(writer);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Could not write index jar", e); Log.e(TAG, "Could not write index jar", e);
Toast.makeText(context, R.string.failed_to_create_index, Toast.LENGTH_LONG).show(); Toast.makeText(context, R.string.failed_to_create_index, Toast.LENGTH_LONG).show();
@ -512,6 +516,7 @@ public class LocalRepoManager {
bi.close(); bi.close();
jo.close(); jo.close();
bo.close(); bo.close();
writer.close();
try { try {
LocalRepoKeyStore.get(context).signZip(xmlIndexJarUnsigned, xmlIndexJar); LocalRepoKeyStore.get(context).signZip(xmlIndexJarUnsigned, xmlIndexJar);

View File

@ -108,13 +108,17 @@ public class SwapService extends Service {
public void scanForPeers() { public void scanForPeers() {
Log.d(TAG, "Scanning for nearby devices to swap with..."); Log.d(TAG, "Scanning for nearby devices to swap with...");
bonjourFinder.scan(); bonjourFinder.scan();
bluetoothFinder.scan(); bluetoothFinder.scan();
} }
public void stopScanningForPeers() { public void stopScanningForPeers() {
bonjourFinder.cancel(); bonjourFinder.cancel();
bluetoothFinder.cancel(); bluetoothFinder.cancel();
} }
@ -452,7 +456,7 @@ public class SwapService extends Service {
} }
public boolean isBluetoothDiscoverable() { public boolean isBluetoothDiscoverable() {
return bluetoothSwap.isConnected(); return bluetoothSwap.isDiscoverable();
} }
public boolean isBonjourDiscoverable() { public boolean isBonjourDiscoverable() {
@ -530,10 +534,11 @@ public class SwapService extends Service {
filter.addAction(WIFI_STATE_CHANGE); filter.addAction(WIFI_STATE_CHANGE);
LocalBroadcastManager.getInstance(this).registerReceiver(receiveSwapStatusChanged, filter); LocalBroadcastManager.getInstance(this).registerReceiver(receiveSwapStatusChanged, filter);
/**
if (wasBluetoothEnabled()) { if (wasBluetoothEnabled()) {
Log.d(TAG, "Previously the user enabled Bluetooth swap, so enabling again automatically."); Log.d(TAG, "Previously the user enabled Bluetooth swap, so enabling again automatically.");
bluetoothSwap.startInBackground(); bluetoothSwap.startInBackground();
} }*/
if (wasWifiEnabled()) { if (wasWifiEnabled()) {
Log.d(TAG, "Previously the user enabled Wifi swap, so enabling again automatically."); Log.d(TAG, "Previously the user enabled Wifi swap, so enabling again automatically.");
@ -574,7 +579,7 @@ public class SwapService extends Service {
public void disableAllSwapping() { public void disableAllSwapping() {
Log.i(TAG, "Asked to stop swapping, will stop bluetooth, wifi, and move service to BG for GC."); Log.i(TAG, "Asked to stop swapping, will stop bluetooth, wifi, and move service to BG for GC.");
getBluetoothSwap().stopInBackground(); // getBluetoothSwap().stopInBackground();
getWifiSwap().stopInBackground(); getWifiSwap().stopInBackground();
// Ensure the user is sent back go the first screen when returning if we have just forceably // Ensure the user is sent back go the first screen when returning if we have just forceably

View File

@ -1,6 +1,7 @@
package org.fdroid.fdroid.localrepo.peers; package org.fdroid.fdroid.localrepo.peers;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
@ -14,6 +15,8 @@ public class BluetoothFinder extends PeerFinder<BluetoothPeer> {
private static final String TAG = "BluetoothFinder"; private static final String TAG = "BluetoothFinder";
public final static int DISCOVERABLE_TIMEOUT = 3600;
private final BluetoothAdapter adapter; private final BluetoothAdapter adapter;
public BluetoothFinder(Context context) { public BluetoothFinder(Context context) {
@ -92,9 +95,17 @@ public class BluetoothFinder extends PeerFinder<BluetoothPeer> {
} }
private void onDeviceFound(BluetoothDevice device) { private void onDeviceFound(BluetoothDevice device) {
if (device != null && device.getName() != null && device.getName().startsWith(BluetoothSwap.BLUETOOTH_NAME_TAG)) {
if(device != null && device.getName() != null &&
(device.getBluetoothClass().getDeviceClass() == BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA||
device.getBluetoothClass().getDeviceClass() == BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA||
device.getBluetoothClass().getDeviceClass() == BluetoothClass.Device.PHONE_SMART))
{
foundPeer(new BluetoothPeer(device)); foundPeer(new BluetoothPeer(device));
} }
// if (device != null && device.getName() != null && device.getName().startsWith(BluetoothSwap.BLUETOOTH_NAME_TAG)) {
// foundPeer(new BluetoothPeer(device));
// }
} }
} }

View File

@ -5,6 +5,7 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.os.AsyncTask;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
@ -17,8 +18,12 @@ public class BluetoothSwap extends SwapType {
private static final String TAG = "BluetoothBroadcastType"; private static final String TAG = "BluetoothBroadcastType";
public final static String BLUETOOTH_NAME_TAG = "FDroid:"; public final static String BLUETOOTH_NAME_TAG = "FDroid:";
private static BluetoothSwap mInstance = null;
@NonNull @NonNull
private final BluetoothAdapter adapter; private final BluetoothAdapter adapter;
private BroadcastReceiver receiver;
private boolean isDiscoverable = false;
@Nullable @Nullable
private BluetoothServer server; private BluetoothServer server;
@ -30,7 +35,10 @@ public class BluetoothSwap extends SwapType {
if (adapter == null) { if (adapter == null) {
return new NoBluetoothType(context); return new NoBluetoothType(context);
} else { } else {
return new BluetoothSwap(context, adapter); if (mInstance == null)
mInstance = new BluetoothSwap(context, adapter);
return mInstance;
} }
} }
@ -38,24 +46,12 @@ public class BluetoothSwap extends SwapType {
super(context); super(context);
this.adapter = adapter; this.adapter = adapter;
context.registerReceiver(new BroadcastReceiver() {
}
@Override @Override
public void onReceive(Context context, Intent intent) { public boolean isDiscoverable () {
switch (intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, -1)) { return isDiscoverable;
case BluetoothAdapter.SCAN_MODE_NONE:
setConnected(false);
break;
case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
if (server != null && server.isRunning()) {
setConnected(true);
}
break;
// Only other is BluetoothAdapter.SCAN_MODE_CONNECTABLE. For now don't handle that.
}
}
}, new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED));
} }
@Override @Override
@ -64,13 +60,40 @@ public class BluetoothSwap extends SwapType {
} }
@Override @Override
public void start() { public synchronized void start() {
if (isConnected())
return;
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, -1)) {
case BluetoothAdapter.SCAN_MODE_NONE:
setConnected(false);
break;
case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
isDiscoverable = true;
if (server != null && server.isRunning()) {
setConnected(true);
}
break;
// Only other is BluetoothAdapter.SCAN_MODE_CONNECTABLE. For now don't handle that.
}
}
};
context.registerReceiver(receiver, new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED));
/*
if (server != null) { if (server != null) {
Log.d(TAG, "Attempting to start Bluetooth swap, but it appears to be running already. Will cancel it so it can be restarted."); Log.d(TAG, "Attempting to start Bluetooth swap, but it appears to be running already. Will cancel it so it can be restarted.");
server.close(); server.close();
server = null; server = null;
} }*/
if (server == null)
server = new BluetoothServer(this, context.getFilesDir()); server = new BluetoothServer(this, context.getFilesDir());
sendBroadcast(SwapService.EXTRA_STARTING); sendBroadcast(SwapService.EXTRA_STARTING);
@ -78,6 +101,7 @@ public class BluetoothSwap extends SwapType {
//store the original bluetoothname, and update this one to be unique //store the original bluetoothname, and update this one to be unique
deviceBluetoothName = adapter.getName(); deviceBluetoothName = adapter.getName();
/*
Log.d(TAG, "Prefixing Bluetooth adapter name with " + BLUETOOTH_NAME_TAG + " to make it identifiable as a swap device."); Log.d(TAG, "Prefixing Bluetooth adapter name with " + BLUETOOTH_NAME_TAG + " to make it identifiable as a swap device.");
if (!deviceBluetoothName.startsWith(BLUETOOTH_NAME_TAG)) if (!deviceBluetoothName.startsWith(BLUETOOTH_NAME_TAG))
adapter.setName(BLUETOOTH_NAME_TAG + deviceBluetoothName); adapter.setName(BLUETOOTH_NAME_TAG + deviceBluetoothName);
@ -85,7 +109,7 @@ public class BluetoothSwap extends SwapType {
if (!adapter.getName().startsWith(BLUETOOTH_NAME_TAG)) { if (!adapter.getName().startsWith(BLUETOOTH_NAME_TAG)) {
Log.e(TAG, "Couldn't change the name of the Bluetooth adapter, it will not get recognized by other swap clients."); Log.e(TAG, "Couldn't change the name of the Bluetooth adapter, it will not get recognized by other swap clients.");
// TODO: Should we bail here? // TODO: Should we bail here?
} }*/
if (!adapter.isEnabled()) { if (!adapter.isEnabled()) {
Log.d(TAG, "Bluetooth adapter is disabled, attempting to enable."); Log.d(TAG, "Bluetooth adapter is disabled, attempting to enable.");
@ -96,8 +120,8 @@ public class BluetoothSwap extends SwapType {
} }
} }
if (adapter.isEnabled()) { if (adapter.isEnabled())
server.start(); {
setConnected(true); setConnected(true);
} else { } else {
Log.i(TAG, "Didn't start Bluetooth swapping server, because Bluetooth is disabled and couldn't be enabled."); Log.i(TAG, "Didn't start Bluetooth swapping server, because Bluetooth is disabled and couldn't be enabled.");
@ -110,6 +134,11 @@ public class BluetoothSwap extends SwapType {
if (server != null && server.isAlive()) { if (server != null && server.isAlive()) {
server.close(); server.close();
setConnected(false); setConnected(false);
// if (receiver != null) {
// context.unregisterReceiver(receiver);
// receiver = null;
// }
} else { } else {
Log.i(TAG, "Attempting to stop Bluetooth swap, but it is not currently running."); Log.i(TAG, "Attempting to stop Bluetooth swap, but it is not currently running.");
} }
@ -141,5 +170,6 @@ public class BluetoothSwap extends SwapType {
protected String getBroadcastAction() { protected String getBroadcastAction() {
return null; return null;
} }
} }
} }

View File

@ -33,6 +33,11 @@ public abstract class SwapType {
abstract protected String getBroadcastAction(); abstract protected String getBroadcastAction();
public boolean isDiscoverable ()
{
return isConnected();
}
protected final void setConnected(boolean connected) { protected final void setConnected(boolean connected) {
if (connected) { if (connected) {
isConnected = true; isConnected = true;
@ -64,13 +69,15 @@ public abstract class SwapType {
} }
public void startInBackground() { public void startInBackground() {
start();
/**
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
start(); start();
return null; return null;
} }
}.execute(); }.execute();*/
} }
public void ensureRunning() { public void ensureRunning() {

View File

@ -3,6 +3,7 @@ package org.fdroid.fdroid.net;
import android.content.Context; import android.content.Context;
import android.util.Log; import android.util.Log;
import org.apache.commons.io.input.BoundedInputStream; 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.BluetoothClient;
import org.fdroid.fdroid.net.bluetooth.BluetoothConnection; import org.fdroid.fdroid.net.bluetooth.BluetoothConnection;
import org.fdroid.fdroid.net.bluetooth.FileDetails; import org.fdroid.fdroid.net.bluetooth.FileDetails;
@ -13,6 +14,8 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.nio.Buffer;
import java.io.BufferedReader;
public class BluetoothDownloader extends Downloader { public class BluetoothDownloader extends Downloader {
@ -30,9 +33,11 @@ public class BluetoothDownloader extends Downloader {
@Override @Override
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
Response response = Request.createGET(sourcePath, connection).send(); Request request = Request.createGET(sourcePath, connection);
Response response = request.send();
fileDetails = response.toFileDetails(); fileDetails = response.toFileDetails();
// TODO: Manage the dependency which includes this class better? // TODO: Manage the dependency which includes this class better?
// Right now, I only needed the one class from apache commons. // Right now, I only needed the one class from apache commons.
// There are countless classes online which provide this functionality, // There are countless classes online which provide this functionality,
@ -43,6 +48,7 @@ public class BluetoothDownloader extends Downloader {
// to us). // to us).
BoundedInputStream stream = new BoundedInputStream(response.toContentStream(), fileDetails.getFileSize()); BoundedInputStream stream = new BoundedInputStream(response.toContentStream(), fileDetails.getFileSize());
stream.setPropagateClose(false); stream.setPropagateClose(false);
return stream; return stream;
} }
@ -76,7 +82,8 @@ public class BluetoothDownloader extends Downloader {
@Override @Override
public void download() throws IOException, InterruptedException { public void download() throws IOException, InterruptedException {
downloadFromStream(); downloadFromStream(1024);
connection.closeQuietly();
} }
@Override @Override
@ -89,4 +96,11 @@ public class BluetoothDownloader extends Downloader {
); );
} }
@Override
public void close ()
{
if (connection != null)
connection.closeQuietly();
}
} }

View File

@ -7,12 +7,15 @@ import android.util.Log;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Reader;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
@ -37,6 +40,7 @@ public abstract class Downloader {
protected int totalBytes = 0; protected int totalBytes = 0;
public abstract InputStream getInputStream() throws IOException; public abstract InputStream getInputStream() throws IOException;
public abstract void close();
Downloader(Context context, URL url, File destFile) Downloader(Context context, URL url, File destFile)
throws FileNotFoundException, MalformedURLException { throws FileNotFoundException, MalformedURLException {
@ -98,7 +102,7 @@ public abstract class Downloader {
public abstract boolean isCached(); public abstract boolean isCached();
protected void downloadFromStream() throws IOException, InterruptedException { protected void downloadFromStream(int bufferSize) throws IOException, InterruptedException {
Utils.DebugLog(TAG, "Downloading from stream"); Utils.DebugLog(TAG, "Downloading from stream");
InputStream input = null; InputStream input = null;
try { try {
@ -108,7 +112,7 @@ public abstract class Downloader {
// we were interrupted before proceeding to the download. // we were interrupted before proceeding to the download.
throwExceptionIfInterrupted(); throwExceptionIfInterrupted();
copyInputToOutputStream(input); copyInputToOutputStream(input, bufferSize);
} finally { } finally {
Utils.closeQuietly(outputStream); Utils.closeQuietly(outputStream);
Utils.closeQuietly(input); Utils.closeQuietly(input);
@ -143,20 +147,30 @@ public abstract class Downloader {
* keeping track of the number of bytes that have flowed through for the * keeping track of the number of bytes that have flowed through for the
* progress counter. * progress counter.
*/ */
protected void copyInputToOutputStream(InputStream input) throws IOException, InterruptedException { protected void copyInputToOutputStream(InputStream input, int bufferSize) throws IOException, InterruptedException {
byte[] buffer = new byte[Utils.BUFFER_SIZE];
int bytesRead = 0; int bytesRead = 0;
this.totalBytes = totalDownloadSize(); this.totalBytes = totalDownloadSize();
byte[] buffer = new byte[bufferSize];
// Getting the total download size could potentially take time, depending on how // Getting the total download size could potentially take time, depending on how
// it is implemented, so we may as well check this before we proceed. // it is implemented, so we may as well check this before we proceed.
throwExceptionIfInterrupted(); throwExceptionIfInterrupted();
sendProgress(bytesRead, totalBytes); sendProgress(bytesRead, totalBytes);
while (true) { while (bytesRead < totalBytes) {
int count = -1;
if (input.available()>0) {
int readLength = Math.min(input.available(), buffer.length);
count = input.read(buffer, 0, readLength);
}
else {
count = input.read(buffer);
}
int count = input.read(buffer);
throwExceptionIfInterrupted(); throwExceptionIfInterrupted();
if (count == -1) { if (count == -1) {
@ -167,8 +181,10 @@ public abstract class Downloader {
bytesRead += count; bytesRead += count;
sendProgress(bytesRead, totalBytes); sendProgress(bytesRead, totalBytes);
outputStream.write(buffer, 0, count); outputStream.write(buffer, 0, count);
} }
outputStream.flush(); outputStream.flush();
outputStream.close();
} }
protected void sendProgress(int bytesRead, int totalBytes) { protected void sendProgress(int bytesRead, int totalBytes) {

View File

@ -9,10 +9,13 @@ import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.MalformedURLException; import java.net.MalformedURLException;
@ -31,6 +34,7 @@ public class HttpDownloader extends Downloader {
protected static final String HEADER_FIELD_ETAG = "ETag"; protected static final String HEADER_FIELD_ETAG = "ETag";
protected HttpURLConnection connection; protected HttpURLConnection connection;
private InputStream stream;
private int statusCode = -1; private int statusCode = -1;
private boolean onlyStream = false; private boolean onlyStream = false;
@ -58,10 +62,16 @@ public class HttpDownloader extends Downloader {
* same one twice, bail with an exception). * same one twice, bail with an exception).
* @throws IOException * @throws IOException
*/ */
@Override
public InputStream getInputStream() throws IOException { public InputStream getInputStream() throws IOException {
setupConnection(); setupConnection();
return connection.getInputStream(); stream = new BufferedInputStream(connection.getInputStream());
return stream;
}
public BufferedReader getBufferedReader () throws IOException
{
return new BufferedReader(new InputStreamReader(getInputStream()));
} }
// Get a remote file. Returns the HTTP response code. // Get a remote file. Returns the HTTP response code.
@ -117,7 +127,7 @@ public class HttpDownloader extends Downloader {
Utils.DebugLog(TAG, sourceUrl + " is cached, so not downloading (HTTP " + statusCode + ")"); Utils.DebugLog(TAG, sourceUrl + " is cached, so not downloading (HTTP " + statusCode + ")");
} else { } else {
Utils.DebugLog(TAG, "Downloading from " + sourceUrl); Utils.DebugLog(TAG, "Downloading from " + sourceUrl);
downloadFromStream(); downloadFromStream(4096);
updateCacheCheck(); updateCacheCheck();
} }
} }
@ -159,4 +169,14 @@ public class HttpDownloader extends Downloader {
return statusCode; return statusCode;
} }
public void close ()
{
try {
if (stream != null)
stream.close();
}
catch (IOException e) {}
connection.disconnect();
}
} }

View File

@ -3,7 +3,10 @@ package org.fdroid.fdroid.net;
import android.content.Context; import android.content.Context;
import com.nostra13.universalimageloader.core.download.BaseImageDownloader; import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
import com.nostra13.universalimageloader.utils.IoUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -19,13 +22,41 @@ public class IconDownloader extends BaseImageDownloader {
@Override @Override
public InputStream getStream(String imageUri, Object extra) throws IOException { public InputStream getStream(String imageUri, Object extra) throws IOException {
switch (Scheme.ofUri(imageUri)) {
Scheme scheme = Scheme.ofUri(imageUri);
switch (scheme) {
case HTTP: case HTTP:
case HTTPS: case HTTPS:
Downloader downloader = DownloaderFactory.create(context, imageUri); Downloader downloader = DownloaderFactory.create(context, imageUri);
return downloader.getInputStream(); return downloader.getInputStream();
default: }
//bluetooth isn't a scheme in the Scheme. library, so we can add a check here
if (imageUri.toLowerCase().startsWith("bluetooth"))
{
Downloader downloader = DownloaderFactory.create(context, imageUri);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = downloader.getInputStream();
int b = -1;
while ((b = is.read())!=-1)
baos.write(b);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
downloader.close();
return bais;
}
return super.getStream(imageUri, extra); return super.getStream(imageUri, extra);
} }
}
} }

View File

@ -25,13 +25,23 @@ public class BluetoothClient {
} }
public BluetoothConnection openConnection() throws IOException { public BluetoothConnection openConnection() throws IOException {
BluetoothSocket socket = null; BluetoothSocket socket = null;
BluetoothConnection connection = null;
try { try {
socket = device.createInsecureRfcommSocketToServiceRecord(BluetoothConstants.fdroidUuid()); socket = device.createInsecureRfcommSocketToServiceRecord(BluetoothConstants.fdroidUuid());
BluetoothConnection connection = new BluetoothConnection(socket); connection = new BluetoothConnection(socket);
connection.open(); connection.open();
return connection; return connection;
} catch (IOException e1) { } catch (IOException e1) {
if (connection != null)
connection.closeQuietly();
throw e1;
/*
Log.e(TAG, "There was an error while establishing Bluetooth connection. Falling back to using reflection..."); Log.e(TAG, "There was an error while establishing Bluetooth connection. Falling back to using reflection...");
Class<?> clazz = socket.getRemoteDevice().getClass(); Class<?> clazz = socket.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[]{Integer.TYPE}; Class<?>[] paramTypes = new Class<?>[]{Integer.TYPE};
@ -41,6 +51,7 @@ public class BluetoothClient {
method = clazz.getMethod("createInsecureRfcommSocket", paramTypes); method = clazz.getMethod("createInsecureRfcommSocket", paramTypes);
Object[] params = new Object[]{1}; Object[] params = new Object[]{1};
BluetoothSocket sockFallback = (BluetoothSocket) method.invoke(socket.getRemoteDevice(), params); BluetoothSocket sockFallback = (BluetoothSocket) method.invoke(socket.getRemoteDevice(), params);
BluetoothConnection connection = new BluetoothConnection(sockFallback); BluetoothConnection connection = new BluetoothConnection(sockFallback);
connection.open(); connection.open();
return connection; return connection;
@ -50,7 +61,7 @@ public class BluetoothClient {
throw e1; throw e1;
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
throw e1; throw e1;
} }*/
// Don't catch exceptions this time, let it bubble up as we did our best but don't // Don't catch exceptions this time, let it bubble up as we did our best but don't
// have anythign else to offer in terms of resolving the problem right now. // have anythign else to offer in terms of resolving the problem right now.

View File

@ -7,6 +7,8 @@ import android.os.Build;
import android.util.Log; import android.util.Log;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -39,8 +41,8 @@ public class BluetoothConnection {
socket.connect(); socket.connect();
} }
input = socket.getInputStream(); input = new BufferedInputStream(socket.getInputStream());
output = socket.getOutputStream(); output = new BufferedOutputStream(socket.getOutputStream());
Log.d(TAG, "Opened connection to Bluetooth device"); Log.d(TAG, "Opened connection to Bluetooth device");
} }
@ -51,12 +53,6 @@ public class BluetoothConnection {
} }
public void close() throws IOException { public void close() throws IOException {
if (input == null || output == null) { closeQuietly();
throw new RuntimeException("Cannot close() a BluetoothConnection before calling open()" );
}
input.close();
output.close();
socket.close();
} }
} }

View File

@ -40,6 +40,8 @@ public class BluetoothServer extends Thread {
public BluetoothServer(BluetoothSwap swap, File webRoot) { public BluetoothServer(BluetoothSwap swap, File webRoot) {
this.webRoot = webRoot; this.webRoot = webRoot;
this.swap = swap; this.swap = swap;
start();
} }
public boolean isRunning() { return isRunning; } public boolean isRunning() { return isRunning; }
@ -81,7 +83,7 @@ public class BluetoothServer extends Thread {
try { try {
BluetoothSocket clientSocket = serverSocket.accept(); BluetoothSocket clientSocket = serverSocket.accept();
if (clientSocket != null) { if (clientSocket != null) {
if (!isInterrupted()) { if (isInterrupted()) {
Log.d(TAG, "Server stopped after socket accepted from client, but before initiating connection."); Log.d(TAG, "Server stopped after socket accepted from client, but before initiating connection.");
break; break;
} }
@ -135,6 +137,8 @@ public class BluetoothServer extends Thread {
break; break;
} }
connection.closeQuietly();
} }
private Response handleRequest(Request request) throws IOException { private Response handleRequest(Request request) throws IOException {

View File

@ -5,9 +5,14 @@ import org.fdroid.fdroid.net.bluetooth.BluetoothConnection;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -27,16 +32,16 @@ public class Request {
private Map<String, String> headers; private Map<String, String> headers;
private BluetoothConnection connection; private BluetoothConnection connection;
private BufferedWriter output; private Writer output;
private BufferedReader input; private InputStream input;
private Request(String method, String path, BluetoothConnection connection) { private Request(String method, String path, BluetoothConnection connection) {
this.method = method; this.method = method;
this.path = path; this.path = path;
this.connection = connection; this.connection = connection;
output = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream())); output = new OutputStreamWriter(connection.getOutputStream());
input = new BufferedReader(new InputStreamReader(connection.getInputStream())); input = connection.getInputStream();
} }
public static Request createHEAD(String path, BluetoothConnection connection) public static Request createHEAD(String path, BluetoothConnection connection)
@ -93,7 +98,7 @@ public class Request {
*/ */
private boolean listen() throws IOException { private boolean listen() throws IOException {
String requestLine = input.readLine(); String requestLine = readLine();
if (requestLine == null || requestLine.trim().length() == 0) if (requestLine == null || requestLine.trim().length() == 0)
return false; return false;
@ -125,11 +130,8 @@ public class Request {
* a space, and then the status label (which may contain spaces). * a space, and then the status label (which may contain spaces).
*/ */
private int readResponseCode() throws IOException { private int readResponseCode() throws IOException {
String line = input.readLine();
if (line == null) { String line = readLine();
// TODO: What to do?
return -1;
}
// TODO: Error handling // TODO: Error handling
int firstSpace = line.indexOf(' '); int firstSpace = line.indexOf(' ');
@ -139,6 +141,36 @@ public class Request {
return Integer.parseInt(status); return Integer.parseInt(status);
} }
private String readLine () throws IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String line = null;
while (line == null) {
while (input.available()>0) {
int b = input.read();
if (((char)b) == '\n') {
if (baos.size() > 0)
line = new String(baos.toByteArray());
return line;
}
baos.write(b);
}
try { Thread.sleep(100); }
catch (Exception e){};
}
return line;
}
/** /**
* Subsequent lines (after the status line) represent the headers, which are case * Subsequent lines (after the status line) represent the headers, which are case
* insensitive and may be multi-line. We don't deal with multi-line headers in * insensitive and may be multi-line. We don't deal with multi-line headers in
@ -146,15 +178,22 @@ public class Request {
*/ */
private Map<String, String> readHeaders() throws IOException { private Map<String, String> readHeaders() throws IOException {
Map<String, String> headers = new HashMap<>(); Map<String, String> headers = new HashMap<>();
String responseLine = input.readLine(); String responseLine = readLine();
while (responseLine != null && responseLine.length() > 0) { while (responseLine != null) {
// TODO: Error handling // TODO: Error handling
String[] parts = responseLine.split(":"); String[] parts = responseLine.split(":");
if (parts.length > 1) {
String header = parts[0].trim(); String header = parts[0].trim();
String value = parts[1].trim(); String value = parts[1].trim();
headers.put(header, value); headers.put(header, value);
responseLine = input.readLine(); }
if (input.available()>0)
responseLine = readLine();
else
break;
} }
return headers; return headers;
} }

View File

@ -282,7 +282,8 @@ public class SwapAppsView extends ListView implements
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Apk apk = getApkToInstall(); Apk apk = getApkToInstall();
String broadcastUrl = intent.getStringExtra(Downloader.EXTRA_ADDRESS); String broadcastUrl = intent.getStringExtra(Downloader.EXTRA_ADDRESS);
if (!TextUtils.equals(Utils.getApkUrl(apk.repoAddress, apk), broadcastUrl)) {
if (apk != null && apk.repoAddress != null && (!TextUtils.equals(Utils.getApkUrl(apk.repoAddress, apk), broadcastUrl))) {
return; return;
} }
@ -373,9 +374,15 @@ public class SwapAppsView extends ListView implements
private void resetView() { private void resetView() {
if (app == null)
return;
progressView.setVisibility(View.GONE); progressView.setVisibility(View.GONE);
progressView.setIndeterminate(true); progressView.setIndeterminate(true);
if (app.name != null)
nameView.setText(app.name); nameView.setText(app.name);
ImageLoader.getInstance().displayImage(app.iconUrl, iconView, displayImageOptions); ImageLoader.getInstance().displayImage(app.iconUrl, iconView, displayImageOptions);
btnInstall.setVisibility(View.GONE); btnInstall.setVisibility(View.GONE);

View File

@ -44,6 +44,7 @@ import org.fdroid.fdroid.data.NewRepoConfig;
import org.fdroid.fdroid.installer.Installer; import org.fdroid.fdroid.installer.Installer;
import org.fdroid.fdroid.localrepo.LocalRepoManager; import org.fdroid.fdroid.localrepo.LocalRepoManager;
import org.fdroid.fdroid.localrepo.SwapService; import org.fdroid.fdroid.localrepo.SwapService;
import org.fdroid.fdroid.localrepo.peers.BluetoothFinder;
import org.fdroid.fdroid.localrepo.peers.Peer; import org.fdroid.fdroid.localrepo.peers.Peer;
import org.fdroid.fdroid.net.ApkDownloader; import org.fdroid.fdroid.net.ApkDownloader;
@ -625,7 +626,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
Log.d(TAG, "Not currently in discoverable mode, so prompting user to enable."); Log.d(TAG, "Not currently in discoverable mode, so prompting user to enable.");
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); // TODO: What about when this expires? What if user manually disables discovery? intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, BluetoothFinder.DISCOVERABLE_TIMEOUT); // 3600 is new maximum! TODO: What about when this expires? What if user manually disables discovery?
startActivityForResult(intent, REQUEST_BLUETOOTH_DISCOVERABLE); startActivityForResult(intent, REQUEST_BLUETOOTH_DISCOVERABLE);
} }