From 7f35420e37176ac703d5d8358c84551525b5f868 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 22 May 2019 16:21:54 +0200 Subject: [PATCH] purge last RxAndroid code, replace with good ol' Intents Rx needs to be used as the basis of the whole system, it doesn't make sense to just have one small part handled by Rx. RxJava is still used in InstallAppProviderService, so that would have to be tackled separately. --- app/build.gradle | 1 - .../fdroid/localrepo/BluetoothManager.java | 35 ++++ .../fdroid/localrepo/BonjourManager.java | 47 +++-- .../fdroid/fdroid/localrepo/SwapService.java | 70 ++++---- .../localrepo/peers/BluetoothFinder.java | 126 -------------- .../fdroid/localrepo/peers/BluetoothPeer.java | 19 +- .../fdroid/localrepo/peers/BonjourFinder.java | 162 ------------------ .../fdroid/localrepo/peers/BonjourPeer.java | 25 ++- .../fdroid/fdroid/localrepo/peers/Peer.java | 11 +- .../fdroid/localrepo/peers/PeerFinder.java | 31 ---- .../fdroid/views/swap/StartSwapView.java | 40 +---- .../views/swap/SwapWorkflowActivity.java | 40 +++++ 12 files changed, 193 insertions(+), 414 deletions(-) delete mode 100644 app/src/full/java/org/fdroid/fdroid/localrepo/peers/BluetoothFinder.java delete mode 100644 app/src/full/java/org/fdroid/fdroid/localrepo/peers/BonjourFinder.java delete mode 100644 app/src/full/java/org/fdroid/fdroid/localrepo/peers/PeerFinder.java diff --git a/app/build.gradle b/app/build.gradle index 2b7d709d6..47fa26d11 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -153,7 +153,6 @@ dependencies { implementation 'commons-net:commons-net:3.6' implementation 'ch.acra:acra:4.9.1' implementation 'io.reactivex:rxjava:1.1.0' - implementation 'io.reactivex:rxandroid:0.23.0' implementation 'com.hannesdorfmann:adapterdelegates3:3.0.1' implementation 'com.ashokvarma.android:bottom-navigation-bar:2.0.5' diff --git a/app/src/full/java/org/fdroid/fdroid/localrepo/BluetoothManager.java b/app/src/full/java/org/fdroid/fdroid/localrepo/BluetoothManager.java index b7f8de854..d6b3cb41c 100644 --- a/app/src/full/java/org/fdroid/fdroid/localrepo/BluetoothManager.java +++ b/app/src/full/java/org/fdroid/fdroid/localrepo/BluetoothManager.java @@ -1,8 +1,11 @@ package org.fdroid.fdroid.localrepo; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; @@ -11,6 +14,8 @@ import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.util.Log; import org.fdroid.fdroid.R; +import org.fdroid.fdroid.Utils; +import org.fdroid.fdroid.localrepo.peers.BluetoothPeer; import org.fdroid.fdroid.net.bluetooth.BluetoothServer; import java.lang.ref.WeakReference; @@ -26,6 +31,9 @@ import java.lang.ref.WeakReference; public class BluetoothManager { private static final String TAG = "BluetoothManager"; + public static final String ACTION_FOUND = "BluetoothNewPeer"; + public static final String EXTRA_PEER = "extraBluetoothPeer"; + public static final String ACTION_STATUS = "BluetoothStatus"; public static final String EXTRA_STATUS = "BluetoothStatusExtra"; public static final int STATUS_STARTING = 0; @@ -74,6 +82,9 @@ public class BluetoothManager { handlerThread = new HandlerThread("BluetoothManager", Process.THREAD_PRIORITY_LESS_FAVORABLE) { @Override protected void onLooperPrepared() { + LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context); + localBroadcastManager.registerReceiver(bluetoothDeviceFound, + new IntentFilter(BluetoothDevice.ACTION_FOUND)); bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (!bluetoothAdapter.enable()) { sendBroadcast(STATUS_ERROR, context.getString(R.string.swap_error_cannot_start_bluetooth)); @@ -85,12 +96,17 @@ public class BluetoothManager { } else { sendBroadcast(STATUS_ERROR, context.getString(R.string.swap_error_cannot_start_bluetooth)); } + for (BluetoothDevice device : bluetoothAdapter.getBondedDevices()) { + sendFoundBroadcast(context, device); + } } }; handlerThread.start(); handler = new Handler(handlerThread.getLooper()) { @Override public void handleMessage(Message msg) { + LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context); + localBroadcastManager.unregisterReceiver(bluetoothDeviceFound); bluetoothServer.close(); if (bluetoothAdapter != null) { bluetoothAdapter.cancelDiscovery(); @@ -132,4 +148,23 @@ public class BluetoothManager { } LocalBroadcastManager.getInstance(context.get()).sendBroadcast(intent); } + + private static final BroadcastReceiver bluetoothDeviceFound = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + sendFoundBroadcast(context, (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); + } + }; + + private static void sendFoundBroadcast(Context context, BluetoothDevice device) { + BluetoothPeer bluetoothPeer = BluetoothPeer.getInstance(device); + if (bluetoothPeer == null) { + Utils.debugLog(TAG, "IGNORING: " + device); + return; + } + Intent intent = new Intent(ACTION_FOUND); + intent.putExtra(EXTRA_PEER, bluetoothPeer); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + LocalBroadcastManager.getInstance(context).sendBroadcast(intent); + } } diff --git a/app/src/full/java/org/fdroid/fdroid/localrepo/BonjourManager.java b/app/src/full/java/org/fdroid/fdroid/localrepo/BonjourManager.java index f9b581e1e..e579a65c2 100644 --- a/app/src/full/java/org/fdroid/fdroid/localrepo/BonjourManager.java +++ b/app/src/full/java/org/fdroid/fdroid/localrepo/BonjourManager.java @@ -2,6 +2,7 @@ package org.fdroid.fdroid.localrepo; import android.content.Context; import android.content.Intent; +import android.net.wifi.WifiManager; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; @@ -12,6 +13,7 @@ import android.util.Log; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Utils; +import org.fdroid.fdroid.localrepo.peers.BonjourPeer; import javax.jmdns.JmDNS; import javax.jmdns.ServiceEvent; @@ -30,9 +32,9 @@ import java.util.HashMap; public class BonjourManager { private static final String TAG = "BonjourManager"; - public static final String ACTION_ADDED = "BonjourAdded"; - public static final String ACTION_RESOLVED = "BonjourResolved"; - public static final String ACTION_REMOVED = "BonjourRemoved"; + public static final String ACTION_FOUND = "BonjourNewPeer"; + public static final String ACTION_REMOVED = "BonjourPeerRemoved"; + public static final String EXTRA_BONJOUR_PEER = "extraBonjourPeer"; public static final String ACTION_STATUS = "BonjourStatus"; public static final String EXTRA_STATUS = "BonjourStatusExtra"; @@ -56,6 +58,7 @@ public class BonjourManager { private static volatile HandlerThread handlerThread; private static ServiceInfo pairService; private static JmDNS jmdns; + private static WifiManager.MulticastLock multicastLock; public static boolean isAlive() { return handlerThread != null && handlerThread.isAlive(); @@ -116,6 +119,8 @@ public class BonjourManager { } sendBroadcast(STATUS_STARTING, null); + final WifiManager wifiManager = (WifiManager) context.getApplicationContext() + .getSystemService(Context.WIFI_SERVICE); handlerThread = new HandlerThread("BonjourManager", Process.THREAD_PRIORITY_LESS_FAVORABLE) { @Override protected void onLooperPrepared() { @@ -124,6 +129,11 @@ public class BonjourManager { jmdns = JmDNS.create(address); jmdns.addServiceListener(HTTP_SERVICE_TYPE, httpServiceListener); jmdns.addServiceListener(HTTPS_SERVICE_TYPE, httpsServiceListener); + + multicastLock = wifiManager.createMulticastLock(context.getPackageName()); + multicastLock.setReferenceCounted(false); + multicastLock.acquire(); + sendBroadcast(STATUS_STARTED, null); } catch (IOException e) { if (handler != null) { @@ -155,15 +165,15 @@ public class BonjourManager { private void handleVisible(String localRepoName, boolean useHttps) { HashMap values = new HashMap<>(); - values.put("path", "/fdroid/repo"); - values.put("name", localRepoName); - values.put("fingerprint", FDroidApp.repo.fingerprint); + values.put(BonjourPeer.PATH, "/fdroid/repo"); + values.put(BonjourPeer.NAME, localRepoName); + values.put(BonjourPeer.FINGERPRINT, FDroidApp.repo.fingerprint); String type; if (useHttps) { - values.put("type", "fdroidrepos"); + values.put(BonjourPeer.TYPE, "fdroidrepos"); type = HTTPS_SERVICE_TYPE; } else { - values.put("type", "fdroidrepo"); + values.put(BonjourPeer.TYPE, "fdroidrepo"); type = HTTP_SERVICE_TYPE; } ServiceInfo newPairService = ServiceInfo.create(type, localRepoName, FDroidApp.port, 0, 0, values); @@ -190,6 +200,9 @@ public class BonjourManager { } private void handleStop() { + if (multicastLock != null) { + multicastLock.release(); + } if (jmdns != null) { jmdns.unregisterAllServices(); Utils.closeQuietly(jmdns); @@ -228,9 +241,14 @@ public class BonjourManager { start(context, localRepoName, useHttps, httpServiceListener, httpsServiceListener); } - private static void sendBroadcast(String action, String message) { + private static void sendBroadcast(String action, ServiceInfo serviceInfo) { + BonjourPeer bonjourPeer = BonjourPeer.getInstance(serviceInfo); + if (bonjourPeer == null) { + Utils.debugLog(TAG, "IGNORING: " + serviceInfo); + return; + } Intent intent = new Intent(action); - intent.putExtra(Intent.EXTRA_TEXT, message); + intent.putExtra(EXTRA_BONJOUR_PEER, bonjourPeer); LocalBroadcastManager.getInstance(context.get()).sendBroadcast(intent); } @@ -250,20 +268,17 @@ public class BonjourManager { private static class SwapServiceListener implements ServiceListener { @Override public void serviceAdded(ServiceEvent serviceEvent) { - Utils.debugLog(TAG, "Service added: " + serviceEvent.getInfo()); - sendBroadcast(ACTION_ADDED, serviceEvent.getInfo().toString()); + // ignored, we only need resolved info } @Override public void serviceRemoved(ServiceEvent serviceEvent) { - Utils.debugLog(TAG, "Service removed: " + serviceEvent.getInfo()); - sendBroadcast(ACTION_REMOVED, serviceEvent.getInfo().toString()); + sendBroadcast(ACTION_REMOVED, serviceEvent.getInfo()); } @Override public void serviceResolved(ServiceEvent serviceEvent) { - Utils.debugLog(TAG, "Service resolved: " + serviceEvent.getInfo()); - sendBroadcast(ACTION_RESOLVED, serviceEvent.getInfo().toString()); + sendBroadcast(ACTION_FOUND, serviceEvent.getInfo()); } } } diff --git a/app/src/full/java/org/fdroid/fdroid/localrepo/SwapService.java b/app/src/full/java/org/fdroid/fdroid/localrepo/SwapService.java index e6e8a0b2e..610aa9afa 100644 --- a/app/src/full/java/org/fdroid/fdroid/localrepo/SwapService.java +++ b/app/src/full/java/org/fdroid/fdroid/localrepo/SwapService.java @@ -31,14 +31,9 @@ import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.data.Schema; import org.fdroid.fdroid.localrepo.peers.Peer; -import org.fdroid.fdroid.localrepo.peers.PeerFinder; import org.fdroid.fdroid.net.Downloader; import org.fdroid.fdroid.net.WifiStateChangeService; import org.fdroid.fdroid.views.swap.SwapWorkflowActivity; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; import java.io.IOException; import java.io.OutputStream; @@ -55,9 +50,7 @@ import java.util.TimerTask; * Central service which manages all of the different moving parts of swap which are required * to enable p2p swapping of apps. */ -@SuppressWarnings("LineLength") public class SwapService extends Service { - private static final String TAG = "SwapService"; private static final String SHARED_PREFERENCES = "swap-state"; @@ -69,6 +62,7 @@ public class SwapService extends Service { @NonNull private final Set appsToSwap = new HashSet<>(); + private final Set activePeers = new HashSet<>(); private static LocalBroadcastManager localBroadcastManager; private static SharedPreferences swapPreferences; @@ -90,41 +84,16 @@ public class SwapService extends Service { context.stopService(intent); } - /** - * Search for peers to swap - */ - private Observable peerFinder; - - /** - * Call {@link Observable#subscribe()} on this in order to be notified of peers - * which are found. Call {@link Subscription#unsubscribe()} on the resulting - * subscription when finished and you no longer want to scan for peers. - *

- * The returned object will scan for peers on a background thread, and emit - * found peers on the mian thread. - *

- * Invoking this in multiple places will return the same, cached, peer finder. - * That is, if in the past it already found some peers, then you subscribe - * to it in the future, the future subscriber will still receive the peers - * that were found previously. - * TODO: What about removing peers that no longer are present? - */ - public Observable scanForPeers() { - Utils.debugLog(TAG, "Scanning for nearby devices to swap with..."); - if (peerFinder == null) { - peerFinder = PeerFinder.createObservable(getApplicationContext()) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .distinct(); - } - return peerFinder; - } - @NonNull public Set getAppsToSwap() { return appsToSwap; } + @NonNull + public Set getActivePeers() { + return activePeers; + } + public void connectToPeer() { if (getPeer() == null) { throw new IllegalStateException("Cannot connect to peer, no peer has been selected."); @@ -387,6 +356,9 @@ public class SwapService extends Service { localBroadcastManager.registerReceiver(onWifiChange, new IntentFilter(WifiStateChangeService.BROADCAST)); localBroadcastManager.registerReceiver(bluetoothStatus, new IntentFilter(BluetoothManager.ACTION_STATUS)); + localBroadcastManager.registerReceiver(bluetoothPeerFound, new IntentFilter(BluetoothManager.ACTION_FOUND)); + localBroadcastManager.registerReceiver(bonjourPeerFound, new IntentFilter(BonjourManager.ACTION_FOUND)); + localBroadcastManager.registerReceiver(bonjourPeerRemoved, new IntentFilter(BonjourManager.ACTION_REMOVED)); localBroadcastManager.registerReceiver(localRepoStatus, new IntentFilter(LocalRepoService.ACTION_STATUS)); BonjourManager.start(this); @@ -418,6 +390,9 @@ public class SwapService extends Service { Preferences.get().unregisterLocalRepoHttpsListeners(httpsEnabledListener); localBroadcastManager.unregisterReceiver(onWifiChange); localBroadcastManager.unregisterReceiver(bluetoothStatus); + localBroadcastManager.unregisterReceiver(bluetoothPeerFound); + localBroadcastManager.unregisterReceiver(bonjourPeerFound); + localBroadcastManager.unregisterReceiver(bonjourPeerRemoved); unregisterReceiver(bluetoothScanModeChanged); @@ -597,4 +572,25 @@ public class SwapService extends Service { } } }; + + private final BroadcastReceiver bluetoothPeerFound = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + activePeers.add((Peer) intent.getParcelableExtra(BluetoothManager.EXTRA_PEER)); + } + }; + + private final BroadcastReceiver bonjourPeerFound = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + activePeers.add((Peer) intent.getParcelableExtra(BonjourManager.EXTRA_BONJOUR_PEER)); + } + }; + + private final BroadcastReceiver bonjourPeerRemoved = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + activePeers.remove((Peer) intent.getParcelableExtra(BonjourManager.EXTRA_BONJOUR_PEER)); + } + }; } \ No newline at end of file diff --git a/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BluetoothFinder.java b/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BluetoothFinder.java deleted file mode 100644 index 8c024e3a5..000000000 --- a/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BluetoothFinder.java +++ /dev/null @@ -1,126 +0,0 @@ -package org.fdroid.fdroid.localrepo.peers; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothClass; -import android.bluetooth.BluetoothDevice; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.util.Log; - -import org.fdroid.fdroid.Utils; - -import rx.Observable; -import rx.Subscriber; -import rx.functions.Action0; -import rx.subscriptions.Subscriptions; - -@SuppressWarnings("LineLength") -final class BluetoothFinder extends PeerFinder { - - public static Observable createBluetoothObservable(final Context context) { - return Observable.create(new Observable.OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - final BluetoothFinder finder = new BluetoothFinder(context, subscriber); - - subscriber.add(Subscriptions.create(new Action0() { - @Override - public void call() { - finder.cancel(); - } - })); - - finder.scan(); - } - }); - } - - private static final String TAG = "BluetoothFinder"; - - private final BluetoothAdapter adapter; - - private BluetoothFinder(Context context, Subscriber subscriber) { - super(context, subscriber); - adapter = BluetoothAdapter.getDefaultAdapter(); - } - - private BroadcastReceiver deviceFoundReceiver; - private BroadcastReceiver scanCompleteReceiver; - - private void scan() { - - if (adapter == null) { - Log.i(TAG, "Not scanning for bluetooth peers to swap with, couldn't find a bluetooth adapter on this device."); - return; - } - - isScanning = true; - - if (deviceFoundReceiver == null) { - deviceFoundReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (BluetoothDevice.ACTION_FOUND.equals(intent.getAction())) { - BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - onDeviceFound(device); - } - } - }; - context.registerReceiver(deviceFoundReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); - } - - if (scanCompleteReceiver == null) { - scanCompleteReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (isScanning) { - Utils.debugLog(TAG, "Scan complete, but we haven't been asked to stop scanning yet, so will restart scan."); - startDiscovery(); - } - } - }; - - // TODO: Unregister this receiver at the appropriate time. - context.registerReceiver(scanCompleteReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)); - } - - startDiscovery(); - } - - private void startDiscovery() { - - if (adapter.isDiscovering()) { - // TODO: Can we reset the discovering timeout, so that it doesn't, e.g. time out in 3 - // seconds because we had already almost completed the previous scan? We could - // cancelDiscovery(), but then it will probably prompt the user again. - Utils.debugLog(TAG, "Requested bluetooth scan when already scanning, so will ignore request."); - return; - } - - if (!adapter.startDiscovery()) { - Log.e(TAG, "Couldn't start bluetooth scanning."); - } - - } - - private void cancel() { - if (adapter != null) { - Utils.debugLog(TAG, "Stopping bluetooth discovery."); - adapter.cancelDiscovery(); - } - - isScanning = false; - } - - private void onDeviceFound(BluetoothDevice device) { - 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)) { - subscriber.onNext(new BluetoothPeer(device)); - } - } - -} diff --git a/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BluetoothPeer.java b/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BluetoothPeer.java index 6f98626c2..42b9c1153 100644 --- a/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BluetoothPeer.java +++ b/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BluetoothPeer.java @@ -1,7 +1,9 @@ package org.fdroid.fdroid.localrepo.peers; +import android.bluetooth.BluetoothClass.Device; import android.bluetooth.BluetoothDevice; import android.os.Parcel; +import android.support.annotation.Nullable; import android.text.TextUtils; import org.fdroid.fdroid.R; @@ -11,7 +13,22 @@ public class BluetoothPeer implements Peer { private final BluetoothDevice device; - public BluetoothPeer(BluetoothDevice device) { + /** + * Return a instance if the {@link BluetoothDevice} is a device that could + * host a swap repo. + */ + @Nullable + public static BluetoothPeer getInstance(@Nullable BluetoothDevice device) { + if (device != null && device.getName() != null && + (device.getBluetoothClass().getDeviceClass() == Device.COMPUTER_HANDHELD_PC_PDA + || device.getBluetoothClass().getDeviceClass() == Device.COMPUTER_PALM_SIZE_PC_PDA + || device.getBluetoothClass().getDeviceClass() == Device.PHONE_SMART)) { + return new BluetoothPeer(device); + } + return null; + } + + private BluetoothPeer(BluetoothDevice device) { this.device = device; } diff --git a/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BonjourFinder.java b/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BonjourFinder.java deleted file mode 100644 index 1f0ff4f5a..000000000 --- a/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BonjourFinder.java +++ /dev/null @@ -1,162 +0,0 @@ -package org.fdroid.fdroid.localrepo.peers; - -import android.content.Context; -import android.net.wifi.WifiManager; -import org.fdroid.fdroid.FDroidApp; -import org.fdroid.fdroid.Utils; -import rx.Observable; -import rx.Subscriber; -import rx.functions.Action0; -import rx.subscriptions.Subscriptions; - -import javax.jmdns.JmDNS; -import javax.jmdns.ServiceEvent; -import javax.jmdns.ServiceInfo; -import javax.jmdns.ServiceListener; -import java.io.IOException; -import java.net.InetAddress; - -import static org.fdroid.fdroid.localrepo.BonjourManager.HTTPS_SERVICE_TYPE; -import static org.fdroid.fdroid.localrepo.BonjourManager.HTTP_SERVICE_TYPE; - -@SuppressWarnings("LineLength") -final class BonjourFinder extends PeerFinder implements ServiceListener { - - public static Observable createBonjourObservable(final Context context) { - return Observable.create(new Observable.OnSubscribe() { - @Override - public void call(Subscriber subscriber) { - final BonjourFinder finder = new BonjourFinder(context, subscriber); - - subscriber.add(Subscriptions.create(new Action0() { - @Override - public void call() { - finder.cancel(); - } - })); - - finder.scan(); - } - }); - } - - private static final String TAG = "BonjourFinder"; - - private JmDNS jmdns; - private WifiManager wifiManager; - private WifiManager.MulticastLock multicastLock; - - private BonjourFinder(Context context, Subscriber subscriber) { - super(context, subscriber); - } - - private void scan() { - - Utils.debugLog(TAG, "Requested Bonjour (mDNS) scan for peers."); - - if (wifiManager == null) { - wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); - multicastLock = wifiManager.createMulticastLock(context.getPackageName()); - multicastLock.setReferenceCounted(false); - } - - if (isScanning) { - Utils.debugLog(TAG, "Requested Bonjour scan, but already scanning. But we will still try to explicitly scan for services."); - return; - } - - isScanning = true; - multicastLock.acquire(); - - try { - Utils.debugLog(TAG, "Searching for Bonjour (mDNS) clients..."); - jmdns = JmDNS.create(InetAddress.getByName(FDroidApp.ipAddressString)); - } catch (IOException e) { - subscriber.onError(e); - return; - } - - Utils.debugLog(TAG, "Adding mDNS service listeners for " + HTTP_SERVICE_TYPE + " and " + HTTPS_SERVICE_TYPE); - jmdns.addServiceListener(HTTP_SERVICE_TYPE, this); - jmdns.addServiceListener(HTTPS_SERVICE_TYPE, this); - listServices(); - } - - private void listServices() { - Utils.debugLog(TAG, "Explicitly querying for services, in addition to waiting for notifications."); - addFDroidServices(jmdns.list(HTTP_SERVICE_TYPE)); - addFDroidServices(jmdns.list(HTTPS_SERVICE_TYPE)); - } - - @Override - public void serviceRemoved(ServiceEvent event) { - } - - @Override - public void serviceAdded(final ServiceEvent event) { - // TODO: Get clarification, but it looks like this is: - // 1) Identifying that there is _a_ bonjour service available - // 2) Adding it to the list to give some sort of feedback to the user - // 3) Requesting more detailed info in an async manner - // 4) If that is in fact an fdroid repo (after requesting info), then add it again - // so that more detailed info can be shown to the user. - // - // If so, when is the old one removed? - addFDroidService(event.getInfo()); - - Utils.debugLog(TAG, "Found JmDNS service, now requesting further details of service"); - jmdns.requestServiceInfo(event.getType(), event.getName(), true); - } - - @Override - public void serviceResolved(ServiceEvent event) { - addFDroidService(event.getInfo()); - } - - private void addFDroidServices(ServiceInfo[] services) { - for (ServiceInfo info : services) { - addFDroidService(info); - } - } - - /** - * Broadcasts the fact that a Bonjour peer was found to swap with. - * Checks that the service is an F-Droid service, and also that it is not the F-Droid service - * for this device (by comparing its signing fingerprint to our signing fingerprint). - */ - private void addFDroidService(ServiceInfo serviceInfo) { - final String type = serviceInfo.getPropertyString("type"); - final String fingerprint = serviceInfo.getPropertyString("fingerprint"); - final boolean isFDroid = type != null && type.startsWith("fdroidrepo"); - final boolean isSelf = FDroidApp.repo != null && fingerprint != null && fingerprint.equalsIgnoreCase(FDroidApp.repo.fingerprint); - if (isFDroid && !isSelf) { - Utils.debugLog(TAG, "Found F-Droid swap Bonjour service:\n" + serviceInfo); - subscriber.onNext(new BonjourPeer(serviceInfo)); - } else { - if (isSelf) { - Utils.debugLog(TAG, "Ignoring Bonjour service because it belongs to this device:\n" + serviceInfo); - } else { - Utils.debugLog(TAG, "Ignoring Bonjour service because it doesn't look like an F-Droid swap repo:\n" + serviceInfo); - } - } - } - - private void cancel() { - Utils.debugLog(TAG, "Cancelling BonjourFinder, releasing multicast lock, removing jmdns service listeners"); - - if (multicastLock != null) { - multicastLock.release(); - } - - isScanning = false; - - if (jmdns == null) { - return; - } - jmdns.removeServiceListener(HTTP_SERVICE_TYPE, this); - jmdns.removeServiceListener(HTTPS_SERVICE_TYPE, this); - jmdns = null; - - } - -} diff --git a/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BonjourPeer.java b/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BonjourPeer.java index cc836efa9..b40809dfa 100644 --- a/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BonjourPeer.java +++ b/app/src/full/java/org/fdroid/fdroid/localrepo/peers/BonjourPeer.java @@ -2,16 +2,39 @@ package org.fdroid.fdroid.localrepo.peers; import android.net.Uri; import android.os.Parcel; +import android.support.annotation.Nullable; import android.text.TextUtils; +import org.fdroid.fdroid.FDroidApp; import javax.jmdns.ServiceInfo; import javax.jmdns.impl.FDroidServiceInfo; public class BonjourPeer extends WifiPeer { + private static final String TAG = "BonjourPeer"; + + public static final String FINGERPRINT = "fingerprint"; + public static final String NAME = "name"; + public static final String PATH = "path"; + public static final String TYPE = "type"; private final FDroidServiceInfo serviceInfo; - public BonjourPeer(ServiceInfo serviceInfo) { + /** + * Return a instance if the {@link ServiceInfo} is fully resolved and does + * not represent this device, but something else on the network. + */ + @Nullable + public static BonjourPeer getInstance(ServiceInfo serviceInfo) { + String type = serviceInfo.getPropertyString(TYPE); + String fingerprint = serviceInfo.getPropertyString(FINGERPRINT); + if (type == null || !type.startsWith("fdroidrepo") + || TextUtils.equals(FDroidApp.repo.fingerprint, fingerprint)) { + return null; + } + return new BonjourPeer(serviceInfo); + } + + private BonjourPeer(ServiceInfo serviceInfo) { this.serviceInfo = new FDroidServiceInfo(serviceInfo); this.name = serviceInfo.getDomain(); this.uri = Uri.parse(this.serviceInfo.getRepoAddress()); diff --git a/app/src/full/java/org/fdroid/fdroid/localrepo/peers/Peer.java b/app/src/full/java/org/fdroid/fdroid/localrepo/peers/Peer.java index c1481e297..14a45db29 100644 --- a/app/src/full/java/org/fdroid/fdroid/localrepo/peers/Peer.java +++ b/app/src/full/java/org/fdroid/fdroid/localrepo/peers/Peer.java @@ -3,11 +3,20 @@ package org.fdroid.fdroid.localrepo.peers; import android.os.Parcelable; import android.support.annotation.DrawableRes; +/** + * TODO This model assumes that "peers" from Bluetooth, Bonjour, and WiFi are + * different things. They are not different repos though, they all point to + * the same repos. This should really be combined to be a single "RemoteRepo" + * class that represents a single device's local repo, and can have zero to + * many ways to connect to it (e.g. Bluetooth, WiFi, USB Thumb Drive, SD Card, + * WiFi Direct, etc). + */ public interface Peer extends Parcelable { String getName(); - @DrawableRes int getIcon(); + @DrawableRes + int getIcon(); boolean equals(Object peer); diff --git a/app/src/full/java/org/fdroid/fdroid/localrepo/peers/PeerFinder.java b/app/src/full/java/org/fdroid/fdroid/localrepo/peers/PeerFinder.java deleted file mode 100644 index 6cbffe4bc..000000000 --- a/app/src/full/java/org/fdroid/fdroid/localrepo/peers/PeerFinder.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.fdroid.fdroid.localrepo.peers; - -import android.content.Context; - -import rx.Observable; -import rx.Subscriber; -import rx.schedulers.Schedulers; - -/** - * Searches for other devices in the vicinity, using specific technologies. - * Once found, emits a {@link Peer} to interested {@link Subscriber}s. - */ -public abstract class PeerFinder { - - protected boolean isScanning; - protected final Context context; - protected final Subscriber subscriber; - - protected PeerFinder(Context context, Subscriber subscriber) { - this.context = context; - this.subscriber = subscriber; - } - - public static Observable createObservable(final Context context) { - return Observable.merge( - BluetoothFinder.createBluetoothObservable(context).subscribeOn(Schedulers.newThread()), - BonjourFinder.createBonjourObservable(context).subscribeOn(Schedulers.newThread()) - ); - } - -} diff --git a/app/src/full/java/org/fdroid/fdroid/views/swap/StartSwapView.java b/app/src/full/java/org/fdroid/fdroid/views/swap/StartSwapView.java index 54fc4fff1..1da4701e3 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/swap/StartSwapView.java +++ b/app/src/full/java/org/fdroid/fdroid/views/swap/StartSwapView.java @@ -31,8 +31,6 @@ import org.fdroid.fdroid.localrepo.SwapService; import org.fdroid.fdroid.localrepo.SwapView; import org.fdroid.fdroid.localrepo.peers.Peer; import org.fdroid.fdroid.net.WifiStateChangeService; -import rx.Subscriber; -import rx.Subscription; import java.util.ArrayList; @@ -57,7 +55,7 @@ public class StartSwapView extends SwapView { super(context, attrs, defStyleAttr, defStyleRes); } - private class PeopleNearbyAdapter extends ArrayAdapter { + class PeopleNearbyAdapter extends ArrayAdapter { PeopleNearbyAdapter(Context context) { super(context, 0, new ArrayList()); @@ -93,45 +91,14 @@ public class StartSwapView extends SwapView { private PeopleNearbyAdapter peopleNearbyAdapter; - /** - * When peers are emitted by the peer finder, add them to the adapter - * so that they will show up in the list of peers. - */ - private final Subscriber onPeerFound = new Subscriber() { - - @Override - public void onCompleted() { - uiShowNotSearchingForPeers(); - } - - @Override - public void onError(Throwable e) { - uiShowNotSearchingForPeers(); - } - - @Override - public void onNext(Peer peer) { - Utils.debugLog(TAG, "Found peer: " + peer + ", adding to list of peers in UI."); - peopleNearbyAdapter.add(peer); - } - }; - - private Subscription peerFinderSubscription; - /** * Remove relevant listeners/subscriptions/etc so that they do not receive and process events * when this view is not in use. *

- * TODO: Not sure if this is the best place to handle being removed from the view. */ @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (peerFinderSubscription != null) { - peerFinderSubscription.unsubscribe(); - peerFinderSubscription = null; - } - if (bluetoothSwitch != null) { bluetoothSwitch.setOnCheckedChangeListener(null); } @@ -143,10 +110,6 @@ public class StartSwapView extends SwapView { protected void onFinishInflate() { super.onFinishInflate(); - if (peerFinderSubscription == null) { - peerFinderSubscription = getActivity().getSwapService().scanForPeers().subscribe(onPeerFound); - } - uiInitPeers(); uiInitBluetooth(); uiInitWifi(); @@ -184,6 +147,7 @@ public class StartSwapView extends SwapView { peopleNearbyAdapter = new PeopleNearbyAdapter(getContext()); peopleNearbyList.setAdapter(peopleNearbyAdapter); + peopleNearbyAdapter.addAll(getActivity().getSwapService().getActivePeers()); peopleNearbyList.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override diff --git a/app/src/full/java/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java b/app/src/full/java/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java index 79c238df7..beda9e9bf 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java +++ b/app/src/full/java/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java @@ -36,10 +36,12 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.ImageView; +import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; @@ -345,7 +347,10 @@ public class SwapWorkflowActivity extends AppCompatActivity { localBroadcastManager.registerReceiver(localRepoStatus, new IntentFilter(LocalRepoService.ACTION_STATUS)); localBroadcastManager.registerReceiver(repoUpdateReceiver, new IntentFilter(UpdateService.LOCAL_ACTION_STATUS)); + localBroadcastManager.registerReceiver(bonjourFound, new IntentFilter(BonjourManager.ACTION_FOUND)); + localBroadcastManager.registerReceiver(bonjourRemoved, new IntentFilter(BonjourManager.ACTION_REMOVED)); localBroadcastManager.registerReceiver(bonjourStatus, new IntentFilter(BonjourManager.ACTION_STATUS)); + localBroadcastManager.registerReceiver(bluetoothFound, new IntentFilter(BluetoothManager.ACTION_FOUND)); localBroadcastManager.registerReceiver(bluetoothStatus, new IntentFilter(BluetoothManager.ACTION_STATUS)); registerReceiver(bluetoothScanModeChanged, @@ -368,7 +373,10 @@ public class SwapWorkflowActivity extends AppCompatActivity { localBroadcastManager.unregisterReceiver(onWifiStateChanged); localBroadcastManager.unregisterReceiver(localRepoStatus); localBroadcastManager.unregisterReceiver(repoUpdateReceiver); + localBroadcastManager.unregisterReceiver(bonjourFound); + localBroadcastManager.unregisterReceiver(bonjourRemoved); localBroadcastManager.unregisterReceiver(bonjourStatus); + localBroadcastManager.unregisterReceiver(bluetoothFound); localBroadcastManager.unregisterReceiver(bluetoothStatus); } @@ -1060,6 +1068,28 @@ public class SwapWorkflowActivity extends AppCompatActivity { } }; + private final BroadcastReceiver bonjourFound = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + ListView peopleNearbyList = container.findViewById(R.id.list_people_nearby); + if (peopleNearbyList != null) { + ArrayAdapter peopleNearbyAdapter = (ArrayAdapter) peopleNearbyList.getAdapter(); + peopleNearbyAdapter.add((Peer) intent.getParcelableExtra(BonjourManager.EXTRA_BONJOUR_PEER)); + } + } + }; + + private final BroadcastReceiver bonjourRemoved = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + ListView peopleNearbyList = container.findViewById(R.id.list_people_nearby); + if (peopleNearbyList != null) { + ArrayAdapter peopleNearbyAdapter = (ArrayAdapter) peopleNearbyList.getAdapter(); + peopleNearbyAdapter.remove((Peer) intent.getParcelableExtra(BonjourManager.EXTRA_BONJOUR_PEER)); + } + } + }; + private final BroadcastReceiver bluetoothStatus = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -1132,7 +1162,17 @@ public class SwapWorkflowActivity extends AppCompatActivity { default: throw new IllegalArgumentException("Bad intent: " + intent); } + } + }; + private final BroadcastReceiver bluetoothFound = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + ListView peopleNearbyList = container.findViewById(R.id.list_people_nearby); + if (peopleNearbyList != null) { + ArrayAdapter peopleNearbyAdapter = (ArrayAdapter) peopleNearbyList.getAdapter(); + peopleNearbyAdapter.add((Peer) intent.getParcelableExtra(BluetoothManager.EXTRA_PEER)); + } } };