RunningActivity.java
Go to the documentation of this file.
1 /*
2  * Copyright 2016 Intermodalics All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package eu.intermodalics.tango_ros_streamer.activities;
18 
19 import android.animation.AnimatorInflater;
20 import android.animation.AnimatorSet;
21 import android.app.DialogFragment;
22 import android.app.FragmentManager;
23 import android.content.Intent;
24 import android.content.ServiceConnection;
25 import android.content.SharedPreferences;
26 import android.net.Uri;
27 import android.content.res.Configuration;
28 import android.net.wifi.WifiManager;
29 import android.os.AsyncTask;
30 import android.os.Bundle;
31 import android.os.StrictMode;
32 import android.preference.PreferenceManager;
33 import android.support.design.widget.Snackbar;
34 import android.support.v7.widget.Toolbar;
35 import android.text.format.Formatter;
36 import android.text.method.ScrollingMovementMethod;
37 import android.util.Log;
38 import android.view.Menu;
39 import android.view.MenuInflater;
40 import android.view.MenuItem;
41 import android.view.View;
42 import android.widget.Button;
43 import android.widget.CompoundButton;
44 import android.widget.Switch;
45 import android.widget.ImageView;
46 import android.widget.TextView;
47 import android.widget.Toast;
48 
49 import org.apache.commons.io.FilenameUtils;
50 import org.ros.address.InetAddressFactory;
51 import org.ros.android.NodeMainExecutorService;
52 import org.ros.android.NodeMainExecutorServiceListener;
53 import org.ros.exception.RosRuntimeException;
54 import org.ros.node.ConnectedNode;
55 import org.ros.node.DefaultNodeListener;
56 import org.ros.node.NodeConfiguration;
57 import org.ros.node.NodeListener;
58 import org.ros.node.NodeMainExecutor;
59 
60 import java.io.File;
61 import java.net.URI;
62 import java.util.ArrayList;
63 import java.util.HashMap;
64 import java.util.List;
65 import java.util.concurrent.CountDownLatch;
66 
70 
71 import eu.intermodalics.tango_ros_common.Logger;
72 import eu.intermodalics.tango_ros_common.MasterConnectionChecker;
73 import eu.intermodalics.tango_ros_common.TangoServiceClientNode;
76 import eu.intermodalics.tango_ros_common.ParameterNode;
79 import tango_ros_messages.TangoConnectRequest;
80 import tango_ros_messages.TangoConnectResponse;
81 
82 public class RunningActivity extends AppCompatRosActivity implements
83  SaveMapDialog.CallbackListener, LoadOccupancyGridDialog.CallbackListener,
84  TangoServiceClientNode.CallbackListener {
85  private static final String TAG = RunningActivity.class.getSimpleName();
86  private static final String TAGS_TO_LOG = TAG + ", " + "tango_client_api, " + "Registrar, "
87  + "DefaultPublisher, " + "native, " + "DefaultPublisher" ;
88  private static final int LOG_TEXT_MAX_LENGTH = 5000;
89  private static final int MAX_TANGO_CONNECTION_TRY = 50;
90 
91  private static final String REQUEST_TANGO_PERMISSION_ACTION = "android.intent.action.REQUEST_TANGO_PERMISSION";
92  public static final String EXTRA_KEY_PERMISSIONTYPE = "PERMISSIONTYPE";
93  public static final String EXTRA_VALUE_ADF = "ADF_LOAD_SAVE_PERMISSION";
94  private static final String EXTRA_VALUE_DATASET = "DATASET_PERMISSION";
95  private static final int REQUEST_CODE_ADF_PERMISSION = 111;
96  private static final int REQUEST_CODE_DATASET_PERMISSION = 112;
97  public static final String RESTART_TANGO = "restart_tango";
98 
99  public static class StartSettingsActivityRequest {
100  public static final int FIRST_RUN = 11;
101  public static final int STANDARD_RUN = 12;
102  }
103 
104  enum RosStatus {
105  UNKNOWN,
106  MASTER_NOT_CONNECTED,
107  MASTER_CONNECTED
108  }
109 
110  // Symmetric implementation to tango_ros_nodelet.h.
111  enum TangoStatus {
112  UNKNOWN,
116  }
117 
118  private SharedPreferences mSharedPref;
120  private boolean mRunLocalMaster = false;
121  private String mMasterUri = "";
122  private CountDownLatch mRosConnectionLatch;
123  private ParameterNode mParameterNode;
124  private TangoServiceClientNode mTangoServiceClientNode;
125  private ImuNode mImuNode;
126  private RosStatus mRosStatus = RosStatus.UNKNOWN;
128  private Logger mLogger;
129  private boolean mCreateNewMap = false;
130  private boolean mMapSaved = false;
131  private HashMap<String, String> mUuidsNamesHashMap;
132  // True after the user answered the ADF permission popup (the permission has not been necessarily granted).
133  private boolean mAdfPermissionHasBeenAnswered = false;
134  // True after the user answered the dataset permission popup (the permission has not been necessarily granted).
135  private boolean mDatasetPermissionHasBeenAnswered = false;
136  private ArrayList<String> mOccupancyGridNameList = new ArrayList<String>();
137 
138  // UI objects.
139  private Menu mToolbarMenu;
140  private TextView mUriTextView;
141  private ImageView mRosLightImageView;
142  private ImageView mTangoLightImageView;
143  private Switch mlogSwitch;
144  private boolean mDisplayLog = false;
145  private TextView mLogTextView;
146  private Button mSaveMapButton;
147  private Button mLoadOccupancyGridButton;
148  private Snackbar mSnackbarLoadNewMap;
149  private Snackbar mSnackbarRosReconnection;
150 
151  public RunningActivity() {
152  super("TangoRosStreamer", "TangoRosStreamer");
153  }
154 
155  protected RunningActivity(String notificationTicker, String notificationTitle) {
156  super(notificationTicker, notificationTitle);
157  }
158 
162  ServiceConnection mTangoServiceConnection = new DefaultTangoServiceConnection(
164  @Override
165  public void execute() {
168  Log.i(TAG, "Version of Tango is ok.");
169  } else {
170  updateTangoStatus(TangoStatus.SERVICE_NOT_CONNECTED);
171  Log.e(TAG, getResources().getString(R.string.tango_version_error));
172  displayToastMessage(R.string.tango_version_error);
173  }
174  } else {
175  updateTangoStatus(TangoStatus.SERVICE_NOT_CONNECTED);
176  Log.e(TAG, getString(R.string.tango_bind_error));
177  displayToastMessage(R.string.tango_bind_error);
178  }
179  }
180  }
181  );
182 
183  private void updateRosStatus(RosStatus status) {
184  if (mRosStatus != status) {
185  mRosStatus = status;
186  }
187  switchRosLight(status);
188  SharedPreferences.Editor editor = mSharedPref.edit();
189  editor.putInt(getString(R.string.ros_status), status.ordinal());
190  editor.commit();
191  }
192 
193  private void switchRosLight(final RosStatus status) {
194  runOnUiThread(new Runnable() {
195  @Override
196  public void run() {
197  if (status == RosStatus.UNKNOWN) {
198  mRosLightImageView.setImageDrawable(getResources().getDrawable(R.drawable.btn_radio_on_orange_light));
199  } else if (status == RosStatus.MASTER_CONNECTED) {
200  // Turn ROS light to green.
201  mRosLightImageView.setImageDrawable(getResources().getDrawable(R.drawable.btn_radio_on_green_light));
202  // Dismiss ROS reconnection snackbar if necessary.
203  if (mSnackbarRosReconnection != null && mSnackbarRosReconnection.isShown()) {
204  mSnackbarRosReconnection.dismiss();
205  }
206  // Set settings icon color to white.
207  mToolbarMenu.findItem(R.id.settings).setIcon(R.drawable.ic_settings_white_24dp);
208  } else if (status == RosStatus.MASTER_NOT_CONNECTED) {
209  // Turn ROS light to red.
210  mRosLightImageView.setImageDrawable(getResources().getDrawable(R.drawable.btn_radio_on_red_light));
211  // Show snackbar with ROS reconnection button.
212  mSnackbarRosReconnection = Snackbar.make(findViewById(android.R.id.content),
213  getString(R.string.snackbar_text_reconnect_ros), Snackbar.LENGTH_INDEFINITE);
214  mSnackbarRosReconnection.setAction(getString(R.string.snackbar_action_text_reconnect_ros),
215  new View.OnClickListener() {
216  @Override
217  public void onClick(View view) {
218  mRunLocalMaster = mSharedPref.getBoolean(getString(R.string.pref_master_is_local_key), false);
219  mMasterUri = mSharedPref.getString(getString(R.string.pref_master_uri_key),
220  getResources().getString(R.string.pref_master_uri_default));
221  mUriTextView.setText(mMasterUri);
223  }
224  }
225  );
226  View snackBarView = mSnackbarRosReconnection.getView();
227  snackBarView.setBackgroundColor(getResources().getColor(android.R.color.holo_red_dark));
228  mSnackbarRosReconnection.show();
229  // Highlight ROS Master URI.
230  AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(RunningActivity.this, R.animator.master_uri_text_animation);
231  set.setTarget(mUriTextView);
232  set.start();
233  // Set settings icon color to red.
234  mToolbarMenu.findItem(R.id.settings).setIcon(R.drawable.ic_settings_red_24dp);
235  }
236  }
237  });
238  }
239 
240  private void updateTangoStatus(TangoStatus status) {
241  if (mTangoStatus != status) {
242  mTangoStatus = status;
243  switchTangoLight(status);
244  if (status == TangoStatus.NO_FIRST_VALID_POSE) {
245  displayToastMessage(R.string.point_device);
246  }
247  }
248  }
249 
250  private void switchTangoLight(final TangoStatus status) {
251  runOnUiThread(new Runnable() {
252  @Override
253  public void run() {
254  if (status == TangoStatus.UNKNOWN) {
255  mTangoLightImageView.setImageDrawable(getResources().getDrawable(R.drawable.btn_radio_on_orange_light));
256  } else if (status == TangoStatus.SERVICE_CONNECTED) {
257  mTangoLightImageView.setImageDrawable(getResources().getDrawable(R.drawable.btn_radio_on_green_light));
258  } else {
259  mTangoLightImageView.setImageDrawable(getResources().getDrawable(R.drawable.btn_radio_on_red_light));
260  }
261  }
262  });
263  }
264 
266  mCreateNewMap = mSharedPref.getBoolean(getString(R.string.pref_create_new_map_key), false);
267  runOnUiThread(new Runnable() {
268  @Override
269  public void run() {
270  mSaveMapButton.setEnabled(!mMapSaved);
271  if (mCreateNewMap) {
272  mSaveMapButton.setVisibility(View.VISIBLE);
273  mLoadOccupancyGridButton.setVisibility(View.GONE);
274  } else {
275  mSaveMapButton.setVisibility(View.GONE);
276  mLoadOccupancyGridButton.setVisibility(View.VISIBLE);
277  }
278  }
279  });
280  }
281 
286  private void displayToastMessage(final int messageId) {
287  runOnUiThread(new Runnable() {
288  @Override
289  public void run() {
290  Toast.makeText(getApplicationContext(), messageId, Toast.LENGTH_LONG).show();
291  }
292  });
293  }
294 
295  private void showSaveMapDialog() {
296  FragmentManager manager = getFragmentManager();
297  SaveMapDialog saveMapDialog = new SaveMapDialog();
298  saveMapDialog.setStyle(DialogFragment.STYLE_NORMAL, R.style.CustomDialog);
299  saveMapDialog.show(manager, "SaveMapDialog");
300  }
301 
302  private void showLoadOccupancyGridDialog(boolean firstTry, java.util.ArrayList<java.lang.String> nameList) {
303  FragmentManager manager = getFragmentManager();
304  LoadOccupancyGridDialog loadOccupancyGridDialog = new LoadOccupancyGridDialog();
305  Bundle bundle = new Bundle();
306  bundle.putBoolean(getString(R.string.show_load_occupancy_grid_empty_key), nameList.isEmpty());
307  bundle.putBoolean(getString(R.string.show_load_occupancy_grid_error_key), !firstTry);
308  bundle.putStringArrayList(getString(R.string.list_names_occupancy_grid_key), nameList);
309  loadOccupancyGridDialog.setArguments(bundle);
310  loadOccupancyGridDialog.setStyle(DialogFragment.STYLE_NORMAL, R.style.CustomDialog);
311  loadOccupancyGridDialog.show(manager, "LoadOccupancyGridDialog");
312  }
313 
314  private void setupUI() {
315  setContentView(R.layout.running_activity);
316  Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
317  setSupportActionBar(toolbar);
318  mUriTextView = (TextView) findViewById(R.id.master_uri);
319  mUriTextView.setText(mMasterUri);
320  mRosLightImageView = (ImageView) findViewById(R.id.is_ros_ok_image);
321  mTangoLightImageView = (ImageView) findViewById(R.id.is_tango_ok_image);
322  mlogSwitch = (Switch) findViewById(R.id.log_switch);
323  mlogSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
324  @Override
325  public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
326  mDisplayLog = isChecked;
327  mLogTextView.setVisibility(isChecked ? View.VISIBLE : View.INVISIBLE);
328  }
329  });
330  mLogTextView = (TextView)findViewById(R.id.log_view);
331  mLogTextView.setMovementMethod(new ScrollingMovementMethod());
332  mSaveMapButton = (Button) findViewById(R.id.save_map_button);
333  mSaveMapButton.setOnClickListener(new View.OnClickListener() {
334  @Override
335  public void onClick(View view) {
337  }
338  });
339  mLoadOccupancyGridButton = (Button) findViewById(R.id.load_occupancy_grid_button);
340  mLoadOccupancyGridButton.setOnClickListener(new View.OnClickListener() {
341  @Override
342  public void onClick(View view) {
343  mOccupancyGridNameList = new ArrayList<String>();
344  try {
345  String directory = mParameterNode.getStringParam(getString(R.string.occupancy_grid_directory_key));
346  File occupancyGridDirectory = new File(directory);
347  if (occupancyGridDirectory != null && occupancyGridDirectory.isDirectory()) {
348  File[] files = occupancyGridDirectory.listFiles();
349  for (File file : files) {
350  if (FilenameUtils.getExtension(file.getName()).equals("yaml")) {
351  mOccupancyGridNameList.add(FilenameUtils.removeExtension(file.getName()));
352  }
353  }
354  }
355  showLoadOccupancyGridDialog(/* firstTry */ true, mOccupancyGridNameList);
356  } catch (RuntimeException e) {
357  e.printStackTrace();
358  }
359  }
360  });
362  }
363 
364  public void onClickOkSaveMapDialog(final String mapName) {
365  assert(mapName !=null && !mapName.isEmpty());
366  mSaveMapButton.setEnabled(false);
367  new AsyncTask<Void, Void, Void>() {
368  @Override
369  protected Void doInBackground(Void... params) {
370  mTangoServiceClientNode.callSaveMapService(mapName);
371  return null;
372  }
373  }.execute();
374  }
375 
376  @Override
377  public void onSaveMapServiceCallFinish(boolean success, final String message,
378  final String mapName, final String mapUuid) {
379  if (success) {
380  mMapSaved = true;
381  displayToastMessage(R.string.save_map_success);
383  runOnUiThread(new Runnable() {
384  @Override
385  public void run() {
386  mSaveMapButton.setEnabled(!mMapSaved);
387  mSnackbarLoadNewMap = Snackbar.make(findViewById(android.R.id.content),
388  getString(R.string.snackbar_text_load_new_map), Snackbar.LENGTH_INDEFINITE);
389  mSnackbarLoadNewMap.setAction(getString(R.string.snackbar_action_text_load_new_map), new View.OnClickListener() {
390  @Override
391  public void onClick(View view) {
392  try {
393  mParameterNode.changeSettingsToLocalizeInMap(mapUuid, getString(R.string.pref_create_new_map_key),
394  getString(R.string.pref_localization_mode_key), getString(R.string.pref_localization_map_uuid_key));
395  restartTango();
396  } catch (RuntimeException e) {
397  e.printStackTrace();
398  }
399  }
400  });
401  mSnackbarLoadNewMap.show();
402  }
403  });
404  } else {
405  Log.e(TAG, "Error while saving map: " + message);
406  displayToastMessage(R.string.save_map_error);
407  }
408  }
409 
410  public void onClickItemLoadOccupancyGridDialog(final String occupancyGridName) {
411  assert(occupancyGridName !=null && !occupancyGridName.isEmpty());
412  mLoadOccupancyGridButton.setEnabled(false);
413  new AsyncTask<Void, Void, Void>() {
414  @Override
415  protected Void doInBackground(Void... params) {
416  mTangoServiceClientNode.callLoadOccupancyGridService(occupancyGridName);
417  return null;
418  }
419  }.execute();
420  }
421 
422  @Override
423  public void onLoadOccupancyGridServiceCallFinish(boolean success, final String message,
424  boolean aligned, String mapUuid) {
425  if (success) {
426  if (aligned) {
427  displayToastMessage(R.string.load_occupancy_grid_success);
428  } else {
429  displayToastMessage(R.string.load_occupancy_grid_not_aligned);
430  }
431  Log.i(TAG, message);
432  } else {
433  Log.e(TAG, "Error while loading occupancy grid: " + message);
434  displayToastMessage(R.string.load_occupancy_grid_error);
435  showLoadOccupancyGridDialog(/* firstTry */ false, mOccupancyGridNameList);
436  }
437  runOnUiThread(new Runnable() {
438  @Override
439  public void run() {
440  mLoadOccupancyGridButton.setEnabled(true);
441  }
442  });
443  }
444 
445  @Override
446  public void onTangoConnectServiceFinish(int response, String message) {
447  if (response != TangoConnectResponse.TANGO_SUCCESS) {
448  Log.e(TAG, "Error connecting to Tango: " + response + ", message: " + message);
449  displayToastMessage(R.string.tango_connect_error);
450  return;
451  }
452  displayToastMessage(R.string.tango_connect_success);
453  }
454 
455  @Override
456  public void onTangoDisconnectServiceFinish(int response, String message) {
457  if (response != TangoConnectResponse.TANGO_SUCCESS) {
458  Log.e(TAG, "Error disconnecting from Tango: " + response + ", message: " + message);
459  // Do not switch Tango lights in this case because Tango disconnect can never fail.
460  // Failure occured due to something else, so Tango is still connected.
461  displayToastMessage(R.string.tango_disconnect_error);
462  return;
463  }
464  displayToastMessage(R.string.tango_disconnect_success);
465  }
466 
467  @Override
468  public void onTangoReconnectServiceFinish(int response, String message) {
469  if (response != TangoConnectResponse.TANGO_SUCCESS) {
470  Log.e(TAG, "Error reconnecting to Tango: " + response + ", message: " + message);
471  displayToastMessage(R.string.tango_reconnect_error);
472  return;
473  }
474  displayToastMessage(R.string.tango_reconnect_success);
475  }
476 
477  public void onGetMapUuidsFinish(List<String> mapUuids, List<String> mapNames) {
478  mUuidsNamesHashMap = new HashMap<>();
479  if (mapUuids == null || mapNames == null) return;
480  assert(mapUuids.size() == mapNames.size());
481  for (int i = 0; i < mapUuids.size(); ++i) {
482  mUuidsNamesHashMap.put(mapUuids.get(i), mapNames.get(i));
483  }
484  if(mParameterNode != null) {
485  try {
486  mParameterNode.setPreferencesFromParameterServer();
487  } catch (RuntimeException e) {
488  e.printStackTrace();
489  }
490  }
491  Intent settingsActivityIntent = new Intent(SettingsActivity.NEW_UUIDS_NAMES_MAP_ALERT);
492  settingsActivityIntent.putExtra(getString(R.string.uuids_names_map), mUuidsNamesHashMap);
493  this.sendBroadcast(settingsActivityIntent);
494  }
495 
496  @Override
497  public void onTangoStatus(int status) {
498  if (status >= TangoStatus.values().length) {
499  Log.e(TAG, "Invalid Tango status " + status);
500  return;
501  }
502  if (status == TangoStatus.SERVICE_CONNECTED.ordinal() && mTangoStatus != TangoStatus.SERVICE_CONNECTED) {
504  try {
505  mParameterNode.setPreferencesFromParameterServer();
506  } catch (RuntimeException e) {
507  e.printStackTrace();
508  }
509  mMapSaved = false;
510  if (mSnackbarLoadNewMap != null && mSnackbarLoadNewMap.isShown()) {
511  mSnackbarLoadNewMap.dismiss();
512  }
513  }
515  updateTangoStatus(TangoStatus.values()[status]);
516  }
517 
518  private void saveUuidsNamestoHashMap() {
519  mTangoServiceClientNode.callGetMapUuidsService();
520  }
521 
522  private void getTangoPermission(String permissionType, int requestCode) {
523  Intent intent = new Intent();
524  intent.setAction(REQUEST_TANGO_PERMISSION_ACTION);
525  intent.putExtra(EXTRA_KEY_PERMISSIONTYPE, permissionType);
526  startActivityForResult(intent, requestCode);
527  }
528 
529  @Override
530  protected void onCreate(Bundle savedInstanceState) {
531  super.onCreate(savedInstanceState);
532  Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
533  public void uncaughtException(Thread t, Throwable e) {
534  Log.e(TAG, "Uncaught exception of type " + e.getClass());
535  e.printStackTrace();
536  }
537  });
538  // The following piece of code allows networking in main thread.
539  // e.g. Restart Tango button calls a ROS service in UI thread.
540  if (android.os.Build.VERSION.SDK_INT > 9) {
541  StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
542  StrictMode.setThreadPolicy(policy);
543  }
544  mSharedPref = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
545  mRunLocalMaster = mSharedPref.getBoolean(getString(R.string.pref_master_is_local_key), false);
546  mMasterUri = mSharedPref.getString(getString(R.string.pref_master_uri_key),
547  getResources().getString(R.string.pref_master_uri_default));
548  mCreateNewMap = mSharedPref.getBoolean(getString(R.string.pref_create_new_map_key), false);
549  String logFileName = mSharedPref.getString(getString(R.string.pref_log_file_key),
550  getString(R.string.pref_log_file_default));
551  setupUI();
552  mLogger = new Logger(this, mLogTextView, TAGS_TO_LOG, logFileName, LOG_TEXT_MAX_LENGTH);
553  }
554 
555  @Override
556  public void onConfigurationChanged(Configuration newConfig) {
557  super.onConfigurationChanged(newConfig);
558  setupUI();
559  switchRosLight(mRosStatus);
560  switchTangoLight(mTangoStatus);
561  mlogSwitch.setChecked(mDisplayLog);
562  mLogTextView.setText(mLogger.getLogText());
563  }
564 
565  @Override
566  public boolean onCreateOptionsMenu(Menu menu) {
567  MenuInflater inflater = getMenuInflater();
568  inflater.inflate(R.menu.menu, menu);
569  mToolbarMenu = menu;
570  return true;
571  }
572 
573  @Override
574  public boolean onOptionsItemSelected(MenuItem item) {
575  switch (item.getItemId()) {
576  case R.id.settings:
577  if(mParameterNode != null) {
578  try {
579  mParameterNode.setPreferencesFromParameterServer();
580  } catch (RuntimeException e) {
581  e.printStackTrace();
582  }
583  }
584  Intent settingsActivityIntent = new Intent(this, SettingsActivity.class);
585  settingsActivityIntent.putExtra(getString(R.string.uuids_names_map), mUuidsNamesHashMap);
586  startActivityForResult(settingsActivityIntent, StartSettingsActivityRequest.STANDARD_RUN);
587  return true;
588  case R.id.share:
589  mLogger.saveLogToFile();
590  Intent shareFileIntent = new Intent(Intent.ACTION_SEND);
591  shareFileIntent.setType("text/plain");
592  shareFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(mLogger.getLogFile()));
593  startActivity(shareFileIntent);
594  return true;
595  default:
596  return super.onOptionsItemSelected(item);
597  }
598  }
599 
600  private void unbindFromTango() {
602  Log.i(TAG, "Unbind tango service");
603  TangoInitializationHelper.unbindTangoService(this, mTangoServiceConnection);
604  updateTangoStatus(TangoStatus.SERVICE_NOT_CONNECTED);
605  }
606  }
607 
608  @Override
609  protected void onDestroy() {
610  super.onDestroy();
611  if (mParameterNode != null) {
612  try {
613  mParameterNode.setPreferencesFromParameterServer();
614  } catch (RuntimeException e) {
615  e.printStackTrace();
616  }
617  }
618  this.nodeMainExecutorService.forceShutdown();
619  }
620 
621  @Override
622  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
623  if (resultCode == RESULT_CANCELED) { // Result code returned when back button is pressed.
624  // Upload new settings to parameter server.
625  if ((requestCode == StartSettingsActivityRequest.STANDARD_RUN ||
626  requestCode == StartSettingsActivityRequest.FIRST_RUN) &&
627  mParameterNode != null) {
628  try {
629  mParameterNode.uploadPreferencesToParameterServer();
630  } catch (RuntimeException e) {
631  e.printStackTrace();
632  }
633  }
634 
635  if (data != null && data.getBooleanExtra(RESTART_TANGO, false)) {
636  restartTango();
637  }
638 
639  if (requestCode == StartSettingsActivityRequest.FIRST_RUN) {
640  mRunLocalMaster = mSharedPref.getBoolean(getString(R.string.pref_master_is_local_key), false);
641  mMasterUri = mSharedPref.getString(getString(R.string.pref_master_uri_key),
642  getResources().getString(R.string.pref_master_uri_default));
643  mUriTextView.setText(mMasterUri);
644  String logFileName = mSharedPref.getString(getString(R.string.pref_log_file_key),
645  getString(R.string.pref_log_file_default));
646  mLogger.setLogFileName(logFileName);
647  mLogger.start();
648  getTangoPermission(EXTRA_VALUE_ADF, REQUEST_CODE_ADF_PERMISSION);
649  getTangoPermission(EXTRA_VALUE_DATASET, REQUEST_CODE_DATASET_PERMISSION);
651  } else if (requestCode == StartSettingsActivityRequest.STANDARD_RUN) {
652  // It is ok to change the log file name at runtime.
653  String logFileName = mSharedPref.getString(getString(R.string.pref_log_file_key),
654  getString(R.string.pref_log_file_default));
655  mLogger.setLogFileName(logFileName);
656  if (mRosStatus == RosStatus.MASTER_NOT_CONNECTED && mSnackbarRosReconnection != null) {
657  // Show snackbar with ROS reconnection button.
658  // It was dismissed when switching to the SettingsActivity.
659  mSnackbarRosReconnection.show();
660  }
661  }
662  }
663 
664  if (requestCode == REQUEST_CODE_ADF_PERMISSION || requestCode == REQUEST_CODE_DATASET_PERMISSION) {
665  if (resultCode == RESULT_CANCELED) {
666  // No Tango permissions granted by the user.
667  displayToastMessage(R.string.tango_permission_denied);
668  }
669  if (requestCode == REQUEST_CODE_ADF_PERMISSION) {
670  // The user answered the ADF permission popup (the permission has not been necessarily granted).
671  mAdfPermissionHasBeenAnswered = true;
672  }
673  if (requestCode == REQUEST_CODE_DATASET_PERMISSION) {
674  // The user answered the dataset permission popup (the permission has not been necessarily granted).
675  mDatasetPermissionHasBeenAnswered = true;
676  }
677  if (mAdfPermissionHasBeenAnswered && mDatasetPermissionHasBeenAnswered) {
678  // Both ADF and dataset permissions popup have been answered by the user, the node
679  // can start.
680  Log.i(TAG, "initAndStartRosJavaNode");
682  }
683 
684  }
685  }
686 
690  private void checkRosMasterConnection() {
691  updateRosStatus(RosStatus.UNKNOWN);
692  mRosConnectionLatch = new CountDownLatch(1);
693  new MasterConnectionChecker(mMasterUri.toString(),
694  new MasterConnectionChecker.UserHook() {
695  @Override
696  public void onSuccess(Object o) {
697  updateRosStatus(RosStatus.MASTER_CONNECTED);
698  mRosConnectionLatch.countDown();
699  }
700 
701  @Override
702  public void onError(Throwable t) {
703  updateRosStatus(RosStatus.MASTER_NOT_CONNECTED);
704  Log.e(TAG, getString(R.string.ros_init_error));
705  displayToastMessage(R.string.ros_init_error);
706  mRosConnectionLatch.countDown();
707  }
708  },
710  ).runTest();
711  waitForLatchUnlock(mRosConnectionLatch, "ROS CONNECTION");
712  }
713 
714  private void restartTango() {
715  if (mParameterNode != null) {
716  try {
717  mParameterNode.setPreferencesFromParameterServer();
718  } catch (RuntimeException e) {
719  e.printStackTrace();
720  }
721  }
722  mTangoServiceClientNode.callTangoConnectService(TangoConnectRequest.RECONNECT);
723  }
724 
725  @Override
726  protected void init(NodeMainExecutor nodeMainExecutor) {
727  NodeConfiguration nodeConfiguration;
728  try {
729  nodeConfiguration = NodeConfiguration.newPublic(InetAddressFactory.newNonLoopback().getHostAddress());
730  nodeConfiguration.setMasterUri(this.nodeMainExecutorService.getMasterUri());
731  } catch (RosRuntimeException e) {
732  e.printStackTrace();
733  Log.e(TAG, getString(R.string.network_error));
734  displayToastMessage(R.string.network_error);
735  return;
736  }
738  if (mRosStatus == RosStatus.MASTER_NOT_CONNECTED) {
739  return;
740  }
741 
742  HashMap<String, String> tangoConfigurationParameters = new HashMap<String, String>();
743  tangoConfigurationParameters.put(getString(R.string.pref_create_new_map_key), "boolean");
744  tangoConfigurationParameters.put(getString(R.string.pref_enable_depth_key), "boolean");
745  tangoConfigurationParameters.put(getString(R.string.pref_enable_color_camera_key), "boolean");
746  tangoConfigurationParameters.put(getString(R.string.pref_localization_mode_key), "int_as_string");
747  tangoConfigurationParameters.put(getString(R.string.pref_localization_map_uuid_key), "string");
748  mParameterNode = new ParameterNode(this, tangoConfigurationParameters);
749  nodeConfiguration.setNodeName(mParameterNode.getDefaultNodeName());
750  nodeMainExecutor.execute(mParameterNode, nodeConfiguration);
751  // ServiceClient node which is responsible for calling ros services.
752  mTangoServiceClientNode = new TangoServiceClientNode();
753  mTangoServiceClientNode.setCallbackListener(this);
754  nodeConfiguration.setNodeName(mTangoServiceClientNode.getDefaultNodeName());
755  nodeMainExecutor.execute(mTangoServiceClientNode, nodeConfiguration);
756  // Create node publishing IMU data.
757  mImuNode = new ImuNode(this);
758  nodeConfiguration.setNodeName(mImuNode.getDefaultNodeName());
759  nodeMainExecutor.execute(mImuNode, nodeConfiguration);
760  // Create and start Tango ROS Node
761  nodeConfiguration.setNodeName(TangoNodeletManager.NODE_NAME);
766  mTangoNodeletManager = new TangoNodeletManager();
767  TangoInitializationHelper.bindTangoService(this, mTangoServiceConnection);
769  nodeMainExecutor.execute(mTangoNodeletManager, nodeConfiguration, new ArrayList<NodeListener>(){{
770  add(new DefaultNodeListener() {
771  @Override
772  public void onStart(ConnectedNode connectedNode) {
773  int count = 0;
774  while (count < MAX_TANGO_CONNECTION_TRY &&
775  !mTangoServiceClientNode.callTangoConnectService(TangoConnectRequest.CONNECT)) {
776  try {
777  count++;
778  Log.e(TAG, "Trying to connect to Tango, attempt " + count);
779  Thread.sleep(200);
780  } catch (InterruptedException e) {
781  e.printStackTrace();
782  }
783  }
784  if (count >= MAX_TANGO_CONNECTION_TRY) {
785  updateTangoStatus(TangoStatus.SERVICE_NOT_CONNECTED);
786  displayToastMessage(R.string.tango_connect_error);
787  }
788  }
789  });
790  }});
791  } else {
792  updateTangoStatus(TangoStatus.SERVICE_NOT_CONNECTED);
793  Log.e(TAG, getResources().getString(R.string.tango_version_error));
794  displayToastMessage(R.string.tango_version_error);
795  }
796  } else {
797  updateTangoStatus(TangoStatus.SERVICE_NOT_CONNECTED);
798  Log.e(TAG, getString(R.string.tango_lib_error));
799  displayToastMessage(R.string.tango_lib_error);
800  }
801  }
802 
808  @Override
809  public void startMasterChooser() {
810  boolean appPreviouslyStarted = mSharedPref.getBoolean(getString(R.string.pref_previously_started_key), false);
811  if (appPreviouslyStarted) {
812  mLogger.start();
813  getTangoPermission(EXTRA_VALUE_ADF, REQUEST_CODE_ADF_PERMISSION);
814  getTangoPermission(EXTRA_VALUE_DATASET, REQUEST_CODE_DATASET_PERMISSION);
815  } else {
816  Intent intent = new Intent(this, SettingsActivity.class);
817  startActivityForResult(intent, StartSettingsActivityRequest.FIRST_RUN);
818  }
819  }
820 
824  private void initAndStartRosJavaNode() {
825  this.nodeMainExecutorService.addListener(new NodeMainExecutorServiceListener() {
826  @Override
827  public void onShutdown(NodeMainExecutorService nodeMainExecutorService) {
828  unbindFromTango();
829  mLogger.saveLogToFile();
830  // This ensures to kill the process started by the app.
831  android.os.Process.killProcess(android.os.Process.myPid());
832  }
833  });
834  if (mRunLocalMaster) {
835  try {
836  this.nodeMainExecutorService.startMaster(/*isPrivate*/ false);
837  mMasterUri = this.nodeMainExecutorService.getMasterUri().toString();
838  // The URI returned by getMasterUri is correct but looks 'weird',
839  // e.g. 'http://android-c90553518bc67cf5:1131'.
840  // Instead of showing this to the user, we show the IP address of the device,
841  // which is also correct and less confusing.
842  WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
843  String deviceIP = Formatter.formatIpAddress(wifiManager.getConnectionInfo().getIpAddress());
844  mUriTextView = (TextView) findViewById(R.id.master_uri);
845  mUriTextView.setText("http://" + deviceIP + ":11311");
846  } catch (RosRuntimeException e) {
847  e.printStackTrace();
848  Log.e(TAG, getString(R.string.local_master_error));
849  displayToastMessage(R.string.local_master_error);
850  return;
851  }
852  }
853  if (mMasterUri != null) {
854  URI masterUri;
855  try {
856  masterUri = URI.create(mMasterUri);
857  } catch (IllegalArgumentException e) {
858  Log.e(TAG, "Wrong URI: " + e.getMessage());
859  return;
860  }
861  this.nodeMainExecutorService.setMasterUri(masterUri);
862  new AsyncTask<Void, Void, Void>() {
863  @Override
864  protected Void doInBackground(Void... params) {
865  RunningActivity.this.init(nodeMainExecutorService);
866  return null;
867  }
868  }.execute();
869  } else {
870  Log.e(TAG, "Master URI is null");
871  }
872  }
873 
879  private void waitForLatchUnlock(CountDownLatch latch, String latchName) {
880  try {
881  Log.i(TAG, "Waiting for " + latchName + " latch release...");
882  latch.await();
883  Log.i(TAG, latchName + " latch released!");
884  } catch (InterruptedException ie) {
885  Log.w(TAG, "Warning: continuing before " + latchName + " latch was released");
886  }
887  }
888 }
TangoStatus
void onGetMapUuidsFinish(List< String > mapUuids, List< String > mapNames)
static final boolean bindTangoService(final Activity activity, ServiceConnection connection)
bool add(const actionlib::TwoIntsGoal &req, actionlib::TwoIntsResult &res)
geometry_msgs::TransformStamped t
RunningActivity(String notificationTicker, String notificationTitle)
static final void unbindTangoService(final Context context, ServiceConnection connection)
void onActivityResult(int requestCode, int resultCode, Intent data)
void waitForLatchUnlock(CountDownLatch latch, String latchName)
ROSCPP_DECL bool execute(const std::string &method, const XmlRpc::XmlRpcValue &request, XmlRpc::XmlRpcValue &response, XmlRpc::XmlRpcValue &payload, bool wait_for_master)
void getTangoPermission(String permissionType, int requestCode)
UNKNOWN
bool runTest(const Chain &chain, const int &representation)
void onClickItemLoadOccupancyGridDialog(final String occupancyGridName)
void onLoadOccupancyGridServiceCallFinish(boolean success, final String message, boolean aligned, String mapUuid)
void run(ClassLoader *loader)
static native boolean isTangoVersionOk(Activity callerActivity)
void showLoadOccupancyGridDialog(boolean firstTry, java.util.ArrayList< java.lang.String > nameList)
void onSaveMapServiceCallFinish(boolean success, final String message, final String mapName, final String mapUuid)


TangoRosStreamer
Author(s):
autogenerated on Mon Jun 10 2019 15:37:54