PageRenderTime 62ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/vrpn_android/comp523android/src/edu/unc/cs/vrpn/Main.java

https://gitlab.com/sat-metalab/vrpn
Java | 513 lines | 324 code | 70 blank | 119 comment | 34 complexity | aa1284341f5446dbce36692f515dfafd MD5 | raw file
  1. package edu.unc.cs.vrpn;
  2. import java.io.IOException;
  3. import java.net.InetAddress;
  4. import java.net.NetworkInterface;
  5. import java.net.ServerSocket;
  6. import java.net.SocketException;
  7. import java.util.ArrayList;
  8. import java.util.Enumeration;
  9. import java.util.List;
  10. import java.util.Timer;
  11. import java.util.TimerTask;
  12. import jni.JniLayer;
  13. import android.app.Activity;
  14. import android.app.ProgressDialog;
  15. import android.content.Context;
  16. import android.hardware.Sensor;
  17. import android.hardware.SensorEventListener;
  18. import android.hardware.SensorManager;
  19. import android.os.Bundle;
  20. import android.os.Handler;
  21. import android.os.PowerManager;
  22. import android.os.PowerManager.WakeLock;
  23. import android.util.Log;
  24. import android.view.KeyEvent;
  25. import android.view.MotionEvent;
  26. import android.view.View;
  27. import android.view.Window;
  28. import android.view.View.OnTouchListener;
  29. import android.widget.Button;
  30. import android.widget.SeekBar;
  31. import android.widget.TextView;
  32. /**
  33. * The remote activity for the Android VRPN controller. This class is not very
  34. * pretty, but is the glue which holds the server together.
  35. *
  36. * @author UNC VRPN Team
  37. *
  38. */
  39. public class Main extends Activity implements OnTouchListener
  40. {
  41. /**
  42. * The interval (in milliseconds) between calls to vrpn's mainloop()
  43. * functions. Calling these on a timer prevents the client application
  44. * from complaining that it hasn't heard from the Android server.
  45. */
  46. public static final int IDLE_INTERVAL = 1000;
  47. /**
  48. * The Tag used by this class for logging
  49. */
  50. static final String TAG = "VRPN";
  51. /**
  52. * The buttons on the default on-screen interface
  53. */
  54. private ArrayList<Button> buttons;
  55. /**
  56. * The horizontal seek bar of the default on-screen interface
  57. */
  58. private SeekBar seekBar1;
  59. /**
  60. * The text area where multitouch coordinates are displayed
  61. */
  62. private TextView multitouchReport;
  63. /**
  64. * The text area where the device's IP address will be displayed
  65. */
  66. private TextView ipAddressReport;
  67. /**
  68. * The text area where the device's port number will be displayed
  69. */
  70. private TextView portNumberReport;
  71. /**
  72. * The text area where the Seek Bar data will be displayed
  73. */
  74. private TextView seekbarReport;
  75. /**
  76. * The text area where the Accelerometer data will be displayed
  77. */
  78. private TextView accelerometerReport;
  79. /**
  80. * The listener for the hardware accelerometer
  81. */
  82. private SensorEventListener listener;
  83. /**
  84. * The timer that calls mainloop to persist the connection
  85. */
  86. private Timer idleTimer;
  87. /**
  88. * Gets the sensor manager in use by the activity.
  89. */
  90. private SensorManager sensorManager;
  91. /**
  92. * The connection between Java and the C++ world of VRPN, using Android's Java Native
  93. * Interface, called the JNI
  94. */
  95. private JniLayer jniLayer;
  96. /**
  97. * The JNI creator object
  98. */
  99. private JniBuilder builder;
  100. /**
  101. * Analog channel for multitouch data
  102. */
  103. private JniBuilder.Analog multitouch;
  104. /**
  105. * Analog channel for accelerometer data
  106. */
  107. private JniBuilder.Analog accelerometer;
  108. private int multitouchAnalogNumber;
  109. /**
  110. * This prevents the phone from sleeping while the app is running. Is acquired in the
  111. * onResume event and released during onPause
  112. */
  113. private WakeLock wakeLock;
  114. /**
  115. * Listener for the Seek Bar data
  116. */
  117. private SeekBar.OnSeekBarChangeListener seekBarChangeListener;
  118. private ProgressDialog waitingForConnectionDialog;
  119. @Override
  120. public void onCreate(Bundle savedInstanceState)
  121. {
  122. super.onCreate(savedInstanceState);
  123. requestWindowFeature(Window.FEATURE_NO_TITLE);
  124. super.setContentView(R.layout.main);
  125. TextView view = (TextView) findViewById(R.id.touchpad);
  126. view.setOnTouchListener(this);
  127. this.multitouchReport = (TextView) findViewById(R.id.onScreenLog);
  128. this.ipAddressReport = (TextView) findViewById(R.id.ipAddress);
  129. this.portNumberReport = (TextView) findViewById(R.id.portNumber);
  130. this.accelerometerReport = (TextView) findViewById(R.id.accelerometerReport);
  131. this.seekbarReport = (TextView) findViewById(R.id.textView_track);
  132. this.buttons = new ArrayList<Button>();
  133. this.buttons.add((Button) findViewById(R.id.Button0));
  134. this.buttons.add((Button) findViewById(R.id.Button1));
  135. JniBuilder.Analog slider = new JniBuilder.Analog("Slider" , 1);
  136. this.multitouch = new JniBuilder.Analog("Multitouch" , 2);
  137. this.accelerometer = new JniBuilder.Analog("Accelerometer" , 3);
  138. this.builder = new JniBuilder();
  139. this.builder.add(this.multitouch , slider , this.accelerometer);
  140. this.builder.add(new JniBuilder.Button("Search") , new JniBuilder.Button() , new JniBuilder.Button());
  141. // the JNI Layer must be instantiated before the ButtonTouchListeners are created
  142. this.jniLayer = builder.toJni();
  143. this.multitouchAnalogNumber = this.builder.getAnalogIndex(this.multitouch);
  144. this.seekBar1 = (SeekBar) findViewById(R.id.textView_seekBar_01);
  145. this.seekBarChangeListener = new VrpnSeekBarChangeListener(this.builder , slider , this);
  146. this.seekBar1.setOnSeekBarChangeListener(this.seekBarChangeListener);
  147. this.setupButtons(this.buttons);
  148. PowerManager pm = (PowerManager) super.getSystemService(Context.POWER_SERVICE);
  149. this.wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK , Main.TAG);
  150. }
  151. @Override
  152. public boolean onKeyDown(int keyCode, KeyEvent event)
  153. {
  154. if (keyCode == KeyEvent.KEYCODE_SEARCH)
  155. {
  156. String message = "Search button "
  157. + ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0 ? "long " : "") + "pressed";
  158. Log.i(Main.TAG , message);
  159. this.logText(message);
  160. this.jniLayer.updateButtonStatus(0 , true);
  161. return true;
  162. }
  163. return super.onKeyDown(keyCode , event);
  164. }
  165. @Override
  166. public boolean onKeyUp(int keyCode, KeyEvent event)
  167. {
  168. if (keyCode == KeyEvent.KEYCODE_SEARCH)
  169. {
  170. String message = "Search button released";
  171. Log.i(Main.TAG , message);
  172. this.logText(message);
  173. this.jniLayer.updateButtonStatus(0 , false);
  174. // finish();
  175. //android.os.Process.killProcess(android.os.Process.myPid());
  176. return true;
  177. }
  178. return super.onKeyUp(keyCode , event);
  179. }
  180. @Override
  181. public void onPause()
  182. {
  183. super.onPause();
  184. if (this.idleTimer != null)
  185. {
  186. this.idleTimer.cancel();
  187. }
  188. if (this.listener != null)
  189. {
  190. this.sensorManager.unregisterListener(this.listener);
  191. }
  192. this.sensorManager = null;
  193. // disconnect from the JNI-VRPN bridge, and release the WakeLock to
  194. // allow the phone to sleep
  195. this.wakeLock.release();
  196. jniLayer.stop();
  197. }
  198. @Override
  199. public void onResume()
  200. {
  201. super.onResume();
  202. this.setupVrpn();
  203. }
  204. class WaitingForConnectionThread extends Thread
  205. {
  206. Handler handler;
  207. public WaitingForConnectionThread(Handler handler)
  208. {
  209. super();
  210. this.handler = handler;
  211. }
  212. public void run()
  213. {
  214. // this is the hack to allow reconnection to the client
  215. // after a disconnect. the hack polls for a open port,
  216. // and loops until the port is open/free'd by GC.
  217. boolean stop = false;
  218. ServerSocket server = null;
  219. while (!stop)
  220. {
  221. try
  222. {
  223. server = new ServerSocket(jniLayer.getPort());
  224. Log.i(TAG, "Connection found!");
  225. server.close();
  226. stop = true;
  227. }
  228. catch(IOException e)
  229. {
  230. if (waitingForConnectionDialog != null)
  231. {
  232. }
  233. Log.v(TAG, "Connecting...");
  234. try {
  235. Thread.sleep(1000);
  236. } catch (InterruptedException e1) {
  237. }
  238. }
  239. }
  240. jniLayer.start();
  241. try {
  242. Thread.sleep(250);
  243. } catch (InterruptedException e) {
  244. }
  245. handler.post(new GotConnectionThread());
  246. }
  247. }
  248. class GotConnectionThread extends Thread {
  249. public void run()
  250. {
  251. if (waitingForConnectionDialog != null)
  252. {
  253. waitingForConnectionDialog.dismiss();
  254. }
  255. }
  256. }
  257. /**
  258. * Creates/recreates the JniLayer and attaches the phone's sensors.
  259. * The content of this method was moved from onResume()
  260. */
  261. public void setupVrpn()
  262. {
  263. //////// This bit of code opens a new thread in case we need to wait for Dalvik to free up the port //////////
  264. waitingForConnectionDialog = ProgressDialog.show(Main.this , "" , "Connecting..." , true);
  265. Handler handler = new Handler();
  266. new WaitingForConnectionThread(handler).start();
  267. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  268. this.sensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);
  269. List<Sensor> sensors = this.sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
  270. if (!sensors.isEmpty())
  271. {
  272. this.listener = new AccelerometerListener(this.builder , this.accelerometer , this);
  273. boolean accelerationSupported = this.sensorManager.registerListener(this.listener ,
  274. sensors.get(0) , SensorManager.SENSOR_DELAY_UI);
  275. if (!accelerationSupported)
  276. {
  277. Log.w(Main.TAG , "Accelerometer offline");
  278. this.multitouchReport.setText("Accelerometer offline");
  279. }
  280. else
  281. {
  282. this.multitouchReport.setText("Accelerometer online");
  283. }
  284. }
  285. else
  286. {
  287. Log.w(Main.TAG , "Accelerometer not found");
  288. this.multitouchReport.setText("Accelerometer not found");
  289. }
  290. Log.i(TAG , "Retrieving IP Address...");
  291. // load the IP Address string from
  292. this.ipAddressReport.setText(String.format(super.getString(R.string.ip_address) , this.getIpAddress()));
  293. this.portNumberReport.setText(String.format(super.getString(R.string.port_number) ,
  294. Integer.toString(jniLayer.getPort())));
  295. this.wakeLock.acquire();
  296. // setup the idle timer
  297. this.idleTimer = new Timer();
  298. this.idleTimer.schedule(new TimerTask() {
  299. @Override
  300. public void run()
  301. {
  302. sendIdle();
  303. }
  304. } , IDLE_INTERVAL, IDLE_INTERVAL);
  305. Log.i(TAG, "setupVrpn finished");
  306. }
  307. @Override
  308. public boolean onTouch(View v, MotionEvent event)
  309. {
  310. this.transmitEvent(event);
  311. this.setMultitouchReport(event);
  312. return true;
  313. }
  314. /**
  315. * Calls the vrpn connection's mainLoop to send an empty message to persist the
  316. * connection.
  317. */
  318. public void sendIdle()
  319. {
  320. this.jniLayer.mainLoop();
  321. }
  322. /**
  323. * Gets IP address of device. Copied from
  324. * http://www.droidnova.com/get-the-ip-address-of-your-device,304.html
  325. *
  326. * @return The IP address of the Droid
  327. */
  328. private String getIpAddress()
  329. {
  330. try
  331. {
  332. for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();)
  333. {
  334. NetworkInterface intf = en.nextElement();
  335. for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();)
  336. {
  337. InetAddress inetAddress = enumIpAddr.nextElement();
  338. if (!inetAddress.isLoopbackAddress()) { return inetAddress.getHostAddress().toString(); }
  339. }
  340. }
  341. }
  342. catch(SocketException ex)
  343. {
  344. Log.e(TAG , ex.toString());
  345. }
  346. return null;
  347. }
  348. private void transmitEvent(MotionEvent event)
  349. {
  350. this.jniLayer.updateAnalogVal(this.multitouchAnalogNumber , 0 , event.getX());
  351. this.jniLayer.updateAnalogVal(this.multitouchAnalogNumber , 1 , event.getY());
  352. this.jniLayer.reportAnalogChg(this.multitouchAnalogNumber);
  353. }
  354. /**
  355. * Update the text on the screen to show the x and y coordinate
  356. * Multitouch data
  357. *
  358. * @param event
  359. * The event which updates the multitouch report
  360. */
  361. private void setMultitouchReport(MotionEvent event)
  362. {
  363. String x;
  364. float xVal = event.getX();
  365. if (xVal >= 0 && xVal < 10)
  366. {
  367. x = " " + xVal;
  368. }
  369. else if (xVal >= 10 && xVal < 100)
  370. {
  371. x = " " + xVal;
  372. }
  373. else if (xVal >= 100)
  374. {
  375. x = "" + xVal;
  376. }
  377. else
  378. {
  379. x = " 0.0";
  380. }
  381. String y;
  382. float yVal = event.getY();
  383. if (yVal >= 0 && yVal < 10)
  384. {
  385. y = " " + yVal;
  386. }
  387. else if (yVal >= 10 && yVal < 100)
  388. {
  389. y = " " + yVal;
  390. }
  391. else if (yVal >= 100)
  392. {
  393. y = "" + yVal;
  394. }
  395. else
  396. {
  397. y = " 0.0";
  398. }
  399. this.logText("x: " + x + ", y: " + y);
  400. }
  401. /**
  402. * Updates text to be displayed in the multitouch textview
  403. *
  404. * @param message the message to display on the screen
  405. */
  406. private void logText(String message)
  407. {
  408. this.multitouchReport.setText(message);
  409. }
  410. /**
  411. * This method sets up and initializes all the button event listeners
  412. *
  413. * @param buttons list of buttons to be initialized
  414. */
  415. private void setupButtons(List<Button> buttons)
  416. {
  417. for (int i = 1, len = buttons.size(); i <= len; i++)
  418. {
  419. ButtonListener bl = new ButtonListener(this.builder.getButton(i) , i , this.jniLayer);
  420. buttons.get(i - 1).setClickable(true);
  421. buttons.get(i - 1).setOnTouchListener(bl);
  422. }
  423. }
  424. /**
  425. * Updates text to be displayed in the accelerometerReport
  426. * textview.
  427. *
  428. * @param message the message to display on the screen
  429. */
  430. void logAccelerometer(String message)
  431. {
  432. this.accelerometerReport.setText(message);
  433. }
  434. /**
  435. * Updates text to be displayed in the seekbarReport textview.
  436. *
  437. * @param message the message to display on the screen
  438. */
  439. void logSlider(String message)
  440. {
  441. this.seekbarReport.setText(message);
  442. }
  443. }