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
00083 throw new Exception("Not started by NDEF_DISCOVERED action; this activity is only intended to run that way");
00084 }
00085
00086
00087 parseNFCData();
00088
00089 Log.i("NfcLaunch", "NFC tag read");
00090 toast("NFC tag read", Toast.LENGTH_SHORT);
00091
00092
00093 connectToSSID();
00094
00095 Log.i("NfcLaunch", "Connected to " + ssid);
00096 toast("Connected to " + ssid, Toast.LENGTH_SHORT);
00097
00098
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
00105 startConcert();
00106
00107
00108 finish();
00109 }
00110 catch (Exception e) {
00111
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)
00131 {
00132 throw new Exception("Payload doesn't match expected length: "
00133 + payload.length +" != " + NFC_PAYLOAD_LENGTH);
00134 }
00135
00136 int offset = 3;
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";
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
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
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);
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
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 }