RTABMapActivity.java
Go to the documentation of this file.
1 package com.introlab.rtabmap;
2 
3 import java.io.BufferedInputStream;
4 import java.io.BufferedOutputStream;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.FilenameFilter;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.io.OutputStream;
12 import java.lang.reflect.InvocationTargetException;
13 import java.lang.reflect.Method;
14 import java.text.SimpleDateFormat;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Date;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.zip.ZipEntry;
21 import java.util.zip.ZipOutputStream;
22 
23 import android.app.ActionBar;
24 import android.app.Activity;
25 import android.app.ActivityManager;
26 import android.app.ActivityManager.MemoryInfo;
27 import android.app.AlertDialog;
28 import android.app.Dialog;
29 import android.app.Notification;
30 import android.app.NotificationManager;
31 import android.app.PendingIntent;
32 import android.app.ProgressDialog;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.DialogInterface;
36 import android.content.DialogInterface.OnShowListener;
37 import android.content.Intent;
38 import android.content.ServiceConnection;
39 import android.content.SharedPreferences;
40 import android.content.pm.ApplicationInfo;
41 import android.content.pm.PackageInfo;
42 import android.content.pm.PackageManager;
43 import android.content.pm.PackageManager.NameNotFoundException;
44 import android.content.res.Configuration;
45 import android.database.Cursor;
46 import android.database.sqlite.SQLiteDatabase;
47 import android.graphics.Bitmap;
48 import android.graphics.Canvas;
49 import android.graphics.Color;
50 import android.graphics.Paint;
51 import android.graphics.Rect;
52 import android.graphics.Typeface;
53 import android.hardware.Camera;
54 import android.hardware.Sensor;
55 import android.hardware.SensorEvent;
56 import android.hardware.SensorEventListener;
57 import android.hardware.SensorManager;
58 import android.graphics.Point;
59 import android.hardware.display.DisplayManager;
60 import android.location.Location;
61 import android.location.LocationListener;
62 import android.location.LocationManager;
63 import android.net.ConnectivityManager;
64 import android.net.NetworkInfo;
65 import android.net.Uri;
66 import android.opengl.GLSurfaceView;
67 import android.os.AsyncTask;
68 import android.os.Bundle;
69 import android.os.Environment;
70 import android.os.Handler;
71 import android.os.Debug;
72 import android.os.IBinder;
73 import android.os.Message;
74 import android.preference.ListPreference;
75 import android.preference.PreferenceManager;
76 import android.text.Editable;
77 import android.text.Html;
78 import android.text.InputType;
79 import android.text.SpannableString;
80 import android.text.TextPaint;
81 import android.text.method.HideReturnsTransformationMethod;
82 import android.text.method.LinkMovementMethod;
83 import android.text.util.Linkify;
84 import android.util.Log;
85 import android.util.TypedValue;
86 import android.view.ContextMenu;
87 import android.view.Display;
88 import android.view.GestureDetector;
89 import android.view.Menu;
90 import android.view.MenuItem;
91 import android.view.MenuItem.OnMenuItemClickListener;
92 import android.view.MenuInflater;
93 import android.view.MotionEvent;
94 import android.view.Surface;
95 import android.view.View;
96 import android.view.ContextMenu.ContextMenuInfo;
97 import android.view.View.OnClickListener;
98 import android.view.View.OnCreateContextMenuListener;
99 import android.view.View.OnTouchListener;
100 import android.view.Window;
101 import android.view.WindowManager;
102 import android.view.inputmethod.EditorInfo;
103 import android.webkit.WebView;
104 import android.webkit.WebViewClient;
105 import android.widget.AdapterView;
106 import android.widget.AdapterView.OnItemLongClickListener;
107 import android.widget.AdapterView.OnItemSelectedListener;
108 import android.widget.ArrayAdapter;
109 import android.widget.Button;
110 import android.widget.EditText;
111 import android.widget.LinearLayout;
112 import android.widget.ListView;
113 import android.widget.NumberPicker;
114 import android.widget.RelativeLayout;
115 import android.widget.SeekBar;
116 import android.widget.SeekBar.OnSeekBarChangeListener;
117 import android.widget.Spinner;
118 import android.widget.TextView;
119 import android.widget.Toast;
120 import android.widget.ToggleButton;
121 
122 import com.google.atap.tangoservice.Tango;
123 
124 // The main activity of the application. This activity shows debug information
125 // and a glSurfaceView that renders graphic content.
126 public class RTABMapActivity extends Activity implements OnClickListener, OnItemSelectedListener, SensorEventListener {
127 
128  // Tag for debug logging.
129  public static final String TAG = RTABMapActivity.class.getSimpleName();
130  public static boolean DISABLE_LOG = true;
131 
132  // The minimum Tango Core version required from this application.
133  private static final int MIN_TANGO_CORE_VERSION = 9377;
134 
135  // The package name of Tang Core, used for checking minimum Tango Core version.
136  private static final String TANGO_PACKAGE_NAME = "com.google.tango";
137 
138  public static final String EXTRA_KEY_PERMISSIONTYPE = "PERMISSIONTYPE";
139  public static final String EXTRA_VALUE_ADF = "ADF_LOAD_SAVE_PERMISSION";
140 
141  public static final String RTABMAP_TMP_DB = "rtabmap.tmp.db";
142  public static final String RTABMAP_TMP_DIR = "tmp";
143  public static final String RTABMAP_TMP_FILENAME = "map";
144  public static final String RTABMAP_SDCARD_PATH = "/sdcard/";
145  public static final String RTABMAP_EXPORT_DIR = "Export/";
146 
147  public static final String RTABMAP_AUTH_TOKEN_KEY = "com.introlab.rtabmap.AUTH_TOKEN";
148  public static final String RTABMAP_FILENAME_KEY = "com.introlab.rtabmap.FILENAME";
149  public static final String RTABMAP_OPENED_DB_PATH_KEY = "com.introlab.rtabmap.OPENED_DB_PATH";
150  public static final String RTABMAP_WORKING_DIR_KEY = "com.introlab.rtabmap.WORKING_DIR";
151  public static final int SKETCHFAB_ACTIVITY_CODE = 999;
152  private String mAuthToken;
153 
154  public static final long NOTOUCH_TIMEOUT = 5000; // 5 sec
155  private boolean mHudVisible = true;
156  private int mSavedRenderingType = 0;
157  private boolean mMenuOpened = false;
158 
159  // UI states
160  private static enum State {
164  STATE_VISUALIZING_WHILE_LOADING
165  }
166  State mState = State.STATE_IDLE;
167 
168  // GLSurfaceView and renderer, all of the graphic content is rendered
169  // through OpenGL ES 2.0 in native code.
170  private Renderer mRenderer = null;
171  private GLSurfaceView mGLView;
172 
173  View mDecorView;
174  int mStatusBarHeight = 0;
175  int mActionBarHeight = 0;
176 
177  ProgressDialog mProgressDialog;
178  ProgressDialog mExportProgressDialog;
179 
180  // Screen size for normalizing the touch input for orbiting the render camera.
181  private Point mScreenSize = new Point();
182  private long mOnPauseStamp = 0;
183  private boolean mOnPause = false;
184  private Date mDateOnPause = new Date();
186  private boolean mBlockBack = true;
187  private long mFreeMemoryOnStart = 0;
188 
189  private MenuItem mItemSave;
190  private MenuItem mItemOpen;
191  private MenuItem mItemPostProcessing;
192  private MenuItem mItemExport;
193  private MenuItem mItemSettings;
194  private MenuItem mItemModes;
195  private MenuItem mItemReset;
196  private MenuItem mItemLocalizationMode;
197  private MenuItem mItemTrajectoryMode;
198  private MenuItem mItemRenderingPointCloud;
199  private MenuItem mItemRenderingMesh;
200  private MenuItem mItemRenderingTextureMesh;
201  private MenuItem mItemDataRecorderMode;
202  private MenuItem mItemStatusVisibility;
203  private MenuItem mItemDebugVisibility;
204 
206  private ToggleButton mButtonPause;
207  private ToggleButton mButtonLighting;
208  private ToggleButton mButtonWireframe;
209  private ToggleButton mButtonBackfaceShown;
211  private Button mButtonSaveOnDevice;
212  private Button mButtonShareOnSketchfab;
213  private SeekBar mSeekBarOrthoCut;
214  private SeekBar mSeekBarGrid;
215 
216  private String mOpenedDatabasePath = "";
217  private String mWorkingDirectory = "";
218  private String mWorkingDirectoryHuman = "";
219 
220  private String mUpdateRate;
221  private String mTimeThr;
222  private String mMaxFeatures;
223  private String mLoopThr;
224  private String mMinInliers;
225  private String mMaxOptimizationError;
226  private boolean mGPSSaved = false;
227 
228  private LocationManager mLocationManager;
229  private LocationListener mLocationListener;
230  private Location mLastKnownLocation;
231  private SensorManager mSensorManager;
232  private float mCompassDeg = 0.0f;
233 
234  private int mTotalLoopClosures = 0;
235  private boolean mMapIsEmpty = false;
236  int mMapNodes = 0;
237 
238  private Toast mToast = null;
239 
240  private AlertDialog mMemoryWarningDialog = null;
241 
242  private final int STATUS_TEXTS_SIZE = 19;
243  private final int STATUS_TEXTS_POSE_INDEX = 5;
244  private String[] mStatusTexts = new String[STATUS_TEXTS_SIZE];
245 
246  GestureDetector mGesDetect = null;
247 
248  //Tango Service connection.
249  ServiceConnection mTangoServiceConnection = new ServiceConnection() {
250  public void onServiceConnected(ComponentName name, final IBinder service) {
251  Thread bindThread = new Thread(new Runnable() {
252  public void run() {
253  if(!RTABMapLib.onTangoServiceConnected(service))
254  {
255  runOnUiThread(new Runnable() {
256  public void run() {
257  mToast.makeText(getApplicationContext(),
258  String.format("Failed to intialize Tango!"), mToast.LENGTH_LONG).show();
259  }
260  });
261  }
262  }
263  });
264  bindThread.start();
265  }
266 
267  public void onServiceDisconnected(ComponentName name) {
268  // Handle this if you need to gracefully shutdown/retry
269  // in the event that Tango itself crashes/gets upgraded while running.
270  mToast.makeText(getApplicationContext(),
271  String.format("Tango disconnected!"), mToast.LENGTH_LONG).show();
272  }
273  };
274 
275  @Override
276  protected void onCreate(Bundle savedInstanceState) {
277  super.onCreate(savedInstanceState);
278  setTitle(R.string.menu_name);
279 
280  mFreeMemoryOnStart = getFreeMemory();
281 
282  // Query screen size, the screen size is used for computing the normalized
283  // touch point.
284  Display display = getWindowManager().getDefaultDisplay();
285  display.getSize(mScreenSize);
286 
287  getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
288  getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
289 
290  // Setting content view of this activity.
291  setContentView(R.layout.activity_rtabmap);
292 
293  // Make sure to initialize all default values
294  SettingsActivity settings;
295 
296  mDecorView = getWindow().getDecorView();
297  mStatusBarHeight = getStatusBarHeight();
298  mActionBarHeight = getActionBarHeight();
299 
300  // Buttons for selecting camera view and Set up button click listeners.
301  mButtonCameraView = (NDSpinner)findViewById(R.id.camera_button);
302  mButtonPause = (ToggleButton)findViewById(R.id.pause_button);
303  mButtonLighting = (ToggleButton)findViewById(R.id.light_button);
304  mButtonWireframe = (ToggleButton)findViewById(R.id.wireframe_button);
305  mButtonBackfaceShown = (ToggleButton)findViewById(R.id.backface_button);
306  mButtonCloseVisualization = (Button)findViewById(R.id.close_visualization_button);
307  mButtonSaveOnDevice = (Button)findViewById(R.id.button_saveOnDevice);
308  mButtonShareOnSketchfab = (Button)findViewById(R.id.button_shareToSketchfab);
309  mButtonCameraView.setOnItemSelectedListener(this);
310  mButtonPause.setOnClickListener(this);
311  mButtonLighting.setOnClickListener(this);
312  mButtonWireframe.setOnClickListener(this);
313  mButtonBackfaceShown.setOnClickListener(this);
314  mButtonCloseVisualization.setOnClickListener(this);
315  mButtonSaveOnDevice.setOnClickListener(this);
316  mButtonShareOnSketchfab.setOnClickListener(this);
317  mButtonLighting.setChecked(false);
318  mButtonLighting.setVisibility(View.INVISIBLE);
319  mButtonWireframe.setChecked(false);
320  mButtonWireframe.setVisibility(View.INVISIBLE);
321  mButtonCloseVisualization.setVisibility(View.INVISIBLE);
322  mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
323  mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
324  if(mItemRenderingMesh != null && mItemRenderingTextureMesh != null)
325  {
326  mButtonBackfaceShown.setVisibility(mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked()?View.VISIBLE:View.INVISIBLE);
327  }
328 
329  ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.camera_view_array, android.R.layout.simple_spinner_item);
330  adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
331  mButtonCameraView.setAdapter(adapter);
332  mButtonCameraView.setOnTouchListener(new OnTouchListener() {
333  @Override
334  public boolean onTouch(View v, MotionEvent event) {
336  return false;
337  }
338  });
339 
340  mSeekBarOrthoCut = (SeekBar)findViewById(R.id.seekBar_ortho_cut);
341  mSeekBarOrthoCut.setMax(120);
342  mSeekBarOrthoCut.setProgress(80);
343  mSeekBarOrthoCut.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
344  @Override
345  public void onProgressChanged(SeekBar seekBar, int progressValue, boolean fromUser) {
346  RTABMapLib.setOrthoCropFactor((float)(120-progressValue)/20.0f - 3.0f);
348  }
349 
350  @Override
351  public void onStartTrackingTouch(SeekBar seekBar) {
352  }
353 
354  @Override
355  public void onStopTrackingTouch(SeekBar seekBar) {
356  }
357  });
358 
359  mSeekBarGrid = (SeekBar)findViewById(R.id.seekBar_grid);
360  mSeekBarGrid.setMax(180);
361  mSeekBarGrid.setProgress(90);
362  mSeekBarGrid.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
363  @Override
364  public void onProgressChanged(SeekBar seekBar, int progressValue, boolean fromUser) {
365  RTABMapLib.setGridRotation(((float)progressValue-90.0f)/2.0f);
367  }
368 
369  @Override
370  public void onStartTrackingTouch(SeekBar seekBar) {
371  }
372 
373  @Override
374  public void onStopTrackingTouch(SeekBar seekBar) {
375  }
376  });
377 
378  mToast = Toast.makeText(getApplicationContext(), "", Toast.LENGTH_SHORT);
379 
380  // OpenGL view where all of the graphics are drawn.
381  mGLView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
382 
383  mGesDetect = new GestureDetector(this, new DoubleTapGestureDetector());
384 
385  // Configure OpenGL renderer
386  mGLView.setEGLContextClientVersion(2);
387  mGLView.setEGLConfigChooser(8, 8, 8, 8, 24, 0);
388  mGLView.setOnTouchListener(new OnTouchListener() {
389  @Override
390  public boolean onTouch(View v, MotionEvent event) {
391 
392  resetNoTouchTimer(getActionBar().isShowing() && mHudVisible == false);
393 
394  mGesDetect.onTouchEvent(event);
395 
396  // Pass the touch event to the native layer for camera control.
397  // Single touch to rotate the camera around the device.
398  // Two fingers to zoom in and out.
399  int pointCount = event.getPointerCount();
400  if (pointCount == 1) {
401  float normalizedX = event.getX(0) / mScreenSize.x;
402  float normalizedY = event.getY(0) / mScreenSize.y;
404  event.getActionMasked(), normalizedX, normalizedY, 0.0f, 0.0f);
405  }
406  if (pointCount == 2) {
407  if (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) {
408  int index = event.getActionIndex() == 0 ? 1 : 0;
409  float normalizedX = event.getX(index) / mScreenSize.x;
410  float normalizedY = event.getY(index) / mScreenSize.y;
412  MotionEvent.ACTION_DOWN, normalizedX, normalizedY, 0.0f, 0.0f);
413  } else {
414  float normalizedX0 = event.getX(0) / mScreenSize.x;
415  float normalizedY0 = event.getY(0) / mScreenSize.y;
416  float normalizedX1 = event.getX(1) / mScreenSize.x;
417  float normalizedY1 = event.getY(1) / mScreenSize.y;
418  RTABMapLib.onTouchEvent(2, event.getActionMasked(),
419  normalizedX0, normalizedY0, normalizedX1, normalizedY1);
420  }
421  }
422  return true;
423  }
424  });
425 
426  // Configure the OpenGL renderer.
427  mRenderer = new Renderer(this);
428  mGLView.setRenderer(mRenderer);
429 
430  mProgressDialog = new ProgressDialog(this);
431  mProgressDialog.setCanceledOnTouchOutside(false);
432  mRenderer.setProgressDialog(mProgressDialog);
433  mRenderer.setToast(mToast);
434  setNavVisibility(true);
435 
436  mExportProgressDialog = new ProgressDialog(this);
437  mExportProgressDialog.setCanceledOnTouchOutside(false);
438  mExportProgressDialog.setCancelable(false);
439  mExportProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
440  mExportProgressDialog.setProgressNumberFormat(null);
441  mExportProgressDialog.setProgressPercentFormat(null);
442  mExportProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
443  @Override
444  public void onClick(DialogInterface dialog, int which) {
446 
447  mProgressDialog.setTitle("");
448  mProgressDialog.setMessage(String.format("Cancelling..."));
449  mProgressDialog.show();
450  }
451  });
452 
453  // Check if the Tango Core is out dated.
454  if (!CheckTangoCoreVersion(MIN_TANGO_CORE_VERSION)) {
455  mToast.makeText(this, "Tango Core out dated, please update in Play Store", mToast.LENGTH_LONG).show();
456  finish();
457  return;
458  }
459 
460  mOpenedDatabasePath = "";
461  mWorkingDirectory = "";
462  mWorkingDirectoryHuman = "";
463  mTotalLoopClosures = 0;
464  mLastFastMovementNotificationStamp = System.currentTimeMillis()/1000;
465 
466  if(Environment.getExternalStorageState().compareTo(Environment.MEDIA_MOUNTED)==0)
467  {
468  File extStore = Environment.getExternalStorageDirectory();
469  mWorkingDirectory = extStore.getAbsolutePath() + "/" + getString(R.string.app_name) + "/";
470  extStore = new File(mWorkingDirectory);
471  extStore.mkdirs();
472  mWorkingDirectoryHuman = RTABMAP_SDCARD_PATH + getString(R.string.app_name) + "/";
473  }
474  else
475  {
476  // show warning that data cannot be saved!
477  mToast.makeText(getApplicationContext(),
478  String.format("Failed to get external storage path (SD-CARD, state=%s). Saving disabled.",
479  Environment.getExternalStorageState()), mToast.LENGTH_LONG).show();
480  }
481 
482  RTABMapLib.onCreate(this);
483  String tmpDatabase = mWorkingDirectory+RTABMAP_TMP_DB;
484  (new File(tmpDatabase)).delete();
485  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
486  boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory), Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
487  RTABMapLib.openDatabase(tmpDatabase, databaseInMemory, false);
488 
489  DisplayManager displayManager = (DisplayManager) getSystemService(DISPLAY_SERVICE);
490  if (displayManager != null) {
491  displayManager.registerDisplayListener(new DisplayManager.DisplayListener() {
492  @Override
493  public void onDisplayAdded(int displayId) {
494 
495  }
496 
497  @Override
498  public void onDisplayChanged(int displayId) {
499  synchronized (this) {
501  Display display = getWindowManager().getDefaultDisplay();
502  display.getSize(mScreenSize);
503  }
504  }
505 
506  @Override
507  public void onDisplayRemoved(int displayId) {}
508  }, null);
509  }
510 
511  // Acquire a reference to the system Location Manager
512  mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
513 
514  // Define a listener that responds to location updates
515  mLocationListener = new LocationListener() {
516  public void onLocationChanged(Location location) {
517  mLastKnownLocation = location;
518  double stamp = location.getTime()/1000.0;
519  if(!DISABLE_LOG) Log.d(TAG, String.format("GPS received at %f (%d)", stamp, location.getTime()));
521  stamp,
522  (double)location.getLongitude(),
523  (double)location.getLatitude(),
524  (double)location.getAltitude(),
525  (double)location.getAccuracy(),
526  (double)mCompassDeg);
527  }
528 
529  public void onStatusChanged(String provider, int status, Bundle extras) {}
530 
531  public void onProviderEnabled(String provider) {}
532 
533  public void onProviderDisabled(String provider) {}
534  };
535 
536  mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
537 
538  DISABLE_LOG = !( 0 != ( getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE ) );
539  }
540 
541  @Override
542  public void onSensorChanged(SensorEvent event) {
543  // get the angle around the z-axis rotated
544  mCompassDeg = event.values[0];
545  }
546 
547  @Override
548  public void onAccuracyChanged(Sensor sensor, int accuracy) {
549  // not in use
550  }
551 
552 
553  public int getStatusBarHeight() {
554  int result = 0;
555  int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
556  if (resourceId > 0) {
557  result = getResources().getDimensionPixelSize(resourceId);
558  }
559  return result;
560  }
561  public int getActionBarHeight() {
562  int result = 0;
563  TypedValue tv = new TypedValue();
564  if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
565  {
566  result = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
567  }
568 
569  return result;
570  }
571 
572  @Override
573  public void onWindowFocusChanged(boolean hasFocus) {
574 
575  super.onWindowFocusChanged(hasFocus);
576 
577  if(!mHudVisible)
578  {
579  mRenderer.setOffset(!hasFocus?-mStatusBarHeight:0);
580  }
581  }
582 
583  // This snippet hides the system bars.
584  private void setNavVisibility(boolean visible) {
585  int newVis = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
586  | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
587  | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
588  if (!visible) {
589  newVis |= View.SYSTEM_UI_FLAG_LOW_PROFILE
590  | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
591  | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
592  | View.SYSTEM_UI_FLAG_IMMERSIVE;
593  mRenderer.setOffset(!hasWindowFocus()?-mStatusBarHeight:0);
594  }
595  else
596  {
597  mRenderer.setOffset(-mStatusBarHeight-mActionBarHeight);
598  }
599 
600  // Set the new desired visibility.
601  mDecorView.setSystemUiVisibility(newVis);
602  }
603 
604  @Override
605  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
606  // Check which request we're responding to
607  if (requestCode == Tango.TANGO_INTENT_ACTIVITYCODE) {
608  // Make sure the request was successful
609  if (resultCode == RESULT_CANCELED) {
610  mToast.makeText(this, "Motion Tracking Permissions Required!", mToast.LENGTH_SHORT).show();
611  finish();
612  }
613  }
614  else if (requestCode == SKETCHFAB_ACTIVITY_CODE) {
615  // Make sure the request was successful
616  if (resultCode == RESULT_OK) {
617  mAuthToken = data.getStringExtra(RTABMAP_AUTH_TOKEN_KEY);
618  }
619  }
620  }
621 
622  @Override
623  public boolean onMenuOpened(int featureId, Menu menu) {
624  mMenuOpened = true;
625  return super.onMenuOpened(featureId, menu);
626  }
627 
628  @Override
629  public void onPanelClosed(int featureId, Menu menu) {
630  mMenuOpened = false;
631  }
632 
633  @Override
634  public void onBackPressed() {
635 
636  if(mBlockBack)
637  {
638  mToast.makeText(this, "Press Back once more to exit", mToast.LENGTH_LONG).show();
639  mBlockBack = false;
640  }
641  else
642  {
643  super.onBackPressed();
644  }
645  }
646 
647  @Override
648  protected void onPause() {
649  super.onPause();
651 
652  if(!DISABLE_LOG) Log.i(TAG, "onPause()");
653  mOnPause = true;
654 
655  if(!mButtonPause.isChecked())
656  {
657  mButtonPause.setChecked(true);
658  pauseMapping();
659  }
660 
661  mLocationManager.removeUpdates(mLocationListener);
662  mSensorManager.unregisterListener(this);
663 
665 
666  unbindService(mTangoServiceConnection);
667 
668  // This deletes OpenGL context!
669  mGLView.onPause();
670 
671  mOnPauseStamp = System.currentTimeMillis()/1000;
672  }
673 
674  @Override
675  protected void onResume() {
676  super.onResume();
677 
678  mProgressDialog.setTitle("");
679  if(mOnPause)
680  {
681  if(System.currentTimeMillis()/1000 - mOnPauseStamp < 1)
682  {
683  mProgressDialog.setMessage(String.format("RTAB-Map has been interrupted by another application, Tango should be re-initialized! Set your phone/tablet in Airplane mode if this happens often."));
684  }
685  else
686  {
687  mProgressDialog.setMessage(String.format("Hold Tight! Initializing Tango Service..."));
688  }
689  mToast.makeText(this, "Mapping is paused!", mToast.LENGTH_LONG).show();
690  }
691  else
692  {
693  mProgressDialog.setMessage(String.format("Hold Tight! Initializing Tango Service...\nTip: If the camera is still drifting just after the mapping has started, do \"Reset\"."));
694  }
695  mProgressDialog.show();
696  mOnPause = false;
698 
699  // update preferences
700  try
701  {
702  if(!DISABLE_LOG) Log.d(TAG, "update preferences...");
703  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
704  mUpdateRate = sharedPref.getString(getString(R.string.pref_key_update_rate), getString(R.string.pref_default_update_rate));
705  String maxSpeed = sharedPref.getString(getString(R.string.pref_key_max_speed), getString(R.string.pref_default_max_speed));
706  mTimeThr = sharedPref.getString(getString(R.string.pref_key_time_thr), getString(R.string.pref_default_time_thr));
707  String memThr = sharedPref.getString(getString(R.string.pref_key_mem_thr), getString(R.string.pref_default_mem_thr));
708  mLoopThr = sharedPref.getString(getString(R.string.pref_key_loop_thr), getString(R.string.pref_default_loop_thr));
709  String simThr = sharedPref.getString(getString(R.string.pref_key_sim_thr), getString(R.string.pref_default_sim_thr));
710  mMinInliers = sharedPref.getString(getString(R.string.pref_key_min_inliers), getString(R.string.pref_default_min_inliers));
711  mMaxOptimizationError = sharedPref.getString(getString(R.string.pref_key_opt_error), getString(R.string.pref_default_opt_error));
712  float maxOptimizationError = Float.parseFloat(mMaxOptimizationError);
713  if(maxOptimizationError >0 && maxOptimizationError<1)
714  {
715  Log.w(TAG, "Migration of " + getString(R.string.pref_key_opt_error) + " from " + mMaxOptimizationError + " to " + getString(R.string.pref_default_opt_error)) ;
716  SharedPreferences.Editor editor = sharedPref.edit();
717  editor.putString(getString(R.string.pref_key_opt_error), getString(R.string.pref_default_opt_error));
718  editor.commit();
719  mMaxOptimizationError = getString(R.string.pref_default_opt_error);
720  }
721  mMaxFeatures = sharedPref.getString(getString(R.string.pref_key_features_voc), getString(R.string.pref_default_features_voc));
722  String maxFeaturesLoop = sharedPref.getString(getString(R.string.pref_key_features), getString(R.string.pref_default_features));
723  String featureType = sharedPref.getString(getString(R.string.pref_key_features_type), getString(R.string.pref_default_features_type));
724  boolean keepAllDb = sharedPref.getBoolean(getString(R.string.pref_key_keep_all_db), Boolean.parseBoolean(getString(R.string.pref_default_keep_all_db)));
725  boolean optimizeFromGraphEnd = sharedPref.getBoolean(getString(R.string.pref_key_optimize_end), Boolean.parseBoolean(getString(R.string.pref_default_optimize_end)));
726  String optimizer = sharedPref.getString(getString(R.string.pref_key_optimizer), getString(R.string.pref_default_optimizer));
727  mGPSSaved = sharedPref.getBoolean(getString(R.string.pref_key_gps_saved), Boolean.parseBoolean(getString(R.string.pref_default_gps_saved)));
728  if(mGPSSaved)
729  {
730  mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mLocationListener);
731  mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_GAME);
732  }
733 
734  if(!DISABLE_LOG) Log.d(TAG, "set mapping parameters");
735  RTABMapLib.setOnlineBlending(sharedPref.getBoolean(getString(R.string.pref_key_blending), Boolean.parseBoolean(getString(R.string.pref_default_blending))));
736  RTABMapLib.setNodesFiltering(sharedPref.getBoolean(getString(R.string.pref_key_nodes_filtering), Boolean.parseBoolean(getString(R.string.pref_default_nodes_filtering))));
737  RTABMapLib.setRawScanSaved(sharedPref.getBoolean(getString(R.string.pref_key_raw_scan_saved), Boolean.parseBoolean(getString(R.string.pref_default_raw_scan_saved))));
738  RTABMapLib.setFullResolution(sharedPref.getBoolean(getString(R.string.pref_key_resolution), Boolean.parseBoolean(getString(R.string.pref_default_resolution))));
739  RTABMapLib.setSmoothing(sharedPref.getBoolean(getString(R.string.pref_key_smoothing), Boolean.parseBoolean(getString(R.string.pref_default_smoothing))));
740  RTABMapLib.setCameraColor(!sharedPref.getBoolean(getString(R.string.pref_key_fisheye), Boolean.parseBoolean(getString(R.string.pref_default_fisheye))));
741  RTABMapLib.setAppendMode(sharedPref.getBoolean(getString(R.string.pref_key_append), Boolean.parseBoolean(getString(R.string.pref_default_append))));
742  RTABMapLib.setMappingParameter("Rtabmap/DetectionRate", mUpdateRate);
743  RTABMapLib.setMappingParameter("Rtabmap/TimeThr", mTimeThr);
744  RTABMapLib.setMappingParameter("Rtabmap/MemoryThr", memThr);
745  RTABMapLib.setMappingParameter("RGBD/LinearSpeedUpdate", maxSpeed);
746  RTABMapLib.setMappingParameter("RGBD/AngularSpeedUpdate", String.valueOf(Float.parseFloat(maxSpeed)/2.0f));
747  RTABMapLib.setMappingParameter("Mem/RehearsalSimilarity", simThr);
748  RTABMapLib.setMappingParameter("Kp/MaxFeatures", mMaxFeatures);
749  RTABMapLib.setMappingParameter("Vis/MaxFeatures", maxFeaturesLoop);
750  RTABMapLib.setMappingParameter("Vis/MinInliers", mMinInliers);
751  RTABMapLib.setMappingParameter("Rtabmap/LoopThr", mLoopThr);
752  RTABMapLib.setMappingParameter("RGBD/OptimizeMaxError", mMaxOptimizationError);
753  RTABMapLib.setMappingParameter("Kp/DetectorStrategy", featureType);
754  RTABMapLib.setMappingParameter("Vis/FeatureType", featureType);
755  RTABMapLib.setMappingParameter("Mem/NotLinkedNodesKept", String.valueOf(keepAllDb));
756  RTABMapLib.setMappingParameter("RGBD/OptimizeFromGraphEnd", String.valueOf(optimizeFromGraphEnd));
757  RTABMapLib.setMappingParameter("Optimizer/Strategy", optimizer);
758 
759  if(!DISABLE_LOG) Log.d(TAG, "set exporting parameters...");
760  RTABMapLib.setCloudDensityLevel(Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_density), getString(R.string.pref_default_density))));
761  RTABMapLib.setMaxCloudDepth(Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_depth), getString(R.string.pref_default_depth))));
762  RTABMapLib.setMinCloudDepth(Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_min_depth), getString(R.string.pref_default_min_depth))));
763  RTABMapLib.setPointSize(Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_point_size), getString(R.string.pref_default_point_size))));
764  RTABMapLib.setMeshAngleTolerance(Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_angle), getString(R.string.pref_default_angle))));
765  RTABMapLib.setMeshTriangleSize(Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_triangle), getString(R.string.pref_default_triangle))));
766  float bgColor = Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_background_color), getString(R.string.pref_default_background_color)));
768  mRenderer.setTextColor(bgColor>=0.6f?0.0f:1.0f);
769 
770  if(!DISABLE_LOG) Log.d(TAG, "set rendering parameters...");
771  RTABMapLib.setClusterRatio(Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_cluster_ratio), getString(R.string.pref_default_cluster_ratio))));
772  RTABMapLib.setMaxGainRadius(Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_gain_max_radius), getString(R.string.pref_default_gain_max_radius))));
773  RTABMapLib.setRenderingTextureDecimation(Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_rendering_texture_decimation), getString(R.string.pref_default_rendering_texture_decimation))));
774 
775  if(mItemRenderingPointCloud != null)
776  {
777  int renderingType = sharedPref.getInt(getString(R.string.pref_key_rendering), Integer.parseInt(getString(R.string.pref_default_rendering)));
778  if(renderingType == 0)
779  {
780  mItemRenderingPointCloud.setChecked(true);
781  }
782  else if(renderingType == 1)
783  {
784  mItemRenderingMesh.setChecked(true);
785  }
786  else
787  {
788  mItemRenderingTextureMesh.setChecked(true);
789  }
791  mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
792  mItemRenderingTextureMesh.isChecked());
793 
794  mButtonBackfaceShown.setVisibility(mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked()?View.VISIBLE:View.INVISIBLE);
795  }
796  }
797  catch(Exception e)
798  {
799  Log.e(TAG, "Error parsing preferences: " + e.getMessage());
800  mToast.makeText(this, String.format("Error parsing preferences: "+e.getMessage()), mToast.LENGTH_LONG).show();
801  }
802 
803  if(!DISABLE_LOG) Log.i(TAG, String.format("onResume()"));
804 
805  if (Tango.hasPermission(this, Tango.PERMISSIONTYPE_MOTION_TRACKING)) {
806 
807  mGLView.onResume();
808 
809  } else {
810  if(!DISABLE_LOG) Log.i(TAG, String.format("Asking for motion tracking permission"));
811  startActivityForResult(
812  Tango.getRequestPermissionIntent(Tango.PERMISSIONTYPE_MOTION_TRACKING),
813  Tango.TANGO_INTENT_ACTIVITYCODE);
814  }
815 
816  TangoInitializationHelper.bindTangoService(getActivity(), mTangoServiceConnection);
817  resetNoTouchTimer(true);
818  }
819 
820  private void setCamera(int type)
821  {
822  if(!DISABLE_LOG) Log.i(TAG, String.format("called setCamera(type=%d);", type));
823 
824  // for convenience, for a refresh of the memory used
825  long freeMemory = getFreeMemory();
826  mStatusTexts[1] = getString(R.string.memory)+String.valueOf(mFreeMemoryOnStart>freeMemory?mFreeMemoryOnStart-freeMemory:0);
827  mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(freeMemory);
829 
830  RTABMapLib.setCamera(type);
831  mButtonCameraView.setSelection(type, true);
832  mSeekBarOrthoCut.setVisibility(type!=3?View.INVISIBLE:View.VISIBLE);
833  mSeekBarGrid.setVisibility(mSeekBarGrid.isEnabled() && type==3?View.VISIBLE:View.INVISIBLE);
834  if(type==3)
835  {
836  mSeekBarOrthoCut.setMax(120);
837  mSeekBarOrthoCut.setProgress(80);
838  }
839  }
840 
841  @Override
842  public void onClick(View v) {
843  // Handle button clicks.
844  switch (v.getId()) {
845  case R.id.gl_surface_view:
846 
847  break;
848  case R.id.pause_button:
849  pauseMapping();
850  break;
851  case R.id.light_button:
852  RTABMapLib.setLighting(mButtonLighting.isChecked());
853  break;
854  case R.id.backface_button:
855  RTABMapLib.setBackfaceCulling(!mButtonBackfaceShown.isChecked());
856  break;
857  case R.id.wireframe_button:
858  RTABMapLib.setWireframe(mButtonWireframe.isChecked());
859  break;
860  case R.id.close_visualization_button:
861  if(mSavedRenderingType==0)
862  {
863  mItemRenderingPointCloud.setChecked(true);
864  }
865  else if(mSavedRenderingType==1)
866  {
867  mItemRenderingMesh.setChecked(true);
868  }
869  else
870  {
871  mItemRenderingTextureMesh.setChecked(true);
872  }
875  break;
876  case R.id.button_saveOnDevice:
877  saveOnDevice();
878  break;
879  case R.id.button_shareToSketchfab:
881  break;
882  default:
883  return;
884  }
886  }
887 
888  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
889  setCamera(pos);
891  }
892 
893  public void onNothingSelected(AdapterView<?> parent) {
895  }
896 
897  private void setAndroidOrientation() {
898  Display display = getWindowManager().getDefaultDisplay();
899  Camera.CameraInfo colorCameraInfo = new Camera.CameraInfo();
900  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
901  boolean fisheye = sharedPref.getBoolean(getString(R.string.pref_key_fisheye), Boolean.parseBoolean(getString(R.string.pref_default_fisheye)));
902  Camera.getCameraInfo(fisheye?1:0, colorCameraInfo);
903  RTABMapLib.setScreenRotation(display.getRotation(), colorCameraInfo.orientation);
904  }
905 
906  class DoubleTapGestureDetector extends GestureDetector.SimpleOnGestureListener {
907 
908  @Override
909  public boolean onDoubleTap(MotionEvent event) {
910  if(!DISABLE_LOG) Log.i(TAG, "onDoubleTap");
911  float normalizedX = event.getX(0) / mScreenSize.x;
912  float normalizedY = event.getY(0) / mScreenSize.y;
913  RTABMapLib.onTouchEvent(3, event.getActionMasked(), normalizedX, normalizedY, 0.0f, 0.0f);
914  return true;
915  }
916  @Override
917  public boolean onSingleTapConfirmed(MotionEvent event) {
918  if(!DISABLE_LOG) Log.i(TAG, "onSingleTapConfirmed");
919  if(mHudVisible)
920  {
921  notouchHandler.removeCallbacks(notouchCallback);
922  notouchHandler.postDelayed(notouchCallback, 0);
923  }
924  else
925  {
926  resetNoTouchTimer(true);
927  }
928  return true;
929  }
930  }
931 
932  @Override
933  public boolean onCreateOptionsMenu(Menu menu) {
934  if(!DISABLE_LOG) Log.i(TAG, "called onCreateOptionsMenu;");
935 
936  MenuInflater inflater = getMenuInflater();
937  inflater.inflate(R.menu.optionmenu, menu);
938 
939  getActionBar().setDisplayShowHomeEnabled(true);
940  getActionBar().setIcon(R.drawable.ic_launcher);
941 
942  mItemSave = menu.findItem(R.id.save);
943  mItemOpen = menu.findItem(R.id.open);
944  mItemPostProcessing = menu.findItem(R.id.post_processing);
945  mItemExport = menu.findItem(R.id.export);
946  mItemSettings = menu.findItem(R.id.settings);
947  mItemModes = menu.findItem(R.id.modes);
948  mItemReset = menu.findItem(R.id.reset);
949  mItemLocalizationMode = menu.findItem(R.id.localization_mode);
950  mItemTrajectoryMode = menu.findItem(R.id.trajectory_mode);
951  mItemRenderingPointCloud = menu.findItem(R.id.point_cloud);
952  mItemRenderingMesh = menu.findItem(R.id.mesh);
953  mItemRenderingTextureMesh = menu.findItem(R.id.texture_mesh);
954  mItemDataRecorderMode = menu.findItem(R.id.data_recorder);
955  mItemStatusVisibility = menu.findItem(R.id.status);
956  mItemDebugVisibility = menu.findItem(R.id.debug);
957  mItemSave.setEnabled(false);
958  mItemExport.setEnabled(false);
959  mItemOpen.setEnabled(false);
960  mItemPostProcessing.setEnabled(false);
961  mItemDataRecorderMode.setEnabled(false);
962 
963  try
964  {
965  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
966  int renderingType = sharedPref.getInt(getString(R.string.pref_key_rendering), Integer.parseInt(getString(R.string.pref_default_rendering)));
967  if(renderingType == 0)
968  {
969  mItemRenderingPointCloud.setChecked(true);
970  }
971  else if(renderingType == 1)
972  {
973  mItemRenderingMesh.setChecked(true);
974  }
975  else
976  {
977  mItemRenderingTextureMesh.setChecked(true);
978  }
980  mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
981  mItemRenderingTextureMesh.isChecked());
982 
983  if(mButtonBackfaceShown != null)
984  {
985  mButtonBackfaceShown.setVisibility(mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked()?View.VISIBLE:View.INVISIBLE);
986  }
987  }
988  catch(Exception e)
989  {
990  Log.e(TAG, "Error parsing rendering preferences: " + e.getMessage());
991  mToast.makeText(this, String.format("Error parsing rendering preferences: "+e.getMessage()), mToast.LENGTH_LONG).show();
992  }
993 
994  updateState(mState);
995 
996  return true;
997  }
998 
999  private long getFreeMemory()
1000  {
1001  MemoryInfo mi = new MemoryInfo();
1002  ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
1003  activityManager.getMemoryInfo(mi);
1004  return mi.availMem / 0x100000L; // MB
1005  }
1006 
1007  private void updateStatusTexts()
1008  {
1009  if(mItemStatusVisibility != null && mItemDebugVisibility != null)
1010  {
1011  if((mItemStatusVisibility.isChecked() || mState == State.STATE_VISUALIZING_WHILE_LOADING) && mItemDebugVisibility.isChecked())
1012  {
1013  mRenderer.updateTexts(mStatusTexts);
1014  }
1015  else if((mItemStatusVisibility.isChecked() || mState == State.STATE_VISUALIZING_WHILE_LOADING))
1016  {
1017  mRenderer.updateTexts(Arrays.copyOfRange(mStatusTexts, 0, STATUS_TEXTS_POSE_INDEX-1));
1018  }
1019  else if(mItemDebugVisibility.isChecked())
1020  {
1021  mRenderer.updateTexts(Arrays.copyOfRange(mStatusTexts, STATUS_TEXTS_POSE_INDEX-1, mStatusTexts.length));
1022  }
1023  else
1024  {
1025  mRenderer.updateTexts(null);
1026  }
1027  }
1028  }
1029 
1030  private void updateStatsUI(
1031  int loopClosureId,
1032  int inliers,
1033  int matches,
1034  int rejected,
1035  float optimizationMaxError,
1036  float optimizationMaxErrorRatio,
1037  boolean fastMovement,
1038  String[] statusTexts)
1039  {
1040  mStatusTexts = statusTexts;
1042 
1043  if(mButtonPause!=null)
1044  {
1045  if(!mButtonPause.isChecked())
1046  {
1047  //check if we are low in memory
1048  long memoryFree = getFreeMemory();
1049  long memoryUsed = mFreeMemoryOnStart>memoryFree?mFreeMemoryOnStart-memoryFree:0;
1050 
1051  if(memoryFree < 400)
1052  {
1053  mButtonPause.setChecked(true);
1054  pauseMapping();
1055 
1056  if(mMemoryWarningDialog!=null)
1057  {
1058  mMemoryWarningDialog.dismiss();
1059  mMemoryWarningDialog = null;
1060  }
1061 
1062  mMemoryWarningDialog = new AlertDialog.Builder(getActivity())
1063  .setTitle("Memory is full!")
1064  .setCancelable(false)
1065  .setMessage(String.format("Scanning has been paused because free memory is too "
1066  + "low (%d MB). You should be able to save the database but some post-processing and exporting options may fail. "
1067  + "\n\nNote that for large environments, you can save multiple databases and "
1068  + "merge them with RTAB-Map Desktop version.", memoryUsed))
1069  .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
1070  public void onClick(DialogInterface dialog, int which) {
1071  mMemoryWarningDialog = null;
1072  }
1073  })
1074  .setNeutralButton("Save", new DialogInterface.OnClickListener() {
1075  public void onClick(DialogInterface dialog, int which) {
1076  saveOnDevice();
1077  mMemoryWarningDialog = null;
1078  }
1079  })
1080  .create();
1081  mMemoryWarningDialog.show();
1082  }
1083  else if(mMemoryWarningDialog == null && memoryUsed*3 > memoryFree && (mItemDataRecorderMode == null || !mItemDataRecorderMode.isChecked()))
1084  {
1085  mMemoryWarningDialog = new AlertDialog.Builder(getActivity())
1086  .setTitle("Warning: Memory is almost full!")
1087  .setCancelable(false)
1088  .setMessage(String.format("Free memory (%d MB) should be at least 3 times the "
1089  + "memory used (%d MB) so that some post-processing and exporting options "
1090  + "have enough memory to work correctly. If you just want to save the database "
1091  + "after scanning, you can continue until the next warning.\n\n"
1092  + "Note that showing only point clouds reduces memory needed for rendering.", memoryFree, memoryUsed))
1093  .setPositiveButton("Pause", new DialogInterface.OnClickListener() {
1094  public void onClick(DialogInterface dialog, int which) {
1095  mButtonPause.setChecked(true);
1096  pauseMapping();
1097  }
1098  })
1099  .setNeutralButton("Continue", new DialogInterface.OnClickListener() {
1100  public void onClick(DialogInterface dialog, int which) {
1101  }
1102  })
1103  .create();
1104  mMemoryWarningDialog.show();
1105  }
1106  }
1107  }
1108 
1109 
1110  if(mButtonPause!=null && !mButtonPause.isChecked())
1111  {
1112  long currentTime = System.currentTimeMillis()/1000;
1113  if(loopClosureId > 0)
1114  {
1115  mToast.setText(String.format("Loop closure detected! (%d/%d inliers)", inliers, matches));
1116  mToast.show();
1117  }
1118  else if(rejected > 0)
1119  {
1120  if(inliers >= Integer.parseInt(mMinInliers))
1121  {
1122  if(optimizationMaxError > 0.0f)
1123  {
1124  mToast.setText(String.format("Loop closure rejected, too high graph optimization error (%.3fm: ratio=%.3f < factor=%sx).", optimizationMaxError, optimizationMaxErrorRatio, mMaxOptimizationError));
1125  }
1126  else
1127  {
1128  mToast.setText(String.format("Loop closure rejected, graph optimization failed! You may try a different Graph Optimizer (see Mapping options)."));
1129  }
1130  }
1131  else
1132  {
1133  mToast.setText(String.format("Loop closure rejected, not enough inliers (%d/%d < %s).", inliers, matches, mMinInliers));
1134  }
1135  mToast.show();
1136  }
1137  else if(fastMovement)
1138  {
1139  if(currentTime - mLastFastMovementNotificationStamp > 3)
1140  {
1141  mToast.setText("Move slower... blurry images are not added to map (\"Settings->Mapping...->Maximum Motion Speed\" is enabled).");
1142  mToast.show();
1143  }
1144  }
1145 
1146  if(!fastMovement)
1147  {
1148  mLastFastMovementNotificationStamp = currentTime;
1149  }
1150  }
1151  }
1152 
1153  // called from jni
1154  public void updateStatsCallback(
1155  final int nodes,
1156  final int words,
1157  final int points,
1158  final int polygons,
1159  final float updateTime,
1160  final int loopClosureId,
1161  final int highestHypId,
1162  final int databaseMemoryUsed,
1163  final int inliers,
1164  final int matches,
1165  final int featuresExtracted,
1166  final float hypothesis,
1167  final int nodesDrawn,
1168  final float fps,
1169  final int rejected,
1170  final float rehearsalValue,
1171  final float optimizationMaxError,
1172  final float optimizationMaxErrorRatio,
1173  final float distanceTravelled,
1174  final int fastMovement,
1175  final float x,
1176  final float y,
1177  final float z,
1178  final float roll,
1179  final float pitch,
1180  final float yaw)
1181  {
1182  if(!DISABLE_LOG) Log.i(TAG, String.format("updateStatsCallback()"));
1183 
1184  final String[] statusTexts = new String[STATUS_TEXTS_SIZE];
1185  if(mButtonPause!=null && !mButtonPause.isChecked())
1186  {
1187  String updateValue = mUpdateRate.compareTo("0")==0?"Max":mUpdateRate;
1188  statusTexts[0] = getString(R.string.status)+(mItemDataRecorderMode!=null&&mItemDataRecorderMode.isChecked()?String.format("Recording (%s Hz)", updateValue):mItemLocalizationMode!=null && mItemLocalizationMode.isChecked()?String.format("Localization (%s Hz)", updateValue):String.format("Mapping (%s Hz)", updateValue));
1189  }
1190  else
1191  {
1192  statusTexts[0] = mStatusTexts[0];
1193  }
1194 
1195  long memoryFree = getFreeMemory();
1196  statusTexts[1] = getString(R.string.memory)+(mFreeMemoryOnStart>memoryFree?mFreeMemoryOnStart-memoryFree:0);
1197  statusTexts[2] = getString(R.string.free_memory)+memoryFree;
1198 
1199  if(loopClosureId > 0)
1200  {
1202  }
1203 
1204  mMapNodes = nodes;
1205 
1206  if(mGPSSaved)
1207  {
1208  if(mLastKnownLocation != null)
1209  {
1210  long millisec = System.currentTimeMillis() - mLastKnownLocation.getTime();
1211  if(millisec > 1000)
1212  {
1213  statusTexts[3] = getString(R.string.gps)+String.format("[too old, %d ms]", millisec);
1214  }
1215  else
1216  {
1217  statusTexts[3] = getString(R.string.gps)+
1218  String.format("%.2f %.2f %.2fm %.0fdeg %.0fm",
1219  mLastKnownLocation.getLongitude(),
1220  mLastKnownLocation.getLatitude(),
1221  mLastKnownLocation.getAltitude(),
1222  mCompassDeg,
1223  mLastKnownLocation.getAccuracy());
1224  }
1225  }
1226  else
1227  {
1228  statusTexts[3] = getString(R.string.gps)+"[not yet available]";
1229  }
1230  }
1231 
1232  String formattedDate = new SimpleDateFormat("HH:mm:ss.SSS").format(new Date());
1233  statusTexts[4] = getString(R.string.time)+formattedDate;
1234 
1235  int index = STATUS_TEXTS_POSE_INDEX;
1236  statusTexts[index++] = getString(R.string.nodes)+nodes+" (" + nodesDrawn + " shown)";
1237  statusTexts[index++] = getString(R.string.words)+words;
1238  statusTexts[index++] = getString(R.string.database_size)+databaseMemoryUsed;
1239  statusTexts[index++] = getString(R.string.points)+points;
1240  statusTexts[index++] = getString(R.string.polygons)+polygons;
1241  statusTexts[index++] = getString(R.string.update_time)+(int)(updateTime) + " / " + (mTimeThr.compareTo("0")==0?"No Limit":mTimeThr);
1242  statusTexts[index++] = getString(R.string.features)+featuresExtracted +" / " + (mMaxFeatures.compareTo("0")==0?"No Limit":mMaxFeatures.compareTo("-1")==0?"Disabled":mMaxFeatures);
1243  statusTexts[index++] = getString(R.string.rehearsal)+(int)(rehearsalValue*100.0f);
1244  statusTexts[index++] = getString(R.string.total_loop)+mTotalLoopClosures;
1245  statusTexts[index++] = getString(R.string.inliers)+inliers;
1246  statusTexts[index++] = getString(R.string.hypothesis)+(int)(hypothesis*100.0f) +" / " + (int)(Float.parseFloat(mLoopThr)*100.0f) + " (" + (loopClosureId>0?loopClosureId:highestHypId)+")";
1247  statusTexts[index++] = getString(R.string.fps)+(int)fps+" Hz";
1248  statusTexts[index++] = getString(R.string.distance)+(int)distanceTravelled+" m";
1249  statusTexts[index++] = String.format("Pose (x,y,z): %.2f %.2f %.2f", x,y,z);
1250 
1251  runOnUiThread(new Runnable() {
1252  public void run() {
1253  updateStatsUI(loopClosureId, inliers, matches, rejected, optimizationMaxError, optimizationMaxErrorRatio, fastMovement!=0, statusTexts);
1254  }
1255  });
1256  }
1257 
1258  private void rtabmapInitEventUI(
1259  int status,
1260  String msg)
1261  {
1262  if(!DISABLE_LOG) Log.i(TAG, String.format("rtabmapInitEventsUI() status=%d msg=%s", status, msg));
1263 
1264  int optimizedMeshDetected = 0;
1265 
1266  if(msg.equals("Loading optimized cloud...done!"))
1267  {
1268  optimizedMeshDetected = 1;
1269  }
1270  else if(msg.equals("Loading optimized mesh...done!"))
1271  {
1272  optimizedMeshDetected = 2;
1273  }
1274  else if(msg.equals("Loading optimized texture mesh...done!"))
1275  {
1276  optimizedMeshDetected = 3;
1277  }
1278  if(optimizedMeshDetected > 0)
1279  {
1281  mSavedRenderingType = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
1282  if(optimizedMeshDetected==1)
1283  {
1284  mItemRenderingPointCloud.setChecked(true);
1285  }
1286  else if(optimizedMeshDetected==2)
1287  {
1288  mItemRenderingMesh.setChecked(true);
1289  }
1290  else // isOBJ
1291  {
1292  mItemRenderingTextureMesh.setChecked(true);
1293  }
1294 
1296  if(mButtonCameraView.getSelectedItemPosition() == 0)
1297  {
1298  setCamera(2);
1299  }
1300  mToast.makeText(getActivity(), String.format("Optimized mesh detected in the database, it is shown while the database is loading..."), mToast.LENGTH_LONG).show();
1301  mProgressDialog.dismiss();
1302  }
1303 
1304  if(mButtonPause!=null)
1305  {
1306  if(mButtonPause.isChecked())
1307  {
1308  mStatusTexts[0] = getString(R.string.status)+(status == 1 && msg.isEmpty()?"Paused":msg);
1309  }
1310  else if(mItemLocalizationMode!=null && mItemDataRecorderMode!=null)
1311  {
1312  mStatusTexts[0] = getString(R.string.status)+(status == 1 && msg.isEmpty()?(mItemDataRecorderMode!=null&&mItemDataRecorderMode.isChecked()?"Recording":mItemLocalizationMode!=null&&mItemLocalizationMode.isChecked()?"Localization":"Mapping"):msg);
1313  }
1314 
1315  long freeMemory = getFreeMemory();
1316  mStatusTexts[1] = getString(R.string.memory)+String.valueOf(mFreeMemoryOnStart>freeMemory?mFreeMemoryOnStart-freeMemory:0);
1317  mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(freeMemory);
1319  }
1320  }
1321 
1322  //called from jni
1324  final int status,
1325  final String msg)
1326  {
1327  if(!DISABLE_LOG) Log.i(TAG, String.format("rtabmapInitEventCallback()"));
1328 
1329  runOnUiThread(new Runnable() {
1330  public void run() {
1331  rtabmapInitEventUI(status, msg);
1332  }
1333  });
1334  }
1335 
1336  private void updateProgressionUI(
1337  int count,
1338  int max)
1339  {
1340  if(!DISABLE_LOG) Log.i(TAG, String.format("updateProgressionUI() count=%d max=%s", count, max));
1341 
1342  mExportProgressDialog.setMax(max);
1343  mExportProgressDialog.setProgress(count);
1344  }
1345 
1346  //called from jni
1348  final int count,
1349  final int max)
1350  {
1351  if(!DISABLE_LOG) Log.i(TAG, String.format("updateProgressionCallback()"));
1352 
1353  runOnUiThread(new Runnable() {
1354  public void run() {
1355  updateProgressionUI(count, max);
1356  }
1357  });
1358  }
1359 
1360  private void tangoEventUI(
1361  int type,
1362  String key,
1363  String value)
1364  {
1376  String str = null;
1377  if(key.equals("TangoServiceException"))
1378  str = String.format("Tango service exception: %s", value);
1379  else if(key.equals("FisheyeOverExposed"))
1380  ;//str = String.format("The fisheye image is over exposed with average pixel value %s px.", value);
1381  else if(key.equals("FisheyeUnderExposed"))
1382  ;//str = String.format("The fisheye image is under exposed with average pixel value %s px.", value);
1383  else if(key.equals("ColorOverExposed"))
1384  ;//str = String.format("The color image is over exposed with average pixel value %s px.", value);
1385  else if(key.equals("ColorUnderExposed"))
1386  ;//str = String.format("The color image is under exposed with average pixel value %s px.", value);
1387  else if(key.equals("CameraTango"))
1388  str = value;
1389  else if(key.equals("TooFewFeaturesTracked"))
1390  {
1391  if(!value.equals("0"))
1392  {
1393  str = String.format("Too few features (%s) were tracked in the fisheye image. This may result in poor odometry!", value);
1394  }
1395  }
1396  else if(key.equals("TooClose"))
1397  {
1398  str = String.format("Too close! Tip: Scan from at least ~1 meter from surfaces.", value);
1399  }
1400  else if(key.equals("TangoPoseEventNotReceived"))
1401  {
1402  str = String.format("No valid tango pose event received since %s sec.", value);
1403  }
1404  else
1405  {
1406  str = String.format("Unknown Tango event detected!? (type=%d, key=%s, value=%s)", type, key, value);
1407  }
1408  if(str!=null)
1409  {
1410  mToast.setText(str);
1411  mToast.show();
1412  }
1413  }
1414 
1415  //called from jni
1416  public void tangoEventCallback(
1417  final int type,
1418  final String key,
1419  final String value)
1420  {
1421  if(mButtonPause != null && !mButtonPause.isChecked())
1422  {
1423  runOnUiThread(new Runnable() {
1424  public void run() {
1425  tangoEventUI(type, key, value);
1426  }
1427  });
1428  }
1429  }
1430 
1431  private boolean CheckTangoCoreVersion(int minVersion) {
1432  int versionNumber = 0;
1434  try {
1435  PackageInfo pi = getApplicationContext().getPackageManager().getPackageInfo(packageName,
1436  PackageManager.GET_META_DATA);
1437  versionNumber = pi.versionCode;
1438  } catch (NameNotFoundException e) {
1439  e.printStackTrace();
1440  }
1441  return (minVersion <= versionNumber);
1442  }
1443 
1444  private RTABMapActivity getActivity() {return this;}
1445 
1446  private void standardOptimization() {
1447  mExportProgressDialog.setTitle("Post-Processing");
1448  mExportProgressDialog.setMessage(String.format("Please wait while optimizing..."));
1449  mExportProgressDialog.setProgress(0);
1450  mExportProgressDialog.show();
1451 
1453  Thread workingThread = new Thread(new Runnable() {
1454  public void run() {
1455  final int loopDetected = RTABMapLib.postProcessing(-1);
1456  runOnUiThread(new Runnable() {
1457  public void run() {
1458  if(mExportProgressDialog.isShowing())
1459  {
1460  mExportProgressDialog.dismiss();
1461  if(loopDetected >= 0)
1462  {
1463  mTotalLoopClosures+=loopDetected;
1464  mProgressDialog.setTitle("Post-Processing");
1465  mProgressDialog.setMessage(String.format("Optimization done! Increasing visual appeal..."));
1466  mProgressDialog.show();
1467  }
1468  else if(loopDetected < 0)
1469  {
1470  mToast.makeText(getActivity(), String.format("Optimization failed!"), mToast.LENGTH_LONG).show();
1471  }
1472  }
1473  else
1474  {
1475  mProgressDialog.dismiss();
1476  mToast.makeText(getActivity(), String.format("Optimization canceled"), mToast.LENGTH_LONG).show();
1477  }
1479  }
1480  });
1481  }
1482  });
1483  workingThread.start();
1484  }
1485 
1486  private Handler notouchHandler = new Handler(){
1487  public void handleMessage(Message msg) {
1488  }
1489  };
1490 
1491  private Runnable notouchCallback = new Runnable() {
1492  @Override
1493  public void run() {
1494  if(!mProgressDialog.isShowing() && !mMenuOpened)
1495  {
1496  setNavVisibility(false);
1497  mHudVisible = false;
1498  updateState(mState);
1499  }
1500  else
1501  {
1503  }
1504  }
1505  };
1506 
1507  public void resetNoTouchTimer(){
1508  resetNoTouchTimer(false);
1509  }
1510 
1511  public void resetNoTouchTimer(boolean showHud){
1512  if(showHud)
1513  {
1514  mHudVisible = true;
1515  setNavVisibility(true);
1516  if(mItemSave != null)
1517  {
1518  updateState(mState);
1519  }
1520  }
1521 
1522  notouchHandler.removeCallbacks(notouchCallback);
1523  notouchHandler.postDelayed(notouchCallback, NOTOUCH_TIMEOUT);
1524  }
1525 
1526  public void stopDisconnectTimer(){
1527  notouchHandler.removeCallbacks(notouchCallback);
1528  }
1529 
1530  private void updateState(State state)
1531  {
1532  if(mState == State.STATE_VISUALIZING && state == State.STATE_IDLE && mMapNodes > 100)
1533  {
1534  mToast.makeText(getActivity(), String.format("Re-adding %d online clouds, this may take some time...", mMapNodes), mToast.LENGTH_LONG).show();
1535  }
1536  mState = state;
1537  if(!DISABLE_LOG) Log.i(TAG, String.format("updateState() state=%s hud=%d", state.toString(), mHudVisible?1:0));
1538  switch(state)
1539  {
1540  case STATE_PROCESSING:
1541  mButtonLighting.setVisibility(View.INVISIBLE);
1542  mButtonWireframe.setVisibility(View.INVISIBLE);
1543  mButtonCloseVisualization.setVisibility(View.INVISIBLE);
1544  mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
1545  mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
1546  mItemSave.setEnabled(false);
1547  mItemExport.setEnabled(false);
1548  mItemOpen.setEnabled(false);
1549  mItemPostProcessing.setEnabled(false);
1550  mItemSettings.setEnabled(false);
1551  mItemReset.setEnabled(false);
1552  mItemModes.setEnabled(false);
1553  mButtonPause.setVisibility(View.INVISIBLE);
1554  break;
1555  case STATE_VISUALIZING:
1556  mButtonLighting.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
1557  mButtonWireframe.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
1558  mButtonCloseVisualization.setVisibility(mHudVisible && mButtonPause.isChecked()?View.VISIBLE:View.INVISIBLE);
1559  mButtonCloseVisualization.setEnabled(true);
1560  mButtonSaveOnDevice.setVisibility(mHudVisible && mButtonPause.isChecked()?View.VISIBLE:View.INVISIBLE);
1561  mButtonShareOnSketchfab.setVisibility(mHudVisible && mButtonPause.isChecked()?View.VISIBLE:View.INVISIBLE);
1562  mItemSave.setEnabled(mButtonPause.isChecked());
1563  mItemExport.setEnabled(mButtonPause.isChecked() && !mItemDataRecorderMode.isChecked());
1564  mItemOpen.setEnabled(false);
1565  mItemPostProcessing.setEnabled(false);
1566  mItemSettings.setEnabled(true);
1567  mItemReset.setEnabled(true);
1568  mItemModes.setEnabled(true);
1569  mButtonPause.setVisibility(mHudVisible && mItemLocalizationMode.isChecked()?View.VISIBLE:View.GONE);
1570  mItemLocalizationMode.setEnabled(mButtonPause.isChecked());
1571  mItemDataRecorderMode.setEnabled(mButtonPause.isChecked());
1572  break;
1573  case STATE_VISUALIZING_WHILE_LOADING:
1574  mButtonLighting.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
1575  mButtonWireframe.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
1576  mButtonCloseVisualization.setVisibility(mHudVisible?View.VISIBLE:View.INVISIBLE);
1577  mButtonCloseVisualization.setEnabled(false);
1578  mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
1579  mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
1580  mItemSave.setEnabled(false);
1581  mItemExport.setEnabled(false);
1582  mItemOpen.setEnabled(false);
1583  mItemPostProcessing.setEnabled(false);
1584  mItemSettings.setEnabled(false);
1585  mItemReset.setEnabled(false);
1586  mItemModes.setEnabled(false);
1587  mButtonPause.setVisibility(View.INVISIBLE);
1588  break;
1589  default:
1590  mButtonLighting.setVisibility(View.INVISIBLE);
1591  mButtonWireframe.setVisibility(View.INVISIBLE);
1592  mButtonCloseVisualization.setVisibility(View.INVISIBLE);
1593  mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
1594  mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
1595  mItemSave.setEnabled(mButtonPause.isChecked());
1596  mItemExport.setEnabled(mButtonPause.isChecked() && !mItemDataRecorderMode.isChecked());
1597  mItemOpen.setEnabled(mButtonPause.isChecked() && !mItemDataRecorderMode.isChecked());
1598  mItemPostProcessing.setEnabled(mButtonPause.isChecked() && !mItemDataRecorderMode.isChecked());
1599  mItemSettings.setEnabled(true);
1600  mItemReset.setEnabled(true);
1601  mItemModes.setEnabled(true);
1602  mButtonPause.setVisibility(mHudVisible?View.VISIBLE:View.INVISIBLE);
1603  mItemDataRecorderMode.setEnabled(mButtonPause.isChecked());
1604  break;
1605  }
1606  mButtonCameraView.setVisibility(mHudVisible?View.VISIBLE:View.INVISIBLE);
1607  mButtonBackfaceShown.setVisibility(mHudVisible && (mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked())?View.VISIBLE:View.INVISIBLE);
1608  mSeekBarOrthoCut.setVisibility(mHudVisible && mButtonCameraView.getSelectedItemPosition() == 3?View.VISIBLE:View.INVISIBLE);
1609  mSeekBarGrid.setVisibility(mHudVisible && mSeekBarGrid.isEnabled() && mButtonCameraView.getSelectedItemPosition() == 3?View.VISIBLE:View.INVISIBLE);
1610  }
1611 
1612  private void pauseMapping() {
1613 
1614  if(mState != State.STATE_VISUALIZING)
1615  {
1617  }
1618  else
1619  {
1621  }
1622 
1623  if(mButtonPause.isChecked())
1624  {
1626 
1627  mStatusTexts[0] = getString(R.string.status)+"Paused";
1628  long freeMemory = getFreeMemory();
1629  mStatusTexts[1] = getString(R.string.memory)+String.valueOf(mFreeMemoryOnStart>freeMemory?mFreeMemoryOnStart-freeMemory:0);
1630  mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(freeMemory);
1632 
1633  mMapIsEmpty = false;
1634  mDateOnPause = new Date();
1635 
1636  long memoryFree = getFreeMemory();
1637  if(!mOnPause && !mItemLocalizationMode.isChecked() && !mItemDataRecorderMode.isChecked() && memoryFree >= 100 && mMapNodes>2)
1638  {
1639  // Do standard post processing?
1640  new AlertDialog.Builder(getActivity())
1641  .setTitle("Mapping Paused! Optimize Now?")
1642  .setMessage("Do you want to do standard map optimization now? This can be also done later using \"Optimize\" menu.")
1643  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
1644  public void onClick(DialogInterface dialog, int which) {
1646  }
1647  })
1648  .setNegativeButton("No", new DialogInterface.OnClickListener() {
1649  public void onClick(DialogInterface dialog, int which) {
1650  // do nothing...
1651  }
1652  })
1653  .show();
1654  }
1655  }
1656  else
1657  {
1658  if(mMemoryWarningDialog != null)
1659  {
1660  mMemoryWarningDialog.dismiss();
1661  mMemoryWarningDialog=null;
1662  }
1664  mLastFastMovementNotificationStamp = System.currentTimeMillis()/1000;
1665 
1666  if(mItemDataRecorderMode.isChecked())
1667  {
1668  mToast.makeText(getActivity(), String.format("Data Recorder Mode: no map is created, only raw data is recorded."), mToast.LENGTH_LONG).show();
1669  }
1670  else if(!mMapIsEmpty)
1671  {
1672  if(mItemLocalizationMode!=null && mItemLocalizationMode.isChecked())
1673  {
1674  mToast.makeText(getActivity(), String.format("Localization mode"), mToast.LENGTH_LONG).show();
1675  }
1676  else
1677  {
1678  mToast.makeText(getActivity(), String.format("On resume, a new map is created. Tip: Try relocalizing in the previous area."), mToast.LENGTH_LONG).show();
1679  }
1680  }
1681  else if(mMapIsEmpty && mItemLocalizationMode!=null && mItemLocalizationMode.isChecked())
1682  {
1683  mItemLocalizationMode.setChecked(false);
1685  mToast.makeText(getActivity(), String.format("Disabled localization mode as the map is empty, now mapping..."), mToast.LENGTH_LONG).show();
1686  }
1687  }
1688  }
1689 
1690  public boolean onOptionsItemSelected(MenuItem item) {
1692  if(!DISABLE_LOG) Log.i(TAG, "called onOptionsItemSelected; selected item: " + item);
1693  int itemId = item.getItemId();
1694  if (itemId == R.id.post_processing_standard)
1695  {
1697  }
1698  else if (itemId == R.id.detect_more_loop_closures)
1699  {
1700  mProgressDialog.setTitle("Post-Processing");
1701  mProgressDialog.setMessage(String.format("Please wait while detecting more loop closures..."));
1702  mProgressDialog.show();
1704  Thread workingThread = new Thread(new Runnable() {
1705  public void run() {
1706  final int loopDetected = RTABMapLib.postProcessing(2);
1707  runOnUiThread(new Runnable() {
1708  public void run() {
1709  mProgressDialog.dismiss();
1710  if(loopDetected >= 0)
1711  {
1712  mTotalLoopClosures+=loopDetected;
1713  mToast.makeText(getActivity(), String.format("Detection done! %d new loop closure(s) added.", loopDetected), mToast.LENGTH_SHORT).show();
1714  }
1715  else if(loopDetected < 0)
1716  {
1717  mToast.makeText(getActivity(), String.format("Detection failed!"), mToast.LENGTH_SHORT).show();
1718  }
1720  }
1721  });
1722  }
1723  });
1724  workingThread.start();
1725  }
1726  else if (itemId == R.id.global_graph_optimization)
1727  {
1728  mProgressDialog.setTitle("Post-Processing");
1729  mProgressDialog.setMessage(String.format("Global graph optimization..."));
1730  mProgressDialog.show();
1732  Thread workingThread = new Thread(new Runnable() {
1733  public void run() {
1734  final int value = RTABMapLib.postProcessing(0);
1735  runOnUiThread(new Runnable() {
1736  public void run() {
1737  mProgressDialog.dismiss();
1738  if(value >= 0)
1739  {
1740  mToast.makeText(getActivity(), String.format("Optimization done!"), mToast.LENGTH_SHORT).show();
1741  }
1742  else if(value < 0)
1743  {
1744  mToast.makeText(getActivity(), String.format("Optimization failed!"), mToast.LENGTH_SHORT).show();
1745  }
1747  }
1748  });
1749  }
1750  });
1751  workingThread.start();
1752  }
1753  else if (itemId == R.id.polygons_filtering)
1754  {
1755  mProgressDialog.setTitle("Post-Processing");
1756  mProgressDialog.setMessage(String.format("Noise filtering..."));
1757  mProgressDialog.show();
1759  }
1760  else if (itemId == R.id.gain_compensation_fast)
1761  {
1762  mProgressDialog.setTitle("Post-Processing");
1763  mProgressDialog.setMessage(String.format("Adjusting Colors (Fast)..."));
1764  mProgressDialog.show();
1766  }
1767  else if (itemId == R.id.gain_compensation_full)
1768  {
1769  mProgressDialog.setTitle("Post-Processing");
1770  mProgressDialog.setMessage(String.format("Adjusting Colors (Full)..."));
1771  mProgressDialog.show();
1773  }
1774  else if (itemId == R.id.bilateral_filtering)
1775  {
1776  mProgressDialog.setTitle("Post-Processing");
1777  mProgressDialog.setMessage(String.format("Mesh smoothing..."));
1778  mProgressDialog.show();
1780  }
1781  else if (itemId == R.id.sba)
1782  {
1783  mProgressDialog.setTitle("Post-Processing");
1784  mProgressDialog.setMessage(String.format("Bundle adjustement..."));
1785  mProgressDialog.show();
1786 
1787  Thread workingThread = new Thread(new Runnable() {
1788  public void run() {
1789  final int value = RTABMapLib.postProcessing(1);
1790  runOnUiThread(new Runnable() {
1791  public void run() {
1792  mProgressDialog.dismiss();
1793  if(value >= 0)
1794  {
1795  mToast.makeText(getActivity(), String.format("Optimization done!"), mToast.LENGTH_SHORT).show();
1796  }
1797  else if(value < 0)
1798  {
1799  mToast.makeText(getActivity(), String.format("Optimization failed!"), mToast.LENGTH_SHORT).show();
1800  }
1801  }
1802  });
1803  }
1804  });
1805  workingThread.start();
1806  }
1807  else if(itemId == R.id.status)
1808  {
1809  item.setChecked(!item.isChecked());
1811  }
1812  else if(itemId == R.id.debug)
1813  {
1814  item.setChecked(!item.isChecked());
1816  }
1817  else if(itemId == R.id.mesh || itemId == R.id.texture_mesh || itemId == R.id.point_cloud)
1818  {
1819  item.setChecked(true);
1821  mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
1822  mItemRenderingTextureMesh.isChecked());
1823 
1825 
1826  if(mState != State.STATE_VISUALIZING)
1827  {
1828  // save preference
1829  int type = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
1830  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
1831  SharedPreferences.Editor editor = sharedPref.edit();
1832  editor.putInt(getString(R.string.pref_key_rendering), type);
1833  // Commit the edits!
1834  editor.commit();
1835  }
1836  }
1837  else if(itemId == R.id.map_shown)
1838  {
1839  item.setChecked(!item.isChecked());
1840  RTABMapLib.setMapCloudShown(item.isChecked());
1841  }
1842  else if(itemId == R.id.odom_shown)
1843  {
1844  item.setChecked(!item.isChecked());
1845  RTABMapLib.setOdomCloudShown(item.isChecked());
1846  }
1847  else if(itemId == R.id.localization_mode)
1848  {
1849  item.setChecked(!item.isChecked());
1850  RTABMapLib.setLocalizationMode(item.isChecked());
1851  if(mState == State.STATE_VISUALIZING)
1852  {
1853  mButtonPause.setVisibility(mItemLocalizationMode.isChecked()?View.VISIBLE:View.GONE);
1854  }
1855  }
1856  else if(itemId == R.id.trajectory_mode)
1857  {
1858  item.setChecked(!item.isChecked());
1859  RTABMapLib.setTrajectoryMode(item.isChecked());
1860  setCamera(item.isChecked()?2:1);
1861  }
1862  else if(itemId == R.id.graph_optimization)
1863  {
1864  item.setChecked(!item.isChecked());
1865  RTABMapLib.setGraphOptimization(item.isChecked());
1866  }
1867  else if(itemId == R.id.graph_visible)
1868  {
1869  item.setChecked(!item.isChecked());
1870  RTABMapLib.setGraphVisible(item.isChecked());
1871  }
1872  else if(itemId == R.id.grid_visible)
1873  {
1874  item.setChecked(!item.isChecked());
1875  mSeekBarGrid.setEnabled(item.isChecked());
1876  mSeekBarGrid.setVisibility(mHudVisible && mSeekBarGrid.isEnabled()&&mButtonCameraView.getSelectedItemPosition() == 3?View.VISIBLE:View.INVISIBLE);
1877  RTABMapLib.setGridVisible(item.isChecked());
1878  }
1879  else if (itemId == R.id.save)
1880  {
1881  AlertDialog.Builder builder = new AlertDialog.Builder(this);
1882  builder.setTitle("RTAB-Map Database Name (*.db):");
1883  final EditText input = new EditText(this);
1884  input.setInputType(InputType.TYPE_CLASS_TEXT);
1885  if(mOpenedDatabasePath.isEmpty())
1886  {
1887  String timeStamp = new SimpleDateFormat("yyMMdd-HHmmss").format(mDateOnPause);
1888  input.setText(timeStamp);
1889  }
1890  else
1891  {
1892  File f = new File(mOpenedDatabasePath);
1893  String name = f.getName();
1894  input.setText(name.substring(0,name.lastIndexOf(".")));
1895  }
1896  input.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
1897  input.setSelectAllOnFocus(true);
1898  input.selectAll();
1899  builder.setView(input);
1900  builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
1901  @Override
1902  public void onClick(DialogInterface dialog, int which)
1903  {
1904  final String fileName = input.getText().toString();
1905  dialog.dismiss();
1906  if(!fileName.isEmpty())
1907  {
1908  File newFile = new File(mWorkingDirectory + fileName + ".db");
1909  if(newFile.exists())
1910  {
1911  new AlertDialog.Builder(getActivity())
1912  .setTitle("File Already Exists")
1913  .setMessage("Do you want to overwrite the existing file?")
1914  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
1915  public void onClick(DialogInterface dialog, int which) {
1916  saveDatabase(fileName);
1917  }
1918  })
1919  .setNegativeButton("No", new DialogInterface.OnClickListener() {
1920  public void onClick(DialogInterface dialog, int which) {
1921  dialog.dismiss();
1922  resetNoTouchTimer(true);
1923  }
1924  })
1925  .show();
1926  }
1927  else
1928  {
1929  saveDatabase(fileName);
1930  }
1931  }
1932  }
1933  });
1934  AlertDialog alertToShow = builder.create();
1935  alertToShow.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
1936  alertToShow.show();
1937  }
1938  else if(itemId == R.id.reset)
1939  {
1940  mTotalLoopClosures = 0;
1941 
1942  int index = STATUS_TEXTS_POSE_INDEX;
1943  mMapNodes = 0;
1944  mStatusTexts[index++] = getString(R.string.nodes)+0;
1945  mStatusTexts[index++] = getString(R.string.words)+0;
1946  mStatusTexts[index++] = getString(R.string.database_size)+0;
1947  mStatusTexts[index++] = getString(R.string.points)+0;
1948  mStatusTexts[index++] = getString(R.string.polygons)+0;
1949  mStatusTexts[index++] = getString(R.string.update_time)+0;
1950  mStatusTexts[index++] = getString(R.string.features)+0;
1951  mStatusTexts[index++] = getString(R.string.rehearsal)+0;
1952  mStatusTexts[index++] = getString(R.string.total_loop)+0;
1953  mStatusTexts[index++] = getString(R.string.inliers)+0;
1954  mStatusTexts[index++] = getString(R.string.hypothesis)+0;
1955  mStatusTexts[index++] = getString(R.string.fps)+0;
1956  mStatusTexts[index++] = getString(R.string.distance)+0;
1957  mStatusTexts[index++] = String.format("Pose (x,y,z): 0 0 0");
1959 
1960  mOpenedDatabasePath = "";
1961  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
1962  boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory), Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
1963  String tmpDatabase = mWorkingDirectory+RTABMAP_TMP_DB;
1964  RTABMapLib.openDatabase(tmpDatabase, databaseInMemory, false);
1965 
1966  mMapIsEmpty = true;
1967  mItemSave.setEnabled(false);
1968  mItemExport.setEnabled(false);
1969  mItemPostProcessing.setEnabled(false);
1971  }
1972  else if(itemId == R.id.data_recorder)
1973  {
1974  final boolean dataRecorderOldState = item.isChecked();
1975  new AlertDialog.Builder(getActivity())
1976  .setTitle("Data Recorder Mode")
1977  .setMessage("Changing from/to data recorder mode will close the current session. Do you want to continue?")
1978  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
1979  public void onClick(DialogInterface dialog, int which) {
1980  // reset
1981  mTotalLoopClosures = 0;
1982  int index = STATUS_TEXTS_POSE_INDEX;
1983  mMapNodes = 0;
1984  mStatusTexts[index++] = getString(R.string.nodes)+0;
1985  mStatusTexts[index++] = getString(R.string.words)+0;
1986  mStatusTexts[index++] = getString(R.string.database_size)+0;
1987  mStatusTexts[index++] = getString(R.string.points)+0;
1988  mStatusTexts[index++] = getString(R.string.polygons)+0;
1989  mStatusTexts[index++] = getString(R.string.update_time)+0;
1990  mStatusTexts[index++] = getString(R.string.features)+0;
1991  mStatusTexts[index++] = getString(R.string.rehearsal)+0;
1992  mStatusTexts[index++] = getString(R.string.total_loop)+0;
1993  mStatusTexts[index++] = getString(R.string.inliers)+0;
1994  mStatusTexts[index++] = getString(R.string.hypothesis)+0;
1995  mStatusTexts[index++] = getString(R.string.fps)+0;
1996  mStatusTexts[index++] = getString(R.string.distance)+0;
1997  mStatusTexts[index++] = String.format("Pose (x,y,z): 0 0 0");
1999 
2000  mItemDataRecorderMode.setChecked(!dataRecorderOldState);
2001  RTABMapLib.setDataRecorderMode(mItemDataRecorderMode.isChecked());
2002 
2003  mOpenedDatabasePath = "";
2004  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
2005  boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory), Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
2006  String tmpDatabase = mWorkingDirectory+RTABMAP_TMP_DB;
2007  RTABMapLib.openDatabase(tmpDatabase, databaseInMemory, false);
2008 
2009  mItemOpen.setEnabled(!mItemDataRecorderMode.isChecked() && mButtonPause.isChecked());
2010  mItemPostProcessing.setEnabled(!mItemDataRecorderMode.isChecked() && mButtonPause.isChecked());
2011  mItemExport.setEnabled(!mItemDataRecorderMode.isChecked() && mButtonPause.isChecked());
2012 
2013  mItemLocalizationMode.setEnabled(!mItemDataRecorderMode.isChecked());
2014 
2015  if(mItemDataRecorderMode.isChecked())
2016  {
2017  mToast.makeText(getActivity(), String.format("Data recorder mode activated! Tip: You can increase data update rate in Parameters menu under Mapping options."), mToast.LENGTH_LONG).show();
2018  }
2019  else
2020  {
2021  mToast.makeText(getActivity(), String.format("Data recorder mode deactivated!"), mToast.LENGTH_LONG).show();
2022  }
2023  }
2024  })
2025  .setNegativeButton("No", new DialogInterface.OnClickListener() {
2026  public void onClick(DialogInterface dialog, int which) {
2027  dialog.dismiss();
2028  }
2029  })
2030  .show();
2031  }
2032  else if(itemId == R.id.export_point_cloud ||
2033  itemId == R.id.export_point_cloud_highrez)
2034  {
2035  final boolean regenerateCloud = itemId == R.id.export_point_cloud_highrez;
2036 
2037  export(false, false, regenerateCloud, false, 0);
2038  }
2039  else if(itemId == R.id.export_optimized_mesh ||
2040  itemId == R.id.export_optimized_mesh_texture)
2041  {
2042  final boolean isOBJ = itemId == R.id.export_optimized_mesh_texture;
2043 
2044  RelativeLayout linearLayout = new RelativeLayout(this);
2045  final NumberPicker aNumberPicker = new NumberPicker(this);
2046  aNumberPicker.setMaxValue(9);
2047  aNumberPicker.setMinValue(0);
2048  aNumberPicker.setWrapSelectorWheel(false);
2049  aNumberPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
2050  aNumberPicker.setFormatter(new NumberPicker.Formatter() {
2051  @Override
2052  public String format(int i) {
2053  if(i==0)
2054  {
2055  return "No Limit";
2056  }
2057  return String.format("%d00 000", i);
2058  }
2059  });
2060  aNumberPicker.setValue(2);
2061 
2062  // Fix to correctly show value on first render
2063  try {
2064  Method method = aNumberPicker.getClass().getDeclaredMethod("changeValueByOne", boolean.class);
2065  method.setAccessible(true);
2066  method.invoke(aNumberPicker, true);
2067  } catch (NoSuchMethodException e) {
2068  e.printStackTrace();
2069  } catch (IllegalArgumentException e) {
2070  e.printStackTrace();
2071  } catch (IllegalAccessException e) {
2072  e.printStackTrace();
2073  } catch (InvocationTargetException e) {
2074  e.printStackTrace();
2075  }
2076 
2077 
2078  RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(50, 50);
2079  RelativeLayout.LayoutParams numPicerParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
2080  numPicerParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
2081 
2082  linearLayout.setLayoutParams(params);
2083  linearLayout.addView(aNumberPicker,numPicerParams);
2084 
2085  AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
2086  alertDialogBuilder.setTitle("Maximum polygons");
2087  alertDialogBuilder.setView(linearLayout);
2088  alertDialogBuilder
2089  .setCancelable(false)
2090  .setPositiveButton("Ok",
2091  new DialogInterface.OnClickListener() {
2092  public void onClick(DialogInterface dialog,
2093  int id) {
2094  export(isOBJ, true, false, true, aNumberPicker.getValue()*100000);
2095  }
2096  })
2097  .setNegativeButton("Cancel",
2098  new DialogInterface.OnClickListener() {
2099  public void onClick(DialogInterface dialog,
2100  int id) {
2101  dialog.cancel();
2102  }
2103  });
2104  AlertDialog alertDialog = alertDialogBuilder.create();
2105  alertDialog.show();
2106  }
2107  else if(itemId == R.id.open)
2108  {
2109  openDatabase();
2110  }
2111  else if(itemId == R.id.settings)
2112  {
2113  Intent intent = new Intent(getActivity(), SettingsActivity.class);
2114  startActivity(intent);
2115  mBlockBack = true;
2116  }
2117  else if(itemId == R.id.about)
2118  {
2119  AboutDialog about = new AboutDialog(this);
2120  about.setTitle("About RTAB-Map");
2121  about.show();
2122  }
2123 
2124  return true;
2125  }
2126 
2127  private void openDatabase()
2128  {
2129  final String[] files = Util.loadFileList(mWorkingDirectory, true);
2130  if(files.length > 0)
2131  {
2132  String[] filesWithSize = new String[files.length];
2133  for(int i = 0; i<filesWithSize.length; ++i)
2134  {
2135  File filePath = new File(mWorkingDirectory+files[i]);
2136  long mb = filePath.length()/(1024*1024);
2137  filesWithSize[i] = files[i] + " ("+mb+" MB)";
2138  }
2139 
2140  ArrayList<HashMap<String, String> > arrayList = new ArrayList<HashMap<String, String> >();
2141  for (int i = 0; i < filesWithSize.length; i++) {
2142  HashMap<String, String> hashMap = new HashMap<String, String>();//create a hashmap to store the data in key value pair
2143  hashMap.put("name", filesWithSize[i]);
2144  hashMap.put("path", mWorkingDirectory + files[i]);
2145  arrayList.add(hashMap);//add the hashmap into arrayList
2146  }
2147  String[] from = {"name", "path"};//string array
2148  int[] to = {R.id.textView, R.id.imageView};//int array of views id's
2149  DatabaseListArrayAdapter simpleAdapter = new DatabaseListArrayAdapter(this, arrayList, R.layout.database_list, from, to);//Create object and set the parameters for simpleAdapter
2150 
2151  AlertDialog.Builder builder = new AlertDialog.Builder(this);
2152  builder.setTitle("Choose Your File (*.db)");
2153  builder.setAdapter(simpleAdapter, new DialogInterface.OnClickListener() {
2154  //builder.setItems(filesWithSize, new DialogInterface.OnClickListener() {
2155  public void onClick(DialogInterface dialog, final int which) {
2156 
2157  // Adjust color now?
2158  new AlertDialog.Builder(getActivity())
2159  .setTitle("Opening database...")
2160  .setMessage("Do you want to adjust colors now?\nThis can be done later under Optimize menu.")
2161  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
2162  public void onClick(DialogInterface dialog, int whichIn) {
2163  openDatabase(files[which], true);
2164  }
2165  })
2166  .setNeutralButton("No", new DialogInterface.OnClickListener() {
2167  public void onClick(DialogInterface dialog, int whichIn) {
2168  openDatabase(files[which], false);
2169  }
2170  })
2171  .show();
2172  return;
2173  }
2174  });
2175 
2176  final AlertDialog ad = builder.create(); //don't show dialog yet
2177  ad.setOnShowListener(new OnShowListener()
2178  {
2179  @Override
2180  public void onShow(DialogInterface dialog)
2181  {
2182  ListView lv = ad.getListView();
2183  ad.registerForContextMenu(lv);
2184  lv.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
2185 
2186  @Override
2187  public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
2188 
2189  if (v.getId()==ad.getListView().getId()) {
2190  AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
2191  final int position = info.position;
2192  menu.setHeaderTitle(files[position]);
2193  menu.add(Menu.NONE, 0, 0, "Rename").setOnMenuItemClickListener(new OnMenuItemClickListener() {
2194  @Override
2195  public boolean onMenuItemClick(MenuItem item) {
2196  AlertDialog.Builder builderRename = new AlertDialog.Builder(getActivity());
2197  builderRename.setTitle("RTAB-Map Database Name (*.db):");
2198  final EditText input = new EditText(getActivity());
2199  input.setInputType(InputType.TYPE_CLASS_TEXT);
2200  input.setText("");
2201  input.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
2202  input.setSelectAllOnFocus(true);
2203  input.selectAll();
2204  builderRename.setView(input);
2205  builderRename.setPositiveButton("OK", new DialogInterface.OnClickListener() {
2206  @Override
2207  public void onClick(DialogInterface dialog, int which)
2208  {
2209  final String fileName = input.getText().toString();
2210  dialog.dismiss();
2211  if(!fileName.isEmpty())
2212  {
2213  File newFile = new File(mWorkingDirectory + fileName + ".db");
2214  if(newFile.exists())
2215  {
2216  new AlertDialog.Builder(getActivity())
2217  .setTitle("File Already Exists")
2218  .setMessage(String.format("Name %s already used, choose another name.", fileName))
2219  .show();
2220  }
2221  else
2222  {
2223  File from = new File(mWorkingDirectory, files[position]);
2224  File to = new File(mWorkingDirectory, fileName + ".db");
2225  from.renameTo(to);
2226  ad.dismiss();
2227  resetNoTouchTimer(true);
2228  }
2229  }
2230  }
2231  });
2232  AlertDialog alertToShow = builderRename.create();
2233  alertToShow.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
2234  alertToShow.show();
2235  return true;
2236  }
2237  });
2238  menu.add(Menu.NONE, 1, 1, "Delete").setOnMenuItemClickListener(new OnMenuItemClickListener() {
2239  @Override
2240  public boolean onMenuItemClick(MenuItem item) {
2241  DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
2242  @Override
2243  public void onClick(DialogInterface dialog, int which) {
2244  switch (which){
2245  case DialogInterface.BUTTON_POSITIVE:
2246  Log.e(TAG, String.format("Yes delete %s!", files[position]));
2247  (new File(mWorkingDirectory+files[position])).delete();
2248  ad.dismiss();
2249  resetNoTouchTimer(true);
2250  break;
2251 
2252  case DialogInterface.BUTTON_NEGATIVE:
2253  //No button clicked
2254  break;
2255  }
2256  }
2257  };
2258  AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
2259  builder.setTitle(String.format("Delete %s", files[position]))
2260  .setMessage("Are you sure?")
2261  .setPositiveButton("Yes", dialogClickListener)
2262  .setNegativeButton("No", dialogClickListener).show();
2263  return true;
2264  }
2265  });
2266  menu.add(Menu.NONE, 2, 2, "Share").setOnMenuItemClickListener(new OnMenuItemClickListener() {
2267  @Override
2268  public boolean onMenuItemClick(MenuItem item) {
2269  // Send to...
2270  File f = new File(mWorkingDirectory+files[position]);
2271  final int fileSizeMB = (int)f.length()/(1024 * 1024);
2272  Intent shareIntent = new Intent();
2273  shareIntent.setAction(Intent.ACTION_SEND);
2274  shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
2275  shareIntent.setType("application/octet-stream");
2276  startActivity(Intent.createChooser(shareIntent, String.format("Sharing database \"%s\" (%d MB)...", files[position], fileSizeMB)));
2277  ad.dismiss();
2278  resetNoTouchTimer(true);
2279  return true;
2280  }
2281  });
2282  }
2283  }
2284  });
2285  }
2286  });
2287  ad.show();
2288  }
2289  }
2290 
2291  private void export(final boolean isOBJ, final boolean meshing, final boolean regenerateCloud, final boolean optimized, final int optimizedMaxPolygons)
2292  {
2293  final String extension = isOBJ? ".obj" : ".ply";
2294 
2295  // get Export settings
2296  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
2297  final String cloudVoxelSizeStr = sharedPref.getString(getString(R.string.pref_key_cloud_voxel), getString(R.string.pref_default_cloud_voxel));
2298  final float cloudVoxelSize = Float.parseFloat(cloudVoxelSizeStr);
2299  final int textureSize = isOBJ?Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_texture_size), getString(R.string.pref_default_texture_size))):0;
2300  final int textureCount = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_texture_count), getString(R.string.pref_default_texture_count)));
2301  final int normalK = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_normal_k), getString(R.string.pref_default_normal_k)));
2302  final float maxTextureDistance = Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_max_texture_distance), getString(R.string.pref_default_max_texture_distance)));
2303  final int minTextureClusterSize = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_min_texture_cluster_size), getString(R.string.pref_default_min_texture_cluster_size)));
2304  final float optimizedVoxelSize = cloudVoxelSize;
2305  final int optimizedDepth = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_opt_depth), getString(R.string.pref_default_opt_depth)));
2306  final float optimizedColorRadius = Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_opt_color_radius), getString(R.string.pref_default_opt_color_radius)));
2307  final boolean optimizedCleanWhitePolygons = sharedPref.getBoolean(getString(R.string.pref_key_opt_clean_white), Boolean.parseBoolean(getString(R.string.pref_default_opt_clean_white)));
2308  final int optimizedMinClusterSize = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_opt_min_cluster_size), getString(R.string.pref_default_opt_min_cluster_size)));
2309  final boolean blockRendering = sharedPref.getBoolean(getString(R.string.pref_key_block_render), Boolean.parseBoolean(getString(R.string.pref_default_block_render)));
2310 
2311 
2312  mExportProgressDialog.setTitle("Exporting");
2313  mExportProgressDialog.setMessage(String.format("Please wait while preparing data to export..."));
2314  mExportProgressDialog.setProgress(0);
2315 
2316  final State previousState = mState;
2317 
2318  mExportProgressDialog.show();
2320 
2321  Thread exportThread = new Thread(new Runnable() {
2322  public void run() {
2323 
2324  final long startTime = System.currentTimeMillis()/1000;
2325 
2326  final boolean success = RTABMapLib.exportMesh(
2327  cloudVoxelSize,
2328  regenerateCloud,
2329  meshing,
2330  textureSize,
2331  textureCount,
2332  normalK,
2333  optimized,
2334  optimizedVoxelSize,
2335  optimizedDepth,
2336  optimizedMaxPolygons,
2337  optimizedColorRadius,
2338  optimizedCleanWhitePolygons,
2339  optimizedMinClusterSize,
2340  maxTextureDistance,
2341  minTextureClusterSize,
2342  blockRendering);
2343  runOnUiThread(new Runnable() {
2344  public void run() {
2345  if(mExportProgressDialog.isShowing())
2346  {
2347  if(success)
2348  {
2349  if(!meshing && cloudVoxelSize>0.0f)
2350  {
2351  mToast.makeText(getActivity(), String.format("Cloud assembled and voxelized at %s m.", cloudVoxelSizeStr), mToast.LENGTH_LONG).show();
2352  }
2353 
2354  final long endTime = System.currentTimeMillis()/1000;
2355 
2356  if(endTime-startTime > 10)
2357  {
2358  // build notification
2359  // the addAction re-use the same intent to keep the example short
2360  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
2361  boolean notifySound = sharedPref.getBoolean(getString(R.string.pref_key_notification_sound), Boolean.parseBoolean(getString(R.string.pref_default_notification_sound)));
2362  Notification n = new Notification.Builder(getActivity())
2363  .setContentTitle(getString(R.string.app_name))
2364  .setContentText("Data generated and ready to be exported!")
2365  .setSmallIcon(R.drawable.ic_launcher)
2366  .setDefaults(notifySound?Notification.DEFAULT_SOUND:0)
2367  .setAutoCancel(true).build();
2368 
2369  NotificationManager notificationManager =
2370  (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
2371 
2372  notificationManager.notify(0, n);
2373  }
2374 
2375  // Visualize the result?
2376  AlertDialog d = new AlertDialog.Builder(getActivity())
2377  .setCancelable(false)
2378  .setTitle("Export Successful! (" + (endTime-startTime) + " sec)")
2379  .setMessage(Html.fromHtml("Do you want visualize the result before saving to file or sharing to <a href=\"https://sketchfab.com/about\">Sketchfab</a>?"))
2380  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
2381  public void onClick(DialogInterface dialog, int which) {
2382  resetNoTouchTimer(true);
2383  mSavedRenderingType = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
2384  if(!meshing)
2385  {
2386  mItemRenderingPointCloud.setChecked(true);
2387  }
2388  else if(!isOBJ)
2389  {
2390  mItemRenderingMesh.setChecked(true);
2391  }
2392  else // isOBJ
2393  {
2394  mItemRenderingTextureMesh.setChecked(true);
2395  }
2396  if(!optimizedCleanWhitePolygons)
2397  {
2398  mButtonLighting.setChecked(true);
2399  RTABMapLib.setLighting(true);
2400  }
2403  if(mButtonCameraView.getSelectedItemPosition() == 0)
2404  {
2405  setCamera(2);
2406  }
2407  }
2408  })
2409  .setNegativeButton("No", new DialogInterface.OnClickListener() {
2410  public void onClick(DialogInterface dialog, int which) {
2412  RTABMapLib.postExportation(false);
2413 
2414  AlertDialog d2 = new AlertDialog.Builder(getActivity())
2415  .setCancelable(false)
2416  .setTitle("Save to...")
2417  .setMessage(Html.fromHtml("Do you want to share to <a href=\"https://sketchfab.com/about\">Sketchfab</a> or save it on device?"))
2418  .setPositiveButton("Share to Sketchfab", new DialogInterface.OnClickListener() {
2419  public void onClick(DialogInterface dialog, int which) {
2420  shareToSketchfab();
2421  }
2422  })
2423  .setNegativeButton("Save on device", new DialogInterface.OnClickListener() {
2424  public void onClick(DialogInterface dialog, int which) {
2425  saveOnDevice();
2426  }
2427  })
2428  .setNeutralButton("Cancel", new DialogInterface.OnClickListener() {
2429  public void onClick(DialogInterface dialog, int which) {
2430  resetNoTouchTimer(true);
2431  }
2432  })
2433  .create();
2434  d2.show();
2435  // Make the textview clickable. Must be called after show()
2436  ((TextView)d2.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
2437  }
2438  })
2439  .create();
2440  d.show();
2441  // Make the textview clickable. Must be called after show()
2442  ((TextView)d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
2443  }
2444  else
2445  {
2446  updateState(previousState);
2447  mToast.makeText(getActivity(), String.format("Exporting map failed!"), mToast.LENGTH_LONG).show();
2448  }
2449  mExportProgressDialog.dismiss();
2450  }
2451  else
2452  {
2453  mProgressDialog.dismiss();
2454  mToast.makeText(getActivity(), String.format("Export canceled"), mToast.LENGTH_LONG).show();
2455  updateState(previousState);
2456  }
2457  }
2458  });
2459  }
2460  });
2461  exportThread.start();
2462  }
2463 
2464  private void saveDatabase(String fileName)
2465  {
2466  final String newDatabasePath = mWorkingDirectory + fileName + ".db";
2467  final String newDatabasePathHuman = mWorkingDirectoryHuman + fileName + ".db";
2468  mProgressDialog.setTitle("Saving");
2469  if(mOpenedDatabasePath.equals(newDatabasePath))
2470  {
2471  mProgressDialog.setMessage(String.format("Please wait while updating \"%s\"...", newDatabasePathHuman));
2472  }
2473  else
2474  {
2475  mProgressDialog.setMessage(String.format("Please wait while saving \"%s\"...", newDatabasePathHuman));
2476  }
2477  mProgressDialog.show();
2478  final State previousState = mState;
2480  Thread saveThread = new Thread(new Runnable() {
2481  public void run() {
2482  RTABMapLib.save(newDatabasePath); // save
2483  runOnUiThread(new Runnable() {
2484  public void run() {
2485  String msg;
2486  if(mOpenedDatabasePath.equals(newDatabasePath))
2487  {
2488  msg = String.format("Database \"%s\" updated.", newDatabasePathHuman);
2489  }
2490  else
2491  {
2492  msg = String.format("Database saved to \"%s\".", newDatabasePathHuman);
2493  }
2494 
2495  // build notification
2496  Intent intent = new Intent(getActivity(), RTABMapActivity.class);
2497  // use System.currentTimeMillis() to have a unique ID for the pending intent
2498  PendingIntent pIntent = PendingIntent.getActivity(getActivity(), (int) System.currentTimeMillis(), intent, 0);
2499  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
2500  boolean notifySound = sharedPref.getBoolean(getString(R.string.pref_key_notification_sound), Boolean.parseBoolean(getString(R.string.pref_default_notification_sound)));
2501  Notification n = new Notification.Builder(getActivity())
2502  .setContentTitle(getString(R.string.app_name))
2503  .setContentText(msg)
2504  .setSmallIcon(R.drawable.ic_launcher)
2505  .setContentIntent(pIntent)
2506  .setDefaults(notifySound?Notification.DEFAULT_SOUND:0)
2507  .setAutoCancel(true).build();
2508 
2509  NotificationManager notificationManager =
2510  (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
2511 
2512  notificationManager.notify(0, n);
2513 
2514  final File f = new File(newDatabasePath);
2515  final int fileSizeMB = (int)f.length()/(1024 * 1024);
2516 
2517  new AlertDialog.Builder(getActivity())
2518  .setTitle("Database saved!")
2519  .setMessage(String.format("Database \"%s\" (%d MB) successfully saved on the SD-CARD! Share it?", newDatabasePathHuman, fileSizeMB))
2520  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
2521  public void onClick(DialogInterface dialog, int which) {
2522  // Send to...
2523  Intent shareIntent = new Intent();
2524  shareIntent.setAction(Intent.ACTION_SEND);
2525  shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
2526  shareIntent.setType("application/octet-stream");
2527  startActivity(Intent.createChooser(shareIntent, "Sharing..."));
2528 
2529  resetNoTouchTimer(true);
2530  if(!mItemDataRecorderMode.isChecked())
2531  {
2532  mOpenedDatabasePath = newDatabasePath;
2533  }
2534  mProgressDialog.dismiss();
2535  updateState(previousState);
2536  }
2537  })
2538  .setNegativeButton("No", new DialogInterface.OnClickListener() {
2539  public void onClick(DialogInterface dialog, int which) {
2540  resetNoTouchTimer(true);
2541  if(!mItemDataRecorderMode.isChecked())
2542  {
2543  mOpenedDatabasePath = newDatabasePath;
2544  }
2545  mProgressDialog.dismiss();
2546  updateState(previousState);
2547  }
2548  })
2549  .show();
2550  }
2551  });
2552  }
2553  });
2554  saveThread.start();
2555  }
2556 
2557  private void saveOnDevice()
2558  {
2559  AlertDialog.Builder builder = new AlertDialog.Builder(this);
2560  builder.setTitle("Model Name:");
2561  final EditText input = new EditText(this);
2562  input.setInputType(InputType.TYPE_CLASS_TEXT);
2563  builder.setView(input);
2564  if(mOpenedDatabasePath.isEmpty())
2565  {
2566  String timeStamp = new SimpleDateFormat("yyMMdd-HHmmss").format(mDateOnPause);
2567  input.setText(timeStamp);
2568  }
2569  else
2570  {
2571  File f = new File(mOpenedDatabasePath);
2572  String name = f.getName();
2573  input.setText(name.substring(0,name.lastIndexOf(".")));
2574  }
2575  input.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
2576  input.setSelectAllOnFocus(true);
2577  input.selectAll();
2578  builder.setCancelable(false);
2579  builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
2580  @Override
2581  public void onClick(DialogInterface dialog, int which)
2582  {
2583  dialog.dismiss();
2584  resetNoTouchTimer(true);
2585  }
2586  });
2587  builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
2588  @Override
2589  public void onClick(DialogInterface dialog, int which)
2590  {
2591  final String fileName = input.getText().toString();
2592  dialog.dismiss();
2593  if(!fileName.isEmpty())
2594  {
2595  File newFile = new File(mWorkingDirectory + RTABMAP_EXPORT_DIR + fileName + ".zip");
2596  if(newFile.exists())
2597  {
2598  new AlertDialog.Builder(getActivity())
2599  .setTitle("File Already Exists")
2600  .setMessage("Do you want to overwrite the existing file?")
2601  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
2602  public void onClick(DialogInterface dialog, int which) {
2603  writeExportedFiles(fileName);
2604  }
2605  })
2606  .setNegativeButton("No", new DialogInterface.OnClickListener() {
2607  public void onClick(DialogInterface dialog, int which) {
2608  saveOnDevice();
2609  }
2610  })
2611  .show();
2612  }
2613  else
2614  {
2615  writeExportedFiles(fileName);
2616  }
2617  }
2618  }
2619  });
2620  AlertDialog alertToShow = builder.create();
2621  alertToShow.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
2622  alertToShow.show();
2623  }
2624 
2625  private void writeExportedFiles(final String fileName)
2626  {
2627  Log.i(TAG, String.format("Write exported mesh to \"%s\"", fileName));
2628 
2629  mProgressDialog.setTitle("Saving to sd-card");
2630  mProgressDialog.setMessage(String.format("Compressing the files..."));
2631  mProgressDialog.show();
2632 
2633  Thread workingThread = new Thread(new Runnable() {
2634  public void run() {
2635  boolean success = false;
2636 
2637  File tmpDir = new File(mWorkingDirectory + RTABMAP_TMP_DIR);
2638  tmpDir.mkdirs();
2639  String[] fileNames = Util.loadFileList(mWorkingDirectory + RTABMAP_TMP_DIR, false);
2640  if(!DISABLE_LOG) Log.i(TAG, String.format("Deleting %d files in \"%s\"", fileNames.length, mWorkingDirectory + RTABMAP_TMP_DIR));
2641  for(int i=0; i<fileNames.length; ++i)
2642  {
2643  File f = new File(mWorkingDirectory + RTABMAP_TMP_DIR + "/" + fileNames[i]);
2644  if(f.delete())
2645  {
2646  if(!DISABLE_LOG) Log.i(TAG, String.format("Deleted \"%s\"", f.getPath()));
2647  }
2648  else
2649  {
2650  if(!DISABLE_LOG) Log.i(TAG, String.format("Failed deleting \"%s\"", f.getPath()));
2651  }
2652  }
2653  File exportDir = new File(mWorkingDirectory + RTABMAP_EXPORT_DIR);
2654  exportDir.mkdirs();
2655 
2656  final String pathHuman = mWorkingDirectoryHuman + RTABMAP_EXPORT_DIR + fileName + ".zip";
2657  final String zipOutput = mWorkingDirectory+RTABMAP_EXPORT_DIR+fileName+".zip";
2658  if(RTABMapLib.writeExportedMesh(mWorkingDirectory + RTABMAP_TMP_DIR, RTABMAP_TMP_FILENAME))
2659  {
2660  fileNames = Util.loadFileList(mWorkingDirectory + RTABMAP_TMP_DIR, false);
2661  if(fileNames.length > 0)
2662  {
2663  String[] filesToZip = new String[fileNames.length];
2664  for(int i=0; i<fileNames.length; ++i)
2665  {
2666  filesToZip[i] = mWorkingDirectory + RTABMAP_TMP_DIR + "/" + fileNames[i];
2667  }
2668 
2669  File toZIPFile = new File(zipOutput);
2670  toZIPFile.delete();
2671 
2672  try
2673  {
2674  Util.zip(filesToZip, zipOutput);
2675  success = true;
2676  }
2677  catch(IOException e)
2678  {
2679  final String msg = e.getMessage();
2680  runOnUiThread(new Runnable() {
2681  public void run() {
2682  mToast.makeText(getActivity(), String.format("Exporting mesh \"%s\" failed! Error=%s", pathHuman, msg), mToast.LENGTH_LONG).show();
2683  }
2684  });
2685  }
2686  }
2687  }
2688 
2689  if(success)
2690  {
2691  runOnUiThread(new Runnable() {
2692  public void run() {
2693  mProgressDialog.dismiss();
2694 
2695  final File f = new File(zipOutput);
2696  final int fileSizeMB = (int)f.length()/(1024 * 1024);
2697 
2698  new AlertDialog.Builder(getActivity())
2699  .setTitle("Database saved!")
2700  .setMessage(String.format("Mesh \"%s\" (%d MB) successfully exported on the SD-CARD! Share it?", pathHuman, fileSizeMB))
2701  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
2702  public void onClick(DialogInterface dialog, int which) {
2703  // Send to...
2704  Intent shareIntent = new Intent();
2705  shareIntent.setAction(Intent.ACTION_SEND);
2706  shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
2707  shareIntent.setType("application/zip");
2708  startActivity(Intent.createChooser(shareIntent, "Sharing..."));
2709 
2710  resetNoTouchTimer(true);
2711  }
2712  })
2713  .setNegativeButton("No", new DialogInterface.OnClickListener() {
2714  public void onClick(DialogInterface dialog, int which) {
2715  resetNoTouchTimer(true);
2716  }
2717  })
2718  .show();
2719  }
2720  });
2721  }
2722  else
2723  {
2724  runOnUiThread(new Runnable() {
2725  public void run() {
2726  mProgressDialog.dismiss();
2727  mToast.makeText(getActivity(), String.format("Exporting mesh \"%s\" failed! No files found in tmp directory!? Last export may have failed or have been canceled.", pathHuman), mToast.LENGTH_LONG).show();
2728  resetNoTouchTimer(true);
2729  }
2730  });
2731  }
2732  }
2733  });
2734  workingThread.start();
2735  }
2736 
2737  private void openDatabase(final String fileName, final boolean optimize)
2738  {
2739  mOpenedDatabasePath = mWorkingDirectory + fileName;
2740 
2741  Log.i(TAG, "Open database " + mOpenedDatabasePath);
2742 
2743  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
2744  final boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory), Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
2745 
2746 
2747  mProgressDialog.setTitle("Loading");
2748  mProgressDialog.setMessage(String.format("Opening database \"%s\"...", fileName));
2749  mProgressDialog.show();
2751 
2752  Thread openThread = new Thread(new Runnable() {
2753  public void run() {
2754 
2755  final String tmpDatabase = mWorkingDirectory+RTABMAP_TMP_DB;
2756  final int status = RTABMapLib.openDatabase2(mOpenedDatabasePath, tmpDatabase, databaseInMemory, optimize);
2757 
2758  runOnUiThread(new Runnable() {
2759  public void run() {
2760  if(status == -1)
2761  {
2763  mProgressDialog.dismiss();
2764  new AlertDialog.Builder(getActivity())
2765  .setCancelable(false)
2766  .setTitle("Error")
2767  .setMessage("The map is loaded but optimization of the map's graph has "
2768  + "failed, so the map cannot be shown. Change the Graph Optimizer approach used"
2769  + " or enable/disable if the graph is optimized from graph "
2770  + "end in \"Settings -> Mapping...\" and try opening again.")
2771  .setPositiveButton("Open Settings", new DialogInterface.OnClickListener() {
2772  public void onClick(DialogInterface dialog, int which) {
2773  Intent intent = new Intent(getActivity(), SettingsActivity.class);
2774  startActivity(intent);
2775  mBlockBack = true;
2776  }
2777  })
2778  .setNegativeButton("Close", new DialogInterface.OnClickListener() {
2779  public void onClick(DialogInterface dialog, int which) {
2780  }
2781  })
2782  .show();
2783  }
2784  else if(status == -2)
2785  {
2787  mProgressDialog.dismiss();
2788  new AlertDialog.Builder(getActivity())
2789  .setCancelable(false)
2790  .setTitle("Error")
2791  .setMessage("Failed to open database: Out of memory! Try "
2792  + "again after lowering Point Cloud Density in Settings.")
2793  .setPositiveButton("Open Settings", new DialogInterface.OnClickListener() {
2794  public void onClick(DialogInterface dialog, int which) {
2795  Intent intent = new Intent(getActivity(), SettingsActivity.class);
2796  startActivity(intent);
2797  mBlockBack = true;
2798  }
2799  })
2800  .setNegativeButton("Close", new DialogInterface.OnClickListener() {
2801  public void onClick(DialogInterface dialog, int which) {
2802  }
2803  })
2804  .show();
2805  }
2806  else
2807  {
2808  if(status >= 1 && status<=3)
2809  {
2810  mProgressDialog.dismiss();
2811  resetNoTouchTimer(true);
2813  mToast.makeText(getActivity(), String.format("Database loaded!"), mToast.LENGTH_LONG).show();
2814  }
2815  else if(!mItemTrajectoryMode.isChecked())
2816  {
2817  if(mButtonCameraView.getSelectedItemPosition() == 0)
2818  {
2819  setCamera(2);
2820  }
2821  // creating meshes...
2823  mProgressDialog.setTitle("Loading");
2824  mProgressDialog.setMessage(String.format("Database \"%s\" loaded. Please wait while rendering point clouds and meshes...", fileName));
2825  }
2826 
2827  }
2828  }
2829  });
2830  }
2831  });
2832  openThread.start();
2833  }
2834 
2835  public void copy(File src, File dst) throws IOException {
2836  InputStream in = new FileInputStream(src);
2837  OutputStream out = new FileOutputStream(dst);
2838 
2839  // Transfer bytes from in to out
2840  byte[] buf = new byte[1024];
2841  int len;
2842  while ((len = in.read(buf)) > 0) {
2843  out.write(buf, 0, len);
2844  }
2845  in.close();
2846  out.close();
2847  }
2848 
2849  private void shareToSketchfab()
2850  {
2851  Intent intent = new Intent(getActivity(), SketchfabActivity.class);
2852 
2853  intent.putExtra(RTABMAP_AUTH_TOKEN_KEY, mAuthToken);
2854  intent.putExtra(RTABMAP_WORKING_DIR_KEY, mWorkingDirectory);
2855 
2856  if(mOpenedDatabasePath.isEmpty())
2857  {
2858  intent.putExtra(RTABMAP_FILENAME_KEY, new SimpleDateFormat("yyMMdd-HHmmss").format(mDateOnPause));
2859  }
2860  else
2861  {
2862  File f = new File(mOpenedDatabasePath);
2863  String name = f.getName();
2864  intent.putExtra(RTABMAP_FILENAME_KEY, name.substring(0,name.lastIndexOf(".")));
2865  }
2866 
2867  startActivityForResult(intent, SKETCHFAB_ACTIVITY_CODE);
2868  mBlockBack = true;
2869  }
2870 }
d
void writeExportedFiles(final String fileName)
GLM_FUNC_DECL T roll(detail::tquat< T, P > const &x)
static native void setLighting(boolean enabled)
static native void setMinCloudDepth(float value)
static native void setOrthoCropFactor(float value)
static native void setMeshAngleTolerance(float value)
static native void setMeshRendering(boolean enabled, boolean withTexture)
static native void setTrajectoryMode(boolean enabled)
void tangoEventCallback(final int type, final String key, final String value)
void onNothingSelected(AdapterView<?> parent)
void updateProgressionCallback(final int count, final int max)
void onSensorChanged(SensorEvent event)
static native int setMappingParameter(String key, String value)
static native void setScreenRotation(int displayRotation, int cameraRotation)
static native void setBackgroundColor(float gray)
f
static native boolean exportMesh(float cloudVoxelSize, boolean regenerateCloud, boolean meshing, int textureSize, int textureCount, int normalK, boolean optimized, float optimizedVoxelSize, int optimizedDepth, int optimizedMaxPolygons, float optimizedColorRadius, boolean optimizedCleanWhitePolygons, int optimizedMinClusterSize, float optimizedMaxTextureDistance, int optimizedMinTextureClusterSize, boolean blockRendering)
ros::Time * timeStamp(M &m)
static native void setClusterRatio(float value)
static native void setMapCloudShown(boolean shown)
static native void setAppendMode(boolean enabled)
void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
static native void setOdomCloudShown(boolean shown)
void setSelection(int position, boolean animate)
Definition: NDSpinner.java:24
void onPanelClosed(int featureId, Menu menu)
void export(final boolean isOBJ, final boolean meshing, final boolean regenerateCloud, final boolean optimized, final int optimizedMaxPolygons)
GLM_FUNC_DECL genType e()
static native void setNodesFiltering(boolean enabled)
static native void setGridRotation(float value)
char * dst
Definition: lz4.h:354
static native int openDatabase(String databasePath, boolean databaseInMemory, boolean optimize)
static native void setMaxCloudDepth(float value)
static native void setMeshTriangleSize(int value)
void setProgressDialog(ProgressDialog progressDialog)
Definition: Renderer.java:60
GLM_FUNC_DECL T pitch(detail::tquat< T, P > const &x)
void onWindowFocusChanged(boolean hasFocus)
static native void setBackfaceCulling(boolean enabled)
static native void setCameraColor(boolean enabled)
static void zip(String file, String zipFile)
Definition: Util.java:23
void rtabmapInitEventCallback(final int status, final String msg)
static native void onPause()
static native void setRawScanSaved(boolean enabled)
static native void setLocalizationMode(boolean enabled)
static native int openDatabase2(String databaseSource, String databasePath, boolean databaseInMemory, boolean optimize)
void onActivityResult(int requestCode, int resultCode, Intent data)
void updateTexts(String[] texts)
Definition: Renderer.java:190
void onAccuracyChanged(Sensor sensor, int accuracy)
static native void setGPS(double stamp, double longitude, double latitude, double altitude, double accuracy, double bearing)
static final String RTABMAP_OPENED_DB_PATH_KEY
boolean onOptionsItemSelected(MenuItem item)
static native void setCloudDensityLevel(int value)
static native void cancelProcessing()
unsigned char Boolean
Definition: ConvertUTF.h:93
GLM_FUNC_DECL genType pi()
void updateProgressionUI(int count, int max)
void setTextColor(float color)
Definition: Renderer.java:220
static native void setPointSize(float value)
void openDatabase(final String fileName, final boolean optimize)
static native void setRenderingTextureDecimation(int value)
void onCreate(Bundle savedInstanceState)
void tangoEventUI(int type, String key, String value)
static String[] loadFileList(String directory, final boolean databasesOnly)
Definition: Util.java:58
RecoveryProgressState state
boolean CheckTangoCoreVersion(int minVersion)
static native void setCamera(int cameraIndex)
static native void setPausedMapping(boolean paused)
static native void setGridVisible(boolean visible)
static native boolean writeExportedMesh(String directory, String name)
GLM_FUNC_DECL genType max(genType const &x, genType const &y)
string packageName()
static native boolean onTangoServiceConnected(IBinder binder)
static native void save(String outputDatabasePath)
void rtabmapInitEventUI(int status, String msg)
boolean onMenuOpened(int featureId, Menu menu)
static native void onTouchEvent(int touchCount, int event0, float x0, float y0, float x1, float y1)
static native void setFullResolution(boolean enabled)
static native void setDataRecorderMode(boolean enabled)
GLM_FUNC_DECL T yaw(detail::tquat< T, P > const &x)
static native boolean postExportation(boolean visualize)
static native void setMaxGainRadius(float value)
static final boolean bindTangoService(final Context context, ServiceConnection connection)
unsigned char byte
void setOffset(int offset)
Definition: Renderer.java:70
void updateStatsCallback(final int nodes, final int words, final int points, final int polygons, final float updateTime, final int loopClosureId, final int highestHypId, final int databaseMemoryUsed, final int inliers, final int matches, final int featuresExtracted, final float hypothesis, final int nodesDrawn, final float fps, final int rejected, final float rehearsalValue, final float optimizationMaxError, final float optimizationMaxErrorRatio, final float distanceTravelled, final int fastMovement, final float x, final float y, final float z, final float roll, final float pitch, final float yaw)
static native void setGraphOptimization(boolean enabled)
void run(ClassLoader *loader)
static native int postProcessing(int approach)
static native void setOnlineBlending(boolean enabled)
static native void onCreate(RTABMapActivity activity)
static native void setWireframe(boolean enabled)
void setToast(Toast toast)
Definition: Renderer.java:65
void updateStatsUI(int loopClosureId, int inliers, int matches, int rejected, float optimizationMaxError, float optimizationMaxErrorRatio, boolean fastMovement, String[] statusTexts)
static native void setSmoothing(boolean enabled)
static native void setGraphVisible(boolean visible)


rtabmap
Author(s): Mathieu Labbe
autogenerated on Wed Jun 5 2019 22:41:32