Refactor finders classes.

Change-Id: Icaf80e1ff13bca059f6ee42a56f36a4b3f65a3fb
This commit is contained in:
Artur Dryomov
2013-06-20 18:23:40 +03:00
committed by Michael Meeks
parent 19015bd364
commit d170ecde6c
5 changed files with 274 additions and 198 deletions

View File

@@ -10,9 +10,7 @@ package org.libreoffice.impressremote.communication;
import java.util.Collection;
import java.util.HashMap;
import org.libreoffice.impressremote.Globals;
import org.libreoffice.impressremote.communication.Server.Protocol;
import java.util.Map;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -22,93 +20,126 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
public class BluetoothFinder {
import org.libreoffice.impressremote.communication.Server.Protocol;
public class BluetoothFinder extends BroadcastReceiver {
// TODO: add removal of cached items
private Context mContext;
private final Context mContext;
BluetoothAdapter mAdapter;
private final Map<String, Server> mServers;
public BluetoothFinder(Context aContext) {
mContext = aContext;
mAdapter = BluetoothAdapter.getDefaultAdapter();
mServers = new HashMap<String, Server>();
}
public void startFinding() {
Log.i(Globals.TAG, "BluetoothFinder.startFinding(): mAdapter=" + mAdapter);
if (mAdapter == null) {
return; // No bluetooth adapter found (emulator, special devices)
public void startSearch() {
if (!isBluetoothAvailable()) {
return;
}
IntentFilter aFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
aFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
aFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
mContext.registerReceiver(mReceiver, aFilter);
mAdapter.startDiscovery();
BluetoothAdapter.getDefaultAdapter().startDiscovery();
registerSearchResultsReceiver();
}
public void stopFinding() {
Log.i(Globals.TAG, "BluetoothFinder.stopFinding(): mAdapter=" + mAdapter);
if (mAdapter == null) {
return; // No bluetooth adapter found (emulator, special devices)
}
mAdapter.cancelDiscovery();
try {
mContext.unregisterReceiver(mReceiver);
} catch (IllegalArgumentException e) {
// The receiver wasn't registered
Log.i(Globals.TAG, "BluetoothFinder.stopFinding: " + e);
private boolean isBluetoothAvailable() {
return BluetoothAdapter.getDefaultAdapter() != null;
}
private void registerSearchResultsReceiver() {
IntentFilter aIntentFilter = new IntentFilter(
BluetoothDevice.ACTION_FOUND);
aIntentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
aIntentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
mContext.registerReceiver(this, aIntentFilter);
}
public void stopSearch() {
if (!isBluetoothAvailable()) {
return;
}
BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
unregisterSearchResultsReceiver();
}
private HashMap<String, Server> mServerList = new HashMap<String, Server>();
public Collection<Server> getServerList() {
return mServerList.values();
private void unregisterSearchResultsReceiver() {
mContext.unregisterReceiver(this);
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
public Collection<Server> getServers() {
return mServers.values();
}
@Override
public void onReceive(Context context, Intent aIntent) {
Log.i(Globals.TAG, "BluetoothFinder: BroadcastReceiver.onReceive: aIntent=" + aIntent);
if (aIntent.getAction().equals(BluetoothDevice.ACTION_FOUND)) {
BluetoothDevice aDevice = (BluetoothDevice) aIntent.getExtras()
.get(BluetoothDevice.EXTRA_DEVICE);
Log.i(Globals.TAG, "BluetoothFinder.onReceive: found " + aDevice.getName() + " at " + aDevice.getAddress());
if (aDevice.getName() == null)
return;
Server aServer = new Server(Protocol.BLUETOOTH,
aDevice.getAddress(), aDevice.getName(),
System.currentTimeMillis());
mServerList.put(aServer.getAddress(), aServer);
Intent aNIntent = new Intent(
CommunicationService.MSG_SERVERLIST_CHANGED);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(
aNIntent);
} else if (aIntent.getAction().equals(
BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
|| aIntent.getAction()
.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
// Start discovery again after a small delay.
// but check whether device is on incase the user manually
// disabled bluetooth
if (mAdapter.isEnabled()) {
Handler aHandler = new Handler();
aHandler.postDelayed(new Runnable() {
@Override
public void run() {
// Looping, huh?
Log.i(Globals.TAG, "BluetothFinder: looping");
}
}, 1000 * 15);
}
@Override
public void onReceive(Context aContext, Intent aIntent) {
if (aIntent.getAction().equals(BluetoothDevice.ACTION_FOUND)) {
BluetoothDevice aBluetoothDevice = (BluetoothDevice) aIntent
.getExtras().get(BluetoothDevice.EXTRA_DEVICE);
if (aBluetoothDevice == null) {
return;
}
createServer(aBluetoothDevice);
callUpdatingServersList();
return;
}
};
if (aIntent.getAction()
.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
startDiscoveryDelayed();
return;
}
if (aIntent.getAction()
.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
startDiscoveryDelayed();
}
}
private void createServer(BluetoothDevice aBluetoothDevice) {
String aServerAddress = aBluetoothDevice.getAddress();
String aServerName = aBluetoothDevice.getName();
Server aServer = new Server(Protocol.BLUETOOTH, aServerAddress,
aServerName, System.currentTimeMillis());
mServers.put(aServerAddress, aServer);
}
private void callUpdatingServersList() {
Intent aServersListChangedIntent = new Intent(
CommunicationService.MSG_SERVERLIST_CHANGED);
LocalBroadcastManager.getInstance(mContext)
.sendBroadcast(aServersListChangedIntent);
}
private void startDiscoveryDelayed() {
// Start discovery again after a small delay.
// but check whether device is on in case the user manually
// disabled bluetooth
if (!BluetoothAdapter.getDefaultAdapter().isEnabled()) {
return;
}
Handler aHandler = new Handler();
aHandler.postDelayed(new Runnable() {
@Override
public void run() {
// Looping, huh?
}
}, 1000 * 15);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@@ -24,6 +24,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.preference.PreferenceManager;
@@ -31,7 +32,7 @@ import android.support.v4.content.LocalBroadcastManager;
public class CommunicationService extends Service implements Runnable {
public enum State {
public static enum State {
DISCONNECTED, SEARCHING, CONNECTING, CONNECTED
}
@@ -48,7 +49,7 @@ public class CommunicationService extends Service implements Runnable {
if (aName != null)
return aName;
}
return android.os.Build.MODEL;
return Build.MODEL;
}
/**
@@ -139,20 +140,20 @@ public class CommunicationService extends Service implements Runnable {
SharedPreferences aPref = PreferenceManager.getDefaultSharedPreferences(this);
boolean bEnableWifi = aPref.getBoolean("option_enablewifi", false);
if (bEnableWifi)
mNetworkFinder.startFinding();
mNetworkFinder.startSearch();
BluetoothAdapter aAdapter = BluetoothAdapter.getDefaultAdapter();
if (aAdapter != null) {
mBluetoothPreviouslyEnabled = aAdapter.isEnabled();
if (!mBluetoothPreviouslyEnabled)
aAdapter.enable();
mBluetoothFinder.startFinding();
mBluetoothFinder.startSearch();
}
}
public void stopSearching() {
Log.i(Globals.TAG, "CommunicationService.stopSearching()");
mNetworkFinder.stopFinding();
mBluetoothFinder.stopFinding();
mNetworkFinder.stopSearch();
mBluetoothFinder.stopSearch();
BluetoothAdapter aAdapter = BluetoothAdapter.getDefaultAdapter();
if (aAdapter != null) {
if (!mBluetoothPreviouslyEnabled) {
@@ -165,8 +166,8 @@ public class CommunicationService extends Service implements Runnable {
Log.i(Globals.TAG, "CommunicationService.connectTo(" + aServer + ")");
synchronized (mConnectionVariableMutex) {
if (mState == State.SEARCHING) {
mNetworkFinder.stopFinding();
mBluetoothFinder.stopFinding();
mNetworkFinder.stopSearch();
mBluetoothFinder.stopSearch();
mState = State.DISCONNECTED;
}
mServerDesired = aServer;
@@ -267,8 +268,8 @@ public class CommunicationService extends Service implements Runnable {
public List<Server> getServers() {
ArrayList<Server> aServers = new ArrayList<Server>();
aServers.addAll(mNetworkFinder.getServerList());
aServers.addAll(mBluetoothFinder.getServerList());
aServers.addAll(mNetworkFinder.getServers());
aServers.addAll(mBluetoothFinder.getServers());
aServers.addAll(mManualServers.values());
return aServers;
}
@@ -324,7 +325,6 @@ public class CommunicationService extends Service implements Runnable {
}
public void removeServer(Server aServer) {
mManualServers.remove(aServer.getAddress());
SharedPreferences aPref = getSharedPreferences(SERVERSTORAGE_KEY,

View File

@@ -80,7 +80,7 @@ public class NetworkClient extends Client {
String aPhoneName = CommunicationService.getDeviceName();
sendCommand(Protocol.Commands
.prepareCommand(Protocol.Commands.PAIR, aPhoneName, mPin));
.prepareCommand(Protocol.Commands.PAIR_WITH_SERVER, aPhoneName, mPin));
}
@Override

View File

@@ -22,9 +22,18 @@ final class Protocol {
private Ports() {
}
public static final int SERVER_SEARCH = 1598;
public static final int CLIENT_CONNECTION = 1599;
}
public static final class Addresses {
private Addresses() {
}
public static final String SERVER_SEARCH = "239.0.0.1";
public static final String SERVER_LOCAL_FOR_EMULATOR = "10.0.2.2";
}
public static final class Messages {
private Messages() {
}
@@ -43,7 +52,8 @@ final class Protocol {
private Commands() {
}
public static final String PAIR = "LO_SERVER_CLIENT_PAIR";
public static final String PAIR_WITH_SERVER = "LO_SERVER_CLIENT_PAIR";
public static final String SEARCH_SERVERS = "LOREMOTE_SEARCH";
public static final String TRANSITION_NEXT = "transition_next";
public static final String TRANSITION_PREVIOUS = "transition_previous";

View File

@@ -9,43 +9,161 @@
package org.libreoffice.impressremote.communication;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.HashMap;
import org.libreoffice.impressremote.Globals;
import org.libreoffice.impressremote.communication.Server.Protocol;
import java.util.Map;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
public class ServerFinder {
private Context mContext;
private static final int PORT = 1598;
private static final String GROUPADDRESS = "239.0.0.1";
private static final String CHARSET = "UTF-8";
private static final long SEARCH_INTERVAL = 1000 * 15;
public class ServerFinder implements Runnable {
private final Context mContext;
private DatagramSocket mSocket = null;
private Thread mListenerThread;
private boolean mFinishRequested;
private Thread mListenerThread = null;
private boolean mFinishRequested = false;
private HashMap<String, Server> mServerList = new HashMap<String, Server>();
private final Map<String, Server> mServers;
public ServerFinder(Context aContext) {
mContext = aContext;
mSocket = null;
mListenerThread = null;
mFinishRequested = false;
mServers = new HashMap<String, Server>();
}
public void startSearch() {
if (mSocket != null) {
return;
}
mFinishRequested = false;
if (mListenerThread == null) {
mListenerThread = new Thread(this);
}
mListenerThread.start();
}
@Override
public void run() {
addLocalServerForEmulator();
long aStartSearchTime = 0;
setUpSearchSocket();
while (!mFinishRequested) {
if (System
.currentTimeMillis() - aStartSearchTime > 1000 * 15) {
sendSearchCommand();
aStartSearchTime = System.currentTimeMillis();
removeStaleServers();
}
listenForServer();
}
}
/**
* Check whether we are on an emulator and add it's host to the list of
* servers if so (although we do not know whether libo is running on
* the host).
*/
private void addLocalServerForEmulator() {
if (!isLocalServerForEmulatorReachable()) {
return;
}
Server aServer = new Server(Server.Protocol.NETWORK,
Protocol.Addresses.SERVER_LOCAL_FOR_EMULATOR, "Android Emulator",
0);
mServers.put(aServer.getAddress(), aServer);
callUpdatingServersList();
}
private boolean isLocalServerForEmulatorReachable() {
try {
InetAddress aLocalServerAddress = InetAddress
.getByName(Protocol.Addresses.SERVER_LOCAL_FOR_EMULATOR);
return aLocalServerAddress.isReachable(100);
} catch (UnknownHostException e) {
return false;
} catch (IOException e) {
return false;
}
}
private void callUpdatingServersList() {
Intent aIntent = new Intent(
CommunicationService.MSG_SERVERLIST_CHANGED);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(aIntent);
}
private void setUpSearchSocket() {
try {
mSocket = new DatagramSocket();
mSocket.setSoTimeout(1000 * 10);
} catch (SocketException e) {
throw new RuntimeException("Unable to open search socket.");
}
}
private void sendSearchCommand() {
try {
mSocket.send(buildSearchPacket());
} catch (IOException e) {
throw new RuntimeException("Unable to send search packet.");
}
}
private DatagramPacket buildSearchPacket() {
try {
String aSearchCommand = Protocol.Commands
.prepareCommand(Protocol.Commands.SEARCH_SERVERS);
DatagramPacket aSearchPacket = new DatagramPacket(
aSearchCommand.getBytes(), aSearchCommand.length());
aSearchPacket.setAddress(
InetAddress.getByName(Protocol.Addresses.SERVER_SEARCH));
aSearchPacket.setPort(Protocol.Ports.SERVER_SEARCH);
return aSearchPacket;
} catch (UnknownHostException e) {
throw new RuntimeException("Unable to find address to search.");
}
}
private void removeStaleServers() {
for (Server aServer : mServers.values()) {
if (aServer.mNoTimeout) {
continue;
}
if (System.currentTimeMillis()
- aServer
.getTimeDiscovered() > 60 * 1000) {
mServers
.remove(aServer.getAddress());
callUpdatingServersList();
}
}
}
private void listenForServer() {
@@ -59,7 +177,8 @@ public class ServerFinder {
int i;
for (i = 0; i < aBuffer.length; i++) {
if (aPacket.getData()[i] == '\n') {
aCommand = new String(aPacket.getData(), 0, i, CHARSET);
aCommand = new String(aPacket.getData(), 0, i,
Protocol.CHARSET);
break;
}
}
@@ -69,7 +188,7 @@ public class ServerFinder {
for (int j = i + 1; j < aBuffer.length; j++) {
if (aPacket.getData()[j] == '\n') {
aName = new String(aPacket.getData(), i + 1, j - (i + 1),
CHARSET);
Protocol.CHARSET);
break;
}
}
@@ -77,13 +196,12 @@ public class ServerFinder {
return;
}
Server aServer = new Server(Server.Protocol.NETWORK, aPacket
.getAddress().getHostAddress(), aName,
System.currentTimeMillis());
mServerList.put(aServer.getAddress(), aServer);
Log.i(Globals.TAG, "ServerFinder.listenForServer: contains " + aName);
.getAddress().getHostAddress(), aName,
System.currentTimeMillis());
mServers.put(aServer.getAddress(), aServer);
notifyActivity();
} catch (java.net.SocketTimeoutException e) {
callUpdatingServersList();
} catch (SocketTimeoutException e) {
// Ignore -- we want to timeout to enable checking whether we
// should stop listening periodically
} catch (IOException e) {
@@ -91,100 +209,17 @@ public class ServerFinder {
}
public void startFinding() {
if (mSocket != null)
return;
mFinishRequested = false;
public void stopSearch() {
if (mListenerThread == null) {
mListenerThread = new Thread() {
@Override
public void run() {
checkAndAddEmulator();
long aTime = 0;
try {
mSocket = new DatagramSocket();
mSocket.setSoTimeout(1000 * 10);
while (!mFinishRequested) {
if (System.currentTimeMillis() - aTime > SEARCH_INTERVAL) {
String aString = "LOREMOTE_SEARCH\n";
DatagramPacket aPacket = new DatagramPacket(
aString.getBytes(CHARSET),
aString.length(),
InetAddress.getByName(GROUPADDRESS),
PORT);
mSocket.send(aPacket);
aTime = System.currentTimeMillis();
// Remove stale servers
for (Server aServer : mServerList.values()) {
if (!aServer.mNoTimeout
&& System.currentTimeMillis()
- aServer.getTimeDiscovered() > 60 * 1000) {
mServerList.remove(aServer.getAddress());
notifyActivity();
}
}
}
listenForServer();
}
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
mListenerThread.start();
return;
}
mFinishRequested = true;
mListenerThread = null;
}
public void stopFinding() {
if (mListenerThread != null) {
mFinishRequested = true;
mListenerThread = null;
}
}
/**
* Check whether we are on an emulator and add it's host to the list of
* servers if so (although we do not know whether libo is running on
* the host).
*/
private void checkAndAddEmulator() {
try {
if (InetAddress.getByName("10.0.2.2").isReachable(100)) {
Log.i(Globals.TAG, "ServerFinder.checkAndAddEmulator: NulledNot, whatever that is supposed to mean");
Server aServer = new Server(Protocol.NETWORK, "10.0.2.2",
"Android Emulator Host", 0);
aServer.mNoTimeout = true;
mServerList.put(aServer.getAddress(), aServer);
notifyActivity();
}
} catch (IOException e) {
// Probably means we can't connect -- i.e. no emulator host
}
}
/**
* Notify the activity that the server list has changed.
*/
private void notifyActivity() {
Intent aIntent = new Intent(CommunicationService.MSG_SERVERLIST_CHANGED);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(aIntent);
}
public Collection<Server> getServerList() {
return mServerList.values();
public Collection<Server> getServers() {
return mServers.values();
}
}