NfcLauncherActivity.java
Go to the documentation of this file.
00001 package com.github.rosjava.android_remocons.rocon_remocon;
00002 
00003 import android.content.Context;
00004 import android.content.Intent;
00005 import android.net.wifi.WifiManager;
00006 import android.nfc.NfcAdapter;
00007 import android.os.AsyncTask;
00008 import android.os.Bundle;
00009 import android.os.Vibrator;
00010 import android.util.Log;
00011 import android.widget.Toast;
00012 
00013 import com.github.robotics_in_concert.rocon_rosjava_core.rocon_interactions.InteractionMode;
00014 import com.github.robotics_in_concert.rocon_rosjava_core.rosjava_utils.ByteArrays;
00015 import com.github.rosjava.android_remocons.common_tools.master.ConcertChecker;
00016 import com.github.rosjava.android_remocons.common_tools.master.MasterId;
00017 import com.github.rosjava.android_remocons.common_tools.master.RoconDescription;
00018 import com.github.rosjava.android_remocons.common_tools.nfc.NfcManager;
00019 import com.github.rosjava.android_remocons.common_tools.nfc.NfcReaderActivity;
00020 import com.github.rosjava.android_remocons.common_tools.rocon.Constants;
00021 import com.github.rosjava.android_remocons.common_tools.system.WifiChecker;
00022 
00023 import java.util.concurrent.ExecutionException;
00024 import java.util.concurrent.TimeUnit;
00025 import java.util.concurrent.TimeoutException;
00026 
00027 import static com.github.rosjava.android_remocons.common_tools.rocon.Constants.NFC_MASTER_HOST_FIELD_LENGTH;
00028 import static com.github.rosjava.android_remocons.common_tools.rocon.Constants.NFC_PASSWORD_FIELD_LENGTH;
00029 import static com.github.rosjava.android_remocons.common_tools.rocon.Constants.NFC_PAYLOAD_LENGTH;
00030 import static com.github.rosjava.android_remocons.common_tools.rocon.Constants.NFC_SSID_FIELD_LENGTH;
00031 
00035 public class NfcLauncherActivity extends NfcReaderActivity {
00036 
00037     private enum Step {
00038         STARTUP,
00039         CONNECT_SSID,
00040         CHECK_CONCERT,
00041         START_CONCERT,
00042         ABORT_LAUNCH;
00043 
00044         public Step next() {
00045             return this.ordinal() < Step.values().length - 1
00046                     ? Step.values()[this.ordinal() + 1]
00047                     : null;
00048         }
00049     }
00050 
00051     private Step launchStep = Step.STARTUP;
00052     private Toast lastToast;
00053     private Vibrator vibrator;
00054     private NfcManager nfcManager;
00055 
00056     private String ssid;
00057     private String password;
00058     private String masterHost;
00059     private short  masterPort;
00060     private MasterId masterId;
00061     private RoconDescription concert;
00062 
00063 
00064         @Override
00065         public void onCreate(Bundle savedInstanceState) {
00066             super.onCreate(savedInstanceState);
00067 
00068         try {
00069             vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
00070             vibrator.vibrate(500);
00071             toast(getString(R.string.app_name) + " started", Toast.LENGTH_SHORT);
00072 
00073             Intent intent = getIntent();
00074             String action = intent.getAction();
00075             Log.d("NfcLaunch", action + " action started");
00076 
00077             nfcManager = new NfcManager(this);
00078             nfcManager.onNewIntent(intent);
00079 
00080             if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action) == false)
00081             {
00082                 // This should not append unless we are debugging
00083                 throw new Exception("Not started by NDEF_DISCOVERED action; this activity is only intended to run that way");
00084             }
00085 
00086             //** Step 1. Parsing NFC Data
00087             parseNFCData();
00088 
00089             Log.i("NfcLaunch", "NFC tag read");
00090             toast("NFC tag read", Toast.LENGTH_SHORT);
00091 
00092             //** Step 2. Connect to SSID
00093             connectToSSID();
00094 
00095             Log.i("NfcLaunch", "Connected to " + ssid);
00096             toast("Connected to " + ssid, Toast.LENGTH_SHORT);
00097 
00098                 //** Step 3. Validate the concert: check for specific topics on masterUri
00099             checkConcert();
00100 
00101             Log.i("NfcLaunch", "Concert " + masterId.getMasterUri() + " up and running");
00102             toast("Concert " + masterId.getMasterUri() + " up and running", Toast.LENGTH_SHORT);
00103 
00104             //** Step 4. Start the concert!
00105             startConcert();
00106 
00107             //** Terminate this app
00108             finish();
00109         }
00110         catch (Exception e) {
00111             // TODO make and "error sound"
00112             Log.e("NfcLaunch", e.getMessage());
00113             toast(e.getMessage(), Toast.LENGTH_LONG);
00114             finish();
00115         }
00116         }
00117 
00118         @Override
00119         public void onResume() {
00120                 super.onResume();
00121         }
00122         
00123         @Override
00124         public void onPause() {
00125                 super.onPause();
00126         }
00127 
00128     private void parseNFCData() throws Exception {
00129         byte[] payload = nfcManager.getPayload();
00130         if (payload.length != NFC_PAYLOAD_LENGTH + 3) // 1 byte for status and 2 lang bytes
00131         {
00132             throw new Exception("Payload doesn't match expected length: "
00133                     + payload.length +" != " + NFC_PAYLOAD_LENGTH);
00134         }
00135 
00136         int offset = 3; // skip 1 byte for status and 2 lang bytes
00137         ssid       = ByteArrays.toString(payload, offset, NFC_SSID_FIELD_LENGTH).trim();
00138         offset    += NFC_SSID_FIELD_LENGTH;
00139         password   = ByteArrays.toString(payload, offset, NFC_PASSWORD_FIELD_LENGTH).trim();
00140         offset    += NFC_PASSWORD_FIELD_LENGTH;
00141         masterHost = ByteArrays.toString(payload, offset, NFC_MASTER_HOST_FIELD_LENGTH).trim();
00142         offset    += NFC_MASTER_HOST_FIELD_LENGTH;
00143         masterPort = ByteArrays.toShort(payload, offset);
00144 
00145         launchStep = launchStep.next();
00146     }
00147 
00148     private void connectToSSID() throws Exception {
00149         String masterUri  = "http://" + masterHost + ":" + masterPort;
00150         String encryption = "WPA2";    // not needed
00151         masterId = new MasterId(masterUri, ssid, encryption, password);
00152 
00153         final WifiChecker wc = new WifiChecker(
00154                 new WifiChecker.SuccessHandler() {
00155                     public void handleSuccess() {
00156                         launchStep = launchStep.next();
00157                     }
00158                 },
00159                 new WifiChecker.FailureHandler() {
00160                     public void handleFailure(String reason) {
00161                         launchStep = Step.ABORT_LAUNCH;
00162                     }
00163                 },
00164                 new WifiChecker.ReconnectionHandler() {
00165                     public boolean doReconnection(String from, String to) {
00166                         // TODO should I ask for permit? maybe it's a bit rude to switch network without asking!
00167                         Log.i("NfcLaunch", "Switching from " + from + " to " + to);
00168                         toast("Switching from " + from + " to " + to, Toast.LENGTH_SHORT);
00169                         return true;
00170                     }
00171                 }
00172         );
00173         toast("Connecting to " + ssid + "...", Toast.LENGTH_SHORT);
00174         wc.beginChecking(masterId, (WifiManager) getSystemService(WIFI_SERVICE));
00175 
00176         if (waitFor(Step.CHECK_CONCERT, 15) == false) {
00177             throw new Exception("Cannot connect to " + ssid + ". Aborting app launch");
00178         }
00179     }
00180 
00181     private void checkConcert() throws Exception {
00182         final ConcertChecker cc = new ConcertChecker(
00183                 new ConcertChecker.ConcertDescriptionReceiver() {
00184                     public void receive(RoconDescription concertDescription) {
00185                         concert = concertDescription;
00186                         if ( concert.getConnectionStatus() == RoconDescription.UNAVAILABLE ) {
00187                             // Check that it's not busy
00188                             Log.e("NfcLaunch", "Concert is unavailable: busy serving another remote controller");
00189                             launchStep = Step.ABORT_LAUNCH;
00190                         } else {
00191                             launchStep = launchStep.next();
00192                         }
00193                     }
00194                 },
00195                 new ConcertChecker.FailureHandler() {
00196                     public void handleFailure(String reason) {
00197                         Log.e("NfcLaunch", "Cannot contact ROS master: " + reason);
00198                         launchStep = Step.ABORT_LAUNCH;
00199                     }
00200                 }
00201         );
00202         toast("Validating " + masterId.getMasterUri() + "...", Toast.LENGTH_SHORT);
00203         cc.beginChecking(masterId);
00204 
00205         if (waitFor(Step.START_CONCERT, 10) == false) {
00206             throw new Exception("Cannot connect to " + masterId.getMasterUri() + ". Aborting app launch");
00207         }
00208     }
00209 
00210     private void startConcert() throws Exception {
00211         Intent intent = new Intent("Remocon");
00212         intent.putExtra(RoconDescription.UNIQUE_KEY, concert);
00213         intent.putExtra(Constants.ACTIVITY_SWITCHER_ID + "." + InteractionMode.CONCERT + "_app_name", "NfcLauncher");
00214         startActivity(intent);
00215     }
00216 
00217     private boolean waitFor(final Step step, final int timeout) {
00218         AsyncTask<Void, Void, Boolean> asyncTask = new AsyncTask<Void, Void, Boolean>() {
00219             @Override
00220             protected Boolean doInBackground(Void... params) {
00221                 while ((launchStep != step) && (launchStep != Step.ABORT_LAUNCH)) {
00222                     try { Thread.sleep(200); }
00223                     catch (InterruptedException e) { return false; }
00224                 }
00225                 return (launchStep == step); // returns false on ABORT_LAUNCH or InterruptedException
00226             }
00227         }.execute();
00228         try {
00229             return asyncTask.get(timeout, TimeUnit.SECONDS);
00230         } catch (InterruptedException e) {
00231             Log.e("NfcLaunch", "Async task interrupted. " + e.getMessage());
00232             return false;
00233         } catch (ExecutionException e) {
00234             Log.e("NfcLaunch", "Async task execution error. " + e.getMessage());
00235             return false;
00236         } catch (TimeoutException e) {
00237             Log.e("NfcLaunch", "Async task timeout (" + timeout + " s). " + e.getMessage());
00238             return false;
00239         }
00240     }
00241 
00242     private void toast(final String message, final int length) {
00243         runOnUiThread(new Runnable() {
00244             @Override
00245             public void run() {
00246                 // We overwrite only short duration toast, as the long ones are normally important
00247                 if ((lastToast != null) && (lastToast.getDuration() == Toast.LENGTH_SHORT))
00248                     lastToast.cancel();
00249                 lastToast = Toast.makeText(getBaseContext(), message, length);
00250                 lastToast.show();
00251             }
00252         });
00253     }
00254 }


android_remocons
Author(s): Daniel Stonier, Kazuto Murase
autogenerated on Sat Jun 8 2019 19:32:24