RTABMapActivity.java
Go to the documentation of this file.
1 package com.introlab.rtabmap;
2 
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.OutputStream;
10 import java.lang.reflect.InvocationTargetException;
11 import java.lang.reflect.Method;
12 import java.text.SimpleDateFormat;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Date;
16 import java.util.HashMap;
17 import java.util.Timer;
18 import java.util.TimerTask;
19 
20 import android.Manifest;
21 import android.app.ActivityManager;
22 import android.app.ActivityManager.MemoryInfo;
23 import android.app.AlertDialog;
24 import android.app.Notification;
25 import android.app.NotificationManager;
26 import android.app.PendingIntent;
27 import android.app.ProgressDialog;
28 import android.content.ComponentName;
29 import android.content.ContentResolver;
30 import android.content.ContentValues;
31 import android.content.Context;
32 import android.content.DialogInterface;
33 import android.content.DialogInterface.OnShowListener;
34 import android.content.Intent;
35 import android.content.ServiceConnection;
36 import android.content.SharedPreferences;
37 import android.content.pm.ApplicationInfo;
38 import android.content.pm.PackageInfo;
39 import android.content.pm.PackageManager;
40 import android.content.pm.PackageManager.NameNotFoundException;
41 import android.database.Cursor;
42 import android.hardware.Camera;
43 import android.hardware.Sensor;
44 import android.hardware.SensorEvent;
45 import android.hardware.SensorEventListener;
46 import android.hardware.SensorManager;
47 import android.graphics.Matrix;
48 import android.graphics.Point;
49 import android.hardware.display.DisplayManager;
50 import android.location.Location;
51 import android.location.LocationListener;
52 import android.location.LocationManager;
53 import android.media.MediaScannerConnection;
54 import android.net.Uri;
55 import android.net.wifi.WifiInfo;
56 import android.net.wifi.WifiManager;
57 import android.opengl.GLSurfaceView;
58 import android.os.Bundle;
59 import android.os.Environment;
60 import android.os.Handler;
61 import android.os.IBinder;
62 import android.os.Message;
63 import android.preference.PreferenceManager;
64 import android.provider.MediaStore;
65 import android.provider.OpenableColumns;
66 import android.support.v4.app.FragmentActivity;
67 import android.support.v4.content.FileProvider;
68 import android.text.InputType;
69 import android.util.Log;
70 import android.util.TypedValue;
71 import android.view.ContextMenu;
72 import android.view.Display;
73 import android.view.GestureDetector;
74 import android.view.Menu;
75 import android.view.MenuItem;
76 import android.view.MenuItem.OnMenuItemClickListener;
77 import android.view.MenuInflater;
78 import android.view.MotionEvent;
79 import android.view.View;
80 import android.view.ContextMenu.ContextMenuInfo;
81 import android.view.View.OnClickListener;
82 import android.view.View.OnCreateContextMenuListener;
83 import android.view.View.OnTouchListener;
84 import android.view.WindowManager;
85 import android.view.inputmethod.EditorInfo;
86 import android.widget.AdapterView;
87 import android.widget.AdapterView.OnItemSelectedListener;
88 import android.widget.ArrayAdapter;
89 import android.widget.Button;
90 import android.widget.EditText;
91 import android.widget.ImageButton;
92 import android.widget.ListView;
93 import android.widget.NumberPicker;
94 import android.widget.RelativeLayout;
95 import android.widget.SeekBar;
96 import android.widget.SeekBar.OnSeekBarChangeListener;
97 import android.widget.Toast;
98 import android.widget.ToggleButton;
99 
100 import com.google.ar.core.ArCoreApk;
101 import com.google.atap.tangoservice.Tango;
102 import com.huawei.hiar.AREnginesApk;
103 
104 
105 // The main activity of the application. This activity shows debug information
106 // and a glSurfaceView that renders graphic content.
107 public class RTABMapActivity extends FragmentActivity implements OnClickListener, OnItemSelectedListener, SensorEventListener {
108 
109  // Opaque native pointer to the native application instance.
110  public static long nativeApplication;
111 
112  // Tag for debug logging.
113  public static final String TAG = RTABMapActivity.class.getSimpleName();
114  public static boolean DISABLE_LOG = false;
115 
116  // The minimum Tango Core version required from this application.
117  private static final int MIN_TANGO_CORE_VERSION = 9377;
118 
119  // The package name of Tang Core, used for checking minimum Tango Core version.
120  private static final String TANGO_PACKAGE_NAME = "com.google.tango";
121 
122  public static final String EXTRA_KEY_PERMISSIONTYPE = "PERMISSIONTYPE";
123  public static final String EXTRA_VALUE_ADF = "ADF_LOAD_SAVE_PERMISSION";
124 
125  public static final String RTABMAP_TMP_DB = "rtabmap.tmp.db";
126  public static final String RTABMAP_TMP_DIR = "tmp";
127  public static final String RTABMAP_TMP_FILENAME = "map";
128  public static final String RTABMAP_SDCARD_PATH = "/Internal storage/";
129  public static final String RTABMAP_EXPORT_DIR = "Export/";
130 
131  public static final String RTABMAP_AUTH_TOKEN_KEY = "com.introlab.rtabmap.AUTH_TOKEN";
132  public static final String RTABMAP_FILENAME_KEY = "com.introlab.rtabmap.FILENAME";
133  public static final String RTABMAP_OPENED_DB_PATH_KEY = "com.introlab.rtabmap.OPENED_DB_PATH";
134  public static final String RTABMAP_WORKING_DIR_KEY = "com.introlab.rtabmap.WORKING_DIR";
135  public static final int SKETCHFAB_ACTIVITY_CODE = 999;
136  private String mAuthToken;
137 
138  public static final long NOTOUCH_TIMEOUT = 5000; // 5 sec
139  private boolean mHudVisible = true;
140  private int mSavedRenderingType = 0;
141  private boolean mMenuOpened = false;
142  private long mSavedStamp = 0;
143 
144  // UI states
145  private static enum State {
146  STATE_WELCOME, // Camera/Motion off - showing only buttons open and start new scan
147  STATE_CAMERA, // Camera/Motion on - not mapping
148  STATE_MAPPING, // Camera/Motion on - mapping
149  STATE_IDLE, // Camera/Motion off
150  STATE_PROCESSING, // Camera/Motion off - post processing
151  STATE_VISUALIZING, // Camera/Motion off - Showing optimized mesh
152  STATE_VISUALIZING_CAMERA, // Camera/Motion on - Showing optimized mesh
153  STATE_VISUALIZING_WHILE_LOADING // Camera/Motion off - Loading data while showing optimized mesh
154  }
155  State mState = State.STATE_WELCOME;
156 
157  // GLSurfaceView and renderer, all of the graphic content is rendered
158  // through OpenGL ES 2.0 in native code.
159  private Renderer mRenderer = null;
160  private GLSurfaceView mGLView;
161 
162  View mDecorView;
163  int mStatusBarHeight = 0;
164  int mActionBarHeight = 0;
165 
166  ProgressDialog mProgressDialog;
167  ProgressDialog mExportProgressDialog;
168 
169  // Screen size for normalizing the touch input for orbiting the render camera.
170  private Point mScreenSize = new Point();
171  private Date mBackClickedTime = new Date();
172  private long mOnPauseStamp = 0;
173  private boolean mOnPause = false;
174  private Date mDateOnPause = new Date();
176  private long mFreeMemoryOnStart = 0;
177 
178  private MenuItem mItemSave;
179  private MenuItem mItemOpen;
180  private MenuItem mItemNewScan;
181  private MenuItem mItemPostProcessing;
182  private MenuItem mItemExport;
183  private MenuItem mItemSettings;
184  private MenuItem mItemModes;
185  private MenuItem mItemResume;
186  private MenuItem mItemLocalizationMode;
187  private MenuItem mItemTrajectoryMode;
188  private MenuItem mItemRenderingPointCloud;
189  private MenuItem mItemRenderingMesh;
190  private MenuItem mItemRenderingTextureMesh;
191  private MenuItem mItemDataRecorderMode;
192  private MenuItem mItemStatusVisibility;
193  private MenuItem mItemDebugVisibility;
194 
196  private ImageButton mButtonStart;
197  private ImageButton mButtonStop;
198  private ToggleButton mButtonLighting;
199  private ToggleButton mButtonWireframe;
200  private ToggleButton mButtonBackfaceShown;
202  private Button mButtonSaveOnDevice;
203  private Button mButtonShareOnSketchfab;
204  private Button mButtonLibrary;
205  private Button mButtonNewScan;
206  private SeekBar mSeekBarOrthoCut;
207  private SeekBar mSeekBarGrid;
208 
209  private String mOpenedDatabasePath = "";
210  private String mWorkingDirectory = "";
211  private String mWorkingDirectoryHuman = "";
212 
213  private String mUpdateRate;
214  private String mTimeThr;
215  private String mMaxFeatures;
216  private String mLoopThr;
217  private String mMinInliers;
218  private String mMaxOptimizationError;
219  private boolean mGPSSaved = false;
220  private boolean mEnvSensorsSaved = false;
221  private boolean mIsARCoreAvailable = false;
222  private boolean mIsAREngineAvailable = false;
223 
224  private LocationManager mLocationManager;
225  private LocationListener mLocationListener;
226  private Location mLastKnownLocation;
227  private SensorManager mSensorManager;
228  private WifiManager mWifiManager;
229  private Timer mEnvSensorsTimer = new Timer();
230  Sensor mAccelerometer;
231  Sensor mMagnetometer;
232  Sensor mAmbientTemperature;
233  Sensor mAmbientLight;
234  Sensor mAmbientAirPressure;
235  Sensor mAmbientRelativeHumidity;
236  private float mCompassDeg = 0.0f;
237  private float[] mLastEnvSensors = new float[5];
238  private boolean[] mLastEnvSensorsSet = new boolean[5];
239 
240  private float[] mLastAccelerometer = new float[3];
241  private float[] mLastMagnetometer = new float[3];
242  private boolean mLastAccelerometerSet = false;
243  private boolean mLastMagnetometerSet = false;
244 
245  private Matrix mDeviceToCamera = new Matrix();
246  private Matrix mRMat = new Matrix();
247  private Matrix mNewR = new Matrix();
248  private float[] mR = new float[9];
249  private float[] mOrientation = new float[3];
250 
251  private int mTotalLoopClosures = 0;
252  int mMapNodes = 0;
253 
254  private Toast mToast = null;
255 
256  private AlertDialog mMemoryWarningDialog = null;
257 
258  private final int STATUS_TEXTS_SIZE = 20;
259  private final int STATUS_TEXTS_POSE_INDEX = 6;
260  private String[] mStatusTexts = new String[STATUS_TEXTS_SIZE];
261 
262  GestureDetector mGesDetect = null;
263 
264  ARCoreSharedCamera mArCoreCamera = null;
265  int mCameraDriver = 0;
266 
267  private String mIntentDbToOpen = null;
268 
269  //Tango Service connection.
270  boolean mCameraServiceConnectionUsed = false;
271  ServiceConnection mCameraServiceConnection = new ServiceConnection() {
272  public void onServiceConnected(ComponentName name, final IBinder service) {
273  Thread bindThread = new Thread(new Runnable() {
274  public void run() {
275  final boolean cameraStartSucess = RTABMapLib.startCamera(nativeApplication, service, getApplicationContext(), getActivity(), mCameraDriver);
276  runOnUiThread(new Runnable() {
277  public void run() {
278  mProgressDialog.dismiss();
279  if(!cameraStartSucess)
280  {
281  mToast.makeText(getApplicationContext(),
282  String.format("Failed to intialize Tango camera! RTAB-Map may not be built with Tango support."), mToast.LENGTH_LONG).show();
283  if(mCameraServiceConnectionUsed)
284  {
285  if(!DISABLE_LOG) Log.i(TAG, String.format("unbindService"));
286  getActivity().unbindService(mCameraServiceConnection);
287  }
288  mCameraServiceConnectionUsed = false;
289  }
290  else
291  {
293  if(mState==State.STATE_VISUALIZING_CAMERA && mItemLocalizationMode.isChecked())
294  {
295  RTABMapLib.setPausedMapping(nativeApplication, false);
296  }
297  }
298  }
299  });
300  }
301  });
302  bindThread.start();
303  }
304 
305  public void onServiceDisconnected(ComponentName name) {
306  // Handle this if you need to gracefully shutdown/retry
307  // in the event that Tango itself crashes/gets upgraded while running.
308  mToast.makeText(getApplicationContext(),
309  String.format("Tango disconnected!"), mToast.LENGTH_LONG).show();
310  }
311  };
312 
313  @Override
314  protected void onCreate(Bundle savedInstanceState) {
315  super.onCreate(savedInstanceState);
316  setTitle(R.string.menu_name);
317 
318  nativeApplication = RTABMapLib.createNativeApplication(this);
319 
320  mFreeMemoryOnStart = getFreeMemory();
321 
322  // Query screen size, the screen size is used for computing the normalized
323  // touch point.
324  Display display = getWindowManager().getDefaultDisplay();
325  display.getSize(mScreenSize);
326 
327  getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
328  getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
329 
330  // Setting content view of this activity.
331  setContentView(R.layout.activity_rtabmap);
332 
333  // Make sure to initialize all default values
334  SettingsActivity settings;
335 
336  mDecorView = getWindow().getDecorView();
337  mStatusBarHeight = getStatusBarHeight();
338  mActionBarHeight = getActionBarHeight();
339 
340  // Buttons for selecting camera view and Set up button click listeners.
341  mButtonCameraView = (NDSpinner)findViewById(R.id.camera_button);
342  mButtonStart = (ImageButton)findViewById(R.id.start_button);
343  mButtonStop = (ImageButton)findViewById(R.id.stop_button);
344  mButtonLighting = (ToggleButton)findViewById(R.id.light_button);
345  mButtonWireframe = (ToggleButton)findViewById(R.id.wireframe_button);
346  mButtonBackfaceShown = (ToggleButton)findViewById(R.id.backface_button);
347  mButtonCloseVisualization = (Button)findViewById(R.id.close_visualization_button);
348  mButtonSaveOnDevice = (Button)findViewById(R.id.button_saveOnDevice);
349  mButtonShareOnSketchfab = (Button)findViewById(R.id.button_shareToSketchfab);
350  mButtonLibrary = (Button)findViewById(R.id.button_library);
351  mButtonNewScan = (Button)findViewById(R.id.button_new_scan);
352  mButtonCameraView.setOnItemSelectedListener(this);
353  mButtonStart.setOnClickListener(this);
354  mButtonStop.setOnClickListener(this);
355  mButtonLighting.setOnClickListener(this);
356  mButtonWireframe.setOnClickListener(this);
357  mButtonBackfaceShown.setOnClickListener(this);
358  mButtonCloseVisualization.setOnClickListener(this);
359  mButtonSaveOnDevice.setOnClickListener(this);
360  mButtonShareOnSketchfab.setOnClickListener(this);
361  mButtonLibrary.setOnClickListener(this);
362  mButtonNewScan.setOnClickListener(this);
363  mButtonLighting.setChecked(false);
364  mButtonLighting.setVisibility(View.INVISIBLE);
365  mButtonWireframe.setChecked(false);
366  mButtonWireframe.setVisibility(View.INVISIBLE);
367  mButtonCloseVisualization.setVisibility(View.INVISIBLE);
368  mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
369  mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
370  mButtonLibrary.setVisibility(View.INVISIBLE);
371  mButtonNewScan.setVisibility(View.INVISIBLE);
372  if(mItemRenderingMesh != null && mItemRenderingTextureMesh != null)
373  {
374  mButtonBackfaceShown.setVisibility(mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked()?View.VISIBLE:View.INVISIBLE);
375  }
376 
377  ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.camera_view_array, android.R.layout.simple_spinner_item);
378  adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
379  mButtonCameraView.setAdapter(adapter);
380  mButtonCameraView.setOnTouchListener(new OnTouchListener() {
381  @Override
382  public boolean onTouch(View v, MotionEvent event) {
384  return false;
385  }
386  });
387 
388  mSeekBarOrthoCut = (SeekBar)findViewById(R.id.seekBar_ortho_cut);
389  mSeekBarOrthoCut.setMax(120);
390  mSeekBarOrthoCut.setProgress(80);
391  mSeekBarOrthoCut.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
392  @Override
393  public void onProgressChanged(SeekBar seekBar, int progressValue, boolean fromUser) {
394  RTABMapLib.setOrthoCropFactor(nativeApplication, (float)(120-progressValue)/20.0f - 3.0f);
396  }
397 
398  @Override
399  public void onStartTrackingTouch(SeekBar seekBar) {
400  }
401 
402  @Override
403  public void onStopTrackingTouch(SeekBar seekBar) {
404  }
405  });
406 
407  mSeekBarGrid = (SeekBar)findViewById(R.id.seekBar_grid);
408  mSeekBarGrid.setMax(180);
409  mSeekBarGrid.setProgress(90);
410  mSeekBarGrid.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
411  @Override
412  public void onProgressChanged(SeekBar seekBar, int progressValue, boolean fromUser) {
413  RTABMapLib.setGridRotation(nativeApplication, ((float)progressValue-90.0f)/2.0f);
415  }
416 
417  @Override
418  public void onStartTrackingTouch(SeekBar seekBar) {
419  }
420 
421  @Override
422  public void onStopTrackingTouch(SeekBar seekBar) {
423  }
424  });
425 
426  mToast = Toast.makeText(getApplicationContext(), "", Toast.LENGTH_SHORT);
427 
428  // OpenGL view where all of the graphics are drawn.
429  mGLView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
430 
431  mGesDetect = new GestureDetector(this, new DoubleTapGestureDetector());
432 
433  // Configure OpenGL renderer
434  mGLView.setEGLContextClientVersion(2);
435  mGLView.setEGLConfigChooser(8, 8, 8, 8, 24, 0);
436  mGLView.setOnTouchListener(new OnTouchListener() {
437  @Override
438  public boolean onTouch(View v, MotionEvent event) {
439 
440  resetNoTouchTimer(getActionBar().isShowing() && mHudVisible == false);
441 
442  mGesDetect.onTouchEvent(event);
443 
444  // Pass the touch event to the native layer for camera control.
445  // Single touch to rotate the camera around the device.
446  // Two fingers to zoom in and out.
447  int pointCount = event.getPointerCount();
448  if (pointCount == 1) {
449  float normalizedX = event.getX(0) / mScreenSize.x;
450  float normalizedY = event.getY(0) / mScreenSize.y;
451  RTABMapLib.onTouchEvent(nativeApplication, 1,
452  event.getActionMasked(), normalizedX, normalizedY, 0.0f, 0.0f);
453  }
454  if (pointCount == 2) {
455  if (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) {
456  int index = event.getActionIndex() == 0 ? 1 : 0;
457  float normalizedX = event.getX(index) / mScreenSize.x;
458  float normalizedY = event.getY(index) / mScreenSize.y;
459  RTABMapLib.onTouchEvent(nativeApplication, 1,
460  MotionEvent.ACTION_DOWN, normalizedX, normalizedY, 0.0f, 0.0f);
461  } else {
462  float normalizedX0 = event.getX(0) / mScreenSize.x;
463  float normalizedY0 = event.getY(0) / mScreenSize.y;
464  float normalizedX1 = event.getX(1) / mScreenSize.x;
465  float normalizedY1 = event.getY(1) / mScreenSize.y;
466  RTABMapLib.onTouchEvent(nativeApplication, 2, event.getActionMasked(),
467  normalizedX0, normalizedY0, normalizedX1, normalizedY1);
468  }
469  }
470  return true;
471  }
472  });
473 
474  // Configure the OpenGL renderer.
475  mRenderer = new Renderer(this);
476  mGLView.setRenderer(mRenderer);
477 
478  mProgressDialog = new ProgressDialog(this);
479  mProgressDialog.setCanceledOnTouchOutside(false);
480  mRenderer.setProgressDialog(mProgressDialog);
481  mRenderer.setToast(mToast);
482  setNavVisibility(true);
483 
484  mExportProgressDialog = new ProgressDialog(this);
485  mExportProgressDialog.setCanceledOnTouchOutside(false);
486  mExportProgressDialog.setCancelable(false);
487  mExportProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
488  mExportProgressDialog.setProgressNumberFormat(null);
489  mExportProgressDialog.setProgressPercentFormat(null);
490  mExportProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
491  @Override
492  public void onClick(DialogInterface dialog, int which) {
493  RTABMapLib.cancelProcessing(nativeApplication);
494 
495  mProgressDialog.setTitle("");
496  mProgressDialog.setMessage(String.format("Cancelling..."));
497  mProgressDialog.show();
498  }
499  });
500 
501  mOpenedDatabasePath = "";
502  mWorkingDirectory = "";
503  mWorkingDirectoryHuman = "";
504  mTotalLoopClosures = 0;
505  mLastFastMovementNotificationStamp = System.currentTimeMillis()/1000;
506 
507 
508  int targetSdkVersion= 0;
509  try {
510  ApplicationInfo app = this.getPackageManager().getApplicationInfo("com.introlab.rtabmap", 0);
511  targetSdkVersion = app.targetSdkVersion;
512  } catch (NameNotFoundException e) {
513  e.printStackTrace();
514  }
515 
516  if(Environment.getExternalStorageState().compareTo(Environment.MEDIA_MOUNTED)==0 &&
517  (targetSdkVersion < 30 || getActivity().getExternalFilesDirs(null).length >=1))
518  {
519  File extStore;
520  if(targetSdkVersion < 30)
521  {
522  extStore = Environment.getExternalStorageDirectory();
523  }
524  else // >= android30
525  {
526  extStore = getActivity().getExternalFilesDirs(null)[0];
527  }
528 
529  mWorkingDirectory = extStore.getAbsolutePath() + "/" + getString(R.string.app_name) + "/";
530  extStore = new File(mWorkingDirectory);
531  extStore.mkdirs();
532  mWorkingDirectoryHuman = RTABMAP_SDCARD_PATH + getString(R.string.app_name) + "/";
533  }
534  else
535  {
536  // show warning that data cannot be saved!
537  mToast.makeText(getApplicationContext(),
538  String.format("Failed to get external storage path (state=%s). Saving disabled.",
539  Environment.getExternalStorageState()), mToast.LENGTH_LONG).show();
540  }
541 
542  DisplayManager displayManager = (DisplayManager) getSystemService(DISPLAY_SERVICE);
543  if (displayManager != null) {
544  displayManager.registerDisplayListener(new DisplayManager.DisplayListener() {
545  @Override
546  public void onDisplayAdded(int displayId) {
547 
548  }
549 
550  @Override
551  public void onDisplayChanged(int displayId) {
552  synchronized (this) {
554  Display display = getWindowManager().getDefaultDisplay();
555  display.getSize(mScreenSize);
556  }
557  }
558 
559  @Override
560  public void onDisplayRemoved(int displayId) {}
561  }, null);
562  }
563 
564  // Acquire a reference to the system Location Manager
565  mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
566 
567  // Define a listener that responds to location updates
568  mLocationListener = new LocationListener() {
569  public void onLocationChanged(Location location) {
570  mLastKnownLocation = location;
571  double stamp = location.getTime()/1000.0;
572  if(!DISABLE_LOG) Log.d(TAG, String.format("GPS received at %f (%d)", stamp, location.getTime()));
574  nativeApplication,
575  stamp,
576  (double)location.getLongitude(),
577  (double)location.getLatitude(),
578  (double)location.getAltitude(),
579  (double)location.getAccuracy(),
580  (double)mCompassDeg);
581  }
582 
583  public void onStatusChanged(String provider, int status, Bundle extras) {}
584 
585  public void onProviderEnabled(String provider) {}
586 
587  public void onProviderDisabled(String provider) {}
588  };
589 
590  mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
591  mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
592  mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
593  mAmbientTemperature = mSensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);
594  mAmbientLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
595  mAmbientAirPressure = mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
596  mAmbientRelativeHumidity = mSensorManager.getDefaultSensor(Sensor.TYPE_RELATIVE_HUMIDITY);
597  float [] values = {1,0,0,0,0,1,0,-1,0};
598  mDeviceToCamera.setValues(values);
599  mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
600 
601  setCamera(1);
602 
603  DISABLE_LOG = !( 0 != ( getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE ) );
604 
605  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
606  String cameraDriverStr = sharedPref.getString(getString(R.string.pref_key_camera_driver), getString(R.string.pref_default_camera_driver));
607  mCameraDriver = Integer.parseInt(cameraDriverStr);
608 
611 
612  if (!PermissionHelper.hasPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
613  PermissionHelper.requestPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
614  }
615  else
616  {
617  // Get intent, action and MIME type
618  Intent intent = getIntent();
619  String action = intent.getAction();
620  String type = intent.getType();
621 
622  if (Intent.ACTION_SEND.equals(action) && type != null) {
623  if ("application/octet-stream".equals(type)) {
624  Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
625  if (imageUri != null) {
626  String fileName = getFileName(imageUri);
627  Log.i(TAG, "Intent received: " + imageUri.getPath() + " Name:" + fileName);
628  if(fileName.endsWith(".db"))
629  {
630  File file = new File(mWorkingDirectory+fileName);
631  if(file.exists())
632  {
633  mToast.makeText(this, fileName + " already exists in RTAB-Map's library! Cannot be copied.", mToast.LENGTH_LONG).show();
634  }
635  else
636  {
637  copy(imageUri, file);
638  mIntentDbToOpen = fileName;
639  }
640  }
641  }
642  }
643  } else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
644  if (type.startsWith("application/")) {
645  ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
646  if (imageUris != null) {
647  boolean added = false;
648  for(Uri imageUri: imageUris)
649  {
650  String fileName = getFileName(imageUri);
651  Log.i(TAG, "Intent received: " + imageUri.getPath() + " Name:" + fileName);
652  if(fileName.endsWith(".db"))
653  {
654  File file = new File(mWorkingDirectory+"/"+getFileName(imageUri));
655  if(!file.exists())
656  {
657  copy(imageUri, file);
658  added = true;
659  }
660  else
661  {
662  Log.e(TAG, fileName + " already exists in RTAB-Map's library! Cannot be copied.");
663  }
664  }
665  }
666  if(added)
667  {
668  openDatabase();
669  }
670  }
671 
672  }
673  }
674 
675  postCreate();
676  }
677  }
678 
679  public void copy(File src, File dst) throws IOException {
680  InputStream in = new FileInputStream(src);
681  OutputStream out = new FileOutputStream(dst);
682 
683  // Transfer bytes from in to out
684  byte[] buf = new byte[1024];
685  int len;
686  while ((len = in.read(buf)) > 0) {
687  out.write(buf, 0, len);
688  }
689  in.close();
690  out.close();
691  }
692 
693  public void copy(Uri uri, File file)
694  {
695  InputStream in;
696  try {
697  in = getApplicationContext().getContentResolver().openInputStream(uri);
698 
699  OutputStream out = new FileOutputStream(file);
700  byte[] buf = new byte[1024];
701  int len;
702  while ((len = in.read(buf)) > 0) {
703  out.write(buf, 0, len);
704  }
705  in.close();
706  out.close();
707  } catch (IOException e) {
708  Log.e(TAG, e.getMessage());
709  }
710  }
711 
712  public String getFileName(Uri uri) {
713  String result = null;
714  if (uri.getScheme().equals("content")) {
715  Cursor cursor = getContentResolver().query(uri, null, null, null, null);
716  try {
717  if (cursor != null && cursor.moveToFirst()) {
718  result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
719  }
720  } finally {
721  cursor.close();
722  }
723  }
724  if (result == null) {
725  result = uri.getPath();
726  int cut = result.lastIndexOf('/');
727  if (cut != -1) {
728  result = result.substring(cut + 1);
729  }
730  }
731  return result;
732  }
733 
734 
735  // Should be called only if read/write permissions are granted!
736  private void postCreate()
737  {
738  Log.i(TAG, "postCreate()");
739 
740  final String[] files = Util.loadFileList(mWorkingDirectory, true);
741  if(files.length == 0)
742  {
743  mButtonLibrary.setVisibility(View.INVISIBLE);
744  }
745  }
746 
748  {
749  Log.i(TAG, String.format("updateCameraDriverSettings() mCameraDriver=%d RTABMapLib.isBuiltWith(%d)=%d", mCameraDriver, mCameraDriver, RTABMapLib.isBuiltWith(nativeApplication, mCameraDriver)?1:0));
750 
751  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
752  String cameraDriverStr = sharedPref.getString(getString(R.string.pref_key_camera_driver), getString(R.string.pref_default_camera_driver));
753  mCameraDriver = Integer.parseInt(cameraDriverStr);
754 
755  if(mCameraDriver == -1)
756  {
757  // Prioritize tango if available
758  mCameraDriver = 0;
759  SharedPreferences.Editor editor = sharedPref.edit();
760  editor.putString(getString(R.string.pref_key_camera_driver), "0");
761  editor.commit();
762  }
763 
764  if(mCameraDriver == 0 && (!CheckTangoCoreVersion(MIN_TANGO_CORE_VERSION) || !RTABMapLib.isBuiltWith(nativeApplication, 0)))
765  {
766  if(mIsAREngineAvailable && RTABMapLib.isBuiltWith(nativeApplication, 2))
767  {
768  SharedPreferences.Editor editor = sharedPref.edit();
769  editor.putString(getString(R.string.pref_key_camera_driver), "2");
770  editor.commit();
771  }
772  else if(mIsARCoreAvailable)
773  {
774  SharedPreferences.Editor editor = sharedPref.edit();
775  editor.putString(getString(R.string.pref_key_camera_driver), "3");
776  editor.commit();
777  }
778  }
779  else if(((mCameraDriver == 1 && (!RTABMapLib.isBuiltWith(nativeApplication, 0) || !mIsARCoreAvailable)) ||
780  (mCameraDriver == 3 && !mIsARCoreAvailable)))
781  {
782  if(CheckTangoCoreVersion(MIN_TANGO_CORE_VERSION) && RTABMapLib.isBuiltWith(nativeApplication, 0))
783  {
784  SharedPreferences.Editor editor = sharedPref.edit();
785  editor.putString(getString(R.string.pref_key_camera_driver), "0");
786  editor.commit();
787  }
788  else if(mIsAREngineAvailable && RTABMapLib.isBuiltWith(nativeApplication, 2))
789  {
790  SharedPreferences.Editor editor = sharedPref.edit();
791  editor.putString(getString(R.string.pref_key_camera_driver), "2");
792  editor.commit();
793  }
794  }
795  else if(mCameraDriver == 2 && (!mIsAREngineAvailable || !RTABMapLib.isBuiltWith(nativeApplication, 2)))
796  {
797  if(CheckTangoCoreVersion(MIN_TANGO_CORE_VERSION) && RTABMapLib.isBuiltWith(nativeApplication, 0))
798  {
799  SharedPreferences.Editor editor = sharedPref.edit();
800  editor.putString(getString(R.string.pref_key_camera_driver), "0");
801  editor.commit();
802  }
803  else if(mIsARCoreAvailable)
804  {
805  SharedPreferences.Editor editor = sharedPref.edit();
806  editor.putString(getString(R.string.pref_key_camera_driver), "3");
807  editor.commit();
808  }
809  }
810  }
811 
812  private void isArCoreAvailable() {
813  ArCoreApk.Availability availability = ArCoreApk.getInstance().checkAvailability(this);
814  if (availability.isTransient()) {
815  // Re-query at 5Hz while compatibility is checked in the background.
816  new Handler().postDelayed(new Runnable() {
817  @Override
818  public void run() {
820  }
821  }, 200);
822  }
823  if (availability.isSupported()) {
824  Log.i(TAG, "ARCore supported");
825  mIsARCoreAvailable = true;
827  } else { // Unsupported or unknown.
828  Log.i(TAG, "ARCore supported");
829  mIsARCoreAvailable = false;
831  }
832  }
833 
834  private void isArEngineAvailable() {
835  try {
836  AREnginesApk.ARAvailability availability = AREnginesApk.checkAvailability(this);
837  if (availability.isTransient()) {
838  // Re-query at 5Hz while compatibility is checked in the background.
839  new Handler().postDelayed(new Runnable() {
840  @Override
841  public void run() {
843  }
844  }, 200);
845  }
846  if (availability.isSupported()) {
847  Log.i(TAG, "AREngine supported");
848  mIsAREngineAvailable = true;
850  } else { // Unsupported or unknown.
851  Log.i(TAG, "AREngine not supported");
852  mIsAREngineAvailable = false;
854  }
855  }
856  catch(UnsatisfiedLinkError e)
857  {
858  Log.i(TAG, "AREngine not supported");
859  mIsAREngineAvailable = false;
860  }
861  }
862 
863 
864  @Override
865  public void onDestroy() {
866  super.onDestroy();
867 
868  if(!DISABLE_LOG) Log.d(TAG, "onDestroy()");
869  // Synchronized to avoid racing onDrawFrame.
870  synchronized (this) {
871  RTABMapLib.destroyNativeApplication(nativeApplication);
872  nativeApplication = 0;
873  }
874  }
875 
876  @Override
877  public void onSensorChanged(SensorEvent event) {
878  if(event.sensor == mAccelerometer || event.sensor == mMagnetometer)
879  {
880  if (event.sensor == mAccelerometer) {
881  System.arraycopy(event.values, 0, mLastAccelerometer, 0, event.values.length);
882  mLastAccelerometerSet = true;
883  } else if (event.sensor == mMagnetometer) {
884  System.arraycopy(event.values, 0, mLastMagnetometer, 0, event.values.length);
885  mLastMagnetometerSet = true;
886  }
887  if (mLastAccelerometerSet && mLastMagnetometerSet) {
888  SensorManager.getRotationMatrix(mR, null, mLastAccelerometer, mLastMagnetometer);
889  mRMat.setValues(mR);
890  mNewR.setConcat(mRMat, mDeviceToCamera) ;
891  mNewR.getValues(mR);
892  SensorManager.getOrientation(mR, mOrientation);
893  mCompassDeg = mOrientation[0] * 180.0f/(float)Math.PI;
894  if(mCompassDeg<0.0f)
895  {
896  mCompassDeg += 360.0f;
897  }
898  }
899  }
900  else if(event.sensor == mAmbientTemperature)
901  {
902  mLastEnvSensors[1] = event.values[0];
903  mLastEnvSensorsSet[1] = true;
904  RTABMapLib.addEnvSensor(nativeApplication, 2, event.values[0]);
905  }
906  else if(event.sensor == mAmbientAirPressure)
907  {
908  mLastEnvSensors[2] = event.values[0];
909  mLastEnvSensorsSet[2] = true;
910  RTABMapLib.addEnvSensor(nativeApplication, 3, event.values[0]);
911  }
912  else if(event.sensor == mAmbientLight)
913  {
914  mLastEnvSensors[3] = event.values[0];
915  mLastEnvSensorsSet[3] = true;
916  RTABMapLib.addEnvSensor(nativeApplication, 4, event.values[0]);
917  }
918  else if(event.sensor == mAmbientRelativeHumidity)
919  {
920  mLastEnvSensors[4] = event.values[0];
921  mLastEnvSensorsSet[4] = true;
922  RTABMapLib.addEnvSensor(nativeApplication, 5, event.values[0]);
923  }
924  }
925 
926  @Override
927  public void onAccuracyChanged(Sensor sensor, int accuracy) {
928  // not in use
929  }
930 
931 
932  public int getStatusBarHeight() {
933  int result = 0;
934  int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
935  if (resourceId > 0) {
936  result = getResources().getDimensionPixelSize(resourceId);
937  }
938  return result;
939  }
940  public int getActionBarHeight() {
941  int result = 0;
942  TypedValue tv = new TypedValue();
943  if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
944  {
945  result = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
946  }
947 
948  return result;
949  }
950 
951  @Override
952  public void onWindowFocusChanged(boolean hasFocus) {
953 
954  super.onWindowFocusChanged(hasFocus);
955 
956  if(!mHudVisible)
957  {
958  mRenderer.setOffset(!hasFocus?-mStatusBarHeight:0);
959  }
960  }
961 
962  // This snippet hides the system bars.
963  private void setNavVisibility(boolean visible) {
964  int newVis = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
965  | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
966  | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
967  if (!visible) {
968  newVis |= View.SYSTEM_UI_FLAG_LOW_PROFILE
969  | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
970  | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
971  | View.SYSTEM_UI_FLAG_IMMERSIVE;
972  mRenderer.setOffset(!hasWindowFocus()?-mStatusBarHeight:0);
973  }
974  else
975  {
976  mRenderer.setOffset(-mStatusBarHeight-mActionBarHeight);
977  }
978 
979  // Set the new desired visibility.
980  mDecorView.setSystemUiVisibility(newVis);
981  }
982 
983  @Override
984  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
985  // Check which request we're responding to
986  if (requestCode == Tango.TANGO_INTENT_ACTIVITYCODE) {
987  // Make sure the request was successful
988  if (resultCode == RESULT_CANCELED) {
989  mToast.makeText(this, "Motion Tracking Permissions Required!", mToast.LENGTH_SHORT).show();
990  }
991  }
992  else if (requestCode == SKETCHFAB_ACTIVITY_CODE) {
993  // Make sure the request was successful
994  if (resultCode == RESULT_OK) {
995  mAuthToken = data.getStringExtra(RTABMAP_AUTH_TOKEN_KEY);
996  }
997  }
998  }
999 
1000  @Override
1001  public boolean onMenuOpened(int featureId, Menu menu) {
1002  mMenuOpened = true;
1003  return super.onMenuOpened(featureId, menu);
1004  }
1005 
1006  @Override
1007  public void onPanelClosed(int featureId, Menu menu) {
1008  mMenuOpened = false;
1009  }
1010 
1011  @Override
1012  public void onBackPressed() {
1013 
1014  Date t = new Date();
1015  boolean doubleBack = t.getTime() - mBackClickedTime.getTime() < 3500; // mToast.LENGTH_LONG
1016  mBackClickedTime = t;
1017  if(mState == State.STATE_MAPPING)
1018  {
1019  stopMapping();
1020  }
1021  else if(mState == State.STATE_CAMERA || mState == State.STATE_VISUALIZING_CAMERA)
1022  {
1023  stopCamera();
1024  }
1025  else if(mState == State.STATE_IDLE || mState == State.STATE_WELCOME)
1026  {
1027  if(doubleBack)
1028  {
1029  super.onBackPressed();
1030  }
1031  else
1032  {
1033  mToast.makeText(this, "Press Back once more to exit", mToast.LENGTH_LONG).show();
1034  }
1035  }
1036  else if(mState == State.STATE_VISUALIZING)
1037  {
1039  RTABMapLib.postExportation(nativeApplication, false);
1040  }
1041  }
1042 
1043  @Override
1044  protected void onPause() {
1045  super.onPause();
1046  mGLView.onPause();
1048 
1049  if(!DISABLE_LOG) Log.i(TAG, "onPause()");
1050  mOnPause = true;
1051 
1052  if(mState == State.STATE_VISUALIZING_CAMERA)
1053  {
1054  stopCamera();
1055  }
1056  else if(mState == State.STATE_MAPPING || mState == State.STATE_CAMERA)
1057  {
1058  stopMapping();
1059  }
1060 
1061  mLocationManager.removeUpdates(mLocationListener);
1062  mSensorManager.unregisterListener(this);
1063  mLastAccelerometerSet = false;
1064  mLastMagnetometerSet= false;
1065  mLastEnvSensorsSet[0] = mLastEnvSensorsSet[1]= mLastEnvSensorsSet[2]= mLastEnvSensorsSet[3]= mLastEnvSensorsSet[4]=false;
1066 
1067  // This deletes OpenGL context!
1068  mGLView.onPause();
1069 
1070  mOnPauseStamp = System.currentTimeMillis()/1000;
1071  }
1072 
1073  private void updatePreferences()
1074  {
1075  // update preferences
1076  try
1077  {
1078  Log.i(TAG, "update preferences...");
1079  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
1080  mUpdateRate = sharedPref.getString(getString(R.string.pref_key_update_rate), getString(R.string.pref_default_update_rate));
1081  String maxSpeed = sharedPref.getString(getString(R.string.pref_key_max_speed), getString(R.string.pref_default_max_speed));
1082  mTimeThr = sharedPref.getString(getString(R.string.pref_key_time_thr), getString(R.string.pref_default_time_thr));
1083  String memThr = sharedPref.getString(getString(R.string.pref_key_mem_thr), getString(R.string.pref_default_mem_thr));
1084  mLoopThr = sharedPref.getString(getString(R.string.pref_key_loop_thr), getString(R.string.pref_default_loop_thr));
1085  String simThr = sharedPref.getString(getString(R.string.pref_key_sim_thr), getString(R.string.pref_default_sim_thr));
1086  mMinInliers = sharedPref.getString(getString(R.string.pref_key_min_inliers), getString(R.string.pref_default_min_inliers));
1087  mMaxOptimizationError = sharedPref.getString(getString(R.string.pref_key_opt_error), getString(R.string.pref_default_opt_error));
1088  float maxOptimizationError = Float.parseFloat(mMaxOptimizationError);
1089  if(maxOptimizationError >0 && maxOptimizationError<1)
1090  {
1091  Log.w(TAG, "Migration of " + getString(R.string.pref_key_opt_error) + " from " + mMaxOptimizationError + " to " + getString(R.string.pref_default_opt_error)) ;
1092  SharedPreferences.Editor editor = sharedPref.edit();
1093  editor.putString(getString(R.string.pref_key_opt_error), getString(R.string.pref_default_opt_error));
1094  editor.commit();
1095  mMaxOptimizationError = getString(R.string.pref_default_opt_error);
1096  }
1097  mMaxFeatures = sharedPref.getString(getString(R.string.pref_key_features_voc), getString(R.string.pref_default_features_voc));
1098  String maxFeaturesLoop = sharedPref.getString(getString(R.string.pref_key_features), getString(R.string.pref_default_features));
1099  String featureType = sharedPref.getString(getString(R.string.pref_key_features_type), getString(R.string.pref_default_features_type));
1100  boolean keepAllDb = sharedPref.getBoolean(getString(R.string.pref_key_keep_all_db), Boolean.parseBoolean(getString(R.string.pref_default_keep_all_db)));
1101  boolean optimizeFromGraphEnd = sharedPref.getBoolean(getString(R.string.pref_key_optimize_end), Boolean.parseBoolean(getString(R.string.pref_default_optimize_end)));
1102  String optimizer = sharedPref.getString(getString(R.string.pref_key_optimizer), getString(R.string.pref_default_optimizer));
1103  String markerDetection = sharedPref.getString(getString(R.string.pref_key_marker_detection), getString(R.string.pref_default_marker_detection));
1104  String markerDetectionDepthError = sharedPref.getString(getString(R.string.pref_key_marker_detection_depth_error), getString(R.string.pref_default_marker_detection_depth_error));
1105  mGPSSaved = PermissionHelper.hasPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) && sharedPref.getBoolean(getString(R.string.pref_key_gps_saved), Boolean.parseBoolean(getString(R.string.pref_default_gps_saved)));
1106  if(mGPSSaved)
1107  {
1108  mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mLocationListener);
1109  mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
1110  mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_UI);
1111  }
1112  mEnvSensorsSaved = sharedPref.getBoolean(getString(R.string.pref_key_env_sensors_saved), Boolean.parseBoolean(getString(R.string.pref_default_env_sensors_saved)));
1113  if(mEnvSensorsSaved)
1114  {
1115  mSensorManager.registerListener(this, mAmbientTemperature, SensorManager.SENSOR_DELAY_NORMAL);
1116  mSensorManager.registerListener(this, mAmbientAirPressure, SensorManager.SENSOR_DELAY_NORMAL);
1117  mSensorManager.registerListener(this, mAmbientLight, SensorManager.SENSOR_DELAY_NORMAL);
1118  mSensorManager.registerListener(this, mAmbientRelativeHumidity, SensorManager.SENSOR_DELAY_NORMAL);
1119  mEnvSensorsTimer.schedule(new TimerTask() {
1120 
1121  @Override
1122  public void run() {
1123  WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
1124  int dbm = 0;
1125  if(wifiInfo != null && (dbm = wifiInfo.getRssi()) > -127)
1126  {
1127  mLastEnvSensors[0] = (float)dbm;
1128  mLastEnvSensorsSet[0] = true;
1129  RTABMapLib.addEnvSensor(nativeApplication, 1, mLastEnvSensors[0]);
1130  }
1131  }
1132 
1133  },0,200);
1134  }
1135 
1136  Log.i(TAG, "set mapping parameters");
1137  RTABMapLib.setOnlineBlending(nativeApplication, sharedPref.getBoolean(getString(R.string.pref_key_blending), Boolean.parseBoolean(getString(R.string.pref_default_blending))));
1138  RTABMapLib.setNodesFiltering(nativeApplication, sharedPref.getBoolean(getString(R.string.pref_key_nodes_filtering), Boolean.parseBoolean(getString(R.string.pref_default_nodes_filtering))));
1139  RTABMapLib.setRawScanSaved(nativeApplication, sharedPref.getBoolean(getString(R.string.pref_key_raw_scan_saved), Boolean.parseBoolean(getString(R.string.pref_default_raw_scan_saved))));
1140  RTABMapLib.setFullResolution(nativeApplication, sharedPref.getBoolean(getString(R.string.pref_key_resolution), Boolean.parseBoolean(getString(R.string.pref_default_resolution))));
1141  RTABMapLib.setSmoothing(nativeApplication, sharedPref.getBoolean(getString(R.string.pref_key_smoothing), Boolean.parseBoolean(getString(R.string.pref_default_smoothing))));
1142  RTABMapLib.setDepthFromMotion(nativeApplication, sharedPref.getBoolean(getString(R.string.pref_key_depth_from_motion), Boolean.parseBoolean(getString(R.string.pref_default_depth_from_motion))));
1143  RTABMapLib.setCameraColor(nativeApplication, !sharedPref.getBoolean(getString(R.string.pref_key_fisheye), Boolean.parseBoolean(getString(R.string.pref_default_fisheye))));
1144  RTABMapLib.setAppendMode(nativeApplication, sharedPref.getBoolean(getString(R.string.pref_key_append), Boolean.parseBoolean(getString(R.string.pref_default_append))));
1145  RTABMapLib.setMappingParameter(nativeApplication, "Rtabmap/DetectionRate", mUpdateRate);
1146  RTABMapLib.setMappingParameter(nativeApplication, "Rtabmap/TimeThr", mTimeThr);
1147  RTABMapLib.setMappingParameter(nativeApplication, "Rtabmap/MemoryThr", memThr);
1148  RTABMapLib.setMappingParameter(nativeApplication, "RGBD/LinearSpeedUpdate", maxSpeed);
1149  RTABMapLib.setMappingParameter(nativeApplication, "RGBD/AngularSpeedUpdate", String.valueOf(Float.parseFloat(maxSpeed)/2.0f));
1150  RTABMapLib.setMappingParameter(nativeApplication, "Mem/RehearsalSimilarity", simThr);
1151  RTABMapLib.setMappingParameter(nativeApplication, "Kp/MaxFeatures", mMaxFeatures);
1152  RTABMapLib.setMappingParameter(nativeApplication, "Vis/MaxFeatures", maxFeaturesLoop);
1153  RTABMapLib.setMappingParameter(nativeApplication, "Vis/MinInliers", mMinInliers);
1154  RTABMapLib.setMappingParameter(nativeApplication, "Rtabmap/LoopThr", mLoopThr);
1155  RTABMapLib.setMappingParameter(nativeApplication, "RGBD/OptimizeMaxError", mMaxOptimizationError);
1156  RTABMapLib.setMappingParameter(nativeApplication, "Kp/DetectorStrategy", featureType);
1157  RTABMapLib.setMappingParameter(nativeApplication, "Vis/FeatureType", featureType);
1158  RTABMapLib.setMappingParameter(nativeApplication, "Mem/NotLinkedNodesKept", String.valueOf(keepAllDb));
1159  RTABMapLib.setMappingParameter(nativeApplication, "RGBD/OptimizeFromGraphEnd", String.valueOf(optimizeFromGraphEnd));
1160  RTABMapLib.setMappingParameter(nativeApplication, "Optimizer/Strategy", optimizer);
1161  if(Integer.parseInt(markerDetection) == -1)
1162  {
1163  RTABMapLib.setMappingParameter(nativeApplication, "RGBD/MarkerDetection", "false");
1164  }
1165  else
1166  {
1167  RTABMapLib.setMappingParameter(nativeApplication, "RGBD/MarkerDetection", "true");
1168  RTABMapLib.setMappingParameter(nativeApplication, "Marker/Dictionary", markerDetection);
1169  RTABMapLib.setMappingParameter(nativeApplication, "Marker/CornerRefinementMethod", Integer.parseInt(markerDetection) > 16?"3":"0");
1170  }
1171  RTABMapLib.setMappingParameter(nativeApplication, "Marker/MaxDepthError", markerDetectionDepthError);
1172 
1173  Log.i(TAG, "set exporting parameters...");
1174  RTABMapLib.setCloudDensityLevel(nativeApplication, Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_density), getString(R.string.pref_default_density))));
1175  RTABMapLib.setMaxCloudDepth(nativeApplication, Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_depth), getString(R.string.pref_default_depth))));
1176  RTABMapLib.setMinCloudDepth(nativeApplication, Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_min_depth), getString(R.string.pref_default_min_depth))));
1177  RTABMapLib.setPointSize(nativeApplication, Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_point_size), getString(R.string.pref_default_point_size))));
1178  RTABMapLib.setMeshAngleTolerance(nativeApplication, Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_angle), getString(R.string.pref_default_angle))));
1179  RTABMapLib.setMeshTriangleSize(nativeApplication, Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_triangle), getString(R.string.pref_default_triangle))));
1180  float bgColor = Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_background_color), getString(R.string.pref_default_background_color)));
1181  RTABMapLib.setBackgroundColor(nativeApplication, bgColor);
1182  mRenderer.setTextColor(bgColor>=0.6f?0.0f:1.0f);
1183 
1184  Log.i(TAG, "set rendering parameters...");
1185  RTABMapLib.setClusterRatio(nativeApplication, Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_cluster_ratio), getString(R.string.pref_default_cluster_ratio))));
1186  RTABMapLib.setMaxGainRadius(nativeApplication, Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_gain_max_radius), getString(R.string.pref_default_gain_max_radius))));
1187  RTABMapLib.setRenderingTextureDecimation(nativeApplication, Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_rendering_texture_decimation), getString(R.string.pref_default_rendering_texture_decimation))));
1188 
1189  if(mItemRenderingPointCloud != null)
1190  {
1191  int renderingType = sharedPref.getInt(getString(R.string.pref_key_rendering), Integer.parseInt(getString(R.string.pref_default_rendering)));
1192  if(renderingType == 0)
1193  {
1194  mItemRenderingPointCloud.setChecked(true);
1195  }
1196  else if(renderingType == 1)
1197  {
1198  mItemRenderingMesh.setChecked(true);
1199  }
1200  else
1201  {
1202  mItemRenderingTextureMesh.setChecked(true);
1203  }
1205  nativeApplication,
1206  mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
1207  mItemRenderingTextureMesh.isChecked());
1208 
1209  mButtonBackfaceShown.setVisibility(mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked()?View.VISIBLE:View.INVISIBLE);
1210  }
1211  }
1212  catch(Exception e)
1213  {
1214  Log.e(TAG, "Error parsing preferences: " + e.getMessage());
1215  mToast.makeText(this, String.format("Error parsing preferences: "+e.getMessage()), mToast.LENGTH_LONG).show();
1216  }
1217  }
1218 
1219  @Override
1220  protected void onResume() {
1221  super.onResume();
1222 
1224 
1227 
1228  if(mState == State.STATE_MAPPING || mState == State.STATE_CAMERA)
1229  {
1230  String message = new String();
1231  if(mOnPause)
1232  {
1233  if(System.currentTimeMillis()/1000 - mOnPauseStamp < 1)
1234  {
1235  message = String.format("RTAB-Map has been interrupted by another application, Camera should be re-initialized! Set your phone/tablet in Airplane mode if this happens often.");
1236  }
1237  else
1238  {
1239  message = String.format("Hold Tight! Initializing Camera Service...");
1240  }
1241  mToast.makeText(this, "Mapping is paused!", mToast.LENGTH_LONG).show();
1242  }
1243  else
1244  {
1245  message = String.format("Hold Tight! Initializing Camera Service...\nTip: If the camera is still drifting just after the mapping has started, do \"Reset\".");
1246  }
1247  startCamera(message);
1248  }
1249  mOnPause = false;
1250 
1251  if(!DISABLE_LOG) Log.i(TAG, String.format("onResume()"));
1252  mGLView.onResume();
1253  }
1254 
1255  @Override
1256  public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {
1257 
1258  switch (requestCode) {
1260  // If request is cancelled, the result arrays are empty.
1261  if (results.length > 0 && results[0] == PackageManager.PERMISSION_GRANTED) {
1262  // permission was granted, yay! Do the
1263  // contacts-related task you need to do.
1264  postCreate();
1265  } else {
1266  // permission denied, boo! Disable the
1267  // functionality that depends on this permission.
1268  Toast.makeText(this, "Storage read/write permissions are needed to run this application", Toast.LENGTH_LONG).show();
1269  if (!PermissionHelper.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
1270  PermissionHelper.launchPermissionSettings(this); // Permission denied with checking "Do not ask again".
1271  }
1272  finish();
1273  }
1274  return;
1275  }
1277  if (results.length > 0 && results[0] == PackageManager.PERMISSION_GRANTED) {
1278  // permission was granted, yay!
1279  startCamera(String.format("Hold Tight! Initializing Camera Service...\n"
1280  + "Tip: If the camera is still drifting just after the mapping has started, do \"Reset\"."));
1281  } else {
1282  Toast.makeText(this, "Camera permission is needed for scanning and motion tracking.", Toast.LENGTH_LONG).show();
1283  if (!PermissionHelper.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
1284  PermissionHelper.launchPermissionSettings(this); // Permission denied with checking "Do not ask again".
1285  }
1286  }
1287  return;
1288  }
1290  if (results.length > 0 && results[0] == PackageManager.PERMISSION_GRANTED) {
1291  // permission was granted, yay!
1292  } else {
1293  Toast.makeText(this, "Internet permission is needed to share scans.", Toast.LENGTH_LONG).show();
1294  if (!PermissionHelper.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
1295  PermissionHelper.launchPermissionSettings(this); // Permission denied with checking "Do not ask again".
1296  }
1297  }
1298  return;
1299  }
1300  }
1301  }
1302 
1303  private void startCamera(final String message)
1304  {
1305  // If we did not yet obtain runtime permission on Android M and above,
1306  // now is a good time to ask the user for it.
1307  if (!PermissionHelper.hasPermission(this, Manifest.permission.CAMERA)) {
1308  PermissionHelper.requestPermission(this, Manifest.permission.CAMERA);
1309  return;
1310  }
1311 
1312  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
1313  String cameraDriverStr = sharedPref.getString(getString(R.string.pref_key_camera_driver), getString(R.string.pref_default_camera_driver));
1314  final boolean depthFromMotion = sharedPref.getBoolean(getString(R.string.pref_key_depth_from_motion), Boolean.parseBoolean(getString(R.string.pref_default_depth_from_motion)));
1315  mCameraDriver = Integer.parseInt(cameraDriverStr);
1316 
1317  Log.i(TAG, String.format("startCamera() driver=%d", mCameraDriver));
1318  if(mCameraDriver == 0) // Tango
1319  {
1320  // Check if the Tango Core is out dated.
1321  if (!CheckTangoCoreVersion(MIN_TANGO_CORE_VERSION)) {
1322  mToast.makeText(this, "Current camera driver selected is Tango, but Tango is not available or outdated on this phone. Abort scanning...", mToast.LENGTH_LONG).show();
1323  return;
1324  }
1325 
1326  if (!Tango.hasPermission(this, Tango.PERMISSIONTYPE_MOTION_TRACKING)) {
1327  if(!DISABLE_LOG) Log.i(TAG, String.format("Asking for motion tracking permission"));
1328  startActivityForResult(
1329  Tango.getRequestPermissionIntent(Tango.PERMISSIONTYPE_MOTION_TRACKING),
1330  Tango.TANGO_INTENT_ACTIVITYCODE);
1331  return;
1332  }
1333  else
1334  {
1335  mCameraServiceConnectionUsed = TangoInitializationHelper.bindTangoService(getActivity(), mCameraServiceConnection);
1336  if(mCameraServiceConnectionUsed)
1337  {
1338  mProgressDialog.setTitle("");
1339  mProgressDialog.setMessage(message);
1340  mProgressDialog.show();
1341  resetNoTouchTimer(true);
1342 
1343  // When the service has been connected, RTABMapLib.onCameraServiceConnected(service) should be called, see above
1344  }
1345  else
1346  {
1347  mToast.makeText(this, "Current camera driver selected is Tango, but Tango service binding failed. Abort scanning...", mToast.LENGTH_LONG).show();
1348  }
1349  }
1350  }
1351  else if(mCameraDriver == 1 || mCameraDriver == 2 || mCameraDriver == 3)
1352  {
1353  if((mCameraDriver == 1 || mCameraDriver == 3) && !mIsARCoreAvailable)
1354  {
1355  mToast.makeText(this, "ARCore not supported on this phone! Cannot start a new scan.", mToast.LENGTH_LONG).show();
1356  return;
1357  }
1358  if(mCameraDriver == 2 && !mIsAREngineAvailable)
1359  {
1360  mToast.makeText(this, "AREngine not supported on this phone! Cannot start a new scan.", mToast.LENGTH_LONG).show();
1361  return;
1362  }
1363 
1364  // only point cloud supported
1365  if(mCameraDriver==1 && !mItemRenderingPointCloud.isChecked())
1366  {
1367  mItemRenderingPointCloud.setChecked(true);
1368  }
1369  Thread bindThread = new Thread(new Runnable() {
1370  public void run() {
1371 
1372  if(mCameraDriver==1 && !depthFromMotion)
1373  {
1375  nativeApplication,
1376  mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
1377  mItemRenderingTextureMesh.isChecked());
1378  }
1379  final boolean cameraStartSucess = RTABMapLib.startCamera(nativeApplication, null, getApplicationContext(), getActivity(), mCameraDriver);
1380  runOnUiThread(new Runnable() {
1381  public void run() {
1382  boolean localSuccess = cameraStartSucess;
1383  if(cameraStartSucess && mCameraDriver == 3)
1384  {
1385  synchronized (this) {
1386  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
1387  String arCoreLocalizationFiltering = sharedPref.getString(getString(R.string.pref_key_arcore_localization_filtering_speed), getString(R.string.pref_default_arcore_localization_filtering_speed));
1388  mArCoreCamera = new ARCoreSharedCamera(getActivity(), Float.parseFloat(arCoreLocalizationFiltering));
1389  mArCoreCamera.setToast(mToast);
1390  mProgressDialog.setTitle("");
1391  mProgressDialog.setMessage(message);
1392  mProgressDialog.show();
1393  if(!mArCoreCamera.openCamera())
1394  {
1395  mToast.makeText(getActivity(), "Current camera driver selected is ARCore Java, but initialization failed. Abort scanning...", mToast.LENGTH_LONG).show();
1396  mArCoreCamera = null;
1397  localSuccess = false;
1398  }
1399  else
1400  {
1401  mRenderer.setCamera(mArCoreCamera);
1402  if((mState==State.STATE_IDLE || mState==State.STATE_WELCOME) && !mArCoreCamera.isDepthSupported())
1403  {
1404  mItemRenderingPointCloud.setChecked(true);
1405  RTABMapLib.setMeshRendering(nativeApplication, false, false);
1406  mToast.makeText(getApplicationContext(), "Depth camera not found, only poses and RGB images can be recorded.", mToast.LENGTH_LONG).show();
1407  }
1408  }
1409  }
1410  }
1411 
1412  mProgressDialog.dismiss();
1413  if(!localSuccess)
1414  {
1415  mToast.makeText(getApplicationContext(),
1416  String.format("Failed to intialize Camera!"), mToast.LENGTH_LONG).show();
1417  }
1418  else
1419  {
1420  if((mState==State.STATE_IDLE || mState==State.STATE_WELCOME) && mCameraDriver == 1 && !depthFromMotion)
1421  {
1422  mToast.makeText(getApplicationContext(), "Currently ARCore NDK driver doesn't support depth, only poses, RGB images and 3d features can be recorded.", mToast.LENGTH_LONG).show();
1423  }
1425  if(mState==State.STATE_VISUALIZING_CAMERA && mItemLocalizationMode.isChecked())
1426  {
1427  RTABMapLib.setPausedMapping(nativeApplication, false);
1428  }
1429  }
1430  }
1431  });
1432  }
1433  });
1434  bindThread.start();
1435  }
1436  else
1437  {
1438  mToast.makeText(this, "Supported camera driver not found! Cannot start a new scan.", mToast.LENGTH_LONG).show();
1439  }
1440  }
1441 
1442  private void setCamera(int type)
1443  {
1444  if(!DISABLE_LOG) Log.i(TAG, String.format("called setCamera(type=%d);", type));
1445 
1446  // for convenience, for a refresh of the memory used
1447  long freeMemory = getFreeMemory();
1448  mStatusTexts[1] = getString(R.string.memory)+String.valueOf(mFreeMemoryOnStart>freeMemory?mFreeMemoryOnStart-freeMemory:0);
1449  mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(freeMemory);
1451 
1452  RTABMapLib.setCamera(nativeApplication, type);
1453  mButtonCameraView.setSelection(type, true);
1454  mSeekBarOrthoCut.setVisibility(type!=3?View.INVISIBLE:View.VISIBLE);
1455  mSeekBarGrid.setVisibility(mSeekBarGrid.isEnabled() && type==3?View.VISIBLE:View.INVISIBLE);
1456  if(type==3)
1457  {
1458  mSeekBarOrthoCut.setMax(120);
1459  mSeekBarOrthoCut.setProgress(80);
1460  }
1461  }
1462 
1463  @Override
1464  public void onClick(View v) {
1465  // Handle button clicks.
1466  switch (v.getId()) {
1467  case R.id.gl_surface_view:
1468  break;
1469  case R.id.start_button:
1470  startMapping();
1471  break;
1472  case R.id.stop_button:
1473  if(mState == State.STATE_VISUALIZING_CAMERA)
1474  {
1475  stopCamera();
1476  }
1477  else
1478  {
1479  stopMapping();
1480  }
1481  break;
1482  case R.id.light_button:
1483  RTABMapLib.setLighting(nativeApplication, mButtonLighting.isChecked());
1484  break;
1485  case R.id.backface_button:
1486  RTABMapLib.setBackfaceCulling(nativeApplication, !mButtonBackfaceShown.isChecked());
1487  break;
1488  case R.id.wireframe_button:
1489  RTABMapLib.setWireframe(nativeApplication, mButtonWireframe.isChecked());
1490  break;
1491  case R.id.close_visualization_button:
1493  RTABMapLib.postExportation(nativeApplication, false);
1494  break;
1495  case R.id.button_saveOnDevice:
1496  saveOnDevice();
1497  break;
1498  case R.id.button_shareToSketchfab:
1499  shareToSketchfab();
1500  break;
1501  case R.id.button_library:
1502  openDatabase();
1503  break;
1504  case R.id.button_new_scan:
1505  newScan();
1506  break;
1507  default:
1508  return;
1509  }
1511  }
1512 
1513  private void closeVisualization()
1514  {
1515  if(mSavedRenderingType==0)
1516  {
1517  mItemRenderingPointCloud.setChecked(true);
1518  }
1519  else if(mSavedRenderingType==1)
1520  {
1521  mItemRenderingMesh.setChecked(true);
1522  }
1523  else
1524  {
1525  mItemRenderingTextureMesh.setChecked(true);
1526  }
1528  }
1529 
1530  public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
1531  setCamera(pos);
1533  }
1534 
1535  public void onNothingSelected(AdapterView<?> parent) {
1537  }
1538 
1539  private void setAndroidOrientation() {
1540  Display display = getWindowManager().getDefaultDisplay();
1541  Camera.CameraInfo colorCameraInfo = new Camera.CameraInfo();
1542  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
1543  boolean fisheye = sharedPref.getBoolean(getString(R.string.pref_key_fisheye), Boolean.parseBoolean(getString(R.string.pref_default_fisheye)));
1544  Camera.getCameraInfo(fisheye?1:0, colorCameraInfo);
1545  RTABMapLib.setScreenRotation(nativeApplication, display.getRotation(), colorCameraInfo.orientation);
1546  }
1547 
1548  class DoubleTapGestureDetector extends GestureDetector.SimpleOnGestureListener {
1549 
1550  @Override
1551  public boolean onDoubleTap(MotionEvent event) {
1552  if(!DISABLE_LOG) Log.i(TAG, "onDoubleTap");
1553  float normalizedX = event.getX(0) / mScreenSize.x;
1554  float normalizedY = event.getY(0) / mScreenSize.y;
1555  RTABMapLib.onTouchEvent(nativeApplication, 3, event.getActionMasked(), normalizedX, normalizedY, 0.0f, 0.0f);
1556  return true;
1557  }
1558  @Override
1559  public boolean onSingleTapConfirmed(MotionEvent event) {
1560  if(!DISABLE_LOG) Log.i(TAG, "onSingleTapConfirmed");
1561  if(mHudVisible)
1562  {
1563  notouchHandler.removeCallbacks(notouchCallback);
1564  notouchHandler.postDelayed(notouchCallback, 0);
1565  }
1566  else
1567  {
1568  resetNoTouchTimer(true);
1569  }
1570  return true;
1571  }
1572  }
1573 
1574  @Override
1575  public boolean onCreateOptionsMenu(Menu menu) {
1576  if(!DISABLE_LOG) Log.i(TAG, "called onCreateOptionsMenu;");
1577 
1578  MenuInflater inflater = getMenuInflater();
1579  inflater.inflate(R.menu.optionmenu, menu);
1580 
1581  getActionBar().setDisplayShowHomeEnabled(true);
1582  getActionBar().setIcon(R.drawable.ic_launcher);
1583 
1584  mItemSave = menu.findItem(R.id.save);
1585  mItemOpen = menu.findItem(R.id.open);
1586  mItemNewScan = menu.findItem(R.id.new_scan);
1587  mItemPostProcessing = menu.findItem(R.id.post_processing);
1588  mItemExport = menu.findItem(R.id.export);
1589  mItemSettings = menu.findItem(R.id.settings);
1590  mItemModes = menu.findItem(R.id.modes);
1591  mItemResume = menu.findItem(R.id.resume);
1592  mItemLocalizationMode = menu.findItem(R.id.localization_mode);
1593  mItemTrajectoryMode = menu.findItem(R.id.trajectory_mode);
1594  mItemRenderingPointCloud = menu.findItem(R.id.point_cloud);
1595  mItemRenderingMesh = menu.findItem(R.id.mesh);
1596  mItemRenderingTextureMesh = menu.findItem(R.id.texture_mesh);
1597  mItemDataRecorderMode = menu.findItem(R.id.data_recorder);
1598  mItemStatusVisibility = menu.findItem(R.id.status);
1599  mItemDebugVisibility = menu.findItem(R.id.debug);
1600  mItemSave.setEnabled(false);
1601  mItemNewScan.setEnabled(true);
1602  mItemExport.setEnabled(false);
1603  mItemOpen.setEnabled(true);
1604  mItemPostProcessing.setEnabled(false);
1605  mItemDataRecorderMode.setEnabled(false);
1606 
1607  try
1608  {
1609  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
1610  int renderingType = sharedPref.getInt(getString(R.string.pref_key_rendering), Integer.parseInt(getString(R.string.pref_default_rendering)));
1611  if(renderingType == 0)
1612  {
1613  mItemRenderingPointCloud.setChecked(true);
1614  }
1615  else if(renderingType == 1)
1616  {
1617  mItemRenderingMesh.setChecked(true);
1618  }
1619  else
1620  {
1621  mItemRenderingTextureMesh.setChecked(true);
1622  }
1624  nativeApplication,
1625  mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
1626  mItemRenderingTextureMesh.isChecked());
1627 
1628  if(mButtonBackfaceShown != null)
1629  {
1630  mButtonBackfaceShown.setVisibility(mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked()?View.VISIBLE:View.INVISIBLE);
1631  }
1632  }
1633  catch(Exception e)
1634  {
1635  Log.e(TAG, "Error parsing rendering preferences: " + e.getMessage());
1636  mToast.makeText(this, String.format("Error parsing rendering preferences: "+e.getMessage()), mToast.LENGTH_LONG).show();
1637  }
1638 
1639  updateState(mState);
1640 
1641  return true;
1642  }
1643 
1644  private long getFreeMemory()
1645  {
1646  MemoryInfo mi = new MemoryInfo();
1647  ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
1648  activityManager.getMemoryInfo(mi);
1649  return mi.availMem / 0x100000L; // MB
1650  }
1651 
1652  private void updateStatusTexts()
1653  {
1654  if(mItemStatusVisibility != null && mItemDebugVisibility != null)
1655  {
1656  if(mState == State.STATE_WELCOME)
1657  {
1658  mRenderer.updateTexts(null);
1659  }
1660  else if((mItemStatusVisibility.isChecked() || mState == State.STATE_VISUALIZING_WHILE_LOADING) && mItemDebugVisibility.isChecked())
1661  {
1662  mRenderer.updateTexts(mStatusTexts);
1663  }
1664  else if((mItemStatusVisibility.isChecked() || mState == State.STATE_VISUALIZING_WHILE_LOADING))
1665  {
1666  mRenderer.updateTexts(Arrays.copyOfRange(mStatusTexts, 0, STATUS_TEXTS_POSE_INDEX-1));
1667  }
1668  else if(mItemDebugVisibility.isChecked())
1669  {
1670  mRenderer.updateTexts(Arrays.copyOfRange(mStatusTexts, STATUS_TEXTS_POSE_INDEX-1, mStatusTexts.length));
1671  }
1672  else
1673  {
1674  mRenderer.updateTexts(null);
1675  }
1676  }
1677  if(mGLView.getRenderMode() == GLSurfaceView.RENDERMODE_WHEN_DIRTY)
1678  {
1679  mGLView.requestRender();
1680  }
1681  }
1682 
1683  private void updateStatsUI(
1684  int loopClosureId,
1685  int inliers,
1686  int matches,
1687  int rejected,
1688  float optimizationMaxError,
1689  float optimizationMaxErrorRatio,
1690  boolean fastMovement,
1691  int landmarkDetected,
1692  String[] statusTexts)
1693  {
1694  for(int i = 1; i<mStatusTexts.length && i<statusTexts.length; ++i)
1695  {
1696  mStatusTexts[i] = statusTexts[i];
1697  }
1698  if(mState == State.STATE_MAPPING)
1699  {
1700  String updateValue = mUpdateRate.compareTo("0")==0?"Max":mUpdateRate;
1701  mStatusTexts[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));
1702  }
1703 
1705 
1706  if(mButtonStart!=null && mState == State.STATE_MAPPING)
1707  {
1708  if(mButtonStart.getVisibility() != View.VISIBLE)
1709  {
1710  //check if we are low in memory
1711  long memoryFree = getFreeMemory();
1712  long memoryUsed = mFreeMemoryOnStart>memoryFree?mFreeMemoryOnStart-memoryFree:0;
1713 
1714  if(memoryFree < 400)
1715  {
1716  stopMapping();
1717 
1718  if(mMemoryWarningDialog!=null)
1719  {
1720  mMemoryWarningDialog.dismiss();
1721  mMemoryWarningDialog = null;
1722  }
1723 
1724  mMemoryWarningDialog = new AlertDialog.Builder(getActivity())
1725  .setTitle("Memory is full!")
1726  .setCancelable(false)
1727  .setMessage(String.format("Scanning has been paused because free memory is too "
1728  + "low (%d MB). You should be able to save the database but some post-processing and exporting options may fail. "
1729  + "\n\nNote that for large environments, you can save multiple databases and "
1730  + "merge them with RTAB-Map Desktop version.", memoryUsed))
1731  .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
1732  public void onClick(DialogInterface dialog, int which) {
1733  mMemoryWarningDialog = null;
1734  }
1735  })
1736  .setNeutralButton("Save", new DialogInterface.OnClickListener() {
1737  public void onClick(DialogInterface dialog, int which) {
1738  saveOnDevice();
1739  mMemoryWarningDialog = null;
1740  }
1741  })
1742  .create();
1743  mMemoryWarningDialog.setCanceledOnTouchOutside(false);
1744  mMemoryWarningDialog.show();
1745  }
1746  else if(mMemoryWarningDialog == null && memoryUsed*3 > memoryFree && (mItemDataRecorderMode == null || !mItemDataRecorderMode.isChecked()))
1747  {
1748  mMemoryWarningDialog = new AlertDialog.Builder(getActivity())
1749  .setTitle("Warning: Memory is almost full!")
1750  .setCancelable(false)
1751  .setMessage(String.format("Free memory (%d MB) should be at least 3 times the "
1752  + "memory used (%d MB) so that some post-processing and exporting options "
1753  + "have enough memory to work correctly. If you just want to save the database "
1754  + "after scanning, you can continue until the next warning.\n\n"
1755  + "Note that showing only point clouds reduces memory needed for rendering.", memoryFree, memoryUsed))
1756  .setPositiveButton("Pause", new DialogInterface.OnClickListener() {
1757  public void onClick(DialogInterface dialog, int which) {
1758  stopMapping();
1759  }
1760  })
1761  .setNeutralButton("Continue", new DialogInterface.OnClickListener() {
1762  public void onClick(DialogInterface dialog, int which) {
1763  }
1764  })
1765  .create();
1766  mMemoryWarningDialog.setCanceledOnTouchOutside(false);
1767  mMemoryWarningDialog.show();
1768  }
1769  }
1770  }
1771 
1772 
1773  if(mState == State.STATE_MAPPING || mState == State.STATE_VISUALIZING_CAMERA)
1774  {
1775  long currentTime = System.currentTimeMillis()/1000;
1776  if(loopClosureId > 0)
1777  {
1778  mToast.setText(String.format("Loop closure detected! (%d/%d inliers)", inliers, matches));
1779  mToast.show();
1780  }
1781  else if(landmarkDetected != 0)
1782  {
1783  mToast.setText(String.format("Marker %d detected!", landmarkDetected));
1784  mToast.show();
1785  }
1786  else if(rejected > 0)
1787  {
1788  if(inliers >= Integer.parseInt(mMinInliers))
1789  {
1790  if(optimizationMaxError > 0.0f)
1791  {
1792  mToast.setText(String.format("Loop closure rejected, too high graph optimization error (%.3fm: ratio=%.3f < factor=%sx).", optimizationMaxError, optimizationMaxErrorRatio, mMaxOptimizationError));
1793  }
1794  else
1795  {
1796  mToast.setText(String.format("Loop closure rejected, graph optimization failed! You may try a different Graph Optimizer (see Mapping options)."));
1797  }
1798  }
1799  else
1800  {
1801  mToast.setText(String.format("Loop closure rejected, not enough inliers (%d/%d < %s).", inliers, matches, mMinInliers));
1802  }
1803  mToast.show();
1804  }
1805  else if(fastMovement)
1806  {
1807  if(currentTime - mLastFastMovementNotificationStamp > 3)
1808  {
1809  mToast.setText("Move slower... blurry images are not added to map (\"Settings->Mapping...->Maximum Motion Speed\" is enabled).");
1810  mToast.show();
1811  }
1812  }
1813 
1814  if(!fastMovement)
1815  {
1816  mLastFastMovementNotificationStamp = currentTime;
1817  }
1818  }
1819  }
1820 
1821  // called from jni
1822  public void updateStatsCallback(
1823  final int nodes,
1824  final int words,
1825  final int points,
1826  final int polygons,
1827  final float updateTime,
1828  final int loopClosureId,
1829  final int highestHypId,
1830  final int databaseMemoryUsed,
1831  final int inliers,
1832  final int matches,
1833  final int featuresExtracted,
1834  final float hypothesis,
1835  final int nodesDrawn,
1836  final float fps,
1837  final int rejected,
1838  final float rehearsalValue,
1839  final float optimizationMaxError,
1840  final float optimizationMaxErrorRatio,
1841  final float distanceTravelled,
1842  final int fastMovement,
1843  final int landmarkDetected,
1844  final float x,
1845  final float y,
1846  final float z,
1847  final float roll,
1848  final float pitch,
1849  final float yaw)
1850  {
1851  if(!DISABLE_LOG) Log.i(TAG, String.format("updateStatsCallback()"));
1852 
1853  final String[] statusTexts = new String[STATUS_TEXTS_SIZE];
1854 
1855  long memoryFree = getFreeMemory();
1856  statusTexts[1] = getString(R.string.memory)+(mFreeMemoryOnStart>memoryFree?mFreeMemoryOnStart-memoryFree:0);
1857  statusTexts[2] = getString(R.string.free_memory)+memoryFree;
1858 
1859  if(loopClosureId > 0)
1860  {
1862  }
1863 
1864  mMapNodes = nodes;
1865 
1866  if(mGPSSaved)
1867  {
1868  if(mLastKnownLocation != null)
1869  {
1870  long millisec = System.currentTimeMillis() - mLastKnownLocation.getTime();
1871  if(millisec > 1000)
1872  {
1873  statusTexts[3] = getString(R.string.gps)+String.format("[too old, %d ms]", millisec);
1874  }
1875  else
1876  {
1877  statusTexts[3] = getString(R.string.gps)+
1878  String.format("%.2f %.2f %.2fm %.0fdeg %.0fm",
1879  mLastKnownLocation.getLongitude(),
1880  mLastKnownLocation.getLatitude(),
1881  mLastKnownLocation.getAltitude(),
1882  mCompassDeg,
1883  mLastKnownLocation.getAccuracy());
1884  }
1885  }
1886  else
1887  {
1888  statusTexts[3] = getString(R.string.gps)+String.format("[not yet available, %.0fdeg]", mCompassDeg);
1889  }
1890  }
1891  if(mEnvSensorsSaved)
1892  {
1893  statusTexts[4] = getString(R.string.env_sensors);
1894 
1895  if(mLastEnvSensorsSet[0])
1896  {
1897  statusTexts[4] += String.format(" %.0f dbm", mLastEnvSensors[0]);
1898  mLastEnvSensorsSet[0] = false;
1899  }
1900  if(mLastEnvSensorsSet[1])
1901  {
1902  statusTexts[4] += String.format(" %.1f %cC", mLastEnvSensors[1], '\u00B0');
1903  mLastEnvSensorsSet[1] = false;
1904  }
1905  if(mLastEnvSensorsSet[2])
1906  {
1907  statusTexts[4] += String.format(" %.1f hPa", mLastEnvSensors[2]);
1908  mLastEnvSensorsSet[2] = false;
1909  }
1910  if(mLastEnvSensorsSet[3])
1911  {
1912  statusTexts[4] += String.format(" %.0f lx", mLastEnvSensors[3]);
1913  mLastEnvSensorsSet[3] = false;
1914  }
1915  if(mLastEnvSensorsSet[4])
1916  {
1917  statusTexts[4] += String.format(" %.0f %%", mLastEnvSensors[4]);
1918  mLastEnvSensorsSet[4] = false;
1919  }
1920  }
1921 
1922  String formattedDate = new SimpleDateFormat("HH:mm:ss.SSS").format(new Date());
1923  statusTexts[5] = getString(R.string.time)+formattedDate;
1924 
1925  int index = STATUS_TEXTS_POSE_INDEX;
1926  statusTexts[index++] = getString(R.string.nodes)+nodes+" (" + nodesDrawn + " shown)";
1927  statusTexts[index++] = getString(R.string.words)+words;
1928  statusTexts[index++] = getString(R.string.database_size)+databaseMemoryUsed;
1929  statusTexts[index++] = getString(R.string.points)+points;
1930  statusTexts[index++] = getString(R.string.polygons)+polygons;
1931  statusTexts[index++] = getString(R.string.update_time)+(int)(updateTime) + " / " + (mTimeThr.compareTo("0")==0?"No Limit":mTimeThr);
1932  statusTexts[index++] = getString(R.string.features)+featuresExtracted +" / " + (mMaxFeatures.compareTo("0")==0?"No Limit":mMaxFeatures.compareTo("-1")==0?"Disabled":mMaxFeatures);
1933  statusTexts[index++] = getString(R.string.rehearsal)+(int)(rehearsalValue*100.0f);
1934  statusTexts[index++] = getString(R.string.total_loop)+mTotalLoopClosures;
1935  statusTexts[index++] = getString(R.string.inliers)+inliers;
1936  statusTexts[index++] = getString(R.string.hypothesis)+(int)(hypothesis*100.0f) +" / " + (int)(Float.parseFloat(mLoopThr)*100.0f) + " (" + (loopClosureId>0?loopClosureId:highestHypId)+")";
1937  statusTexts[index++] = getString(R.string.fps)+(int)fps+" Hz";
1938  statusTexts[index++] = getString(R.string.distance)+(int)distanceTravelled+" m";
1939  statusTexts[index++] = String.format("Pose (x,y,z): %.2f %.2f %.2f", x,y,z);
1940 
1941  runOnUiThread(new Runnable() {
1942  public void run() {
1943  updateStatsUI(loopClosureId, inliers, matches, rejected, optimizationMaxError, optimizationMaxErrorRatio, fastMovement!=0, landmarkDetected, statusTexts);
1944  }
1945  });
1946  }
1947 
1948  private void rtabmapInitEventUI(
1949  int status,
1950  String msg)
1951  {
1952  if(!DISABLE_LOG) Log.i(TAG, String.format("rtabmapInitEventsUI() status=%d msg=%s", status, msg));
1953 
1954  int optimizedMeshDetected = 0;
1955 
1956  if(msg.equals("Loading optimized cloud...done!"))
1957  {
1958  optimizedMeshDetected = 1;
1959  }
1960  else if(msg.equals("Loading optimized mesh...done!"))
1961  {
1962  optimizedMeshDetected = 2;
1963  }
1964  else if(msg.equals("Loading optimized texture mesh...done!"))
1965  {
1966  optimizedMeshDetected = 3;
1967  }
1968  if(optimizedMeshDetected > 0)
1969  {
1971  mSavedRenderingType = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
1972  if(optimizedMeshDetected==1)
1973  {
1974  mItemRenderingPointCloud.setChecked(true);
1975  }
1976  else if(optimizedMeshDetected==2)
1977  {
1978  mItemRenderingMesh.setChecked(true);
1979  }
1980  else // isOBJ
1981  {
1982  mItemRenderingTextureMesh.setChecked(true);
1983  }
1984 
1986  if(mButtonCameraView.getSelectedItemPosition() == 0)
1987  {
1988  setCamera(2);
1989  }
1990  mToast.makeText(getActivity(), String.format("Optimized mesh detected in the database, it is shown while the database is loading..."), mToast.LENGTH_LONG).show();
1991  mProgressDialog.dismiss();
1992  }
1993 
1994  if(mButtonStart!=null)
1995  {
1996  mStatusTexts[0] = getString(R.string.status)+(status == 1 && msg.isEmpty()?mState == State.STATE_CAMERA?"Camera Preview":"Idle":msg);
1997 
1998  long freeMemory = getFreeMemory();
1999  mStatusTexts[1] = getString(R.string.memory)+String.valueOf(mFreeMemoryOnStart>freeMemory?mFreeMemoryOnStart-freeMemory:0);
2000  mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(freeMemory);
2002  }
2003  }
2004 
2005  //called from jni
2007  final int status,
2008  final String msg)
2009  {
2010  if(!DISABLE_LOG) Log.i(TAG, String.format("rtabmapInitEventCallback()"));
2011 
2012  runOnUiThread(new Runnable() {
2013  public void run() {
2014  rtabmapInitEventUI(status, msg);
2015  }
2016  });
2017  }
2018 
2019  private void updateProgressionUI(
2020  int count,
2021  int max)
2022  {
2023  if(!DISABLE_LOG) Log.i(TAG, String.format("updateProgressionUI() count=%d max=%s", count, max));
2024 
2025  mExportProgressDialog.setMax(max);
2026  mExportProgressDialog.setProgress(count);
2027  }
2028 
2029  //called from jni
2031  final int count,
2032  final int max)
2033  {
2034  if(!DISABLE_LOG) Log.i(TAG, String.format("updateProgressionCallback()"));
2035 
2036  runOnUiThread(new Runnable() {
2037  public void run() {
2038  updateProgressionUI(count, max);
2039  }
2040  });
2041  }
2042 
2043  private void cameraEventUI(
2044  int type,
2045  String key,
2046  String value)
2047  {
2059  String str = null;
2060  if(key.equals("TangoServiceException"))
2061  str = String.format("Tango service exception: %s", value);
2062  else if(key.equals("FisheyeOverExposed"))
2063  ;//str = String.format("The fisheye image is over exposed with average pixel value %s px.", value);
2064  else if(key.equals("FisheyeUnderExposed"))
2065  ;//str = String.format("The fisheye image is under exposed with average pixel value %s px.", value);
2066  else if(key.equals("ColorOverExposed"))
2067  ;//str = String.format("The color image is over exposed with average pixel value %s px.", value);
2068  else if(key.equals("ColorUnderExposed"))
2069  ;//str = String.format("The color image is under exposed with average pixel value %s px.", value);
2070  else if(key.equals("CameraTango"))
2071  str = value;
2072  else if(key.equals("TooFewFeaturesTracked"))
2073  {
2074  if(!value.equals("0"))
2075  {
2076  str = String.format("Too few features (%s) were tracked in the fisheye image. This may result in poor odometry!", value);
2077  }
2078  }
2079  else if(key.equals("TooClose"))
2080  {
2081  if(mState != State.STATE_VISUALIZING_CAMERA)
2082  {
2083  str = String.format("Too close! Tip: Scan from at least ~1 meter from surfaces.", value);
2084  }
2085  }
2086  else if(key.equals("TangoPoseEventNotReceived"))
2087  {
2088  str = String.format("No valid tango pose event received since %s sec.", value);
2089  }
2090  else
2091  {
2092  str = String.format("Unknown Camera event detected!? (type=%d, key=%s, value=%s)", type, key, value);
2093  }
2094  if(str!=null)
2095  {
2096  mToast.setText(str);
2097  mToast.show();
2098  }
2099  }
2100 
2101  //called from jni
2102  public void cameraEventCallback(
2103  final int type,
2104  final String key,
2105  final String value)
2106  {
2107  runOnUiThread(new Runnable() {
2108  public void run() {
2109  cameraEventUI(type, key, value);
2110  }
2111  });
2112  }
2113 
2114  private boolean CheckTangoCoreVersion(int minVersion) {
2115  int versionNumber = 0;
2116  String packageName = TANGO_PACKAGE_NAME;
2117  try {
2118  PackageInfo pi = getApplicationContext().getPackageManager().getPackageInfo(packageName,
2119  PackageManager.GET_META_DATA);
2120  versionNumber = pi.versionCode;
2121  } catch (NameNotFoundException e) {
2122  e.printStackTrace();
2123  }
2124  return (minVersion <= versionNumber);
2125  }
2126 
2127  private RTABMapActivity getActivity() {return this;}
2128 
2129  private void standardOptimization(final boolean withStandardMeshExport) {
2130 
2131  mExportProgressDialog.setTitle("Post-Processing");
2132  mExportProgressDialog.setMessage(String.format("Please wait while optimizing..."));
2133  mExportProgressDialog.setProgress(0);
2134  mExportProgressDialog.show();
2135 
2137  Thread workingThread = new Thread(new Runnable() {
2138  public void run() {
2139  final int loopDetected = RTABMapLib.postProcessing(nativeApplication, -1);
2140  runOnUiThread(new Runnable() {
2141  public void run() {
2143  if(mExportProgressDialog.isShowing())
2144  {
2145  mExportProgressDialog.dismiss();
2146  if(loopDetected >= 0)
2147  {
2148  mTotalLoopClosures+=loopDetected;
2149  if(withStandardMeshExport)
2150  {
2151  export(true, true, false, true, 200000);
2152  }
2153  else
2154  {
2155  mProgressDialog.setTitle("Post-Processing");
2156  mProgressDialog.setMessage(String.format("Optimization done! Increasing visual appeal..."));
2157  mProgressDialog.show();
2158 
2159  if(mOpenedDatabasePath.isEmpty())
2160  {
2161  save();
2162  }
2163  }
2164  }
2165  else if(loopDetected < 0)
2166  {
2167  mToast.makeText(getActivity(), String.format("Optimization failed!"), mToast.LENGTH_LONG).show();
2168  }
2169  }
2170  else
2171  {
2172  mProgressDialog.dismiss();
2173  mToast.makeText(getActivity(), String.format("Optimization canceled"), mToast.LENGTH_LONG).show();
2174  }
2175  }
2176  });
2177  }
2178  });
2179  workingThread.start();
2180  }
2181 
2182  private Handler notouchHandler = new Handler(){
2183  public void handleMessage(Message msg) {
2184  }
2185  };
2186 
2187  private Runnable notouchCallback = new Runnable() {
2188  @Override
2189  public void run() {
2190  if(!mProgressDialog.isShowing() && !mMenuOpened)
2191  {
2192  setNavVisibility(false);
2193  mHudVisible = false;
2194  updateState(mState);
2195  }
2196  else
2197  {
2199  }
2200  }
2201  };
2202 
2203  public void resetNoTouchTimer(){
2204  resetNoTouchTimer(false);
2205  }
2206 
2207  public void resetNoTouchTimer(boolean showHud){
2208  if(showHud)
2209  {
2210  mHudVisible = true;
2211  setNavVisibility(true);
2212  if(mItemSave != null)
2213  {
2214  updateState(mState);
2215  }
2216  }
2217 
2218  notouchHandler.removeCallbacks(notouchCallback);
2219  notouchHandler.postDelayed(notouchCallback, NOTOUCH_TIMEOUT);
2220 
2221  if(mGLView.getRenderMode() == GLSurfaceView.RENDERMODE_WHEN_DIRTY)
2222  {
2223  mGLView.requestRender();
2224  }
2225  }
2226 
2227  public void stopDisconnectTimer(){
2228  notouchHandler.removeCallbacks(notouchCallback);
2229  Timer timer = new Timer();
2230  timer.cancel();
2231  }
2232 
2233  private void updateState(State state)
2234  {
2235  if(mState == State.STATE_VISUALIZING && state == State.STATE_IDLE && mMapNodes > 100)
2236  {
2237  mToast.makeText(getActivity(), String.format("Re-adding %d online clouds, this may take some time...", mMapNodes), mToast.LENGTH_LONG).show();
2238  }
2239  mState = state;
2240  if(!DISABLE_LOG) Log.i(TAG, String.format("updateState() state=%s hud=%d", state.toString(), mHudVisible?1:0));
2241  mStatusTexts[0] = state.toString();
2242  switch(state)
2243  {
2244  case STATE_MAPPING:
2245  case STATE_CAMERA:
2246  mButtonLighting.setVisibility(View.INVISIBLE);
2247  mButtonWireframe.setVisibility(View.INVISIBLE);
2248  mButtonCloseVisualization.setVisibility(View.INVISIBLE);
2249  mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
2250  mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
2251  mButtonLibrary.setVisibility(View.INVISIBLE);
2252  mButtonNewScan.setVisibility(View.INVISIBLE);
2253  mItemSave.setEnabled(false);
2254  mItemExport.setEnabled(false);
2255  mItemOpen.setEnabled(false);
2256  mItemNewScan.setEnabled(true);
2257  mItemPostProcessing.setEnabled(false);
2258  mItemSettings.setEnabled(false);
2259  mItemResume.setEnabled(false);
2260  mItemModes.setEnabled(mState == State.STATE_CAMERA);
2261  mItemLocalizationMode.setEnabled(true);
2262  mItemTrajectoryMode.setEnabled(true);
2263  mItemDataRecorderMode.setEnabled(true);
2264  mButtonStart.setVisibility(mState == State.STATE_CAMERA?View.VISIBLE:View.INVISIBLE);
2265  mButtonStop.setVisibility(mHudVisible && mState == State.STATE_MAPPING?View.VISIBLE:View.INVISIBLE);
2266  break;
2267  case STATE_PROCESSING:
2268  mButtonLighting.setVisibility(View.INVISIBLE);
2269  mButtonWireframe.setVisibility(View.INVISIBLE);
2270  mButtonCloseVisualization.setVisibility(View.INVISIBLE);
2271  mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
2272  mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
2273  mButtonLibrary.setVisibility(View.INVISIBLE);
2274  mButtonNewScan.setVisibility(View.INVISIBLE);
2275  mItemSave.setEnabled(false);
2276  mItemExport.setEnabled(false);
2277  mItemOpen.setEnabled(false);
2278  mItemNewScan.setEnabled(false);
2279  mItemPostProcessing.setEnabled(false);
2280  mItemSettings.setEnabled(false);
2281  mItemResume.setEnabled(false);
2282  mItemModes.setEnabled(false);
2283  mButtonStart.setVisibility(View.INVISIBLE);
2284  mButtonStop.setVisibility(View.INVISIBLE);
2285  break;
2286  case STATE_VISUALIZING:
2287  case STATE_VISUALIZING_CAMERA:
2288  mButtonLighting.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
2289  mButtonWireframe.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
2290  mButtonCloseVisualization.setVisibility(mHudVisible && mState != State.STATE_VISUALIZING_CAMERA?View.VISIBLE:View.INVISIBLE);
2291  mButtonCloseVisualization.setEnabled(true);
2292  mButtonSaveOnDevice.setVisibility(mHudVisible && mState != State.STATE_VISUALIZING_CAMERA?View.VISIBLE:View.INVISIBLE);
2293  //mButtonShareOnSketchfab.setVisibility(mHudVisible && mState != State.STATE_VISUALIZING_CAMERA?View.VISIBLE:View.INVISIBLE);
2294  mButtonLibrary.setVisibility(View.INVISIBLE);
2295  mButtonNewScan.setVisibility(View.INVISIBLE);
2296  mItemSave.setEnabled(mState != State.STATE_VISUALIZING_CAMERA);
2297  mItemExport.setEnabled(!mItemDataRecorderMode.isChecked() && mState != State.STATE_VISUALIZING_CAMERA);
2298  mItemOpen.setEnabled(mState != State.STATE_VISUALIZING_CAMERA);
2299  mItemNewScan.setEnabled(mState != State.STATE_VISUALIZING_CAMERA);
2300  mItemPostProcessing.setEnabled(false);
2301  mItemSettings.setEnabled(mState != State.STATE_VISUALIZING_CAMERA);
2302  mItemResume.setEnabled(mMapNodes>0 && mState == State.STATE_VISUALIZING);
2303  mItemModes.setEnabled(mState == State.STATE_VISUALIZING);
2304  mItemLocalizationMode.setEnabled(true);
2305  mItemTrajectoryMode.setEnabled(false);
2306  mItemDataRecorderMode.setEnabled(false);
2307  mButtonStart.setVisibility(View.INVISIBLE);
2308  mButtonStop.setVisibility(mHudVisible && mState == State.STATE_VISUALIZING_CAMERA?View.VISIBLE:View.INVISIBLE);
2309  break;
2310  case STATE_VISUALIZING_WHILE_LOADING:
2311  mButtonLighting.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
2312  mButtonWireframe.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
2313  mButtonCloseVisualization.setVisibility(mHudVisible?View.VISIBLE:View.INVISIBLE);
2314  mButtonCloseVisualization.setEnabled(false);
2315  mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
2316  mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
2317  mButtonLibrary.setVisibility(View.INVISIBLE);
2318  mButtonNewScan.setVisibility(View.INVISIBLE);
2319  mItemSave.setEnabled(false);
2320  mItemExport.setEnabled(false);
2321  mItemOpen.setEnabled(false);
2322  mItemPostProcessing.setEnabled(false);
2323  mItemSettings.setEnabled(false);
2324  mItemResume.setEnabled(false);
2325  mItemModes.setEnabled(false);
2326  mButtonStart.setVisibility(View.INVISIBLE);
2327  mButtonStop.setVisibility(View.INVISIBLE);
2328  break;
2329  default: // IDLE // WELCOME
2330  mButtonLighting.setVisibility(View.INVISIBLE);
2331  mButtonWireframe.setVisibility(View.INVISIBLE);
2332  mButtonCloseVisualization.setVisibility(View.INVISIBLE);
2333  mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
2334  mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
2335  mButtonLibrary.setVisibility(mState==State.STATE_WELCOME?View.VISIBLE:View.INVISIBLE);
2336  mButtonNewScan.setVisibility(mState==State.STATE_WELCOME?View.VISIBLE:View.INVISIBLE);
2337  mItemSave.setEnabled(mMapNodes>0);
2338  mItemExport.setEnabled(mMapNodes>0);
2339  mItemOpen.setEnabled(true);
2340  mItemNewScan.setEnabled(true);
2341  mItemPostProcessing.setEnabled(mMapNodes>0);
2342  mItemSettings.setEnabled(true);
2343  mItemResume.setEnabled(mMapNodes>0);
2344  mItemModes.setEnabled(true);
2345  mButtonStart.setVisibility(View.INVISIBLE);
2346  mButtonStop.setVisibility(View.INVISIBLE);
2347  mItemLocalizationMode.setEnabled(true);
2348  mItemDataRecorderMode.setEnabled(true);
2349  mItemTrajectoryMode.setEnabled(true);
2350  break;
2351  }
2352  mButtonCameraView.setVisibility(mHudVisible?View.VISIBLE:View.INVISIBLE);
2353  mButtonBackfaceShown.setVisibility(mHudVisible && (mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked())?View.VISIBLE:View.INVISIBLE);
2354  mSeekBarOrthoCut.setVisibility(mHudVisible && mButtonCameraView.getSelectedItemPosition() == 3?View.VISIBLE:View.INVISIBLE);
2355  mSeekBarGrid.setVisibility(mHudVisible && mSeekBarGrid.isEnabled() && mButtonCameraView.getSelectedItemPosition() == 3?View.VISIBLE:View.INVISIBLE);
2356 
2357  if(mState != State.STATE_MAPPING && mState != State.STATE_CAMERA && mState!=State.STATE_VISUALIZING_CAMERA)
2358  {
2359  mGLView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
2360  mGLView.requestRender();
2361  }
2362  else
2363  {
2364  mGLView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
2365  }
2366 
2367  if(mState == State.STATE_WELCOME && mIntentDbToOpen != null)
2368  {
2369  openDatabase(mIntentDbToOpen, false);
2370  mIntentDbToOpen = null;
2371  }
2372  }
2373 
2374  private void startMapping() {
2375  if(!DISABLE_LOG) Log.i(TAG, String.format("startMapping()"));
2376 
2378 
2379  if(mMemoryWarningDialog != null)
2380  {
2381  mMemoryWarningDialog.dismiss();
2382  mMemoryWarningDialog=null;
2383  }
2384  RTABMapLib.setPausedMapping(nativeApplication, false);
2385  mLastFastMovementNotificationStamp = System.currentTimeMillis()/1000;
2386 
2387  if(mItemDataRecorderMode.isChecked())
2388  {
2389  mToast.makeText(getActivity(), String.format("Data Recorder Mode: no map is created, only raw data is recorded."), mToast.LENGTH_LONG).show();
2390  }
2391  else if(mMapNodes>0)
2392  {
2393  if(mItemLocalizationMode!=null && mItemLocalizationMode.isChecked())
2394  {
2395  mToast.makeText(getActivity(), String.format("Localization mode"), mToast.LENGTH_LONG).show();
2396  }
2397  else
2398  {
2399  mToast.makeText(getActivity(), String.format("On resume, a new map is created. Tip: Try relocalizing in the previous area."), mToast.LENGTH_LONG).show();
2400  }
2401  }
2402  else if(mMapNodes==0 && mItemLocalizationMode!=null && mItemLocalizationMode.isChecked())
2403  {
2404  mItemLocalizationMode.setChecked(false);
2405  RTABMapLib.setLocalizationMode(nativeApplication, false);
2406  mToast.makeText(getActivity(), String.format("Disabled localization mode as the map is empty, now mapping..."), mToast.LENGTH_LONG).show();
2407  }
2408  }
2409 
2410  private void stopCamera() {
2411 
2412  mProgressDialog.setTitle("");
2413  mProgressDialog.setMessage("Stopping camera...");
2414  mProgressDialog.show();
2415 
2416  if(mState == State.STATE_VISUALIZING_CAMERA)
2417  {
2418  RTABMapLib.setPausedMapping(nativeApplication, true);
2420  }
2421  else
2422  {
2423  if(mMapNodes==0)
2424  {
2426  }
2427  else
2428  {
2430  }
2431  }
2432 
2433  if(mArCoreCamera != null)
2434  {
2435  synchronized (this) {
2436  mRenderer.setCamera(null);
2437  mArCoreCamera.close();
2438  mArCoreCamera = null;
2439  }
2440  }
2441 
2442  Thread stopThread = new Thread(new Runnable() {
2443  public void run() {
2444  if(!DISABLE_LOG) Log.i(TAG, String.format("stopCamera()"));
2445  RTABMapLib.stopCamera(nativeApplication);
2446  if(mCameraServiceConnectionUsed)
2447  {
2448  if(!DISABLE_LOG) Log.i(TAG, String.format("unbindService"));
2449  getActivity().unbindService(mCameraServiceConnection);
2450  }
2451  mCameraServiceConnectionUsed = false;
2452 
2453  runOnUiThread(new Runnable() {
2454  public void run() {
2455  setCamera(2);
2456  if(!DISABLE_LOG) Log.i(TAG, String.format("stopMapping(): runOnUiThread"));
2457  mProgressDialog.dismiss();
2458  }
2459  });
2460  }
2461  });
2462  stopThread.start();
2463  }
2464 
2465  private void stopMapping() {
2466 
2467  mProgressDialog.setTitle("");
2468  mProgressDialog.setMessage("Stopping camera...");
2469  mProgressDialog.show();
2470 
2472 
2473  if(mArCoreCamera != null)
2474  {
2475  synchronized (this) {
2476  mRenderer.setCamera(null);
2477  mArCoreCamera.close();
2478  mArCoreCamera = null;
2479  }
2480  }
2481 
2482  Thread stopThread = new Thread(new Runnable() {
2483  public void run() {
2484  if(!DISABLE_LOG) Log.i(TAG, String.format("setPausedMapping()"));
2485  RTABMapLib.setPausedMapping(nativeApplication, true);
2486  if(!DISABLE_LOG) Log.i(TAG, String.format("stopCamera()"));
2487  RTABMapLib.stopCamera(nativeApplication);
2488  if(mCameraServiceConnectionUsed)
2489  {
2490  if(!DISABLE_LOG) Log.i(TAG, String.format("unbindService"));
2491  getActivity().unbindService(mCameraServiceConnection);
2492  }
2493  mCameraServiceConnectionUsed = false;
2494 
2495  runOnUiThread(new Runnable() {
2496  public void run() {
2497  if(!DISABLE_LOG) Log.i(TAG, String.format("stopMapping(): runOnUiThread"));
2498  mProgressDialog.dismiss();
2499 
2500  setCamera(2);
2501 
2502  long freeMemory = getFreeMemory();
2503  mStatusTexts[1] = getString(R.string.memory)+String.valueOf(mFreeMemoryOnStart>freeMemory?mFreeMemoryOnStart-freeMemory:0);
2504  mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(freeMemory);
2506 
2507  mDateOnPause = new Date();
2508 
2509  long memoryFree = getFreeMemory();
2510  if(!mOnPause && !mItemLocalizationMode.isChecked() && !mItemDataRecorderMode.isChecked() && memoryFree >= 100 && mMapNodes>2)
2511  {
2512  if(!DISABLE_LOG) Log.i(TAG, String.format("Do standard processing>?"));
2513  // Do standard post processing?
2514  AlertDialog d2 = new AlertDialog.Builder(getActivity())
2515  .setCancelable(false)
2516  .setTitle("Mapping Stopped! Optimize Now?")
2517  .setMessage("Do you want to do standard graph and mesh optimizations now? This can be also done later using \"Optimize\" and \"Export\" menus.")
2518  .setNeutralButton("Only Graph", new DialogInterface.OnClickListener() {
2519  public void onClick(DialogInterface dialog, int which) {
2520  standardOptimization(false);
2521  }
2522  })
2523  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
2524  public void onClick(DialogInterface dialog, int which) {
2525  standardOptimization(true);
2526  }
2527  })
2528  .setNegativeButton("No", new DialogInterface.OnClickListener() {
2529  public void onClick(DialogInterface dialog, int which) {
2530  if(mOpenedDatabasePath.isEmpty())
2531  {
2532  save();
2533  }
2534  }
2535  })
2536  .create();
2537  d2.setCanceledOnTouchOutside(false);
2538  d2.show();
2539  }
2540  }
2541  });
2542  }
2543  });
2544  stopThread.start();
2545  }
2546 
2547  public boolean onOptionsItemSelected(MenuItem item) {
2549  if(!DISABLE_LOG) Log.i(TAG, "called onOptionsItemSelected; selected item: " + item);
2550  int itemId = item.getItemId();
2551  if (itemId == R.id.post_processing_standard)
2552  {
2553  standardOptimization(false);
2554  }
2555  else if (itemId == R.id.detect_more_loop_closures)
2556  {
2557  mProgressDialog.setTitle("Post-Processing");
2558  mProgressDialog.setMessage(String.format("Please wait while detecting more loop closures..."));
2559  mProgressDialog.show();
2561  Thread workingThread = new Thread(new Runnable() {
2562  public void run() {
2563  final int loopDetected = RTABMapLib.postProcessing(nativeApplication, 2);
2564  runOnUiThread(new Runnable() {
2565  public void run() {
2566  mProgressDialog.dismiss();
2567  if(loopDetected >= 0)
2568  {
2569  mTotalLoopClosures+=loopDetected;
2570  mToast.makeText(getActivity(), String.format("Detection done! %d new loop closure(s) added.", loopDetected), mToast.LENGTH_SHORT).show();
2571  }
2572  else if(loopDetected < 0)
2573  {
2574  mToast.makeText(getActivity(), String.format("Detection failed!"), mToast.LENGTH_SHORT).show();
2575  }
2577  }
2578  });
2579  }
2580  });
2581  workingThread.start();
2582  }
2583  else if (itemId == R.id.global_graph_optimization)
2584  {
2585  mProgressDialog.setTitle("Post-Processing");
2586  mProgressDialog.setMessage(String.format("Global graph optimization..."));
2587  mProgressDialog.show();
2589  Thread workingThread = new Thread(new Runnable() {
2590  public void run() {
2591  final int value = RTABMapLib.postProcessing(nativeApplication, 0);
2592  runOnUiThread(new Runnable() {
2593  public void run() {
2594  mProgressDialog.dismiss();
2595  if(value >= 0)
2596  {
2597  mToast.makeText(getActivity(), String.format("Optimization done!"), mToast.LENGTH_SHORT).show();
2598  }
2599  else if(value < 0)
2600  {
2601  mToast.makeText(getActivity(), String.format("Optimization failed!"), mToast.LENGTH_SHORT).show();
2602  }
2604  }
2605  });
2606  }
2607  });
2608  workingThread.start();
2609  }
2610  else if (itemId == R.id.polygons_filtering)
2611  {
2612  mProgressDialog.setTitle("Post-Processing");
2613  mProgressDialog.setMessage(String.format("Noise filtering..."));
2614  mProgressDialog.show();
2615  RTABMapLib.postProcessing(nativeApplication, 4);
2616  }
2617  else if (itemId == R.id.gain_compensation_fast)
2618  {
2619  mProgressDialog.setTitle("Post-Processing");
2620  mProgressDialog.setMessage(String.format("Adjusting Colors (Fast)..."));
2621  mProgressDialog.show();
2622  RTABMapLib.postProcessing(nativeApplication, 5);
2623  }
2624  else if (itemId == R.id.gain_compensation_full)
2625  {
2626  mProgressDialog.setTitle("Post-Processing");
2627  mProgressDialog.setMessage(String.format("Adjusting Colors (Full)..."));
2628  mProgressDialog.show();
2629  RTABMapLib.postProcessing(nativeApplication, 6);
2630  }
2631  else if (itemId == R.id.bilateral_filtering)
2632  {
2633  mProgressDialog.setTitle("Post-Processing");
2634  mProgressDialog.setMessage(String.format("Mesh smoothing..."));
2635  mProgressDialog.show();
2636  RTABMapLib.postProcessing(nativeApplication, 7);
2637  }
2638  else if (itemId == R.id.sba)
2639  {
2640  mProgressDialog.setTitle("Post-Processing");
2641  mProgressDialog.setMessage(String.format("Bundle adjustement..."));
2642  mProgressDialog.show();
2643 
2644  Thread workingThread = new Thread(new Runnable() {
2645  public void run() {
2646  final int value = RTABMapLib.postProcessing(nativeApplication, 1);
2647  runOnUiThread(new Runnable() {
2648  public void run() {
2649  mProgressDialog.dismiss();
2650  if(value >= 0)
2651  {
2652  mToast.makeText(getActivity(), String.format("Optimization done!"), mToast.LENGTH_SHORT).show();
2653  }
2654  else if(value < 0)
2655  {
2656  mToast.makeText(getActivity(), String.format("Optimization failed!"), mToast.LENGTH_SHORT).show();
2657  }
2658  }
2659  });
2660  }
2661  });
2662  workingThread.start();
2663  }
2664  else if(itemId == R.id.status)
2665  {
2666  item.setChecked(!item.isChecked());
2668  }
2669  else if(itemId == R.id.debug)
2670  {
2671  item.setChecked(!item.isChecked());
2673  }
2674  else if(itemId == R.id.mesh || itemId == R.id.texture_mesh || itemId == R.id.point_cloud)
2675  {
2676  item.setChecked(true);
2678  nativeApplication,
2679  mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
2680  mItemRenderingTextureMesh.isChecked());
2681 
2683 
2684  if(mState != State.STATE_VISUALIZING)
2685  {
2686  // save preference
2687  int type = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
2688  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
2689  SharedPreferences.Editor editor = sharedPref.edit();
2690  editor.putInt(getString(R.string.pref_key_rendering), type);
2691  // Commit the edits!
2692  editor.commit();
2693  }
2694  }
2695  else if(itemId == R.id.map_shown)
2696  {
2697  item.setChecked(!item.isChecked());
2698  RTABMapLib.setMapCloudShown(nativeApplication, item.isChecked());
2699  }
2700  else if(itemId == R.id.odom_shown)
2701  {
2702  item.setChecked(!item.isChecked());
2703  RTABMapLib.setOdomCloudShown(nativeApplication, item.isChecked());
2704  }
2705  else if(itemId == R.id.localization_mode)
2706  {
2707  item.setChecked(!item.isChecked());
2708  RTABMapLib.setLocalizationMode(nativeApplication, item.isChecked());
2709  }
2710  else if(itemId == R.id.trajectory_mode)
2711  {
2712  item.setChecked(!item.isChecked());
2713  RTABMapLib.setTrajectoryMode(nativeApplication, item.isChecked());
2714  setCamera(item.isChecked()?2:1);
2715  }
2716  else if(itemId == R.id.graph_optimization)
2717  {
2718  item.setChecked(!item.isChecked());
2719  RTABMapLib.setGraphOptimization(nativeApplication, item.isChecked());
2720  }
2721  else if(itemId == R.id.graph_visible)
2722  {
2723  item.setChecked(!item.isChecked());
2724  RTABMapLib.setGraphVisible(nativeApplication, item.isChecked());
2725  }
2726  else if(itemId == R.id.grid_visible)
2727  {
2728  item.setChecked(!item.isChecked());
2729  mSeekBarGrid.setEnabled(item.isChecked());
2730  mSeekBarGrid.setVisibility(mHudVisible && mSeekBarGrid.isEnabled()&&mButtonCameraView.getSelectedItemPosition() == 3?View.VISIBLE:View.INVISIBLE);
2731  RTABMapLib.setGridVisible(nativeApplication, item.isChecked());
2732  }
2733  else if (itemId == R.id.save)
2734  {
2735  save();
2736  }
2737  else if(itemId == R.id.resume)
2738  {
2739  resumeScan();
2740  }
2741  else if(itemId == R.id.new_scan)
2742  {
2743  newScan();
2744  }
2745  else if(itemId == R.id.data_recorder)
2746  {
2747  final boolean dataRecorderOldState = item.isChecked();
2748  AlertDialog d2 = new AlertDialog.Builder(getActivity())
2749  .setCancelable(false)
2750  .setTitle("Data Recorder Mode")
2751  .setMessage("Changing from/to data recorder mode will close the current session. Do you want to continue?")
2752  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
2753  public void onClick(DialogInterface dialog, int which) {
2754  // reset
2755  mTotalLoopClosures = 0;
2756  int index = STATUS_TEXTS_POSE_INDEX;
2757  mMapNodes = 0;
2758  mStatusTexts[index++] = getString(R.string.nodes)+0;
2759  mStatusTexts[index++] = getString(R.string.words)+0;
2760  mStatusTexts[index++] = getString(R.string.database_size)+0;
2761  mStatusTexts[index++] = getString(R.string.points)+0;
2762  mStatusTexts[index++] = getString(R.string.polygons)+0;
2763  mStatusTexts[index++] = getString(R.string.update_time)+0;
2764  mStatusTexts[index++] = getString(R.string.features)+0;
2765  mStatusTexts[index++] = getString(R.string.rehearsal)+0;
2766  mStatusTexts[index++] = getString(R.string.total_loop)+0;
2767  mStatusTexts[index++] = getString(R.string.inliers)+0;
2768  mStatusTexts[index++] = getString(R.string.hypothesis)+0;
2769  mStatusTexts[index++] = getString(R.string.fps)+0;
2770  mStatusTexts[index++] = getString(R.string.distance)+0;
2771  mStatusTexts[index++] = String.format("Pose (x,y,z): 0 0 0");
2773 
2774  mItemDataRecorderMode.setChecked(!dataRecorderOldState);
2775  RTABMapLib.setDataRecorderMode(nativeApplication, mItemDataRecorderMode.isChecked());
2776 
2777  mOpenedDatabasePath = "";
2778  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
2779  boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory), Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
2780  String tmpDatabase = mWorkingDirectory+RTABMAP_TMP_DB;
2781  RTABMapLib.openDatabase(nativeApplication, tmpDatabase, databaseInMemory, false, true);
2782 
2783  mItemLocalizationMode.setEnabled(!mItemDataRecorderMode.isChecked());
2784 
2785  if(mItemDataRecorderMode.isChecked())
2786  {
2787  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();
2788  }
2789  else
2790  {
2791  mToast.makeText(getActivity(), String.format("Data recorder mode deactivated!"), mToast.LENGTH_LONG).show();
2792  }
2793  }
2794  })
2795  .setNegativeButton("No", new DialogInterface.OnClickListener() {
2796  public void onClick(DialogInterface dialog, int which) {
2797  dialog.dismiss();
2798  }
2799  })
2800  .create();
2801  d2.setCanceledOnTouchOutside(false);
2802  d2.show();
2803  }
2804  else if(itemId == R.id.export_point_cloud ||
2805  itemId == R.id.export_point_cloud_highrez)
2806  {
2807  final boolean regenerateCloud = itemId == R.id.export_point_cloud_highrez;
2808 
2809  export(false, false, regenerateCloud, false, 0);
2810  }
2811  else if(itemId == R.id.export_optimized_mesh ||
2812  itemId == R.id.export_optimized_mesh_texture)
2813  {
2814  final boolean isOBJ = itemId == R.id.export_optimized_mesh_texture;
2815 
2816  RelativeLayout linearLayout = new RelativeLayout(this);
2817  final NumberPicker aNumberPicker = new NumberPicker(this);
2818  aNumberPicker.setMaxValue(9);
2819  aNumberPicker.setMinValue(0);
2820  aNumberPicker.setWrapSelectorWheel(false);
2821  aNumberPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
2822  aNumberPicker.setFormatter(new NumberPicker.Formatter() {
2823  @Override
2824  public String format(int i) {
2825  if(i==0)
2826  {
2827  return "No Limit";
2828  }
2829  return String.format("%d00 000", i);
2830  }
2831  });
2832  aNumberPicker.setValue(2);
2833 
2834  // Fix to correctly show value on first render
2835  try {
2836  Method method = aNumberPicker.getClass().getDeclaredMethod("changeValueByOne", boolean.class);
2837  method.setAccessible(true);
2838  method.invoke(aNumberPicker, true);
2839  } catch (NoSuchMethodException e) {
2840  e.printStackTrace();
2841  } catch (IllegalArgumentException e) {
2842  e.printStackTrace();
2843  } catch (IllegalAccessException e) {
2844  e.printStackTrace();
2845  } catch (InvocationTargetException e) {
2846  e.printStackTrace();
2847  }
2848 
2849 
2850  RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(50, 50);
2851  RelativeLayout.LayoutParams numPicerParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
2852  numPicerParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
2853 
2854  linearLayout.setLayoutParams(params);
2855  linearLayout.addView(aNumberPicker,numPicerParams);
2856 
2857  AlertDialog ad = new AlertDialog.Builder(this)
2858  .setTitle("Maximum polygons")
2859  .setView(linearLayout)
2860  .setCancelable(false)
2861  .setPositiveButton("Ok",
2862  new DialogInterface.OnClickListener() {
2863  public void onClick(DialogInterface dialog,
2864  int id) {
2865  export(isOBJ, true, false, true, aNumberPicker.getValue()*100000);
2866  }
2867  })
2868  .setNegativeButton("Cancel",
2869  new DialogInterface.OnClickListener() {
2870  public void onClick(DialogInterface dialog,
2871  int id) {
2872  dialog.cancel();
2873  }
2874  }).create();
2875  ad.setCanceledOnTouchOutside(false);
2876  ad.show();
2877  }
2878  else if(itemId == R.id.open)
2879  {
2880  openDatabase();
2881  }
2882  else if(itemId == R.id.settings)
2883  {
2884  Intent intent = new Intent(getActivity(), SettingsActivity.class);
2885  startActivity(intent);
2886  }
2887  else if(itemId == R.id.about)
2888  {
2889  AboutDialog about = new AboutDialog(this);
2890  about.setTitle("About RTAB-Map");
2891  about.show();
2892  }
2893 
2894  return true;
2895  }
2896 
2897  private void resumeScan()
2898  {
2899  setCamera(mState==State.STATE_VISUALIZING?0:1);
2900  startCamera(String.format("Hold Tight! Initializing Camera Service...\n"
2901  + "Tip: If the camera is still drifting just after the mapping has started, do \"Reset\"."));
2902  }
2903 
2904  private void newScan() {
2905 
2906  if(mState == State.STATE_VISUALIZING)
2907  {
2908  mMapNodes = 0; // To avoid "re-adding clouds..." message when changing state from VIS to IDLE
2910  }
2911 
2912  mTotalLoopClosures = 0;
2913 
2914  int index = STATUS_TEXTS_POSE_INDEX;
2915  mMapNodes = 0;
2916  mStatusTexts[index++] = getString(R.string.nodes)+0;
2917  mStatusTexts[index++] = getString(R.string.words)+0;
2918  mStatusTexts[index++] = getString(R.string.database_size)+0;
2919  mStatusTexts[index++] = getString(R.string.points)+0;
2920  mStatusTexts[index++] = getString(R.string.polygons)+0;
2921  mStatusTexts[index++] = getString(R.string.update_time)+0;
2922  mStatusTexts[index++] = getString(R.string.features)+0;
2923  mStatusTexts[index++] = getString(R.string.rehearsal)+0;
2924  mStatusTexts[index++] = getString(R.string.total_loop)+0;
2925  mStatusTexts[index++] = getString(R.string.inliers)+0;
2926  mStatusTexts[index++] = getString(R.string.hypothesis)+0;
2927  mStatusTexts[index++] = getString(R.string.fps)+0;
2928  mStatusTexts[index++] = getString(R.string.distance)+0;
2929  mStatusTexts[index++] = String.format("Pose (x,y,z): 0 0 0");
2931 
2932  mOpenedDatabasePath = "";
2933  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
2934  boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory), Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
2935  final String tmpDatabase = mWorkingDirectory+RTABMAP_TMP_DB;
2936 
2937  File newFile = new File(tmpDatabase);
2938  final int fileSizeMB = (int)newFile.length()/(1024 * 1024);
2939  if(!(mState == State.STATE_CAMERA || mState ==State.STATE_MAPPING) &&
2940  newFile.exists() &&
2941  fileSizeMB>1) // >1MB
2942  {
2943  AlertDialog d2 = new AlertDialog.Builder(getActivity())
2944  .setCancelable(false)
2945  .setTitle("Recovery")
2946  .setMessage(String.format("The previous session (%d MB) was not correctly saved, do you want to recover it?", fileSizeMB))
2947  .setNegativeButton("Ignore", new DialogInterface.OnClickListener() {
2948  public void onClick(DialogInterface dialog, int which) {
2949  (new File(tmpDatabase)).delete();
2950  newScan();
2951  }
2952  })
2953  .setNeutralButton("Cancel", new DialogInterface.OnClickListener() {
2954  public void onClick(DialogInterface dialog, int which) {
2955  // do nothing
2956  }
2957  })
2958  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
2959  public void onClick(DialogInterface dialog, int which) {
2960  final String fileName = new SimpleDateFormat("yyMMdd-HHmmss").format(new Date()) + ".db";
2961  final String outputDbPath = mWorkingDirectory + fileName;
2962 
2963  mExportProgressDialog.setTitle("Recovering");
2964  mExportProgressDialog.setMessage(String.format("Please wait while recovering data..."));
2965  mExportProgressDialog.setProgress(0);
2966 
2967  final State previousState = mState;
2968 
2969  mExportProgressDialog.show();
2971 
2972  Thread exportThread = new Thread(new Runnable() {
2973  public void run() {
2974 
2975  final long startTime = System.currentTimeMillis()/1000;
2976 
2977  final boolean success = RTABMapLib.recover(
2978  nativeApplication,
2979  tmpDatabase,
2980  outputDbPath);
2981  runOnUiThread(new Runnable() {
2982  public void run() {
2983  if(mExportProgressDialog.isShowing())
2984  {
2985  if(success)
2986  {
2987  AlertDialog d2 = new AlertDialog.Builder(getActivity())
2988  .setCancelable(false)
2989  .setTitle("Database saved!")
2990  .setMessage(String.format("Database \"%s\" (%d MB) successfully saved!", fileName, fileSizeMB))
2991  .setPositiveButton("OK", new DialogInterface.OnClickListener() {
2992  public void onClick(DialogInterface dialog, int which) {
2993  openDatabase(fileName, false);
2994  }
2995  })
2996  .create();
2997  d2.setCanceledOnTouchOutside(true);
2998  d2.show();
2999  }
3000  else
3001  {
3002  updateState(previousState);
3003  mToast.makeText(getActivity(), String.format("Recovery failed!"), mToast.LENGTH_LONG).show();
3004  }
3005  mExportProgressDialog.dismiss();
3006  }
3007  else
3008  {
3009  mToast.makeText(getActivity(), String.format("Recovery canceled"), mToast.LENGTH_LONG).show();
3010  updateState(previousState);
3011  }
3012  }
3013  });
3014  }
3015  });
3016  exportThread.start();
3017 
3019  }
3020  })
3021  .create();
3022  d2.setCanceledOnTouchOutside(false);
3023  d2.show();
3024  }
3025  else
3026  {
3027 
3028  RTABMapLib.openDatabase(nativeApplication, tmpDatabase, databaseInMemory, false, true);
3029 
3030  if(!(mState == State.STATE_CAMERA || mState ==State.STATE_MAPPING))
3031  {
3032  setCamera(0);
3033  startCamera(String.format("Hold Tight! Initializing Camera Service...\n"
3034  + "Tip: If the camera is still drifting just after the mapping has started, do \"Reset\"."));
3035  }
3036  }
3037  }
3038 
3039 
3040  private void openDatabase()
3041  {
3042  final String[] files = Util.loadFileList(mWorkingDirectory, true);
3043  if(files.length > 0)
3044  {
3045  String[] filesWithSize = new String[files.length];
3046  for(int i = 0; i<filesWithSize.length; ++i)
3047  {
3048  File filePath = new File(mWorkingDirectory+files[i]);
3049  long mb = filePath.length()/(1024*1024);
3050  filesWithSize[i] = files[i] + " ("+mb+" MB)";
3051  }
3052 
3053  ArrayList<HashMap<String, String> > arrayList = new ArrayList<HashMap<String, String> >();
3054  for (int i = 0; i < filesWithSize.length; i++) {
3055  HashMap<String, String> hashMap = new HashMap<String, String>();//create a hashmap to store the data in key value pair
3056  hashMap.put("name", filesWithSize[i]);
3057  hashMap.put("path", mWorkingDirectory + files[i]);
3058  arrayList.add(hashMap);//add the hashmap into arrayList
3059  }
3060  String[] from = {"name", "path"};//string array
3061  int[] to = {R.id.textView, R.id.imageView};//int array of views id's
3062  DatabaseListArrayAdapter simpleAdapter = new DatabaseListArrayAdapter(this, arrayList, R.layout.database_list, from, to);//Create object and set the parameters for simpleAdapter
3063 
3064  AlertDialog.Builder builder = new AlertDialog.Builder(this);
3065  builder.setCancelable(true);
3066  builder.setTitle("Choose Your File (*.db)");
3067  builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
3068  public void onClick(DialogInterface dialog, int whichIn) {
3069  // do nothing
3070  }
3071  });
3072  builder.setAdapter(simpleAdapter, new DialogInterface.OnClickListener() {
3073  //builder.setItems(filesWithSize, new DialogInterface.OnClickListener() {
3074  public void onClick(DialogInterface dialog, final int which) {
3075 
3076  // Adjust color now?
3077  AlertDialog d2 = new AlertDialog.Builder(getActivity())
3078  .setCancelable(false)
3079  .setTitle("Opening database...")
3080  .setMessage("Do you want to adjust colors now?\nThis can be done later under Optimize menu.")
3081  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
3082  public void onClick(DialogInterface dialog, int whichIn) {
3083  openDatabase(files[which], true);
3084  }
3085  })
3086  .setNeutralButton("No", new DialogInterface.OnClickListener() {
3087  public void onClick(DialogInterface dialog, int whichIn) {
3088  openDatabase(files[which], false);
3089  }
3090  })
3091  .create();
3092  d2.setCanceledOnTouchOutside(false);
3093  d2.show();
3094  return;
3095  }
3096  });
3097 
3098  final AlertDialog ad = builder.create(); //don't show dialog yet
3099  ad.setCanceledOnTouchOutside(true);
3100  ad.setOnShowListener(new OnShowListener()
3101  {
3102  @Override
3103  public void onShow(DialogInterface dialog)
3104  {
3105  ListView lv = ad.getListView();
3106  ad.registerForContextMenu(lv);
3107  lv.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
3108 
3109  @Override
3110  public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
3111 
3112  if (v.getId()==ad.getListView().getId()) {
3113  AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
3114  final int position = info.position;
3115  menu.setHeaderTitle(files[position]);
3116  menu.add(Menu.NONE, 0, 0, "Rename").setOnMenuItemClickListener(new OnMenuItemClickListener() {
3117  @Override
3118  public boolean onMenuItemClick(MenuItem item) {
3119  AlertDialog.Builder builderRename = new AlertDialog.Builder(getActivity());
3120  builderRename.setCancelable(false);
3121  builderRename.setTitle("RTAB-Map Database Name (*.db):");
3122  final EditText input = new EditText(getActivity());
3123  input.setInputType(InputType.TYPE_CLASS_TEXT);
3124  input.setText("");
3125  input.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
3126  input.setSelectAllOnFocus(true);
3127  input.selectAll();
3128  builderRename.setView(input);
3129  builderRename.setPositiveButton("OK", new DialogInterface.OnClickListener() {
3130  @Override
3131  public void onClick(DialogInterface dialog, int which)
3132  {
3133  final String fileName = input.getText().toString();
3134  dialog.dismiss();
3135  if(!fileName.isEmpty())
3136  {
3137  File newFile = new File(mWorkingDirectory + fileName + ".db");
3138  if(newFile.exists())
3139  {
3140  AlertDialog d2 = new AlertDialog.Builder(getActivity())
3141  .setCancelable(false)
3142  .setTitle("File Already Exists")
3143  .setMessage(String.format("Name %s already used, choose another name.", fileName))
3144  .create();
3145  d2.setCanceledOnTouchOutside(false);
3146  d2.show();
3147  }
3148  else
3149  {
3150  File from = new File(mWorkingDirectory, files[position]);
3151  File to = new File(mWorkingDirectory, fileName + ".db");
3152  from.renameTo(to);
3153 
3154  long stamp = System.currentTimeMillis();
3155  if(stamp-mSavedStamp < 10000)
3156  {
3157  try {
3158  Thread.sleep(10000 - (stamp-mSavedStamp));
3159  }
3160  catch(InterruptedException e){}
3161  }
3162 
3163  refreshSystemMediaScanDataBase(getActivity(), files[position]);
3164  refreshSystemMediaScanDataBase(getActivity(), to.getAbsolutePath());
3165 
3166  ad.dismiss();
3167  resetNoTouchTimer(true);
3168  }
3169  }
3170  }
3171  });
3172  AlertDialog alertToShow = builderRename.create();
3173  alertToShow.setCanceledOnTouchOutside(false);
3174  alertToShow.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
3175  alertToShow.show();
3176  return true;
3177  }
3178  });
3179  menu.add(Menu.NONE, 1, 1, "Delete").setOnMenuItemClickListener(new OnMenuItemClickListener() {
3180  @Override
3181  public boolean onMenuItemClick(MenuItem item) {
3182  DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
3183  @Override
3184  public void onClick(DialogInterface dialog, int which) {
3185  switch (which){
3186  case DialogInterface.BUTTON_POSITIVE:
3187  Log.e(TAG, String.format("Yes delete %s!", files[position]));
3188  (new File(mWorkingDirectory+files[position])).delete();
3189  refreshSystemMediaScanDataBase(getActivity(), mWorkingDirectory+files[position]);
3190  ad.dismiss();
3191  resetNoTouchTimer(true);
3192  break;
3193 
3194  case DialogInterface.BUTTON_NEGATIVE:
3195  //No button clicked
3196  break;
3197  }
3198  }
3199  };
3200  AlertDialog dialog = new AlertDialog.Builder(getActivity())
3201  .setCancelable(false)
3202  .setTitle(String.format("Delete %s", files[position]))
3203  .setMessage("Are you sure?")
3204  .setPositiveButton("Yes", dialogClickListener)
3205  .setNegativeButton("No", dialogClickListener).create();
3206  dialog.setCanceledOnTouchOutside(false);
3207  dialog.show();
3208  return true;
3209  }
3210  });
3211  menu.add(Menu.NONE, 2, 2, "Share").setOnMenuItemClickListener(new OnMenuItemClickListener() {
3212  @Override
3213  public boolean onMenuItemClick(MenuItem item) {
3214 
3215  if (!PermissionHelper.hasPermission(getActivity(), Manifest.permission.INTERNET)) {
3216  PermissionHelper.requestPermission(getActivity(), Manifest.permission.INTERNET);
3217  return false;
3218  }
3219 
3220  // Send to...
3221  File f = new File(mWorkingDirectory+files[position]);
3222  final int fileSizeMB = (int)f.length()/(1024 * 1024);
3223  Intent shareIntent = new Intent();
3224  shareIntent.setAction(Intent.ACTION_SEND);
3225  shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
3226  Uri fileUri = FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() + ".provider", f);
3227  shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
3228  shareIntent.setType("application/octet-stream");
3229  startActivity(Intent.createChooser(shareIntent, String.format("Sharing database \"%s\" (%d MB)...", files[position], fileSizeMB)));
3230  ad.dismiss();
3231  resetNoTouchTimer(true);
3232  return true;
3233  }
3234  });
3235  }
3236  }
3237  });
3238  }
3239  });
3240  ad.show();
3241  }
3242  }
3243 
3244  private void export(final boolean isOBJ, final boolean meshing, final boolean regenerateCloud, final boolean optimized, final int optimizedMaxPolygons)
3245  {
3246  final String extension = isOBJ? ".obj" : ".ply";
3247 
3248  // get Export settings
3249  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
3250  final String cloudVoxelSizeStr = sharedPref.getString(getString(R.string.pref_key_cloud_voxel), getString(R.string.pref_default_cloud_voxel));
3251  final float cloudVoxelSize = Float.parseFloat(cloudVoxelSizeStr);
3252  final int textureSize = isOBJ?Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_texture_size), getString(R.string.pref_default_texture_size))):0;
3253  final int textureCount = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_texture_count), getString(R.string.pref_default_texture_count)));
3254  final int normalK = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_normal_k), getString(R.string.pref_default_normal_k)));
3255  final float maxTextureDistance = Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_max_texture_distance), getString(R.string.pref_default_max_texture_distance)));
3256  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)));
3257  final float optimizedVoxelSize = cloudVoxelSize;
3258  final int optimizedDepth = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_opt_depth), getString(R.string.pref_default_opt_depth)));
3259  final float optimizedColorRadius = Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_opt_color_radius), getString(R.string.pref_default_opt_color_radius)));
3260  final boolean optimizedCleanWhitePolygons = sharedPref.getBoolean(getString(R.string.pref_key_opt_clean_white), Boolean.parseBoolean(getString(R.string.pref_default_opt_clean_white)));
3261  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)));
3262  final boolean blockRendering = sharedPref.getBoolean(getString(R.string.pref_key_block_render), Boolean.parseBoolean(getString(R.string.pref_default_block_render)));
3263 
3264 
3265  mExportProgressDialog.setTitle("Exporting");
3266  mExportProgressDialog.setMessage(String.format("Please wait while preparing data to export..."));
3267  mExportProgressDialog.setProgress(0);
3268 
3269  final State previousState = mState;
3270 
3271  mExportProgressDialog.show();
3273 
3274  Thread exportThread = new Thread(new Runnable() {
3275  public void run() {
3276 
3277  final long startTime = System.currentTimeMillis()/1000;
3278 
3279  final boolean success = RTABMapLib.exportMesh(
3280  nativeApplication,
3281  cloudVoxelSize,
3282  regenerateCloud,
3283  meshing,
3284  textureSize,
3285  textureCount,
3286  normalK,
3287  optimized,
3288  optimizedVoxelSize,
3289  optimizedDepth,
3290  optimizedMaxPolygons,
3291  optimizedColorRadius,
3292  optimizedCleanWhitePolygons,
3293  optimizedMinClusterSize,
3294  maxTextureDistance,
3295  minTextureClusterSize,
3296  blockRendering);
3297  runOnUiThread(new Runnable() {
3298  public void run() {
3299  if(mExportProgressDialog.isShowing())
3300  {
3301  if(success)
3302  {
3303  if(!meshing && cloudVoxelSize>0.0f)
3304  {
3305  mToast.makeText(getActivity(), String.format("Cloud assembled and voxelized at %s m.", cloudVoxelSizeStr), mToast.LENGTH_LONG).show();
3306  }
3307 
3308  final long endTime = System.currentTimeMillis()/1000;
3309 
3310  if(endTime-startTime > 10)
3311  {
3312  // build notification
3313  // the addAction re-use the same intent to keep the example short
3314  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
3315  boolean notifySound = sharedPref.getBoolean(getString(R.string.pref_key_notification_sound), Boolean.parseBoolean(getString(R.string.pref_default_notification_sound)));
3316  Notification n = new Notification.Builder(getActivity())
3317  .setContentTitle(getString(R.string.app_name))
3318  .setContentText("Data generated and ready to be exported!")
3319  .setSmallIcon(R.drawable.ic_launcher)
3320  .setDefaults(notifySound?Notification.DEFAULT_SOUND:0)
3321  .setAutoCancel(true).build();
3322 
3323  NotificationManager notificationManager =
3324  (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
3325 
3326  notificationManager.notify(0, n);
3327  }
3328 
3329  // Visualize the result
3330  resetNoTouchTimer(true);
3331  mSavedRenderingType = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
3332  if(!meshing)
3333  {
3334  mItemRenderingPointCloud.setChecked(true);
3335  }
3336  else if(!isOBJ)
3337  {
3338  mItemRenderingMesh.setChecked(true);
3339  }
3340  else // isOBJ
3341  {
3342  mItemRenderingTextureMesh.setChecked(true);
3343  }
3344  if(!optimizedCleanWhitePolygons)
3345  {
3346  mButtonLighting.setChecked(true);
3347  RTABMapLib.setLighting(nativeApplication, true);
3348  }
3350  RTABMapLib.postExportation(nativeApplication, true);
3351  if(mButtonCameraView.getSelectedItemPosition() == 0)
3352  {
3353  setCamera(2);
3354  }
3355  if(mOpenedDatabasePath.isEmpty())
3356  {
3357  save();
3358  }
3359  }
3360  else
3361  {
3362  updateState(previousState);
3363  mToast.makeText(getActivity(), String.format("Exporting map failed!"), mToast.LENGTH_LONG).show();
3364  }
3365  mExportProgressDialog.dismiss();
3366  }
3367  else
3368  {
3369  mProgressDialog.dismiss();
3370  mToast.makeText(getActivity(), String.format("Export canceled"), mToast.LENGTH_LONG).show();
3371  updateState(previousState);
3372  }
3373  }
3374  });
3375  }
3376  });
3377  exportThread.start();
3378  }
3379 
3380  public void save()
3381  {
3382  AlertDialog.Builder builder = new AlertDialog.Builder(this);
3383  builder.setCancelable(false);
3384  builder.setTitle("RTAB-Map Database Name (*.db):");
3385  final EditText input = new EditText(this);
3386  input.setInputType(InputType.TYPE_CLASS_TEXT);
3387  if(mOpenedDatabasePath.isEmpty())
3388  {
3389  String timeStamp = new SimpleDateFormat("yyMMdd-HHmmss").format(mDateOnPause);
3390  input.setText(timeStamp);
3391  }
3392  else
3393  {
3394  File f = new File(mOpenedDatabasePath);
3395  String name = f.getName();
3396  input.setText(name.substring(0,name.lastIndexOf(".")));
3397  }
3398  input.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
3399  input.setSelectAllOnFocus(true);
3400  input.selectAll();
3401  builder.setView(input);
3402  builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
3403  @Override
3404  public void onClick(DialogInterface dialog, int which)
3405  {
3406  //do nothing
3407  }
3408  });
3409  builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
3410  @Override
3411  public void onClick(DialogInterface dialog, int which)
3412  {
3413  final String fileName = input.getText().toString();
3414  dialog.dismiss();
3415  if(!fileName.isEmpty())
3416  {
3417  File newFile = new File(mWorkingDirectory + fileName + ".db");
3418  if(newFile.exists())
3419  {
3420  AlertDialog d2 = new AlertDialog.Builder(getActivity())
3421  .setCancelable(false)
3422  .setTitle("File Already Exists")
3423  .setMessage("Do you want to overwrite the existing file?")
3424  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
3425  public void onClick(DialogInterface dialog, int which) {
3426  saveDatabase(fileName);
3427  }
3428  })
3429  .setNegativeButton("No", new DialogInterface.OnClickListener() {
3430  public void onClick(DialogInterface dialog, int which) {
3431  dialog.dismiss();
3432  resetNoTouchTimer(true);
3433  }
3434  })
3435  .create();
3436  d2.setCanceledOnTouchOutside(false);
3437  d2.show();
3438  }
3439  else
3440  {
3441  saveDatabase(fileName);
3442  }
3443  }
3444  }
3445  });
3446  AlertDialog alertToShow = builder.create();
3447  alertToShow.setCanceledOnTouchOutside(false);
3448  alertToShow.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
3449  alertToShow.show();
3450  }
3451 
3457  public static void refreshSystemMediaScanDataBase(Context context, String docPath){
3458  Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
3459  Uri contentUri = Uri.fromFile(new File(docPath));
3460  mediaScanIntent.setData(contentUri);
3461  context.sendBroadcast(mediaScanIntent);
3462  }
3463 
3464  private void saveDatabase(final String fileName)
3465  {
3466  final String newDatabasePath = mWorkingDirectory + fileName + ".db";
3467  final String newDatabasePathHuman = mWorkingDirectoryHuman + fileName + ".db";
3468  mProgressDialog.setTitle("Saving");
3469  if(mOpenedDatabasePath.equals(newDatabasePath))
3470  {
3471  mProgressDialog.setMessage(String.format("Please wait while updating \"%s\"...", newDatabasePathHuman));
3472  }
3473  else
3474  {
3475  mProgressDialog.setMessage(String.format("Please wait while saving \"%s\"...", newDatabasePathHuman));
3476  }
3477  mProgressDialog.show();
3478  final State previousState = mState;
3480  Thread saveThread = new Thread(new Runnable() {
3481  public void run() {
3482  RTABMapLib.save(nativeApplication, newDatabasePath); // save
3483  runOnUiThread(new Runnable() {
3484  public void run() {
3485  String msg;
3486  if(mOpenedDatabasePath.equals(newDatabasePath))
3487  {
3488  msg = String.format("Database \"%s\" updated.", newDatabasePathHuman);
3489  }
3490  else
3491  {
3492  refreshSystemMediaScanDataBase(getActivity(), newDatabasePath);
3493  mSavedStamp = System.currentTimeMillis();
3494  msg = String.format("Database saved to \"%s\".", newDatabasePathHuman);
3495  }
3496 
3497  // build notification
3498  Intent intent = new Intent(getActivity(), RTABMapActivity.class);
3499  // use System.currentTimeMillis() to have a unique ID for the pending intent
3500  PendingIntent pIntent = PendingIntent.getActivity(getActivity(), (int) System.currentTimeMillis(), intent, 0);
3501  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
3502  boolean notifySound = sharedPref.getBoolean(getString(R.string.pref_key_notification_sound), Boolean.parseBoolean(getString(R.string.pref_default_notification_sound)));
3503  Notification n = new Notification.Builder(getActivity())
3504  .setContentTitle(getString(R.string.app_name))
3505  .setContentText(msg)
3506  .setSmallIcon(R.drawable.ic_launcher)
3507  .setContentIntent(pIntent)
3508  .setDefaults(notifySound?Notification.DEFAULT_SOUND:0)
3509  .setAutoCancel(true).build();
3510 
3511  NotificationManager notificationManager =
3512  (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
3513 
3514  notificationManager.notify(0, n);
3515 
3516  final File f = new File(newDatabasePath);
3517  final int fileSizeMB = (int)f.length()/(1024 * 1024);
3518 
3519  if(!mItemDataRecorderMode.isChecked())
3520  {
3521  mOpenedDatabasePath = newDatabasePath;
3522  }
3523  mProgressDialog.dismiss();
3524  updateState(previousState);
3525 
3526  AlertDialog d2 = new AlertDialog.Builder(getActivity())
3527  .setCancelable(false)
3528  .setTitle("Database saved!")
3529  .setMessage(String.format("Database \"%s\" (%d MB) successfully saved!", newDatabasePathHuman, fileSizeMB))
3530  .setPositiveButton("OK", new DialogInterface.OnClickListener() {
3531  public void onClick(DialogInterface dialog, int which) {
3532  resetNoTouchTimer(true);
3533  }
3534  })
3535  .create();
3536  d2.setCanceledOnTouchOutside(true);
3537  d2.show();
3538  }
3539  });
3540  }
3541  });
3542  saveThread.start();
3543  }
3544 
3545  private void saveOnDevice()
3546  {
3547  AlertDialog.Builder builder = new AlertDialog.Builder(this);
3548  builder.setTitle("Model Name:");
3549  final EditText input = new EditText(this);
3550  input.setInputType(InputType.TYPE_CLASS_TEXT);
3551  builder.setView(input);
3552  if(mOpenedDatabasePath.isEmpty())
3553  {
3554  String timeStamp = new SimpleDateFormat("yyMMdd-HHmmss").format(mDateOnPause);
3555  input.setText(timeStamp);
3556  }
3557  else
3558  {
3559  File f = new File(mOpenedDatabasePath);
3560  String name = f.getName();
3561  input.setText(name.substring(0,name.lastIndexOf(".")));
3562  }
3563  input.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
3564  input.setSelectAllOnFocus(true);
3565  input.selectAll();
3566  builder.setCancelable(false);
3567  builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
3568  @Override
3569  public void onClick(DialogInterface dialog, int which)
3570  {
3571  dialog.dismiss();
3572  resetNoTouchTimer(true);
3573  }
3574  });
3575  builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
3576  @Override
3577  public void onClick(DialogInterface dialog, int which)
3578  {
3579  final String fileName = input.getText().toString();
3580  dialog.dismiss();
3581  if(!fileName.isEmpty())
3582  {
3583  writeExportedFiles(fileName);
3584  }
3585  }
3586  });
3587  AlertDialog alertToShow = builder.create();
3588  alertToShow.setCanceledOnTouchOutside(false);
3589  alertToShow.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
3590  alertToShow.show();
3591  }
3592 
3593  private void writeExportedFiles(final String fileName)
3594  {
3595  Log.i(TAG, String.format("Write exported mesh to \"%s\"", fileName));
3596 
3597  mProgressDialog.setTitle("Exporting");
3598  mProgressDialog.setMessage(String.format("Compressing the files..."));
3599  mProgressDialog.show();
3600 
3601  Thread workingThread = new Thread(new Runnable() {
3602  public void run() {
3603  boolean success = false;
3604 
3605  File tmpDir = new File(mWorkingDirectory + RTABMAP_TMP_DIR);
3606  tmpDir.mkdirs();
3607  String[] fileNames = Util.loadFileList(mWorkingDirectory + RTABMAP_TMP_DIR, false);
3608  if(!DISABLE_LOG) Log.i(TAG, String.format("Deleting %d files in \"%s\"", fileNames.length, mWorkingDirectory + RTABMAP_TMP_DIR));
3609  for(int i=0; i<fileNames.length; ++i)
3610  {
3611  File f = new File(mWorkingDirectory + RTABMAP_TMP_DIR + "/" + fileNames[i]);
3612  if(f.delete())
3613  {
3614  if(!DISABLE_LOG) Log.i(TAG, String.format("Deleted \"%s\"", f.getPath()));
3615  }
3616  else
3617  {
3618  if(!DISABLE_LOG) Log.i(TAG, String.format("Failed deleting \"%s\"", f.getPath()));
3619  }
3620  }
3621  File exportDir = new File(mWorkingDirectory + RTABMAP_EXPORT_DIR);
3622  exportDir.mkdirs();
3623 
3624  final String pathHuman = mWorkingDirectoryHuman + RTABMAP_EXPORT_DIR + fileName + ".zip";
3625  final String zipOutput = mWorkingDirectory+RTABMAP_EXPORT_DIR+fileName+".zip";
3626  if(RTABMapLib.writeExportedMesh(nativeApplication, mWorkingDirectory + RTABMAP_TMP_DIR, RTABMAP_TMP_FILENAME))
3627  {
3628  fileNames = Util.loadFileList(mWorkingDirectory + RTABMAP_TMP_DIR, false);
3629  if(fileNames.length > 0)
3630  {
3631  String[] filesToZip = new String[fileNames.length];
3632  for(int i=0; i<fileNames.length; ++i)
3633  {
3634  filesToZip[i] = mWorkingDirectory + RTABMAP_TMP_DIR + "/" + fileNames[i];
3635  }
3636 
3637  File toZIPFile = new File(zipOutput);
3638  toZIPFile.delete();
3639 
3640  try
3641  {
3642  Util.zip(filesToZip, zipOutput);
3643  success = true;
3644  }
3645  catch(IOException e)
3646  {
3647  final String msg = e.getMessage();
3648  runOnUiThread(new Runnable() {
3649  public void run() {
3650  mToast.makeText(getActivity(), String.format("Exporting mesh \"%s\" failed! Error=%s", fileName, msg), mToast.LENGTH_LONG).show();
3651  }
3652  });
3653  }
3654  }
3655  }
3656 
3657  if(success)
3658  {
3659  runOnUiThread(new Runnable() {
3660  public void run() {
3661  mProgressDialog.dismiss();
3662 
3663  final File f = new File(zipOutput);
3664  final int fileSizeMB = (int)f.length()/(1024 * 1024);
3665 
3666  AlertDialog d = new AlertDialog.Builder(getActivity())
3667  .setCancelable(false)
3668  .setTitle("Mesh Saved!")
3669  .setMessage(String.format("Mesh \"%s\" (%d MB) successfully exported! Share it?", pathHuman, fileSizeMB))
3670  .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
3671  public void onClick(DialogInterface dialog, int which) {
3672  // Send to...
3673  Intent shareIntent = new Intent();
3674  shareIntent.setAction(Intent.ACTION_SEND);
3675  shareIntent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() + ".provider", f));
3676  shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
3677  shareIntent.setType("application/zip");
3678  startActivity(Intent.createChooser(shareIntent, "Sharing..."));
3679 
3680  resetNoTouchTimer(true);
3681  }
3682  })
3683  .setNegativeButton("No", new DialogInterface.OnClickListener() {
3684  public void onClick(DialogInterface dialog, int which) {
3685  resetNoTouchTimer(true);
3686  }
3687  }).create();
3688  d.setCanceledOnTouchOutside(false);
3689  d.show();
3690  }
3691  });
3692  }
3693  else
3694  {
3695  runOnUiThread(new Runnable() {
3696  public void run() {
3697  mProgressDialog.dismiss();
3698  mToast.makeText(getActivity(), String.format("Exporting mesh \"%s\" failed! No files found in tmp directory!? Last export may have failed or have been canceled.", fileName), mToast.LENGTH_LONG).show();
3699  resetNoTouchTimer(true);
3700  }
3701  });
3702  }
3703  }
3704  });
3705  workingThread.start();
3706  }
3707 
3708  private void openDatabase(final String fileName, final boolean optimize)
3709  {
3710  mOpenedDatabasePath = mWorkingDirectory + fileName;
3711 
3712  Log.i(TAG, "Open database " + mOpenedDatabasePath);
3713 
3714  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
3715  final boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory), Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
3716 
3717  mProgressDialog.setTitle("Loading");
3718  mProgressDialog.setMessage(String.format("Opening database \"%s\"...", fileName));
3719  mProgressDialog.show();
3721 
3722  Thread openThread = new Thread(new Runnable() {
3723  public void run() {
3724 
3725  final int status = RTABMapLib.openDatabase(nativeApplication, mOpenedDatabasePath, databaseInMemory, optimize, false);
3726 
3727  runOnUiThread(new Runnable() {
3728  public void run() {
3729  if(status == -1)
3730  {
3732  mProgressDialog.dismiss();
3733  AlertDialog d = new AlertDialog.Builder(getActivity())
3734  .setCancelable(false)
3735  .setTitle("Error")
3736  .setMessage("The map is loaded but optimization of the map's graph has "
3737  + "failed, so the map cannot be shown. Change the Graph Optimizer approach used"
3738  + " or enable/disable if the graph is optimized from graph "
3739  + "end in \"Settings -> Mapping...\" and try opening again.")
3740  .setPositiveButton("Open Settings", new DialogInterface.OnClickListener() {
3741  public void onClick(DialogInterface dialog, int which) {
3742  Intent intent = new Intent(getActivity(), SettingsActivity.class);
3743  startActivity(intent);
3744  }
3745  })
3746  .setNegativeButton("Close", new DialogInterface.OnClickListener() {
3747  public void onClick(DialogInterface dialog, int which) {
3748  }
3749  }).create();
3750  d.setCanceledOnTouchOutside(false);
3751  d.show();
3752  }
3753  else if(status == -2)
3754  {
3756  mProgressDialog.dismiss();
3757  AlertDialog d = new AlertDialog.Builder(getActivity())
3758  .setCancelable(false)
3759  .setTitle("Error")
3760  .setMessage("Failed to open database: Out of memory! Try "
3761  + "again after lowering Point Cloud Density in Settings.")
3762  .setPositiveButton("Open Settings", new DialogInterface.OnClickListener() {
3763  public void onClick(DialogInterface dialog, int which) {
3764  Intent intent = new Intent(getActivity(), SettingsActivity.class);
3765  startActivity(intent);
3766  }
3767  })
3768  .setNegativeButton("Close", new DialogInterface.OnClickListener() {
3769  public void onClick(DialogInterface dialog, int which) {
3770  }
3771  }).create();
3772  d.setCanceledOnTouchOutside(false);
3773  d.show();
3774  }
3775  else
3776  {
3777  if(status >= 1 && status<=3)
3778  {
3779  mProgressDialog.dismiss();
3780  resetNoTouchTimer(true);
3782  mToast.makeText(getActivity(), String.format("Database loaded!"), mToast.LENGTH_LONG).show();
3783  }
3784  else if(!mItemTrajectoryMode.isChecked())
3785  {
3786  if(mButtonCameraView.getSelectedItemPosition() == 0)
3787  {
3788  setCamera(2);
3789  }
3790  // creating meshes...
3792  mProgressDialog.setTitle("Loading");
3793  mProgressDialog.setMessage(String.format("Database \"%s\" loaded. Please wait while rendering point clouds and meshes...", fileName));
3794  }
3795 
3796  }
3797  }
3798  });
3799  }
3800  });
3801  openThread.start();
3802  }
3803 
3804  private void shareToSketchfab()
3805  {
3806  if (!PermissionHelper.hasPermission(this, Manifest.permission.INTERNET)) {
3807  PermissionHelper.requestPermission(this, Manifest.permission.INTERNET);
3808  return;
3809  }
3810 
3811  Intent intent = new Intent(getActivity(), SketchfabActivity.class);
3812 
3813  intent.putExtra(RTABMAP_AUTH_TOKEN_KEY, mAuthToken);
3814  intent.putExtra(RTABMAP_WORKING_DIR_KEY, mWorkingDirectory);
3815 
3816  if(mOpenedDatabasePath.isEmpty())
3817  {
3818  intent.putExtra(RTABMAP_FILENAME_KEY, new SimpleDateFormat("yyMMdd-HHmmss").format(mDateOnPause));
3819  }
3820  else
3821  {
3822  File f = new File(mOpenedDatabasePath);
3823  String name = f.getName();
3824  intent.putExtra(RTABMAP_FILENAME_KEY, name.substring(0,name.lastIndexOf(".")));
3825  }
3826 
3827  startActivityForResult(intent, SKETCHFAB_ACTIVITY_CODE);
3828  }
3829 }
d
void writeExportedFiles(final String fileName)
GLM_FUNC_DECL T roll(detail::tquat< T, P > const &x)
static native void destroyNativeApplication(long nativeApplication)
static native void setMeshTriangleSize(long nativeApplication, int value)
static String [] loadFileList(final String directory, final boolean databasesOnly)
Definition: Util.java:62
static native void setDepthFromMotion(long nativeApplication, boolean enabled)
static boolean hasPermission(Activity activity, String permission)
void onNothingSelected(AdapterView<?> parent)
void updateProgressionCallback(final int count, final int max)
void run(class_loader::ClassLoader *loader)
void onSensorChanged(SensorEvent event)
f
static native void setMinCloudDepth(long nativeApplication, float value)
ros::Time * timeStamp(M &m)
void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
static native void setLighting(long nativeApplication, boolean enabled)
static native boolean writeExportedMesh(long nativeApplication, String directory, String name)
void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results)
static native void setCameraColor(long nativeApplication, boolean enabled)
static native void setPausedMapping(long nativeApplication, boolean paused)
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()
char * dst
Definition: lz4.h:354
static native int openDatabase(long nativeApplication, String databasePath, boolean databaseInMemory, boolean optimize, boolean clearDatabase)
static native void setRawScanSaved(long nativeApplication, boolean enabled)
static native void setScreenRotation(long nativeApplication, int displayRotation, int cameraRotation)
static native void setFullResolution(long nativeApplication, boolean enabled)
static native boolean postExportation(long nativeApplication, boolean visualize)
void setProgressDialog(ProgressDialog progressDialog)
Definition: Renderer.java:63
static void requestPermission(Activity activity, String permission)
GLM_FUNC_DECL T pitch(detail::tquat< T, P > const &x)
void onWindowFocusChanged(boolean hasFocus)
static native void setRenderingTextureDecimation(long nativeApplication, int value)
static native void setBackgroundColor(long nativeApplication, float gray)
void saveDatabase(final String fileName)
static void zip(String file, String zipFile)
Definition: Util.java:27
void rtabmapInitEventCallback(final int status, final String msg)
static native boolean recover(long nativeApplication, String from, String to)
static native void setDataRecorderMode(long nativeApplication, boolean enabled)
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 int landmarkDetected, final float x, final float y, final float z, final float roll, final float pitch, final float yaw)
PM::Matrix Matrix
static native void setLocalizationMode(long nativeApplication, boolean enabled)
void standardOptimization(final boolean withStandardMeshExport)
void onActivityResult(int requestCode, int resultCode, Intent data)
static native void setSmoothing(long nativeApplication, boolean enabled)
void updateTexts(String[] texts)
Definition: Renderer.java:234
FloatingPoint< float > Float
static native void setCloudDensityLevel(long nativeApplication, int value)
void onAccuracyChanged(Sensor sensor, int accuracy)
static native void setMaxCloudDepth(long nativeApplication, float value)
static native void setGridVisible(long nativeApplication, boolean visible)
static final String RTABMAP_OPENED_DB_PATH_KEY
boolean onOptionsItemSelected(MenuItem item)
string name
static native void addEnvSensor(long nativeApplication, int type, float value)
static native void stopCamera(long nativeApplication)
static native boolean startCamera(long nativeApplication, IBinder binder, Context context, Activity activity, int driver)
static native void setGridRotation(long nativeApplication, float value)
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:264
void setCamera(ARCoreSharedCamera camera)
Definition: Renderer.java:78
void openDatabase(final String fileName, final boolean optimize)
static native void setMeshRendering(long nativeApplication, boolean enabled, boolean withTexture)
void cameraEventUI(int type, String key, String value)
void startCamera(final String message)
static native void setOdomCloudShown(long nativeApplication, boolean shown)
void onCreate(Bundle savedInstanceState)
static native void setGPS(long nativeApplication, double stamp, double longitude, double latitude, double altitude, double accuracy, double bearing)
static native void setGraphVisible(long nativeApplication, boolean visible)
void updateStatsUI(int loopClosureId, int inliers, int matches, int rejected, float optimizationMaxError, float optimizationMaxErrorRatio, boolean fastMovement, int landmarkDetected, String[] statusTexts)
static boolean shouldShowRequestPermissionRationale(Activity activity, String permission)
params
static native void setOrthoCropFactor(long nativeApplication, float value)
RecoveryProgressState state
boolean CheckTangoCoreVersion(int minVersion)
static native void setPointSize(long nativeApplication, float value)
static native void setOnlineBlending(long nativeApplication, boolean enabled)
static native boolean exportMesh(long nativeApplication, 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)
static native boolean isBuiltWith(long nativeApplication, int cameraDriver)
QApplication * app
static native long createNativeApplication(RTABMapActivity activity)
GLM_FUNC_DECL genType max(genType const &x, genType const &y)
void rtabmapInitEventUI(int status, String msg)
boolean onMenuOpened(int featureId, Menu menu)
GLM_FUNC_DECL T yaw(detail::tquat< T, P > const &x)
static void refreshSystemMediaScanDataBase(Context context, String docPath)
static native void setTrajectoryMode(long nativeApplication, boolean enabled)
static native void setGraphOptimization(long nativeApplication, boolean enabled)
static native void setClusterRatio(long nativeApplication, float value)
static native void setMapCloudShown(long nativeApplication, boolean shown)
static native int postProcessing(long nativeApplication, int approach)
static final boolean bindTangoService(final Context context, ServiceConnection connection)
static native void cancelProcessing(long nativeApplication)
unsigned char byte
void setOffset(int offset)
Definition: Renderer.java:73
static native void setMaxGainRadius(long nativeApplication, float value)
static native void save(long nativeApplication, String outputDatabasePath)
static native void setBackfaceCulling(long nativeApplication, boolean enabled)
static native void setWireframe(long nativeApplication, boolean enabled)
static native void onTouchEvent(long nativeApplication, int touchCount, int event0, float x0, float y0, float x1, float y1)
void setToast(Toast toast)
Definition: Renderer.java:68
static native int setMappingParameter(long nativeApplication, String key, String value)
static native void setMeshAngleTolerance(long nativeApplication, float value)
static native void setAppendMode(long nativeApplication, boolean enabled)
void cameraEventCallback(final int type, final String key, final String value)
static native void setCamera(long nativeApplication, int cameraIndex)
static void launchPermissionSettings(Activity activity)
static native void setNodesFiltering(long nativeApplication, boolean enabled)


rtabmap
Author(s): Mathieu Labbe
autogenerated on Mon Jan 23 2023 03:37:30