1 package com.introlab.rtabmap;
3 import java.io.BufferedInputStream;
4 import java.io.BufferedOutputStream;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.FilenameFilter;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.io.OutputStream;
12 import java.lang.reflect.InvocationTargetException;
13 import java.lang.reflect.Method;
14 import java.text.SimpleDateFormat;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Date;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.zip.ZipEntry;
21 import java.util.zip.ZipOutputStream;
23 import android.app.ActionBar;
24 import android.app.Activity;
25 import android.app.ActivityManager;
26 import android.app.ActivityManager.MemoryInfo;
27 import android.app.AlertDialog;
28 import android.app.Dialog;
29 import android.app.Notification;
30 import android.app.NotificationManager;
31 import android.app.PendingIntent;
32 import android.app.ProgressDialog;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.DialogInterface;
36 import android.content.DialogInterface.OnShowListener;
37 import android.content.Intent;
38 import android.content.ServiceConnection;
39 import android.content.SharedPreferences;
40 import android.content.pm.ApplicationInfo;
41 import android.content.pm.PackageInfo;
42 import android.content.pm.PackageManager;
43 import android.content.pm.PackageManager.NameNotFoundException;
44 import android.content.res.Configuration;
45 import android.database.Cursor;
46 import android.database.sqlite.SQLiteDatabase;
47 import android.graphics.Bitmap;
48 import android.graphics.Canvas;
49 import android.graphics.Color;
50 import android.graphics.Paint;
51 import android.graphics.Rect;
52 import android.graphics.Typeface;
53 import android.hardware.Camera;
54 import android.hardware.Sensor;
55 import android.hardware.SensorEvent;
56 import android.hardware.SensorEventListener;
57 import android.hardware.SensorManager;
58 import android.graphics.Point;
59 import android.hardware.display.DisplayManager;
60 import android.location.Location;
61 import android.location.LocationListener;
62 import android.location.LocationManager;
63 import android.net.ConnectivityManager;
64 import android.net.NetworkInfo;
65 import android.net.Uri;
66 import android.opengl.GLSurfaceView;
67 import android.os.AsyncTask;
68 import android.os.Bundle;
69 import android.os.Environment;
70 import android.os.Handler;
71 import android.os.Debug;
72 import android.os.IBinder;
73 import android.os.Message;
74 import android.preference.ListPreference;
75 import android.preference.PreferenceManager;
76 import android.text.Editable;
77 import android.text.Html;
78 import android.text.InputType;
79 import android.text.SpannableString;
80 import android.text.TextPaint;
81 import android.text.method.HideReturnsTransformationMethod;
82 import android.text.method.LinkMovementMethod;
83 import android.text.util.Linkify;
84 import android.util.Log;
85 import android.util.TypedValue;
86 import android.view.ContextMenu;
87 import android.view.Display;
88 import android.view.GestureDetector;
89 import android.view.Menu;
90 import android.view.MenuItem;
91 import android.view.MenuItem.OnMenuItemClickListener;
92 import android.view.MenuInflater;
93 import android.view.MotionEvent;
94 import android.view.Surface;
95 import android.view.View;
96 import android.view.ContextMenu.ContextMenuInfo;
97 import android.view.View.OnClickListener;
98 import android.view.View.OnCreateContextMenuListener;
99 import android.view.View.OnTouchListener;
100 import android.view.Window;
101 import android.view.WindowManager;
102 import android.view.inputmethod.EditorInfo;
103 import android.webkit.WebView;
104 import android.webkit.WebViewClient;
105 import android.widget.AdapterView;
106 import android.widget.AdapterView.OnItemLongClickListener;
107 import android.widget.AdapterView.OnItemSelectedListener;
108 import android.widget.ArrayAdapter;
109 import android.widget.Button;
110 import android.widget.EditText;
111 import android.widget.LinearLayout;
112 import android.widget.ListView;
113 import android.widget.NumberPicker;
114 import android.widget.RelativeLayout;
115 import android.widget.SeekBar;
116 import android.widget.SeekBar.OnSeekBarChangeListener;
117 import android.widget.Spinner;
118 import android.widget.TextView;
119 import android.widget.Toast;
120 import android.widget.ToggleButton;
122 import com.google.atap.tangoservice.Tango;
126 public class RTABMapActivity extends Activity implements OnClickListener, OnItemSelectedListener, SensorEventListener {
164 STATE_VISUALIZING_WHILE_LOADING
174 int mStatusBarHeight = 0;
175 int mActionBarHeight = 0;
177 ProgressDialog mProgressDialog;
178 ProgressDialog mExportProgressDialog;
246 GestureDetector mGesDetect = null;
249 ServiceConnection mTangoServiceConnection =
new ServiceConnection() {
250 public void onServiceConnected(ComponentName name,
final IBinder service) {
251 Thread bindThread =
new Thread(
new Runnable() {
255 runOnUiThread(
new Runnable() {
257 mToast.makeText(getApplicationContext(),
258 String.format(
"Failed to intialize Tango!"), mToast.LENGTH_LONG).show();
267 public void onServiceDisconnected(ComponentName name) {
270 mToast.makeText(getApplicationContext(),
271 String.format(
"Tango disconnected!"), mToast.LENGTH_LONG).show();
276 protected void onCreate(Bundle savedInstanceState) {
277 super.onCreate(savedInstanceState);
278 setTitle(R.string.menu_name);
284 Display display = getWindowManager().getDefaultDisplay();
285 display.getSize(mScreenSize);
287 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
288 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
291 setContentView(R.layout.activity_rtabmap);
296 mDecorView = getWindow().getDecorView();
301 mButtonCameraView = (
NDSpinner)findViewById(R.id.camera_button);
302 mButtonPause = (ToggleButton)findViewById(R.id.pause_button);
303 mButtonLighting = (ToggleButton)findViewById(R.id.light_button);
304 mButtonWireframe = (ToggleButton)findViewById(R.id.wireframe_button);
305 mButtonBackfaceShown = (ToggleButton)findViewById(R.id.backface_button);
306 mButtonCloseVisualization = (Button)findViewById(R.id.close_visualization_button);
307 mButtonSaveOnDevice = (Button)findViewById(R.id.button_saveOnDevice);
308 mButtonShareOnSketchfab = (Button)findViewById(R.id.button_shareToSketchfab);
309 mButtonCameraView.setOnItemSelectedListener(
this);
310 mButtonPause.setOnClickListener(
this);
311 mButtonLighting.setOnClickListener(
this);
312 mButtonWireframe.setOnClickListener(
this);
313 mButtonBackfaceShown.setOnClickListener(
this);
314 mButtonCloseVisualization.setOnClickListener(
this);
315 mButtonSaveOnDevice.setOnClickListener(
this);
316 mButtonShareOnSketchfab.setOnClickListener(
this);
317 mButtonLighting.setChecked(
false);
318 mButtonLighting.setVisibility(View.INVISIBLE);
319 mButtonWireframe.setChecked(
false);
320 mButtonWireframe.setVisibility(View.INVISIBLE);
321 mButtonCloseVisualization.setVisibility(View.INVISIBLE);
322 mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
323 mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
324 if(mItemRenderingMesh != null && mItemRenderingTextureMesh != null)
326 mButtonBackfaceShown.setVisibility(mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked()?View.VISIBLE:View.INVISIBLE);
329 ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.camera_view_array, android.R.layout.simple_spinner_item);
330 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
331 mButtonCameraView.setAdapter(adapter);
332 mButtonCameraView.setOnTouchListener(
new OnTouchListener() {
334 public boolean onTouch(View v, MotionEvent event) {
340 mSeekBarOrthoCut = (SeekBar)findViewById(R.id.seekBar_ortho_cut);
341 mSeekBarOrthoCut.setMax(120);
342 mSeekBarOrthoCut.setProgress(80);
343 mSeekBarOrthoCut.setOnSeekBarChangeListener(
new OnSeekBarChangeListener() {
345 public void onProgressChanged(SeekBar seekBar,
int progressValue,
boolean fromUser) {
351 public void onStartTrackingTouch(SeekBar seekBar) {
355 public void onStopTrackingTouch(SeekBar seekBar) {
359 mSeekBarGrid = (SeekBar)findViewById(R.id.seekBar_grid);
360 mSeekBarGrid.setMax(180);
361 mSeekBarGrid.setProgress(90);
362 mSeekBarGrid.setOnSeekBarChangeListener(
new OnSeekBarChangeListener() {
364 public void onProgressChanged(SeekBar seekBar,
int progressValue,
boolean fromUser) {
370 public void onStartTrackingTouch(SeekBar seekBar) {
374 public void onStopTrackingTouch(SeekBar seekBar) {
378 mToast = Toast.makeText(getApplicationContext(),
"", Toast.LENGTH_SHORT);
381 mGLView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
383 mGesDetect =
new GestureDetector(
this,
new DoubleTapGestureDetector());
386 mGLView.setEGLContextClientVersion(2);
387 mGLView.setEGLConfigChooser(8, 8, 8, 8, 24, 0);
388 mGLView.setOnTouchListener(
new OnTouchListener() {
390 public boolean onTouch(View v, MotionEvent event) {
394 mGesDetect.onTouchEvent(event);
399 int pointCount =
event.getPointerCount();
400 if (pointCount == 1) {
401 float normalizedX =
event.getX(0) / mScreenSize.x;
402 float normalizedY =
event.getY(0) / mScreenSize.y;
404 event.getActionMasked(), normalizedX, normalizedY, 0.0f, 0.0f);
406 if (pointCount == 2) {
407 if (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) {
408 int index =
event.getActionIndex() == 0 ? 1 : 0;
409 float normalizedX =
event.getX(index) / mScreenSize.x;
410 float normalizedY =
event.getY(index) / mScreenSize.y;
412 MotionEvent.ACTION_DOWN, normalizedX, normalizedY, 0.0f, 0.0f);
414 float normalizedX0 =
event.getX(0) / mScreenSize.x;
415 float normalizedY0 =
event.getY(0) / mScreenSize.y;
416 float normalizedX1 =
event.getX(1) / mScreenSize.x;
417 float normalizedY1 =
event.getY(1) / mScreenSize.y;
419 normalizedX0, normalizedY0, normalizedX1, normalizedY1);
428 mGLView.setRenderer(mRenderer);
430 mProgressDialog =
new ProgressDialog(
this);
431 mProgressDialog.setCanceledOnTouchOutside(
false);
436 mExportProgressDialog =
new ProgressDialog(
this);
437 mExportProgressDialog.setCanceledOnTouchOutside(
false);
438 mExportProgressDialog.setCancelable(
false);
439 mExportProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
440 mExportProgressDialog.setProgressNumberFormat(null);
441 mExportProgressDialog.setProgressPercentFormat(null);
442 mExportProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
"Cancel",
new DialogInterface.OnClickListener() {
444 public void onClick(DialogInterface dialog,
int which) {
447 mProgressDialog.setTitle(
"");
448 mProgressDialog.setMessage(String.format(
"Cancelling..."));
449 mProgressDialog.show();
455 mToast.makeText(
this,
"Tango Core out dated, please update in Play Store", mToast.LENGTH_LONG).show();
460 mOpenedDatabasePath =
"";
461 mWorkingDirectory =
"";
462 mWorkingDirectoryHuman =
"";
463 mTotalLoopClosures = 0;
464 mLastFastMovementNotificationStamp = System.currentTimeMillis()/1000;
466 if(Environment.getExternalStorageState().compareTo(Environment.MEDIA_MOUNTED)==0)
468 File extStore = Environment.getExternalStorageDirectory();
469 mWorkingDirectory = extStore.getAbsolutePath() +
"/" + getString(R.string.app_name) +
"/";
470 extStore =
new File(mWorkingDirectory);
472 mWorkingDirectoryHuman = RTABMAP_SDCARD_PATH + getString(R.string.app_name) +
"/";
477 mToast.makeText(getApplicationContext(),
478 String.format(
"Failed to get external storage path (SD-CARD, state=%s). Saving disabled.",
479 Environment.getExternalStorageState()), mToast.LENGTH_LONG).show();
484 (
new File(tmpDatabase)).
delete();
485 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
486 boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory),
Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
489 DisplayManager displayManager = (DisplayManager) getSystemService(DISPLAY_SERVICE);
490 if (displayManager != null) {
491 displayManager.registerDisplayListener(
new DisplayManager.DisplayListener() {
493 public void onDisplayAdded(
int displayId) {
498 public void onDisplayChanged(
int displayId) {
499 synchronized (
this) {
501 Display display = getWindowManager().getDefaultDisplay();
502 display.getSize(mScreenSize);
507 public void onDisplayRemoved(
int displayId) {}
512 mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
515 mLocationListener =
new LocationListener() {
516 public void onLocationChanged(Location location) {
517 mLastKnownLocation = location;
518 double stamp = location.getTime()/1000.0;
519 if(!DISABLE_LOG) Log.d(TAG, String.format(
"GPS received at %f (%d)", stamp, location.getTime()));
522 (
double)location.getLongitude(),
523 (double)location.getLatitude(),
524 (double)location.getAltitude(),
525 (double)location.getAccuracy(),
526 (double)mCompassDeg);
529 public void onStatusChanged(String provider,
int status, Bundle extras) {}
531 public void onProviderEnabled(String provider) {}
533 public void onProviderDisabled(String provider) {}
536 mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
538 DISABLE_LOG = !( 0 != ( getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE ) );
544 mCompassDeg =
event.values[0];
555 int resourceId = getResources().getIdentifier(
"status_bar_height",
"dimen",
"android");
556 if (resourceId > 0) {
557 result = getResources().getDimensionPixelSize(resourceId);
563 TypedValue tv =
new TypedValue();
564 if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv,
true))
566 result = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
575 super.onWindowFocusChanged(hasFocus);
579 mRenderer.
setOffset(!hasFocus?-mStatusBarHeight:0);
585 int newVis = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
586 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
587 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
589 newVis |= View.SYSTEM_UI_FLAG_LOW_PROFILE
590 | View.SYSTEM_UI_FLAG_FULLSCREEN
591 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
592 | View.SYSTEM_UI_FLAG_IMMERSIVE;
593 mRenderer.
setOffset(!hasWindowFocus()?-mStatusBarHeight:0);
597 mRenderer.
setOffset(-mStatusBarHeight-mActionBarHeight);
601 mDecorView.setSystemUiVisibility(newVis);
607 if (requestCode == Tango.TANGO_INTENT_ACTIVITYCODE) {
609 if (resultCode == RESULT_CANCELED) {
610 mToast.makeText(
this,
"Motion Tracking Permissions Required!", mToast.LENGTH_SHORT).show();
614 else if (requestCode == SKETCHFAB_ACTIVITY_CODE) {
616 if (resultCode == RESULT_OK) {
617 mAuthToken = data.getStringExtra(RTABMAP_AUTH_TOKEN_KEY);
625 return super.onMenuOpened(featureId, menu);
638 mToast.makeText(
this,
"Press Back once more to exit", mToast.LENGTH_LONG).show();
643 super.onBackPressed();
652 if(!DISABLE_LOG) Log.i(TAG,
"onPause()");
655 if(!mButtonPause.isChecked())
657 mButtonPause.setChecked(
true);
661 mLocationManager.removeUpdates(mLocationListener);
662 mSensorManager.unregisterListener(
this);
666 unbindService(mTangoServiceConnection);
671 mOnPauseStamp = System.currentTimeMillis()/1000;
678 mProgressDialog.setTitle(
"");
681 if(System.currentTimeMillis()/1000 - mOnPauseStamp < 1)
683 mProgressDialog.setMessage(String.format(
"RTAB-Map has been interrupted by another application, Tango should be re-initialized! Set your phone/tablet in Airplane mode if this happens often."));
687 mProgressDialog.setMessage(String.format(
"Hold Tight! Initializing Tango Service..."));
689 mToast.makeText(
this,
"Mapping is paused!", mToast.LENGTH_LONG).show();
693 mProgressDialog.setMessage(String.format(
"Hold Tight! Initializing Tango Service...\nTip: If the camera is still drifting just after the mapping has started, do \"Reset\"."));
695 mProgressDialog.show();
702 if(!DISABLE_LOG) Log.d(TAG,
"update preferences...");
703 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
704 mUpdateRate = sharedPref.getString(getString(R.string.pref_key_update_rate), getString(R.string.pref_default_update_rate));
705 String maxSpeed = sharedPref.getString(getString(R.string.pref_key_max_speed), getString(R.string.pref_default_max_speed));
706 mTimeThr = sharedPref.getString(getString(R.string.pref_key_time_thr), getString(R.string.pref_default_time_thr));
707 String memThr = sharedPref.getString(getString(R.string.pref_key_mem_thr), getString(R.string.pref_default_mem_thr));
708 mLoopThr = sharedPref.getString(getString(R.string.pref_key_loop_thr), getString(R.string.pref_default_loop_thr));
709 String simThr = sharedPref.getString(getString(R.string.pref_key_sim_thr), getString(R.string.pref_default_sim_thr));
710 mMinInliers = sharedPref.getString(getString(R.string.pref_key_min_inliers), getString(R.string.pref_default_min_inliers));
711 mMaxOptimizationError = sharedPref.getString(getString(R.string.pref_key_opt_error), getString(R.string.pref_default_opt_error));
712 float maxOptimizationError = Float.parseFloat(mMaxOptimizationError);
713 if(maxOptimizationError >0 && maxOptimizationError<1)
715 Log.w(TAG,
"Migration of " + getString(R.string.pref_key_opt_error) +
" from " + mMaxOptimizationError +
" to " + getString(R.string.pref_default_opt_error)) ;
716 SharedPreferences.Editor editor = sharedPref.edit();
717 editor.putString(getString(R.string.pref_key_opt_error), getString(R.string.pref_default_opt_error));
719 mMaxOptimizationError = getString(R.string.pref_default_opt_error);
721 mMaxFeatures = sharedPref.getString(getString(R.string.pref_key_features_voc), getString(R.string.pref_default_features_voc));
722 String maxFeaturesLoop = sharedPref.getString(getString(R.string.pref_key_features), getString(R.string.pref_default_features));
723 String featureType = sharedPref.getString(getString(R.string.pref_key_features_type), getString(R.string.pref_default_features_type));
724 boolean keepAllDb = sharedPref.getBoolean(getString(R.string.pref_key_keep_all_db),
Boolean.parseBoolean(getString(R.string.pref_default_keep_all_db)));
725 boolean optimizeFromGraphEnd = sharedPref.getBoolean(getString(R.string.pref_key_optimize_end),
Boolean.parseBoolean(getString(R.string.pref_default_optimize_end)));
726 String optimizer = sharedPref.getString(getString(R.string.pref_key_optimizer), getString(R.string.pref_default_optimizer));
727 mGPSSaved = sharedPref.getBoolean(getString(R.string.pref_key_gps_saved),
Boolean.parseBoolean(getString(R.string.pref_default_gps_saved)));
730 mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mLocationListener);
731 mSensorManager.registerListener(
this, mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_GAME);
734 if(!DISABLE_LOG) Log.d(TAG,
"set mapping parameters");
736 RTABMapLib.
setNodesFiltering(sharedPref.getBoolean(getString(R.string.pref_key_nodes_filtering),
Boolean.parseBoolean(getString(R.string.pref_default_nodes_filtering))));
737 RTABMapLib.
setRawScanSaved(sharedPref.getBoolean(getString(R.string.pref_key_raw_scan_saved),
Boolean.parseBoolean(getString(R.string.pref_default_raw_scan_saved))));
739 RTABMapLib.
setSmoothing(sharedPref.getBoolean(getString(R.string.pref_key_smoothing),
Boolean.parseBoolean(getString(R.string.pref_default_smoothing))));
740 RTABMapLib.
setCameraColor(!sharedPref.getBoolean(getString(R.string.pref_key_fisheye),
Boolean.parseBoolean(getString(R.string.pref_default_fisheye))));
741 RTABMapLib.
setAppendMode(sharedPref.getBoolean(getString(R.string.pref_key_append),
Boolean.parseBoolean(getString(R.string.pref_default_append))));
759 if(!DISABLE_LOG) Log.d(TAG,
"set exporting parameters...");
760 RTABMapLib.
setCloudDensityLevel(Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_density), getString(R.string.pref_default_density))));
761 RTABMapLib.
setMaxCloudDepth(Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_depth), getString(R.string.pref_default_depth))));
762 RTABMapLib.
setMinCloudDepth(Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_min_depth), getString(R.string.pref_default_min_depth))));
763 RTABMapLib.
setPointSize(Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_point_size), getString(R.string.pref_default_point_size))));
765 RTABMapLib.
setMeshTriangleSize(Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_triangle), getString(R.string.pref_default_triangle))));
766 float bgColor = Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_background_color), getString(R.string.pref_default_background_color)));
770 if(!DISABLE_LOG) Log.d(TAG,
"set rendering parameters...");
771 RTABMapLib.
setClusterRatio(Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_cluster_ratio), getString(R.string.pref_default_cluster_ratio))));
772 RTABMapLib.
setMaxGainRadius(Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_gain_max_radius), getString(R.string.pref_default_gain_max_radius))));
773 RTABMapLib.
setRenderingTextureDecimation(Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_rendering_texture_decimation), getString(R.string.pref_default_rendering_texture_decimation))));
775 if(mItemRenderingPointCloud != null)
777 int renderingType = sharedPref.getInt(getString(R.string.pref_key_rendering), Integer.parseInt(getString(R.string.pref_default_rendering)));
778 if(renderingType == 0)
780 mItemRenderingPointCloud.setChecked(
true);
782 else if(renderingType == 1)
784 mItemRenderingMesh.setChecked(
true);
788 mItemRenderingTextureMesh.setChecked(
true);
791 mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
792 mItemRenderingTextureMesh.isChecked());
794 mButtonBackfaceShown.setVisibility(mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked()?View.VISIBLE:View.INVISIBLE);
799 Log.e(TAG,
"Error parsing preferences: " + e.getMessage());
800 mToast.makeText(
this, String.format(
"Error parsing preferences: "+e.getMessage()), mToast.LENGTH_LONG).show();
803 if(!DISABLE_LOG) Log.i(TAG, String.format(
"onResume()"));
805 if (Tango.hasPermission(
this, Tango.PERMISSIONTYPE_MOTION_TRACKING)) {
810 if(!DISABLE_LOG) Log.i(TAG, String.format(
"Asking for motion tracking permission"));
811 startActivityForResult(
812 Tango.getRequestPermissionIntent(Tango.PERMISSIONTYPE_MOTION_TRACKING),
813 Tango.TANGO_INTENT_ACTIVITYCODE);
822 if(!DISABLE_LOG) Log.i(TAG, String.format(
"called setCamera(type=%d);", type));
826 mStatusTexts[1] = getString(R.string.memory)+String.valueOf(mFreeMemoryOnStart>freeMemory?mFreeMemoryOnStart-freeMemory:0);
827 mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(freeMemory);
832 mSeekBarOrthoCut.setVisibility(type!=3?View.INVISIBLE:View.VISIBLE);
833 mSeekBarGrid.setVisibility(mSeekBarGrid.isEnabled() && type==3?View.VISIBLE:View.INVISIBLE);
836 mSeekBarOrthoCut.setMax(120);
837 mSeekBarOrthoCut.setProgress(80);
845 case R.id.gl_surface_view:
848 case R.id.pause_button:
851 case R.id.light_button:
854 case R.id.backface_button:
857 case R.id.wireframe_button:
860 case R.id.close_visualization_button:
861 if(mSavedRenderingType==0)
863 mItemRenderingPointCloud.setChecked(
true);
865 else if(mSavedRenderingType==1)
867 mItemRenderingMesh.setChecked(
true);
871 mItemRenderingTextureMesh.setChecked(
true);
876 case R.id.button_saveOnDevice:
879 case R.id.button_shareToSketchfab:
898 Display display = getWindowManager().getDefaultDisplay();
899 Camera.CameraInfo colorCameraInfo =
new Camera.CameraInfo();
900 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
901 boolean fisheye = sharedPref.getBoolean(getString(R.string.pref_key_fisheye),
Boolean.parseBoolean(getString(R.string.pref_default_fisheye)));
902 Camera.getCameraInfo(fisheye?1:0, colorCameraInfo);
906 class DoubleTapGestureDetector
extends GestureDetector.SimpleOnGestureListener {
909 public boolean onDoubleTap(MotionEvent event) {
910 if(!DISABLE_LOG) Log.i(TAG,
"onDoubleTap");
911 float normalizedX =
event.getX(0) / mScreenSize.x;
912 float normalizedY =
event.getY(0) / mScreenSize.y;
917 public boolean onSingleTapConfirmed(MotionEvent event) {
918 if(!DISABLE_LOG) Log.i(TAG,
"onSingleTapConfirmed");
934 if(!DISABLE_LOG) Log.i(TAG,
"called onCreateOptionsMenu;");
936 MenuInflater inflater = getMenuInflater();
937 inflater.inflate(R.menu.optionmenu, menu);
939 getActionBar().setDisplayShowHomeEnabled(
true);
940 getActionBar().setIcon(R.drawable.ic_launcher);
942 mItemSave = menu.findItem(R.id.save);
943 mItemOpen = menu.findItem(R.id.open);
944 mItemPostProcessing = menu.findItem(R.id.post_processing);
945 mItemExport = menu.findItem(R.id.export);
946 mItemSettings = menu.findItem(R.id.settings);
947 mItemModes = menu.findItem(R.id.modes);
948 mItemReset = menu.findItem(R.id.reset);
949 mItemLocalizationMode = menu.findItem(R.id.localization_mode);
950 mItemTrajectoryMode = menu.findItem(R.id.trajectory_mode);
951 mItemRenderingPointCloud = menu.findItem(R.id.point_cloud);
952 mItemRenderingMesh = menu.findItem(R.id.mesh);
953 mItemRenderingTextureMesh = menu.findItem(R.id.texture_mesh);
954 mItemDataRecorderMode = menu.findItem(R.id.data_recorder);
955 mItemStatusVisibility = menu.findItem(R.id.status);
956 mItemDebugVisibility = menu.findItem(R.id.debug);
957 mItemSave.setEnabled(
false);
958 mItemExport.setEnabled(
false);
959 mItemOpen.setEnabled(
false);
960 mItemPostProcessing.setEnabled(
false);
961 mItemDataRecorderMode.setEnabled(
false);
965 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
966 int renderingType = sharedPref.getInt(getString(R.string.pref_key_rendering), Integer.parseInt(getString(R.string.pref_default_rendering)));
967 if(renderingType == 0)
969 mItemRenderingPointCloud.setChecked(
true);
971 else if(renderingType == 1)
973 mItemRenderingMesh.setChecked(
true);
977 mItemRenderingTextureMesh.setChecked(
true);
980 mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
981 mItemRenderingTextureMesh.isChecked());
983 if(mButtonBackfaceShown != null)
985 mButtonBackfaceShown.setVisibility(mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked()?View.VISIBLE:View.INVISIBLE);
990 Log.e(TAG,
"Error parsing rendering preferences: " + e.getMessage());
991 mToast.makeText(
this, String.format(
"Error parsing rendering preferences: "+e.getMessage()), mToast.LENGTH_LONG).show();
1001 MemoryInfo mi =
new MemoryInfo();
1002 ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
1003 activityManager.getMemoryInfo(mi);
1004 return mi.availMem / 0x100000L;
1009 if(mItemStatusVisibility != null && mItemDebugVisibility != null)
1017 mRenderer.
updateTexts(Arrays.copyOfRange(mStatusTexts, 0, STATUS_TEXTS_POSE_INDEX-1));
1019 else if(mItemDebugVisibility.isChecked())
1021 mRenderer.
updateTexts(Arrays.copyOfRange(mStatusTexts, STATUS_TEXTS_POSE_INDEX-1, mStatusTexts.length));
1035 float optimizationMaxError,
1036 float optimizationMaxErrorRatio,
1037 boolean fastMovement,
1038 String[] statusTexts)
1040 mStatusTexts = statusTexts;
1043 if(mButtonPause!=null)
1045 if(!mButtonPause.isChecked())
1049 long memoryUsed = mFreeMemoryOnStart>memoryFree?mFreeMemoryOnStart-memoryFree:0;
1051 if(memoryFree < 400)
1053 mButtonPause.setChecked(
true);
1056 if(mMemoryWarningDialog!=null)
1058 mMemoryWarningDialog.dismiss();
1059 mMemoryWarningDialog = null;
1062 mMemoryWarningDialog =
new AlertDialog.Builder(
getActivity())
1063 .setTitle(
"Memory is full!")
1064 .setCancelable(
false)
1065 .setMessage(String.format(
"Scanning has been paused because free memory is too " 1066 +
"low (%d MB). You should be able to save the database but some post-processing and exporting options may fail. " 1067 +
"\n\nNote that for large environments, you can save multiple databases and " 1068 +
"merge them with RTAB-Map Desktop version.", memoryUsed))
1069 .setPositiveButton(
"Ok",
new DialogInterface.OnClickListener() {
1070 public void onClick(DialogInterface dialog,
int which) {
1071 mMemoryWarningDialog = null;
1074 .setNeutralButton(
"Save",
new DialogInterface.OnClickListener() {
1075 public void onClick(DialogInterface dialog,
int which) {
1077 mMemoryWarningDialog = null;
1081 mMemoryWarningDialog.show();
1083 else if(mMemoryWarningDialog == null && memoryUsed*3 > memoryFree && (mItemDataRecorderMode == null || !mItemDataRecorderMode.isChecked()))
1085 mMemoryWarningDialog =
new AlertDialog.Builder(
getActivity())
1086 .setTitle(
"Warning: Memory is almost full!")
1087 .setCancelable(
false)
1088 .setMessage(String.format(
"Free memory (%d MB) should be at least 3 times the " 1089 +
"memory used (%d MB) so that some post-processing and exporting options " 1090 +
"have enough memory to work correctly. If you just want to save the database " 1091 +
"after scanning, you can continue until the next warning.\n\n" 1092 +
"Note that showing only point clouds reduces memory needed for rendering.", memoryFree, memoryUsed))
1093 .setPositiveButton(
"Pause",
new DialogInterface.OnClickListener() {
1094 public void onClick(DialogInterface dialog,
int which) {
1095 mButtonPause.setChecked(
true);
1099 .setNeutralButton(
"Continue",
new DialogInterface.OnClickListener() {
1100 public void onClick(DialogInterface dialog,
int which) {
1104 mMemoryWarningDialog.show();
1110 if(mButtonPause!=null && !mButtonPause.isChecked())
1112 long currentTime = System.currentTimeMillis()/1000;
1113 if(loopClosureId > 0)
1115 mToast.setText(String.format(
"Loop closure detected! (%d/%d inliers)", inliers, matches));
1118 else if(rejected > 0)
1120 if(inliers >= Integer.parseInt(mMinInliers))
1122 if(optimizationMaxError > 0.0
f)
1124 mToast.setText(String.format(
"Loop closure rejected, too high graph optimization error (%.3fm: ratio=%.3f < factor=%sx).", optimizationMaxError, optimizationMaxErrorRatio, mMaxOptimizationError));
1128 mToast.setText(String.format(
"Loop closure rejected, graph optimization failed! You may try a different Graph Optimizer (see Mapping options)."));
1133 mToast.setText(String.format(
"Loop closure rejected, not enough inliers (%d/%d <Â %s).", inliers, matches, mMinInliers));
1137 else if(fastMovement)
1139 if(currentTime - mLastFastMovementNotificationStamp > 3)
1141 mToast.setText(
"Move slower... blurry images are not added to map (\"Settings->Mapping...->Maximum Motion Speed\" is enabled).");
1148 mLastFastMovementNotificationStamp = currentTime;
1159 final float updateTime,
1160 final int loopClosureId,
1161 final int highestHypId,
1162 final int databaseMemoryUsed,
1165 final int featuresExtracted,
1166 final float hypothesis,
1167 final int nodesDrawn,
1170 final float rehearsalValue,
1171 final float optimizationMaxError,
1172 final float optimizationMaxErrorRatio,
1173 final float distanceTravelled,
1174 final int fastMovement,
1182 if(!DISABLE_LOG) Log.i(TAG, String.format(
"updateStatsCallback()"));
1185 if(mButtonPause!=null && !mButtonPause.isChecked())
1187 String updateValue = mUpdateRate.compareTo(
"0")==0?
"Max":
mUpdateRate;
1188 statusTexts[0] = getString(R.string.status)+(mItemDataRecorderMode!=null&&mItemDataRecorderMode.isChecked()?String.format(
"Recording (%s Hz)", updateValue):mItemLocalizationMode!=null && mItemLocalizationMode.isChecked()?String.format(
"Localization (%s Hz)", updateValue):String.format(
"Mapping (%s Hz)", updateValue));
1192 statusTexts[0] = mStatusTexts[0];
1196 statusTexts[1] = getString(R.string.memory)+(mFreeMemoryOnStart>memoryFree?mFreeMemoryOnStart-memoryFree:0);
1197 statusTexts[2] = getString(R.string.free_memory)+memoryFree;
1199 if(loopClosureId > 0)
1208 if(mLastKnownLocation != null)
1210 long millisec = System.currentTimeMillis() - mLastKnownLocation.getTime();
1213 statusTexts[3] = getString(R.string.gps)+String.format(
"[too old, %d ms]", millisec);
1217 statusTexts[3] = getString(R.string.gps)+
1218 String.format(
"%.2f %.2f %.2fm %.0fdeg %.0fm",
1219 mLastKnownLocation.getLongitude(),
1220 mLastKnownLocation.getLatitude(),
1221 mLastKnownLocation.getAltitude(),
1223 mLastKnownLocation.getAccuracy());
1228 statusTexts[3] = getString(R.string.gps)+
"[not yet available]";
1232 String formattedDate =
new SimpleDateFormat(
"HH:mm:ss.SSS").format(
new Date());
1233 statusTexts[4] = getString(R.string.time)+formattedDate;
1236 statusTexts[index++] = getString(R.string.nodes)+nodes+
" (" + nodesDrawn +
" shown)";
1237 statusTexts[index++] = getString(R.string.words)+words;
1238 statusTexts[index++] = getString(R.string.database_size)+databaseMemoryUsed;
1239 statusTexts[index++] = getString(R.string.points)+points;
1240 statusTexts[index++] = getString(R.string.polygons)+polygons;
1241 statusTexts[index++] = getString(R.string.update_time)+(int)(updateTime) +
" / " + (mTimeThr.compareTo(
"0")==0?
"No Limit":
mTimeThr);
1242 statusTexts[index++] = getString(R.string.features)+featuresExtracted +
" / " + (mMaxFeatures.compareTo(
"0")==0?
"No Limit":mMaxFeatures.compareTo(
"-1")==0?
"Disabled":
mMaxFeatures);
1243 statusTexts[index++] = getString(R.string.rehearsal)+(int)(rehearsalValue*100.0
f);
1245 statusTexts[index++] = getString(R.string.inliers)+inliers;
1246 statusTexts[index++] = getString(R.string.hypothesis)+(int)(hypothesis*100.0f) +
" / " + (int)(Float.parseFloat(mLoopThr)*100.0f) +
" (" + (loopClosureId>0?loopClosureId:highestHypId)+
")";
1247 statusTexts[index++] = getString(R.string.fps)+(int)fps+
" Hz";
1248 statusTexts[index++] = getString(R.string.distance)+(int)distanceTravelled+
" m";
1249 statusTexts[index++] = String.format(
"Pose (x,y,z): %.2f %.2f %.2f", x,y,z);
1251 runOnUiThread(
new Runnable() {
1253 updateStatsUI(loopClosureId, inliers, matches, rejected, optimizationMaxError, optimizationMaxErrorRatio, fastMovement!=0, statusTexts);
1262 if(!DISABLE_LOG) Log.i(TAG, String.format(
"rtabmapInitEventsUI() status=%d msg=%s", status, msg));
1264 int optimizedMeshDetected = 0;
1266 if(msg.equals(
"Loading optimized cloud...done!"))
1268 optimizedMeshDetected = 1;
1270 else if(msg.equals(
"Loading optimized mesh...done!"))
1272 optimizedMeshDetected = 2;
1274 else if(msg.equals(
"Loading optimized texture mesh...done!"))
1276 optimizedMeshDetected = 3;
1278 if(optimizedMeshDetected > 0)
1281 mSavedRenderingType = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
1282 if(optimizedMeshDetected==1)
1284 mItemRenderingPointCloud.setChecked(
true);
1286 else if(optimizedMeshDetected==2)
1288 mItemRenderingMesh.setChecked(
true);
1292 mItemRenderingTextureMesh.setChecked(
true);
1296 if(mButtonCameraView.getSelectedItemPosition() == 0)
1300 mToast.makeText(
getActivity(), String.format(
"Optimized mesh detected in the database, it is shown while the database is loading..."), mToast.LENGTH_LONG).show();
1301 mProgressDialog.dismiss();
1304 if(mButtonPause!=null)
1306 if(mButtonPause.isChecked())
1308 mStatusTexts[0] = getString(R.string.status)+(status == 1 && msg.isEmpty()?
"Paused":msg);
1310 else if(mItemLocalizationMode!=null && mItemDataRecorderMode!=null)
1312 mStatusTexts[0] = getString(R.string.status)+(status == 1 && msg.isEmpty()?(mItemDataRecorderMode!=null&&mItemDataRecorderMode.isChecked()?
"Recording":mItemLocalizationMode!=null&&mItemLocalizationMode.isChecked()?
"Localization":
"Mapping"):msg);
1316 mStatusTexts[1] = getString(R.string.memory)+String.valueOf(mFreeMemoryOnStart>freeMemory?mFreeMemoryOnStart-freeMemory:0);
1317 mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(freeMemory);
1327 if(!DISABLE_LOG) Log.i(TAG, String.format(
"rtabmapInitEventCallback()"));
1329 runOnUiThread(
new Runnable() {
1340 if(!DISABLE_LOG) Log.i(TAG, String.format(
"updateProgressionUI() count=%d max=%s", count, max));
1342 mExportProgressDialog.setMax(max);
1343 mExportProgressDialog.setProgress(count);
1351 if(!DISABLE_LOG) Log.i(TAG, String.format(
"updateProgressionCallback()"));
1353 runOnUiThread(
new Runnable() {
1377 if(key.equals(
"TangoServiceException"))
1378 str = String.format(
"Tango service exception: %s", value);
1379 else if(key.equals(
"FisheyeOverExposed"))
1381 else if(key.equals(
"FisheyeUnderExposed"))
1383 else if(key.equals(
"ColorOverExposed"))
1385 else if(key.equals(
"ColorUnderExposed"))
1387 else if(key.equals(
"CameraTango"))
1389 else if(key.equals(
"TooFewFeaturesTracked"))
1391 if(!value.equals(
"0"))
1393 str = String.format(
"Too few features (%s) were tracked in the fisheye image. This may result in poor odometry!", value);
1396 else if(key.equals(
"TooClose"))
1398 str = String.format(
"Too close! Tip: Scan from at least ~1 meter from surfaces.", value);
1400 else if(key.equals(
"TangoPoseEventNotReceived"))
1402 str = String.format(
"No valid tango pose event received since %s sec.", value);
1406 str = String.format(
"Unknown Tango event detected!? (type=%d, key=%s, value=%s)", type, key, value);
1410 mToast.setText(str);
1421 if(mButtonPause != null && !mButtonPause.isChecked())
1423 runOnUiThread(
new Runnable() {
1432 int versionNumber = 0;
1435 PackageInfo
pi = getApplicationContext().getPackageManager().getPackageInfo(packageName,
1436 PackageManager.GET_META_DATA);
1437 versionNumber = pi.versionCode;
1438 }
catch (NameNotFoundException
e) {
1439 e.printStackTrace();
1441 return (minVersion <= versionNumber);
1447 mExportProgressDialog.setTitle(
"Post-Processing");
1448 mExportProgressDialog.setMessage(String.format(
"Please wait while optimizing..."));
1449 mExportProgressDialog.setProgress(0);
1450 mExportProgressDialog.show();
1453 Thread workingThread =
new Thread(
new Runnable() {
1456 runOnUiThread(
new Runnable() {
1458 if(mExportProgressDialog.isShowing())
1460 mExportProgressDialog.dismiss();
1461 if(loopDetected >= 0)
1463 mTotalLoopClosures+=loopDetected;
1464 mProgressDialog.setTitle(
"Post-Processing");
1465 mProgressDialog.setMessage(String.format(
"Optimization done! Increasing visual appeal..."));
1466 mProgressDialog.show();
1468 else if(loopDetected < 0)
1470 mToast.makeText(
getActivity(), String.format(
"Optimization failed!"), mToast.LENGTH_LONG).show();
1475 mProgressDialog.dismiss();
1476 mToast.makeText(
getActivity(), String.format(
"Optimization canceled"), mToast.LENGTH_LONG).show();
1483 workingThread.start();
1487 public void handleMessage(Message msg) {
1497 mHudVisible =
false;
1516 if(mItemSave != null)
1522 notouchHandler.removeCallbacks(notouchCallback);
1523 notouchHandler.postDelayed(notouchCallback, NOTOUCH_TIMEOUT);
1527 notouchHandler.removeCallbacks(notouchCallback);
1534 mToast.makeText(
getActivity(), String.format(
"Re-adding %d online clouds, this may take some time...", mMapNodes), mToast.LENGTH_LONG).show();
1537 if(!DISABLE_LOG) Log.i(TAG, String.format(
"updateState() state=%s hud=%d", state.toString(), mHudVisible?1:0));
1540 case STATE_PROCESSING:
1541 mButtonLighting.setVisibility(View.INVISIBLE);
1542 mButtonWireframe.setVisibility(View.INVISIBLE);
1543 mButtonCloseVisualization.setVisibility(View.INVISIBLE);
1544 mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
1545 mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
1546 mItemSave.setEnabled(
false);
1547 mItemExport.setEnabled(
false);
1548 mItemOpen.setEnabled(
false);
1549 mItemPostProcessing.setEnabled(
false);
1550 mItemSettings.setEnabled(
false);
1551 mItemReset.setEnabled(
false);
1552 mItemModes.setEnabled(
false);
1553 mButtonPause.setVisibility(View.INVISIBLE);
1555 case STATE_VISUALIZING:
1556 mButtonLighting.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
1557 mButtonWireframe.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
1558 mButtonCloseVisualization.setVisibility(mHudVisible && mButtonPause.isChecked()?View.VISIBLE:View.INVISIBLE);
1559 mButtonCloseVisualization.setEnabled(
true);
1560 mButtonSaveOnDevice.setVisibility(mHudVisible && mButtonPause.isChecked()?View.VISIBLE:View.INVISIBLE);
1561 mButtonShareOnSketchfab.setVisibility(mHudVisible && mButtonPause.isChecked()?View.VISIBLE:View.INVISIBLE);
1562 mItemSave.setEnabled(mButtonPause.isChecked());
1563 mItemExport.setEnabled(mButtonPause.isChecked() && !mItemDataRecorderMode.isChecked());
1564 mItemOpen.setEnabled(
false);
1565 mItemPostProcessing.setEnabled(
false);
1566 mItemSettings.setEnabled(
true);
1567 mItemReset.setEnabled(
true);
1568 mItemModes.setEnabled(
true);
1569 mButtonPause.setVisibility(mHudVisible && mItemLocalizationMode.isChecked()?View.VISIBLE:View.GONE);
1570 mItemLocalizationMode.setEnabled(mButtonPause.isChecked());
1571 mItemDataRecorderMode.setEnabled(mButtonPause.isChecked());
1573 case STATE_VISUALIZING_WHILE_LOADING:
1574 mButtonLighting.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
1575 mButtonWireframe.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
1576 mButtonCloseVisualization.setVisibility(mHudVisible?View.VISIBLE:View.INVISIBLE);
1577 mButtonCloseVisualization.setEnabled(
false);
1578 mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
1579 mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
1580 mItemSave.setEnabled(
false);
1581 mItemExport.setEnabled(
false);
1582 mItemOpen.setEnabled(
false);
1583 mItemPostProcessing.setEnabled(
false);
1584 mItemSettings.setEnabled(
false);
1585 mItemReset.setEnabled(
false);
1586 mItemModes.setEnabled(
false);
1587 mButtonPause.setVisibility(View.INVISIBLE);
1590 mButtonLighting.setVisibility(View.INVISIBLE);
1591 mButtonWireframe.setVisibility(View.INVISIBLE);
1592 mButtonCloseVisualization.setVisibility(View.INVISIBLE);
1593 mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
1594 mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
1595 mItemSave.setEnabled(mButtonPause.isChecked());
1596 mItemExport.setEnabled(mButtonPause.isChecked() && !mItemDataRecorderMode.isChecked());
1597 mItemOpen.setEnabled(mButtonPause.isChecked() && !mItemDataRecorderMode.isChecked());
1598 mItemPostProcessing.setEnabled(mButtonPause.isChecked() && !mItemDataRecorderMode.isChecked());
1599 mItemSettings.setEnabled(
true);
1600 mItemReset.setEnabled(
true);
1601 mItemModes.setEnabled(
true);
1602 mButtonPause.setVisibility(mHudVisible?View.VISIBLE:View.INVISIBLE);
1603 mItemDataRecorderMode.setEnabled(mButtonPause.isChecked());
1606 mButtonCameraView.setVisibility(mHudVisible?View.VISIBLE:View.INVISIBLE);
1607 mButtonBackfaceShown.setVisibility(mHudVisible && (mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked())?View.VISIBLE:View.INVISIBLE);
1608 mSeekBarOrthoCut.setVisibility(mHudVisible && mButtonCameraView.getSelectedItemPosition() == 3?View.VISIBLE:View.INVISIBLE);
1609 mSeekBarGrid.setVisibility(mHudVisible && mSeekBarGrid.isEnabled() && mButtonCameraView.getSelectedItemPosition() == 3?View.VISIBLE:View.INVISIBLE);
1623 if(mButtonPause.isChecked())
1627 mStatusTexts[0] = getString(R.string.status)+
"Paused";
1629 mStatusTexts[1] = getString(R.string.memory)+String.valueOf(mFreeMemoryOnStart>freeMemory?mFreeMemoryOnStart-freeMemory:0);
1630 mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(freeMemory);
1633 mMapIsEmpty =
false;
1634 mDateOnPause =
new Date();
1637 if(!mOnPause && !mItemLocalizationMode.isChecked() && !mItemDataRecorderMode.isChecked() && memoryFree >= 100 && mMapNodes>2)
1641 .setTitle(
"Mapping Paused! Optimize Now?")
1642 .setMessage(
"Do you want to do standard map optimization now? This can be also done later using \"Optimize\" menu.")
1643 .setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
1644 public void onClick(DialogInterface dialog,
int which) {
1648 .setNegativeButton(
"No",
new DialogInterface.OnClickListener() {
1649 public void onClick(DialogInterface dialog,
int which) {
1658 if(mMemoryWarningDialog != null)
1660 mMemoryWarningDialog.dismiss();
1661 mMemoryWarningDialog=null;
1664 mLastFastMovementNotificationStamp = System.currentTimeMillis()/1000;
1666 if(mItemDataRecorderMode.isChecked())
1668 mToast.makeText(
getActivity(), String.format(
"Data Recorder Mode: no map is created, only raw data is recorded."), mToast.LENGTH_LONG).show();
1670 else if(!mMapIsEmpty)
1672 if(mItemLocalizationMode!=null && mItemLocalizationMode.isChecked())
1674 mToast.makeText(
getActivity(), String.format(
"Localization mode"), mToast.LENGTH_LONG).show();
1678 mToast.makeText(
getActivity(), String.format(
"On resume, a new map is created. Tip: Try relocalizing in the previous area."), mToast.LENGTH_LONG).show();
1681 else if(mMapIsEmpty && mItemLocalizationMode!=null && mItemLocalizationMode.isChecked())
1683 mItemLocalizationMode.setChecked(
false);
1685 mToast.makeText(
getActivity(), String.format(
"Disabled localization mode as the map is empty, now mapping..."), mToast.LENGTH_LONG).show();
1692 if(!DISABLE_LOG) Log.i(TAG,
"called onOptionsItemSelected; selected item: " + item);
1693 int itemId = item.getItemId();
1694 if (itemId == R.id.post_processing_standard)
1698 else if (itemId == R.id.detect_more_loop_closures)
1700 mProgressDialog.setTitle(
"Post-Processing");
1701 mProgressDialog.setMessage(String.format(
"Please wait while detecting more loop closures..."));
1702 mProgressDialog.show();
1704 Thread workingThread =
new Thread(
new Runnable() {
1707 runOnUiThread(
new Runnable() {
1709 mProgressDialog.dismiss();
1710 if(loopDetected >= 0)
1712 mTotalLoopClosures+=loopDetected;
1713 mToast.makeText(
getActivity(), String.format(
"Detection done! %d new loop closure(s) added.", loopDetected), mToast.LENGTH_SHORT).show();
1715 else if(loopDetected < 0)
1717 mToast.makeText(
getActivity(), String.format(
"Detection failed!"), mToast.LENGTH_SHORT).show();
1724 workingThread.start();
1726 else if (itemId == R.id.global_graph_optimization)
1728 mProgressDialog.setTitle(
"Post-Processing");
1729 mProgressDialog.setMessage(String.format(
"Global graph optimization..."));
1730 mProgressDialog.show();
1732 Thread workingThread =
new Thread(
new Runnable() {
1735 runOnUiThread(
new Runnable() {
1737 mProgressDialog.dismiss();
1740 mToast.makeText(
getActivity(), String.format(
"Optimization done!"), mToast.LENGTH_SHORT).show();
1744 mToast.makeText(
getActivity(), String.format(
"Optimization failed!"), mToast.LENGTH_SHORT).show();
1751 workingThread.start();
1753 else if (itemId == R.id.polygons_filtering)
1755 mProgressDialog.setTitle(
"Post-Processing");
1756 mProgressDialog.setMessage(String.format(
"Noise filtering..."));
1757 mProgressDialog.show();
1760 else if (itemId == R.id.gain_compensation_fast)
1762 mProgressDialog.setTitle(
"Post-Processing");
1763 mProgressDialog.setMessage(String.format(
"Adjusting Colors (Fast)..."));
1764 mProgressDialog.show();
1767 else if (itemId == R.id.gain_compensation_full)
1769 mProgressDialog.setTitle(
"Post-Processing");
1770 mProgressDialog.setMessage(String.format(
"Adjusting Colors (Full)..."));
1771 mProgressDialog.show();
1774 else if (itemId == R.id.bilateral_filtering)
1776 mProgressDialog.setTitle(
"Post-Processing");
1777 mProgressDialog.setMessage(String.format(
"Mesh smoothing..."));
1778 mProgressDialog.show();
1781 else if (itemId == R.id.sba)
1783 mProgressDialog.setTitle(
"Post-Processing");
1784 mProgressDialog.setMessage(String.format(
"Bundle adjustement..."));
1785 mProgressDialog.show();
1787 Thread workingThread =
new Thread(
new Runnable() {
1790 runOnUiThread(
new Runnable() {
1792 mProgressDialog.dismiss();
1795 mToast.makeText(
getActivity(), String.format(
"Optimization done!"), mToast.LENGTH_SHORT).show();
1799 mToast.makeText(
getActivity(), String.format(
"Optimization failed!"), mToast.LENGTH_SHORT).show();
1805 workingThread.start();
1807 else if(itemId == R.id.status)
1809 item.setChecked(!item.isChecked());
1812 else if(itemId == R.id.debug)
1814 item.setChecked(!item.isChecked());
1817 else if(itemId == R.id.mesh || itemId == R.id.texture_mesh || itemId == R.id.point_cloud)
1819 item.setChecked(
true);
1821 mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
1822 mItemRenderingTextureMesh.isChecked());
1829 int type = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
1830 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
1831 SharedPreferences.Editor editor = sharedPref.edit();
1832 editor.putInt(getString(R.string.pref_key_rendering), type);
1837 else if(itemId == R.id.map_shown)
1839 item.setChecked(!item.isChecked());
1842 else if(itemId == R.id.odom_shown)
1844 item.setChecked(!item.isChecked());
1847 else if(itemId == R.id.localization_mode)
1849 item.setChecked(!item.isChecked());
1853 mButtonPause.setVisibility(mItemLocalizationMode.isChecked()?View.VISIBLE:View.GONE);
1856 else if(itemId == R.id.trajectory_mode)
1858 item.setChecked(!item.isChecked());
1862 else if(itemId == R.id.graph_optimization)
1864 item.setChecked(!item.isChecked());
1867 else if(itemId == R.id.graph_visible)
1869 item.setChecked(!item.isChecked());
1872 else if(itemId == R.id.grid_visible)
1874 item.setChecked(!item.isChecked());
1875 mSeekBarGrid.setEnabled(item.isChecked());
1876 mSeekBarGrid.setVisibility(mHudVisible && mSeekBarGrid.isEnabled()&&mButtonCameraView.getSelectedItemPosition() == 3?View.VISIBLE:View.INVISIBLE);
1879 else if (itemId == R.id.save)
1881 AlertDialog.Builder builder =
new AlertDialog.Builder(
this);
1882 builder.setTitle(
"RTAB-Map Database Name (*.db):");
1883 final EditText input =
new EditText(
this);
1884 input.setInputType(InputType.TYPE_CLASS_TEXT);
1885 if(mOpenedDatabasePath.isEmpty())
1887 String
timeStamp =
new SimpleDateFormat(
"yyMMdd-HHmmss").format(mDateOnPause);
1888 input.setText(timeStamp);
1892 File
f =
new File(mOpenedDatabasePath);
1893 String name = f.getName();
1894 input.setText(name.substring(0,name.lastIndexOf(
".")));
1896 input.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
1897 input.setSelectAllOnFocus(
true);
1899 builder.setView(input);
1900 builder.setPositiveButton(
"OK",
new DialogInterface.OnClickListener() {
1902 public void onClick(DialogInterface dialog,
int which)
1904 final String fileName = input.getText().toString();
1906 if(!fileName.isEmpty())
1908 File newFile =
new File(mWorkingDirectory + fileName +
".db");
1909 if(newFile.exists())
1912 .setTitle(
"File Already Exists")
1913 .setMessage(
"Do you want to overwrite the existing file?")
1914 .setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
1915 public void onClick(DialogInterface dialog,
int which) {
1919 .setNegativeButton(
"No",
new DialogInterface.OnClickListener() {
1920 public void onClick(DialogInterface dialog,
int which) {
1934 AlertDialog alertToShow = builder.create();
1935 alertToShow.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
1938 else if(itemId == R.id.reset)
1940 mTotalLoopClosures = 0;
1944 mStatusTexts[index++] = getString(R.string.nodes)+0;
1945 mStatusTexts[index++] = getString(R.string.words)+0;
1946 mStatusTexts[index++] = getString(R.string.database_size)+0;
1947 mStatusTexts[index++] = getString(R.string.points)+0;
1948 mStatusTexts[index++] = getString(R.string.polygons)+0;
1949 mStatusTexts[index++] = getString(R.string.update_time)+0;
1950 mStatusTexts[index++] = getString(R.string.features)+0;
1951 mStatusTexts[index++] = getString(R.string.rehearsal)+0;
1952 mStatusTexts[index++] = getString(R.string.total_loop)+0;
1953 mStatusTexts[index++] = getString(R.string.inliers)+0;
1954 mStatusTexts[index++] = getString(R.string.hypothesis)+0;
1955 mStatusTexts[index++] = getString(R.string.fps)+0;
1956 mStatusTexts[index++] = getString(R.string.distance)+0;
1957 mStatusTexts[index++] = String.format(
"Pose (x,y,z): 0 0 0");
1960 mOpenedDatabasePath =
"";
1961 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
1962 boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory),
Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
1967 mItemSave.setEnabled(
false);
1968 mItemExport.setEnabled(
false);
1969 mItemPostProcessing.setEnabled(
false);
1972 else if(itemId == R.id.data_recorder)
1974 final boolean dataRecorderOldState = item.isChecked();
1976 .setTitle(
"Data Recorder Mode")
1977 .setMessage(
"Changing from/to data recorder mode will close the current session. Do you want to continue?")
1978 .setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
1979 public void onClick(DialogInterface dialog,
int which) {
1981 mTotalLoopClosures = 0;
1984 mStatusTexts[index++] = getString(R.string.nodes)+0;
1985 mStatusTexts[index++] = getString(R.string.words)+0;
1986 mStatusTexts[index++] = getString(R.string.database_size)+0;
1987 mStatusTexts[index++] = getString(R.string.points)+0;
1988 mStatusTexts[index++] = getString(R.string.polygons)+0;
1989 mStatusTexts[index++] = getString(R.string.update_time)+0;
1990 mStatusTexts[index++] = getString(R.string.features)+0;
1991 mStatusTexts[index++] = getString(R.string.rehearsal)+0;
1992 mStatusTexts[index++] = getString(R.string.total_loop)+0;
1993 mStatusTexts[index++] = getString(R.string.inliers)+0;
1994 mStatusTexts[index++] = getString(R.string.hypothesis)+0;
1995 mStatusTexts[index++] = getString(R.string.fps)+0;
1996 mStatusTexts[index++] = getString(R.string.distance)+0;
1997 mStatusTexts[index++] = String.format(
"Pose (x,y,z): 0 0 0");
2000 mItemDataRecorderMode.setChecked(!dataRecorderOldState);
2003 mOpenedDatabasePath =
"";
2004 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
getActivity());
2005 boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory),
Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
2009 mItemOpen.setEnabled(!mItemDataRecorderMode.isChecked() && mButtonPause.isChecked());
2010 mItemPostProcessing.setEnabled(!mItemDataRecorderMode.isChecked() && mButtonPause.isChecked());
2011 mItemExport.setEnabled(!mItemDataRecorderMode.isChecked() && mButtonPause.isChecked());
2013 mItemLocalizationMode.setEnabled(!mItemDataRecorderMode.isChecked());
2015 if(mItemDataRecorderMode.isChecked())
2017 mToast.makeText(
getActivity(), String.format(
"Data recorder mode activated! Tip: You can increase data update rate in Parameters menu under Mapping options."), mToast.LENGTH_LONG).show();
2021 mToast.makeText(
getActivity(), String.format(
"Data recorder mode deactivated!"), mToast.LENGTH_LONG).show();
2025 .setNegativeButton(
"No",
new DialogInterface.OnClickListener() {
2026 public void onClick(DialogInterface dialog,
int which) {
2032 else if(itemId == R.id.export_point_cloud ||
2033 itemId == R.id.export_point_cloud_highrez)
2035 final boolean regenerateCloud = itemId == R.id.export_point_cloud_highrez;
2037 export(
false,
false, regenerateCloud,
false, 0);
2039 else if(itemId == R.id.export_optimized_mesh ||
2040 itemId == R.id.export_optimized_mesh_texture)
2042 final boolean isOBJ = itemId == R.id.export_optimized_mesh_texture;
2044 RelativeLayout linearLayout =
new RelativeLayout(
this);
2045 final NumberPicker aNumberPicker =
new NumberPicker(
this);
2046 aNumberPicker.setMaxValue(9);
2047 aNumberPicker.setMinValue(0);
2048 aNumberPicker.setWrapSelectorWheel(
false);
2049 aNumberPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
2050 aNumberPicker.setFormatter(
new NumberPicker.Formatter() {
2052 public String format(
int i) {
2057 return String.format(
"%d00 000", i);
2060 aNumberPicker.setValue(2);
2064 Method method = aNumberPicker.getClass().getDeclaredMethod(
"changeValueByOne",
boolean.
class);
2065 method.setAccessible(
true);
2066 method.invoke(aNumberPicker,
true);
2067 }
catch (NoSuchMethodException
e) {
2068 e.printStackTrace();
2069 }
catch (IllegalArgumentException e) {
2070 e.printStackTrace();
2071 }
catch (IllegalAccessException e) {
2072 e.printStackTrace();
2073 }
catch (InvocationTargetException e) {
2074 e.printStackTrace();
2078 RelativeLayout.LayoutParams params =
new RelativeLayout.LayoutParams(50, 50);
2079 RelativeLayout.LayoutParams numPicerParams =
new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
2080 numPicerParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
2082 linearLayout.setLayoutParams(params);
2083 linearLayout.addView(aNumberPicker,numPicerParams);
2085 AlertDialog.Builder alertDialogBuilder =
new AlertDialog.Builder(
this);
2086 alertDialogBuilder.setTitle(
"Maximum polygons");
2087 alertDialogBuilder.setView(linearLayout);
2089 .setCancelable(
false)
2090 .setPositiveButton(
"Ok",
2091 new DialogInterface.OnClickListener() {
2092 public void onClick(DialogInterface dialog,
2094 export(isOBJ,
true,
false,
true, aNumberPicker.getValue()*100000);
2097 .setNegativeButton(
"Cancel",
2098 new DialogInterface.OnClickListener() {
2099 public void onClick(DialogInterface dialog,
2104 AlertDialog alertDialog = alertDialogBuilder.create();
2107 else if(itemId == R.id.open)
2111 else if(itemId == R.id.settings)
2114 startActivity(intent);
2117 else if(itemId == R.id.about)
2120 about.setTitle(
"About RTAB-Map");
2130 if(files.length > 0)
2132 String[] filesWithSize =
new String[files.length];
2133 for(
int i = 0; i<filesWithSize.length; ++i)
2135 File filePath =
new File(mWorkingDirectory+files[i]);
2136 long mb = filePath.length()/(1024*1024);
2137 filesWithSize[i] = files[i] +
" ("+mb+
" MB)";
2140 ArrayList<HashMap<String, String> > arrayList =
new ArrayList<HashMap<String, String> >();
2141 for (
int i = 0; i < filesWithSize.length; i++) {
2142 HashMap<String, String> hashMap =
new HashMap<String, String>();
2143 hashMap.put(
"name", filesWithSize[i]);
2144 hashMap.put(
"path", mWorkingDirectory + files[i]);
2145 arrayList.add(hashMap);
2147 String[] from = {
"name",
"path"};
2148 int[] to = {R.id.textView, R.id.imageView};
2151 AlertDialog.Builder builder =
new AlertDialog.Builder(
this);
2152 builder.setTitle(
"Choose Your File (*.db)");
2153 builder.setAdapter(simpleAdapter,
new DialogInterface.OnClickListener() {
2155 public void onClick(DialogInterface dialog,
final int which) {
2159 .setTitle(
"Opening database...")
2160 .setMessage(
"Do you want to adjust colors now?\nThis can be done later under Optimize menu.")
2161 .setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
2162 public void onClick(DialogInterface dialog,
int whichIn) {
2166 .setNeutralButton(
"No",
new DialogInterface.OnClickListener() {
2167 public void onClick(DialogInterface dialog,
int whichIn) {
2176 final AlertDialog ad = builder.create();
2177 ad.setOnShowListener(
new OnShowListener()
2180 public void onShow(DialogInterface dialog)
2182 ListView lv = ad.getListView();
2183 ad.registerForContextMenu(lv);
2184 lv.setOnCreateContextMenuListener(
new OnCreateContextMenuListener() {
2187 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
2189 if (v.getId()==ad.getListView().getId()) {
2190 AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
2191 final int position = info.position;
2192 menu.setHeaderTitle(files[position]);
2193 menu.add(Menu.NONE, 0, 0,
"Rename").setOnMenuItemClickListener(
new OnMenuItemClickListener() {
2195 public boolean onMenuItemClick(MenuItem item) {
2196 AlertDialog.Builder builderRename =
new AlertDialog.Builder(
getActivity());
2197 builderRename.setTitle(
"RTAB-Map Database Name (*.db):");
2198 final EditText input =
new EditText(
getActivity());
2199 input.setInputType(InputType.TYPE_CLASS_TEXT);
2201 input.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
2202 input.setSelectAllOnFocus(
true);
2204 builderRename.setView(input);
2205 builderRename.setPositiveButton(
"OK",
new DialogInterface.OnClickListener() {
2207 public void onClick(DialogInterface dialog,
int which)
2209 final String fileName = input.getText().toString();
2211 if(!fileName.isEmpty())
2213 File newFile =
new File(mWorkingDirectory + fileName +
".db");
2214 if(newFile.exists())
2217 .setTitle(
"File Already Exists")
2218 .setMessage(String.format(
"Name %s already used, choose another name.", fileName))
2223 File from =
new File(mWorkingDirectory, files[position]);
2224 File to =
new File(mWorkingDirectory, fileName +
".db");
2232 AlertDialog alertToShow = builderRename.create();
2233 alertToShow.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
2238 menu.add(Menu.NONE, 1, 1,
"Delete").setOnMenuItemClickListener(
new OnMenuItemClickListener() {
2240 public boolean onMenuItemClick(MenuItem item) {
2241 DialogInterface.OnClickListener dialogClickListener =
new DialogInterface.OnClickListener() {
2243 public void onClick(DialogInterface dialog,
int which) {
2245 case DialogInterface.BUTTON_POSITIVE:
2246 Log.e(TAG, String.format(
"Yes delete %s!", files[position]));
2247 (
new File(mWorkingDirectory+files[position])).
delete();
2252 case DialogInterface.BUTTON_NEGATIVE:
2258 AlertDialog.Builder builder =
new AlertDialog.Builder(
getActivity());
2259 builder.setTitle(String.format(
"Delete %s", files[position]))
2260 .setMessage(
"Are you sure?")
2261 .setPositiveButton(
"Yes", dialogClickListener)
2262 .setNegativeButton(
"No", dialogClickListener).show();
2266 menu.add(Menu.NONE, 2, 2,
"Share").setOnMenuItemClickListener(
new OnMenuItemClickListener() {
2268 public boolean onMenuItemClick(MenuItem item) {
2270 File
f =
new File(mWorkingDirectory+files[position]);
2271 final int fileSizeMB = (int)f.length()/(1024 * 1024);
2272 Intent shareIntent =
new Intent();
2273 shareIntent.setAction(Intent.ACTION_SEND);
2274 shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
2275 shareIntent.setType(
"application/octet-stream");
2276 startActivity(Intent.createChooser(shareIntent, String.format(
"Sharing database \"%s\" (%d MB)...", files[position], fileSizeMB)));
2291 private void export(
final boolean isOBJ,
final boolean meshing,
final boolean regenerateCloud,
final boolean optimized,
final int optimizedMaxPolygons)
2293 final String extension = isOBJ?
".obj" :
".ply";
2296 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
2297 final String cloudVoxelSizeStr = sharedPref.getString(getString(R.string.pref_key_cloud_voxel), getString(R.string.pref_default_cloud_voxel));
2298 final float cloudVoxelSize = Float.parseFloat(cloudVoxelSizeStr);
2299 final int textureSize = isOBJ?Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_texture_size), getString(R.string.pref_default_texture_size))):0;
2300 final int textureCount = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_texture_count), getString(R.string.pref_default_texture_count)));
2301 final int normalK = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_normal_k), getString(R.string.pref_default_normal_k)));
2302 final float maxTextureDistance = Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_max_texture_distance), getString(R.string.pref_default_max_texture_distance)));
2303 final int minTextureClusterSize = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_min_texture_cluster_size), getString(R.string.pref_default_min_texture_cluster_size)));
2304 final float optimizedVoxelSize = cloudVoxelSize;
2305 final int optimizedDepth = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_opt_depth), getString(R.string.pref_default_opt_depth)));
2306 final float optimizedColorRadius = Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_opt_color_radius), getString(R.string.pref_default_opt_color_radius)));
2307 final boolean optimizedCleanWhitePolygons = sharedPref.getBoolean(getString(R.string.pref_key_opt_clean_white),
Boolean.parseBoolean(getString(R.string.pref_default_opt_clean_white)));
2308 final int optimizedMinClusterSize = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_opt_min_cluster_size), getString(R.string.pref_default_opt_min_cluster_size)));
2309 final boolean blockRendering = sharedPref.getBoolean(getString(R.string.pref_key_block_render),
Boolean.parseBoolean(getString(R.string.pref_default_block_render)));
2312 mExportProgressDialog.setTitle(
"Exporting");
2313 mExportProgressDialog.setMessage(String.format(
"Please wait while preparing data to export..."));
2314 mExportProgressDialog.setProgress(0);
2316 final State previousState = mState;
2318 mExportProgressDialog.show();
2321 Thread exportThread =
new Thread(
new Runnable() {
2324 final long startTime = System.currentTimeMillis()/1000;
2336 optimizedMaxPolygons,
2337 optimizedColorRadius,
2338 optimizedCleanWhitePolygons,
2339 optimizedMinClusterSize,
2341 minTextureClusterSize,
2343 runOnUiThread(
new Runnable() {
2345 if(mExportProgressDialog.isShowing())
2349 if(!meshing && cloudVoxelSize>0.0
f)
2351 mToast.makeText(
getActivity(), String.format(
"Cloud assembled and voxelized at %s m.", cloudVoxelSizeStr), mToast.LENGTH_LONG).show();
2354 final long endTime = System.currentTimeMillis()/1000;
2356 if(endTime-startTime > 10)
2360 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
getActivity());
2361 boolean notifySound = sharedPref.getBoolean(getString(R.string.pref_key_notification_sound),
Boolean.parseBoolean(getString(R.string.pref_default_notification_sound)));
2362 Notification n =
new Notification.Builder(
getActivity())
2363 .setContentTitle(getString(R.string.app_name))
2364 .setContentText(
"Data generated and ready to be exported!")
2365 .setSmallIcon(R.drawable.ic_launcher)
2366 .setDefaults(notifySound?Notification.DEFAULT_SOUND:0)
2367 .setAutoCancel(
true).build();
2369 NotificationManager notificationManager =
2370 (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
2372 notificationManager.notify(0, n);
2377 .setCancelable(
false)
2378 .setTitle(
"Export Successful! (" + (endTime-startTime) +
" sec)")
2379 .setMessage(Html.fromHtml(
"Do you want visualize the result before saving to file or sharing to <a href=\"https://sketchfab.com/about\">Sketchfab</a>?"))
2380 .setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
2381 public void onClick(DialogInterface dialog,
int which) {
2383 mSavedRenderingType = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
2386 mItemRenderingPointCloud.setChecked(
true);
2390 mItemRenderingMesh.setChecked(
true);
2394 mItemRenderingTextureMesh.setChecked(
true);
2396 if(!optimizedCleanWhitePolygons)
2398 mButtonLighting.setChecked(
true);
2403 if(mButtonCameraView.getSelectedItemPosition() == 0)
2409 .setNegativeButton(
"No",
new DialogInterface.OnClickListener() {
2410 public void onClick(DialogInterface dialog,
int which) {
2414 AlertDialog d2 =
new AlertDialog.Builder(
getActivity())
2415 .setCancelable(
false)
2416 .setTitle(
"Save to...")
2417 .setMessage(Html.fromHtml(
"Do you want to share to <a href=\"https://sketchfab.com/about\">Sketchfab</a> or save it on device?"))
2418 .setPositiveButton(
"Share to Sketchfab",
new DialogInterface.OnClickListener() {
2419 public void onClick(DialogInterface dialog,
int which) {
2423 .setNegativeButton(
"Save on device",
new DialogInterface.OnClickListener() {
2424 public void onClick(DialogInterface dialog,
int which) {
2428 .setNeutralButton(
"Cancel",
new DialogInterface.OnClickListener() {
2429 public void onClick(DialogInterface dialog,
int which) {
2436 ((TextView)d2.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
2442 ((TextView)d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
2447 mToast.makeText(
getActivity(), String.format(
"Exporting map failed!"), mToast.LENGTH_LONG).show();
2449 mExportProgressDialog.dismiss();
2453 mProgressDialog.dismiss();
2454 mToast.makeText(
getActivity(), String.format(
"Export canceled"), mToast.LENGTH_LONG).show();
2461 exportThread.start();
2466 final String newDatabasePath = mWorkingDirectory + fileName +
".db";
2467 final String newDatabasePathHuman = mWorkingDirectoryHuman + fileName +
".db";
2468 mProgressDialog.setTitle(
"Saving");
2469 if(mOpenedDatabasePath.equals(newDatabasePath))
2471 mProgressDialog.setMessage(String.format(
"Please wait while updating \"%s\"...", newDatabasePathHuman));
2475 mProgressDialog.setMessage(String.format(
"Please wait while saving \"%s\"...", newDatabasePathHuman));
2477 mProgressDialog.show();
2478 final State previousState = mState;
2480 Thread saveThread =
new Thread(
new Runnable() {
2483 runOnUiThread(
new Runnable() {
2486 if(mOpenedDatabasePath.equals(newDatabasePath))
2488 msg = String.format(
"Database \"%s\" updated.", newDatabasePathHuman);
2492 msg = String.format(
"Database saved to \"%s\".", newDatabasePathHuman);
2498 PendingIntent pIntent = PendingIntent.getActivity(
getActivity(), (
int) System.currentTimeMillis(), intent, 0);
2499 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
getActivity());
2500 boolean notifySound = sharedPref.getBoolean(getString(R.string.pref_key_notification_sound),
Boolean.parseBoolean(getString(R.string.pref_default_notification_sound)));
2501 Notification n =
new Notification.Builder(
getActivity())
2502 .setContentTitle(getString(R.string.app_name))
2503 .setContentText(msg)
2504 .setSmallIcon(R.drawable.ic_launcher)
2505 .setContentIntent(pIntent)
2506 .setDefaults(notifySound?Notification.DEFAULT_SOUND:0)
2507 .setAutoCancel(
true).build();
2509 NotificationManager notificationManager =
2510 (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
2512 notificationManager.notify(0, n);
2514 final File
f =
new File(newDatabasePath);
2515 final int fileSizeMB = (int)f.length()/(1024 * 1024);
2518 .setTitle(
"Database saved!")
2519 .setMessage(String.format(
"Database \"%s\" (%d MB) successfully saved on the SD-CARD! Share it?", newDatabasePathHuman, fileSizeMB))
2520 .setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
2521 public void onClick(DialogInterface dialog,
int which) {
2523 Intent shareIntent =
new Intent();
2524 shareIntent.setAction(Intent.ACTION_SEND);
2525 shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
2526 shareIntent.setType(
"application/octet-stream");
2527 startActivity(Intent.createChooser(shareIntent,
"Sharing..."));
2530 if(!mItemDataRecorderMode.isChecked())
2532 mOpenedDatabasePath = newDatabasePath;
2534 mProgressDialog.dismiss();
2538 .setNegativeButton(
"No",
new DialogInterface.OnClickListener() {
2539 public void onClick(DialogInterface dialog,
int which) {
2541 if(!mItemDataRecorderMode.isChecked())
2543 mOpenedDatabasePath = newDatabasePath;
2545 mProgressDialog.dismiss();
2559 AlertDialog.Builder builder =
new AlertDialog.Builder(
this);
2560 builder.setTitle(
"Model Name:");
2561 final EditText input =
new EditText(
this);
2562 input.setInputType(InputType.TYPE_CLASS_TEXT);
2563 builder.setView(input);
2564 if(mOpenedDatabasePath.isEmpty())
2566 String
timeStamp =
new SimpleDateFormat(
"yyMMdd-HHmmss").format(mDateOnPause);
2567 input.setText(timeStamp);
2571 File
f =
new File(mOpenedDatabasePath);
2572 String name = f.getName();
2573 input.setText(name.substring(0,name.lastIndexOf(
".")));
2575 input.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
2576 input.setSelectAllOnFocus(
true);
2578 builder.setCancelable(
false);
2579 builder.setNegativeButton(
"Cancel",
new DialogInterface.OnClickListener() {
2581 public void onClick(DialogInterface dialog,
int which)
2587 builder.setPositiveButton(
"Ok",
new DialogInterface.OnClickListener() {
2589 public void onClick(DialogInterface dialog,
int which)
2591 final String fileName = input.getText().toString();
2593 if(!fileName.isEmpty())
2595 File newFile =
new File(mWorkingDirectory + RTABMAP_EXPORT_DIR + fileName +
".zip");
2596 if(newFile.exists())
2599 .setTitle(
"File Already Exists")
2600 .setMessage(
"Do you want to overwrite the existing file?")
2601 .setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
2602 public void onClick(DialogInterface dialog,
int which) {
2606 .setNegativeButton(
"No",
new DialogInterface.OnClickListener() {
2607 public void onClick(DialogInterface dialog,
int which) {
2620 AlertDialog alertToShow = builder.create();
2621 alertToShow.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
2627 Log.i(TAG, String.format(
"Write exported mesh to \"%s\"", fileName));
2629 mProgressDialog.setTitle(
"Saving to sd-card");
2630 mProgressDialog.setMessage(String.format(
"Compressing the files..."));
2631 mProgressDialog.show();
2633 Thread workingThread =
new Thread(
new Runnable() {
2635 boolean success =
false;
2637 File tmpDir =
new File(mWorkingDirectory + RTABMAP_TMP_DIR);
2639 String[] fileNames =
Util.
loadFileList(mWorkingDirectory + RTABMAP_TMP_DIR,
false);
2640 if(!DISABLE_LOG) Log.i(TAG, String.format(
"Deleting %d files in \"%s\"", fileNames.length, mWorkingDirectory + RTABMAP_TMP_DIR));
2641 for(
int i=0; i<fileNames.length; ++i)
2643 File
f =
new File(mWorkingDirectory + RTABMAP_TMP_DIR +
"/" + fileNames[i]);
2646 if(!DISABLE_LOG) Log.i(TAG, String.format(
"Deleted \"%s\"", f.getPath()));
2650 if(!DISABLE_LOG) Log.i(TAG, String.format(
"Failed deleting \"%s\"", f.getPath()));
2653 File exportDir =
new File(mWorkingDirectory + RTABMAP_EXPORT_DIR);
2656 final String pathHuman = mWorkingDirectoryHuman + RTABMAP_EXPORT_DIR + fileName +
".zip";
2657 final String zipOutput = mWorkingDirectory+RTABMAP_EXPORT_DIR+fileName+
".zip";
2661 if(fileNames.length > 0)
2663 String[] filesToZip =
new String[fileNames.length];
2664 for(
int i=0; i<fileNames.length; ++i)
2666 filesToZip[i] = mWorkingDirectory + RTABMAP_TMP_DIR +
"/" + fileNames[i];
2669 File toZIPFile =
new File(zipOutput);
2674 Util.
zip(filesToZip, zipOutput);
2677 catch(IOException
e)
2679 final String msg = e.getMessage();
2680 runOnUiThread(
new Runnable() {
2682 mToast.makeText(
getActivity(), String.format(
"Exporting mesh \"%s\" failed! Error=%s", pathHuman, msg), mToast.LENGTH_LONG).show();
2691 runOnUiThread(
new Runnable() {
2693 mProgressDialog.dismiss();
2695 final File
f =
new File(zipOutput);
2696 final int fileSizeMB = (int)f.length()/(1024 * 1024);
2699 .setTitle(
"Database saved!")
2700 .setMessage(String.format(
"Mesh \"%s\" (%d MB) successfully exported on the SD-CARD! Share it?", pathHuman, fileSizeMB))
2701 .setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
2702 public void onClick(DialogInterface dialog,
int which) {
2704 Intent shareIntent =
new Intent();
2705 shareIntent.setAction(Intent.ACTION_SEND);
2706 shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
2707 shareIntent.setType(
"application/zip");
2708 startActivity(Intent.createChooser(shareIntent,
"Sharing..."));
2713 .setNegativeButton(
"No",
new DialogInterface.OnClickListener() {
2714 public void onClick(DialogInterface dialog,
int which) {
2724 runOnUiThread(
new Runnable() {
2726 mProgressDialog.dismiss();
2727 mToast.makeText(
getActivity(), String.format(
"Exporting mesh \"%s\" failed! No files found in tmp directory!? Last export may have failed or have been canceled.", pathHuman), mToast.LENGTH_LONG).show();
2734 workingThread.start();
2739 mOpenedDatabasePath = mWorkingDirectory + fileName;
2741 Log.i(TAG,
"Open database " + mOpenedDatabasePath);
2743 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
getActivity());
2744 final boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory),
Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
2747 mProgressDialog.setTitle(
"Loading");
2748 mProgressDialog.setMessage(String.format(
"Opening database \"%s\"...", fileName));
2749 mProgressDialog.show();
2752 Thread openThread =
new Thread(
new Runnable() {
2758 runOnUiThread(
new Runnable() {
2763 mProgressDialog.dismiss();
2765 .setCancelable(
false)
2767 .setMessage(
"The map is loaded but optimization of the map's graph has " 2768 +
"failed, so the map cannot be shown. Change the Graph Optimizer approach used" 2769 +
" or enable/disable if the graph is optimized from graph " 2770 +
"end in \"Settings -> Mapping...\" and try opening again.")
2771 .setPositiveButton(
"Open Settings",
new DialogInterface.OnClickListener() {
2772 public void onClick(DialogInterface dialog,
int which) {
2774 startActivity(intent);
2778 .setNegativeButton(
"Close",
new DialogInterface.OnClickListener() {
2779 public void onClick(DialogInterface dialog,
int which) {
2784 else if(status == -2)
2787 mProgressDialog.dismiss();
2789 .setCancelable(
false)
2791 .setMessage(
"Failed to open database: Out of memory! Try " 2792 +
"again after lowering Point Cloud Density in Settings.")
2793 .setPositiveButton(
"Open Settings",
new DialogInterface.OnClickListener() {
2794 public void onClick(DialogInterface dialog,
int which) {
2796 startActivity(intent);
2800 .setNegativeButton(
"Close",
new DialogInterface.OnClickListener() {
2801 public void onClick(DialogInterface dialog,
int which) {
2808 if(status >= 1 && status<=3)
2810 mProgressDialog.dismiss();
2813 mToast.makeText(
getActivity(), String.format(
"Database loaded!"), mToast.LENGTH_LONG).show();
2815 else if(!mItemTrajectoryMode.isChecked())
2817 if(mButtonCameraView.getSelectedItemPosition() == 0)
2823 mProgressDialog.setTitle(
"Loading");
2824 mProgressDialog.setMessage(String.format(
"Database \"%s\" loaded. Please wait while rendering point clouds and meshes...", fileName));
2835 public void copy(File src, File
dst)
throws IOException {
2836 InputStream in =
new FileInputStream(src);
2837 OutputStream out =
new FileOutputStream(
dst);
2842 while ((len = in.read(buf)) > 0) {
2843 out.write(buf, 0, len);
2853 intent.putExtra(RTABMAP_AUTH_TOKEN_KEY, mAuthToken);
2854 intent.putExtra(RTABMAP_WORKING_DIR_KEY, mWorkingDirectory);
2856 if(mOpenedDatabasePath.isEmpty())
2858 intent.putExtra(RTABMAP_FILENAME_KEY,
new SimpleDateFormat(
"yyMMdd-HHmmss").format(mDateOnPause));
2862 File
f =
new File(mOpenedDatabasePath);
2863 String name = f.getName();
2864 intent.putExtra(RTABMAP_FILENAME_KEY, name.substring(0,name.lastIndexOf(
".")));
2867 startActivityForResult(intent, SKETCHFAB_ACTIVITY_CODE);
void writeExportedFiles(final String fileName)
static final String RTABMAP_EXPORT_DIR
GLM_FUNC_DECL T roll(detail::tquat< T, P > const &x)
static boolean DISABLE_LOG
static native void setLighting(boolean enabled)
static native void setMinCloudDepth(float value)
static final int SKETCHFAB_ACTIVITY_CODE
MenuItem mItemRenderingTextureMesh
static native void setOrthoCropFactor(float value)
static native void setMeshAngleTolerance(float value)
String mOpenedDatabasePath
static native void setMeshRendering(boolean enabled, boolean withTexture)
static final String RTABMAP_TMP_DB
static native void setTrajectoryMode(boolean enabled)
static final String RTABMAP_FILENAME_KEY
void tangoEventCallback(final int type, final String key, final String value)
void onNothingSelected(AdapterView<?> parent)
MenuItem mItemPostProcessing
void updateProgressionCallback(final int count, final int max)
void onSensorChanged(SensorEvent event)
static final int MIN_TANGO_CORE_VERSION
static native int setMappingParameter(String key, String value)
static native void setScreenRotation(int displayRotation, int cameraRotation)
static native void setBackgroundColor(float gray)
static native boolean exportMesh(float cloudVoxelSize, boolean regenerateCloud, boolean meshing, int textureSize, int textureCount, int normalK, boolean optimized, float optimizedVoxelSize, int optimizedDepth, int optimizedMaxPolygons, float optimizedColorRadius, boolean optimizedCleanWhitePolygons, int optimizedMinClusterSize, float optimizedMaxTextureDistance, int optimizedMinTextureClusterSize, boolean blockRendering)
ros::Time * timeStamp(M &m)
static native void setClusterRatio(float value)
static native void setMapCloudShown(boolean shown)
static native void setAppendMode(boolean enabled)
void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
static native void setOdomCloudShown(boolean shown)
MenuItem mItemStatusVisibility
void setSelection(int position, boolean animate)
final int STATUS_TEXTS_POSE_INDEX
void onPanelClosed(int featureId, Menu menu)
void export(final boolean isOBJ, final boolean meshing, final boolean regenerateCloud, final boolean optimized, final int optimizedMaxPolygons)
MenuItem mItemRenderingPointCloud
GLM_FUNC_DECL genType e()
static native void setNodesFiltering(boolean enabled)
static native void setGridRotation(float value)
static native int openDatabase(String databasePath, boolean databaseInMemory, boolean optimize)
final int STATUS_TEXTS_SIZE
static native void setMaxCloudDepth(float value)
ToggleButton mButtonLighting
static native void setMeshTriangleSize(int value)
void setProgressDialog(ProgressDialog progressDialog)
GLM_FUNC_DECL T pitch(detail::tquat< T, P > const &x)
void onWindowFocusChanged(boolean hasFocus)
static native void setBackfaceCulling(boolean enabled)
LocationManager mLocationManager
static final long NOTOUCH_TIMEOUT
static native void setCameraColor(boolean enabled)
static void zip(String file, String zipFile)
void rtabmapInitEventCallback(final int status, final String msg)
static native void onPause()
static native void setRawScanSaved(boolean enabled)
static native void setLocalizationMode(boolean enabled)
static native int openDatabase2(String databaseSource, String databasePath, boolean databaseInMemory, boolean optimize)
static final String EXTRA_KEY_PERMISSIONTYPE
void onActivityResult(int requestCode, int resultCode, Intent data)
AlertDialog mMemoryWarningDialog
void resetNoTouchTimer(boolean showHud)
void updateTexts(String[] texts)
void onAccuracyChanged(Sensor sensor, int accuracy)
static native void setGPS(double stamp, double longitude, double latitude, double altitude, double accuracy, double bearing)
static final String RTABMAP_OPENED_DB_PATH_KEY
boolean onOptionsItemSelected(MenuItem item)
static native void setCloudDensityLevel(int value)
SensorManager mSensorManager
ToggleButton mButtonPause
static native void cancelProcessing()
MenuItem mItemDataRecorderMode
GLM_FUNC_DECL genType pi()
void updateProgressionUI(int count, int max)
void setTextColor(float color)
NDSpinner mButtonCameraView
static native void setPointSize(float value)
void openDatabase(final String fileName, final boolean optimize)
static final String RTABMAP_WORKING_DIR_KEY
MenuItem mItemTrajectoryMode
MenuItem mItemRenderingMesh
static native void setRenderingTextureDecimation(int value)
void onCreate(Bundle savedInstanceState)
void setAndroidOrientation()
void standardOptimization()
void tangoEventUI(int type, String key, String value)
static String[] loadFileList(String directory, final boolean databasesOnly)
RecoveryProgressState state
boolean CheckTangoCoreVersion(int minVersion)
void saveDatabase(String fileName)
void updateState(State state)
static native void setCamera(int cameraIndex)
void copy(File src, File dst)
String mMaxOptimizationError
Button mButtonShareOnSketchfab
static final String EXTRA_VALUE_ADF
LocationListener mLocationListener
static native void setPausedMapping(boolean paused)
static native void setGridVisible(boolean visible)
static native boolean writeExportedMesh(String directory, String name)
GLM_FUNC_DECL genType max(genType const &x, genType const &y)
MenuItem mItemDebugVisibility
void stopDisconnectTimer()
static native boolean onTangoServiceConnected(IBinder binder)
static native void save(String outputDatabasePath)
Button mButtonSaveOnDevice
void rtabmapInitEventUI(int status, String msg)
boolean onMenuOpened(int featureId, Menu menu)
void setNavVisibility(boolean visible)
MenuItem mItemLocalizationMode
static native void onTouchEvent(int touchCount, int event0, float x0, float y0, float x1, float y1)
String mWorkingDirectoryHuman
static native void setFullResolution(boolean enabled)
static native void setDataRecorderMode(boolean enabled)
GLM_FUNC_DECL T yaw(detail::tquat< T, P > const &x)
Button mButtonCloseVisualization
static final String RTABMAP_AUTH_TOKEN_KEY
RTABMapActivity getActivity()
static native boolean postExportation(boolean visualize)
static native void setMaxGainRadius(float value)
static final boolean bindTangoService(final Context context, ServiceConnection connection)
static final String TANGO_PACKAGE_NAME
Location mLastKnownLocation
static final String RTABMAP_TMP_DIR
void setOffset(int offset)
void updateStatsCallback(final int nodes, final int words, final int points, final int polygons, final float updateTime, final int loopClosureId, final int highestHypId, final int databaseMemoryUsed, final int inliers, final int matches, final int featuresExtracted, final float hypothesis, final int nodesDrawn, final float fps, final int rejected, final float rehearsalValue, final float optimizationMaxError, final float optimizationMaxErrorRatio, final float distanceTravelled, final int fastMovement, final float x, final float y, final float z, final float roll, final float pitch, final float yaw)
ToggleButton mButtonWireframe
boolean onCreateOptionsMenu(Menu menu)
STATE_VISUALIZING_WHILE_LOADING
static native void setGraphOptimization(boolean enabled)
ToggleButton mButtonBackfaceShown
void run(ClassLoader *loader)
static native int postProcessing(int approach)
static native void setOnlineBlending(boolean enabled)
static native void onCreate(RTABMapActivity activity)
static native void setWireframe(boolean enabled)
long mLastFastMovementNotificationStamp
void setToast(Toast toast)
static final String RTABMAP_SDCARD_PATH
void updateStatsUI(int loopClosureId, int inliers, int matches, int rejected, float optimizationMaxError, float optimizationMaxErrorRatio, boolean fastMovement, String[] statusTexts)
static native void setSmoothing(boolean enabled)
static final String RTABMAP_TMP_FILENAME
static native void setGraphVisible(boolean visible)