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