diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 96a0120..89bc1ea 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -6,7 +6,7 @@ + android:targetSdkVersion="19" /> @@ -21,6 +21,7 @@ + - + android:inputType="text" /> + --> diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 7a7935b..653f02a 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -7,7 +7,7 @@ Cámara abierta exitosamente La cámara no pudo abrirse Conectar con NxtAR - Encender cámara + Conectar con dispositivo controlador CamActivity Dirección IP de NxtAR La dirección IP no es válida diff --git a/res/values/strings.xml b/res/values/strings.xml index 81d9ef4..03383cc 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8,7 +8,7 @@ Camera could not be opened CamActivity Connect with NxtAR - Start camera + Connect with controller device NxtAR IP Address Invalid IP address Fill out the IP address field diff --git a/src/ve/ucv/ciens/ccg/nxtcam/CamActivity.java b/src/ve/ucv/ciens/ccg/nxtcam/CamActivity.java index 514d281..244308f 100644 --- a/src/ve/ucv/ciens/ccg/nxtcam/CamActivity.java +++ b/src/ve/ucv/ciens/ccg/nxtcam/CamActivity.java @@ -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.ProjectConstants; import android.app.Activity; +import android.content.Intent; import android.hardware.Camera; import android.os.AsyncTask; import android.os.Bundle; @@ -23,6 +24,7 @@ public class CamActivity extends Activity{ private CameraPreview cPreview; private CameraSetupTask camSetupTask; private ImageTransferThread imThread; + private String serverIp; /******************* * Android methods * @@ -35,6 +37,9 @@ public class CamActivity extends Activity{ cPreview = new CameraPreview(this, hwCamera); setContentView(cPreview); + Intent intent = getIntent(); + serverIp = intent.getStringExtra("address"); + imThread = new ImageTransferThread(); } diff --git a/src/ve/ucv/ciens/ccg/nxtcam/MainActivity.java b/src/ve/ucv/ciens/ccg/nxtcam/MainActivity.java index a076766..3f93625 100644 --- a/src/ve/ucv/ciens/ccg/nxtcam/MainActivity.java +++ b/src/ve/ucv/ciens/ccg/nxtcam/MainActivity.java @@ -1,13 +1,18 @@ package ve.ucv.ciens.ccg.nxtcam; +import java.io.IOException; +import java.net.DatagramPacket; import java.net.InetAddress; +import java.net.MulticastSocket; import java.net.UnknownHostException; import ve.ucv.ciens.ccg.nxtcam.utils.Logger; +import ve.ucv.ciens.ccg.nxtcam.utils.ProjectConstants; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.wifi.WifiManager; +import android.net.wifi.WifiManager.MulticastLock; import android.os.AsyncTask; import android.os.Bundle; import android.view.Menu; @@ -16,14 +21,32 @@ import android.widget.Button; import android.widget.TextView; 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 { + // Cosntant fields. private final String TAG = "NXTCAM_MAIN"; private final String CLASS_NAME = MainActivity.class.getSimpleName(); + // Gui components. private Button startButton; - private TextView ipField; + //private TextView ipField; + // Resources. private WifiManager wifiManager; + + // Variables. private boolean wifiOnByMe; @Override @@ -31,35 +54,34 @@ public class MainActivity extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + // Set up fields. + wifiOnByMe = false; + + // Set up gui components. startButton = (Button)findViewById(R.id.startButton); startButton.setOnClickListener(startClickListener); + // ipField = (TextView)findViewById(R.id.ipAddressField); - ipField = (TextView)findViewById(R.id.ipAddressField); - + // Set up services. wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE); - - if(!wifiManager.isWifiEnabled()) setWifi(true); + if(!wifiManager.isWifiEnabled()) + setWifi(true); } @Override public void onResume(){ super.onResume(); - if(!wifiManager.isWifiEnabled()) setWifi(true); + if(!wifiManager.isWifiEnabled()) + setWifi(true); } @Override public void onPause(){ super.onPause(); - if(wifiManager.isWifiEnabled() && wifiOnByMe) setWifi(false); - } - - @Override - public void onDestroy(){ - super.onDestroy(); - - if(wifiManager.isWifiEnabled() && wifiOnByMe) wifiManager.setWifiEnabled(false); + if(wifiManager.isWifiEnabled() && wifiOnByMe) + setWifi(false); } @Override @@ -69,10 +91,17 @@ public class MainActivity extends Activity { 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."); Intent intent = new Intent(this, CamActivity.class); + intent.putExtra("address", ipAddress); startActivity(intent); }else{ 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); - Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + ".ImageTransferThread() :: setting wifi to " + (on ? "on" : "off")); - if(on) + /** + * Sets the state of the device's WiFi radio. + * + * @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; else wifiOnByMe = false; } - private void validateIpAddress(){ + /*private void validateIpAddress(){ if(ipField.getText().toString().compareTo("") != 0){ Logger.log(Logger.LOG_TYPES.DEBUG, TAG, CLASS_NAME + "validateIpAddress() :: Launching verification task."); 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."); Toast.makeText(this, R.string.emptyIpToast, Toast.LENGTH_SHORT).show(); } - } + }*/ + /** + * Event listener for the connection button. + */ private final View.OnClickListener startClickListener = new View.OnClickListener() { @Override public void onClick(View v) { - validateIpAddress(); + //validateIpAddress(); + ServiceDiscoveryTask serviceDiscovery = new ServiceDiscoveryTask(); + serviceDiscovery.execute(); } }; - private class VerifyIpAddressTask extends AsyncTask{ + /** + * Asynchronous task for ad hoc UDP service discovery. + * + * @author Miguel Angel Astor Romero + */ + private class ServiceDiscoveryTask extends AsyncTask{ + + 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{ private final String CLASS_NAME = VerifyIpAddressTask.class.getSimpleName(); @Override @@ -127,5 +238,5 @@ public class MainActivity extends Activity { super.onPostExecute(result); startCamActivity(result); } - }; + };*/ } diff --git a/src/ve/ucv/ciens/ccg/nxtcam/network/ServiceDiscoveryThread.java b/src/ve/ucv/ciens/ccg/nxtcam/network/ServiceDiscoveryThread.java deleted file mode 100644 index 48b960f..0000000 --- a/src/ve/ucv/ciens/ccg/nxtcam/network/ServiceDiscoveryThread.java +++ /dev/null @@ -1,13 +0,0 @@ -package ve.ucv.ciens.ccg.nxtcam.network; - -public class ServiceDiscoveryThread extends Thread { - - public ServiceDiscoveryThread(){ - - } - - @Override - public void run(){ - - } -} diff --git a/src/ve/ucv/ciens/ccg/nxtcam/utils/ProjectConstants.java b/src/ve/ucv/ciens/ccg/nxtcam/utils/ProjectConstants.java index 6a29166..aed7b89 100644 --- a/src/ve/ucv/ciens/ccg/nxtcam/utils/ProjectConstants.java +++ b/src/ve/ucv/ciens/ccg/nxtcam/utils/ProjectConstants.java @@ -5,5 +5,7 @@ public abstract class ProjectConstants { public static final int SERVER_TCP_PORT_1 = 9989; 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; }