Setting up service discovery

This commit is contained in:
2013-11-05 18:22:25 -04:30
parent 9635163950
commit 5663418b47
9 changed files with 151 additions and 44 deletions

View File

@@ -6,7 +6,7 @@
<uses-sdk <uses-sdk
android:minSdkVersion="10" android:minSdkVersion="10"
android:targetSdkVersion="18" /> android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -21,6 +21,7 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<application <application
android:allowBackup="true" android:allowBackup="true"

View File

@@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target. # Project target.
target=android-18 target=android-19

View File

@@ -17,7 +17,7 @@
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:text="@string/start_button" /> android:text="@string/start_button" />
<EditText <!-- <EditText
android:id="@+id/ipAddressField" android:id="@+id/ipAddressField"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -34,5 +34,6 @@
android:layout_alignBottom="@+id/ipAddressField" android:layout_alignBottom="@+id/ipAddressField"
android:layout_toLeftOf="@+id/ipAddressField" android:layout_toLeftOf="@+id/ipAddressField"
android:text="@string/ipAddressLabel" /> android:text="@string/ipAddressLabel" />
-->
</RelativeLayout> </RelativeLayout>

View File

@@ -7,7 +7,7 @@
<string name="camera_success">Cámara abierta exitosamente</string> <string name="camera_success">Cámara abierta exitosamente</string>
<string name="camera_failure">La cámara no pudo abrirse</string> <string name="camera_failure">La cámara no pudo abrirse</string>
<string name="get_server_button">Conectar con NxtAR</string> <string name="get_server_button">Conectar con NxtAR</string>
<string name="start_button">Encender cámara</string> <string name="start_button">Conectar con dispositivo controlador</string>
<string name="title_activity_cam">CamActivity</string> <string name="title_activity_cam">CamActivity</string>
<string name="ipAddressLabel">Dirección IP de NxtAR</string> <string name="ipAddressLabel">Dirección IP de NxtAR</string>
<string name="badIpToast">La dirección IP no es válida</string> <string name="badIpToast">La dirección IP no es válida</string>

View File

@@ -8,7 +8,7 @@
<string name="camera_failure">Camera could not be opened</string> <string name="camera_failure">Camera could not be opened</string>
<string name="title_activity_cam">CamActivity</string> <string name="title_activity_cam">CamActivity</string>
<string name="get_server_button">Connect with NxtAR</string> <string name="get_server_button">Connect with NxtAR</string>
<string name="start_button">Start camera</string> <string name="start_button">Connect with controller device</string>
<string name="ipAddressLabel">NxtAR IP Address</string> <string name="ipAddressLabel">NxtAR IP Address</string>
<string name="badIpToast">Invalid IP address</string> <string name="badIpToast">Invalid IP address</string>
<string name="emptyIpToast">Fill out the IP address field</string> <string name="emptyIpToast">Fill out the IP address field</string>

View File

@@ -5,6 +5,7 @@ import ve.ucv.ciens.ccg.nxtcam.network.ImageTransferThread;
import ve.ucv.ciens.ccg.nxtcam.utils.Logger; import ve.ucv.ciens.ccg.nxtcam.utils.Logger;
import ve.ucv.ciens.ccg.nxtcam.utils.ProjectConstants; import ve.ucv.ciens.ccg.nxtcam.utils.ProjectConstants;
import android.app.Activity; import android.app.Activity;
import android.content.Intent;
import android.hardware.Camera; import android.hardware.Camera;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
@@ -23,6 +24,7 @@ public class CamActivity extends Activity{
private CameraPreview cPreview; private CameraPreview cPreview;
private CameraSetupTask camSetupTask; private CameraSetupTask camSetupTask;
private ImageTransferThread imThread; private ImageTransferThread imThread;
private String serverIp;
/******************* /*******************
* Android methods * * Android methods *
@@ -35,6 +37,9 @@ public class CamActivity extends Activity{
cPreview = new CameraPreview(this, hwCamera); cPreview = new CameraPreview(this, hwCamera);
setContentView(cPreview); setContentView(cPreview);
Intent intent = getIntent();
serverIp = intent.getStringExtra("address");
imThread = new ImageTransferThread(); imThread = new ImageTransferThread();
} }

View File

@@ -1,13 +1,18 @@
package ve.ucv.ciens.ccg.nxtcam; package ve.ucv.ciens.ccg.nxtcam;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import ve.ucv.ciens.ccg.nxtcam.utils.Logger; import ve.ucv.ciens.ccg.nxtcam.utils.Logger;
import ve.ucv.ciens.ccg.nxtcam.utils.ProjectConstants;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.MulticastLock;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
@@ -16,14 +21,32 @@ import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
/**
* Entry point por the NxtCAM application.
*
* This activity shows a splashscreen and handles the search for the controller device
* via a simle ad hoc UDP based service discovery method. Basically, it just listens for
* multicast packets sent by the controller device on the multicast address defined in
* ProjectConstants.java. When the packet is received the next activity of the application
* is launched with the ip address found. The service discovery process continues until a
* datagram carrying the string "NxtAR server here!" is received.
*
* @author miky
*
*/
public class MainActivity extends Activity { public class MainActivity extends Activity {
// Cosntant fields.
private final String TAG = "NXTCAM_MAIN"; private final String TAG = "NXTCAM_MAIN";
private final String CLASS_NAME = MainActivity.class.getSimpleName(); private final String CLASS_NAME = MainActivity.class.getSimpleName();
// Gui components.
private Button startButton; private Button startButton;
private TextView ipField; //private TextView ipField;
// Resources.
private WifiManager wifiManager; private WifiManager wifiManager;
// Variables.
private boolean wifiOnByMe; private boolean wifiOnByMe;
@Override @Override
@@ -31,35 +54,34 @@ public class MainActivity extends Activity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
// Set up fields.
wifiOnByMe = false;
// Set up gui components.
startButton = (Button)findViewById(R.id.startButton); startButton = (Button)findViewById(R.id.startButton);
startButton.setOnClickListener(startClickListener); startButton.setOnClickListener(startClickListener);
// ipField = (TextView)findViewById(R.id.ipAddressField);
ipField = (TextView)findViewById(R.id.ipAddressField); // Set up services.
wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE); wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
if(!wifiManager.isWifiEnabled())
if(!wifiManager.isWifiEnabled()) setWifi(true); setWifi(true);
} }
@Override @Override
public void onResume(){ public void onResume(){
super.onResume(); super.onResume();
if(!wifiManager.isWifiEnabled()) setWifi(true); if(!wifiManager.isWifiEnabled())
setWifi(true);
} }
@Override @Override
public void onPause(){ public void onPause(){
super.onPause(); super.onPause();
if(wifiManager.isWifiEnabled() && wifiOnByMe) setWifi(false); if(wifiManager.isWifiEnabled() && wifiOnByMe)
} setWifi(false);
@Override
public void onDestroy(){
super.onDestroy();
if(wifiManager.isWifiEnabled() && wifiOnByMe) wifiManager.setWifiEnabled(false);
} }
@Override @Override
@@ -69,10 +91,17 @@ public class MainActivity extends Activity {
return true; return true;
} }
private void startCamActivity(boolean canStart){ /**
if(canStart){ * Start the camera capture activity if a server was found through service discovery.
*
* @param serverFound Indicates if a server was found, doh!
* @param ipAddress The ip address of the server.
*/
private void startCamActivity(boolean serverFound, String ipAddress){
if(serverFound){
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".startCamActivity() :: Launching camera activity."); Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".startCamActivity() :: Launching camera activity.");
Intent intent = new Intent(this, CamActivity.class); Intent intent = new Intent(this, CamActivity.class);
intent.putExtra("address", ipAddress);
startActivity(intent); startActivity(intent);
}else{ }else{
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".startCamActivity() :: Cannot launch camera activity."); Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".startCamActivity() :: Cannot launch camera activity.");
@@ -80,16 +109,21 @@ public class MainActivity extends Activity {
} }
} }
private void setWifi(boolean on){ /**
wifiManager.setWifiEnabled(on); * Sets the state of the device's WiFi radio.
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".ImageTransferThread() :: setting wifi to " + (on ? "on" : "off")); *
if(on) * @param radioState The state to set the radio to; true for on, false for off.
*/
private void setWifi(boolean radioState){
wifiManager.setWifiEnabled(radioState);
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".setWifi() :: setting wifi to " + (radioState ? "on" : "off"));
if(radioState)
wifiOnByMe = true; wifiOnByMe = true;
else else
wifiOnByMe = false; wifiOnByMe = false;
} }
private void validateIpAddress(){ /*private void validateIpAddress(){
if(ipField.getText().toString().compareTo("") != 0){ if(ipField.getText().toString().compareTo("") != 0){
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + "validateIpAddress() :: Launching verification task."); Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + "validateIpAddress() :: Launching verification task.");
VerifyIpAddressTask verifyIp = new VerifyIpAddressTask(); VerifyIpAddressTask verifyIp = new VerifyIpAddressTask();
@@ -98,16 +132,93 @@ public class MainActivity extends Activity {
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + "validateIpAddress() :: Ip address field is empty."); Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + "validateIpAddress() :: Ip address field is empty.");
Toast.makeText(this, R.string.emptyIpToast, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.emptyIpToast, Toast.LENGTH_SHORT).show();
} }
} }*/
/**
* Event listener for the connection button.
*/
private final View.OnClickListener startClickListener = new View.OnClickListener() { private final View.OnClickListener startClickListener = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
validateIpAddress(); //validateIpAddress();
ServiceDiscoveryTask serviceDiscovery = new ServiceDiscoveryTask();
serviceDiscovery.execute();
} }
}; };
private class VerifyIpAddressTask extends AsyncTask<String, Void, Boolean>{ /**
* Asynchronous task for ad hoc UDP service discovery.
*
* @author Miguel Angel Astor Romero
*/
private class ServiceDiscoveryTask extends AsyncTask<Void, Void, Boolean>{
private final String CLASS_NAME = ServiceDiscoveryTask.class.getSimpleName();
private MulticastSocket udpSocket;
private DatagramPacket packet;
private MulticastLock multicastLock;
public ServiceDiscoveryTask(){
// Open a multicast socket and join the project's multicast group.
try{
udpSocket = new MulticastSocket(ProjectConstants.SERVER_UDP_PORT);
InetAddress group = InetAddress.getByName(ProjectConstants.MULTICAST_ADDRESS);
udpSocket.joinGroup(group);
}catch(IOException io){
Logger.log(Logger.LOG_TYPES.ERROR, TAG ,CLASS_NAME + ".ServiceDiscoveryTask() :: " + io.getMessage());
}
}
@Override
protected Boolean doInBackground(Void... params){
boolean result, done = false;
byte[] buffer = (new String("Server is here")).getBytes();
// Create a buffer and tell Android we want to receive multicast datagrams.
packet = new DatagramPacket(buffer, buffer.length);
multicastLock = wifiManager.createMulticastLock(TAG);
multicastLock.setReferenceCounted(true);
multicastLock.acquire();
// Listen for a UDP datagram on the multicast group.
// If the datagram received contains a string with it's content equal to "NxtAR server here!"
// then assume the server found is a valid controller device.
try{
while(!done){
udpSocket.receive(packet);
Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".run() :: Found a server at " + packet.getAddress().getHostAddress());
String received = new String(packet.getData());
if(received.compareTo("NxtAR server here!") == 0)
done = true;
}
result = true;
}catch(IOException io){
Logger.log(Logger.LOG_TYPES.ERROR, TAG, CLASS_NAME + ".doInBackground() :: " + io.getMessage());
result = false;
}
// Tell Android we do not want to receive more UDP datagrams to save battery life.
if(multicastLock != null){
multicastLock.release();
multicastLock = null;
}
return result;
}
@Override
protected void onPostExecute(Boolean result){
super.onPostExecute(result);
// If a server was found then start the next activity.
if(packet != null)
startCamActivity(result, packet.getAddress().getHostAddress());
else
startCamActivity(false, null);
}
};
/* private class VerifyIpAddressTask extends AsyncTask<String, Void, Boolean>{
private final String CLASS_NAME = VerifyIpAddressTask.class.getSimpleName(); private final String CLASS_NAME = VerifyIpAddressTask.class.getSimpleName();
@Override @Override
@@ -127,5 +238,5 @@ public class MainActivity extends Activity {
super.onPostExecute(result); super.onPostExecute(result);
startCamActivity(result); startCamActivity(result);
} }
}; };*/
} }

View File

@@ -1,13 +0,0 @@
package ve.ucv.ciens.ccg.nxtcam.network;
public class ServiceDiscoveryThread extends Thread {
public ServiceDiscoveryThread(){
}
@Override
public void run(){
}
}

View File

@@ -5,5 +5,7 @@ public abstract class ProjectConstants {
public static final int SERVER_TCP_PORT_1 = 9989; public static final int SERVER_TCP_PORT_1 = 9989;
public static final int SERVER_TCP_PORT_2 = 9990; public static final int SERVER_TCP_PORT_2 = 9990;
public static final String MULTICAST_ADDRESS = "230.0.0.1";
public static final boolean DEBUG = true; public static final boolean DEBUG = true;
} }