1 package com.introlab.rtabmap;
4 import java.io.FileInputStream;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.OutputStream;
10 import java.lang.reflect.InvocationTargetException;
11 import java.lang.reflect.Method;
12 import java.text.SimpleDateFormat;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Date;
16 import java.util.HashMap;
17 import java.util.Timer;
18 import java.util.TimerTask;
20 import android.Manifest;
21 import android.app.ActivityManager;
22 import android.app.ActivityManager.MemoryInfo;
23 import android.app.AlertDialog;
24 import android.app.Notification;
25 import android.app.NotificationManager;
26 import android.app.PendingIntent;
27 import android.app.ProgressDialog;
28 import android.content.ComponentName;
29 import android.content.ContentResolver;
30 import android.content.ContentValues;
31 import android.content.Context;
32 import android.content.DialogInterface;
33 import android.content.DialogInterface.OnShowListener;
34 import android.content.Intent;
35 import android.content.ServiceConnection;
36 import android.content.SharedPreferences;
37 import android.content.pm.ApplicationInfo;
38 import android.content.pm.PackageInfo;
39 import android.content.pm.PackageManager;
40 import android.content.pm.PackageManager.NameNotFoundException;
41 import android.database.Cursor;
42 import android.hardware.Camera;
43 import android.hardware.Sensor;
44 import android.hardware.SensorEvent;
45 import android.hardware.SensorEventListener;
46 import android.hardware.SensorManager;
47 import android.graphics.Matrix;
48 import android.graphics.Point;
49 import android.hardware.display.DisplayManager;
50 import android.location.Location;
51 import android.location.LocationListener;
52 import android.location.LocationManager;
53 import android.media.MediaScannerConnection;
54 import android.net.Uri;
55 import android.net.wifi.WifiInfo;
56 import android.net.wifi.WifiManager;
57 import android.opengl.GLSurfaceView;
58 import android.os.Bundle;
59 import android.os.Environment;
60 import android.os.Handler;
61 import android.os.IBinder;
62 import android.os.Message;
63 import android.preference.PreferenceManager;
64 import android.provider.MediaStore;
65 import android.provider.OpenableColumns;
66 import android.support.v4.app.FragmentActivity;
67 import android.support.v4.content.FileProvider;
68 import android.text.InputType;
69 import android.util.Log;
70 import android.util.TypedValue;
71 import android.view.ContextMenu;
72 import android.view.Display;
73 import android.view.GestureDetector;
74 import android.view.Menu;
75 import android.view.MenuItem;
76 import android.view.MenuItem.OnMenuItemClickListener;
77 import android.view.MenuInflater;
78 import android.view.MotionEvent;
79 import android.view.View;
80 import android.view.ContextMenu.ContextMenuInfo;
81 import android.view.View.OnClickListener;
82 import android.view.View.OnCreateContextMenuListener;
83 import android.view.View.OnTouchListener;
84 import android.view.WindowManager;
85 import android.view.inputmethod.EditorInfo;
86 import android.widget.AdapterView;
87 import android.widget.AdapterView.OnItemSelectedListener;
88 import android.widget.ArrayAdapter;
89 import android.widget.Button;
90 import android.widget.EditText;
91 import android.widget.ImageButton;
92 import android.widget.ListView;
93 import android.widget.NumberPicker;
94 import android.widget.RelativeLayout;
95 import android.widget.SeekBar;
96 import android.widget.SeekBar.OnSeekBarChangeListener;
97 import android.widget.Toast;
98 import android.widget.ToggleButton;
100 import com.google.ar.core.ArCoreApk;
101 import com.google.atap.tangoservice.Tango;
102 import com.huawei.hiar.AREnginesApk;
107 public class RTABMapActivity extends FragmentActivity implements OnClickListener, OnItemSelectedListener, SensorEventListener {
153 STATE_VISUALIZING_WHILE_LOADING
163 int mStatusBarHeight = 0;
164 int mActionBarHeight = 0;
166 ProgressDialog mProgressDialog;
167 ProgressDialog mExportProgressDialog;
230 Sensor mAccelerometer;
231 Sensor mMagnetometer;
232 Sensor mAmbientTemperature;
233 Sensor mAmbientLight;
234 Sensor mAmbientAirPressure;
235 Sensor mAmbientRelativeHumidity;
248 private float[]
mR =
new float[9];
262 GestureDetector mGesDetect =
null;
265 int mCameraDriver = 0;
270 boolean mCameraServiceConnectionUsed =
false;
271 ServiceConnection mCameraServiceConnection =
new ServiceConnection() {
272 public void onServiceConnected(ComponentName
name,
final IBinder service) {
273 Thread bindThread =
new Thread(
new Runnable() {
276 runOnUiThread(
new Runnable() {
278 mProgressDialog.dismiss();
279 if(!cameraStartSucess)
281 mToast.makeText(getApplicationContext(),
282 String.format(
"Failed to intialize Tango camera! RTAB-Map may not be built with Tango support."),
mToast.LENGTH_LONG).show();
283 if(mCameraServiceConnectionUsed)
286 getActivity().unbindService(mCameraServiceConnection);
288 mCameraServiceConnectionUsed =
false;
305 public void onServiceDisconnected(ComponentName
name) {
308 mToast.makeText(getApplicationContext(),
309 String.format(
"Tango disconnected!"),
mToast.LENGTH_LONG).show();
314 protected void onCreate(Bundle savedInstanceState) {
315 super.onCreate(savedInstanceState);
316 setTitle(
R.string.menu_name);
324 Display display = getWindowManager().getDefaultDisplay();
328 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
329 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
332 setContentView(
R.layout.activity_rtabmap);
337 mDecorView = getWindow().getDecorView();
344 mButtonStop = (ImageButton)findViewById(
R.id.stop_button);
378 ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this,
R.array.camera_view_array, android.R.layout.simple_spinner_item);
379 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
383 public boolean onTouch(View
v, MotionEvent event) {
394 public void onProgressChanged(SeekBar seekBar,
int progressValue,
boolean fromUser) {
400 public void onStartTrackingTouch(SeekBar seekBar) {
404 public void onStopTrackingTouch(SeekBar seekBar) {
411 mSeekBarGrid.setOnSeekBarChangeListener(
new OnSeekBarChangeListener() {
413 public void onProgressChanged(SeekBar seekBar,
int progressValue,
boolean fromUser) {
419 public void onStartTrackingTouch(SeekBar seekBar) {
423 public void onStopTrackingTouch(SeekBar seekBar) {
427 mToast = Toast.makeText(getApplicationContext(),
"", Toast.LENGTH_SHORT);
430 mGLView = (GLSurfaceView) findViewById(
R.id.gl_surface_view);
432 mGesDetect =
new GestureDetector(
this,
new DoubleTapGestureDetector());
435 mGLView.setEGLContextClientVersion(2);
436 mGLView.setEGLConfigChooser(8, 8, 8, 8, 24, 0);
437 mGLView.setOnTouchListener(
new OnTouchListener() {
439 public boolean onTouch(View
v, MotionEvent event) {
443 mGesDetect.onTouchEvent(event);
448 int pointCount =
event.getPointerCount();
449 if (pointCount == 1) {
453 event.getActionMasked(), normalizedX, normalizedY, 0.0f, 0.0f);
455 if (pointCount == 2) {
456 if (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) {
457 int index =
event.getActionIndex() == 0 ? 1 : 0;
458 float normalizedX =
event.getX(index) /
mScreenSize.x;
459 float normalizedY =
event.getY(index) /
mScreenSize.y;
461 MotionEvent.ACTION_DOWN, normalizedX, normalizedY, 0.0f, 0.0f);
463 float normalizedX0 =
event.getX(0) /
mScreenSize.x;
464 float normalizedY0 =
event.getY(0) /
mScreenSize.y;
465 float normalizedX1 =
event.getX(1) /
mScreenSize.x;
466 float normalizedY1 =
event.getY(1) /
mScreenSize.y;
468 normalizedX0, normalizedY0, normalizedX1, normalizedY1);
479 mProgressDialog =
new ProgressDialog(
this);
480 mProgressDialog.setCanceledOnTouchOutside(
false);
485 mExportProgressDialog =
new ProgressDialog(
this);
486 mExportProgressDialog.setCanceledOnTouchOutside(
false);
487 mExportProgressDialog.setCancelable(
false);
488 mExportProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
489 mExportProgressDialog.setProgressNumberFormat(
null);
490 mExportProgressDialog.setProgressPercentFormat(
null);
491 mExportProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
"Cancel",
new DialogInterface.OnClickListener() {
493 public void onClick(DialogInterface dialog, int which) {
494 RTABMapLib.cancelProcessing(nativeApplication);
496 mProgressDialog.setTitle(
"");
497 mProgressDialog.setMessage(String.format(
"Cancelling..."));
498 mProgressDialog.show();
509 int targetSdkVersion= 0;
511 ApplicationInfo
app = this.getPackageManager().getApplicationInfo(
"com.introlab.rtabmap", 0);
512 targetSdkVersion =
app.targetSdkVersion;
513 }
catch (NameNotFoundException
e) {
517 if(Environment.getExternalStorageState().compareTo(Environment.MEDIA_MOUNTED)==0 &&
521 if(targetSdkVersion < 30)
523 extStore = Environment.getExternalStorageDirectory();
527 extStore =
getActivity().getExternalFilesDirs(
null)[0];
530 mWorkingDirectory = extStore.getAbsolutePath() +
"/" + getString(
R.string.app_name) +
"/";
538 mToast.makeText(getApplicationContext(),
539 String.format(
"Failed to get external storage path (state=%s). Saving disabled.",
540 Environment.getExternalStorageState()),
mToast.LENGTH_LONG).show();
543 DisplayManager displayManager = (DisplayManager) getSystemService(DISPLAY_SERVICE);
544 if (displayManager !=
null) {
545 displayManager.registerDisplayListener(
new DisplayManager.DisplayListener() {
547 public void onDisplayAdded(
int displayId) {
552 public void onDisplayChanged(
int displayId) {
553 synchronized (
this) {
555 Display display = getWindowManager().getDefaultDisplay();
561 public void onDisplayRemoved(
int displayId) {}
566 mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
570 public void onLocationChanged(Location location) {
572 double stamp = location.getTime()/1000.0;
573 if(!
DISABLE_LOG) Log.d(
TAG, String.format(
"GPS received at %f (%d)", stamp, location.getTime()));
577 (
double)location.getLongitude(),
578 (
double)location.getLatitude(),
579 (
double)location.getAltitude(),
580 (
double)location.getAccuracy(),
584 public void onStatusChanged(String provider,
int status, Bundle extras) {}
586 public void onProviderEnabled(String provider) {}
588 public void onProviderDisabled(String provider) {}
591 mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
592 mAccelerometer =
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
593 mMagnetometer =
mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
594 mAmbientTemperature =
mSensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);
595 mAmbientLight =
mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
596 mAmbientAirPressure =
mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
597 mAmbientRelativeHumidity =
mSensorManager.getDefaultSensor(Sensor.TYPE_RELATIVE_HUMIDITY);
598 float []
values = {1,0,0,0,0,1,0,-1,0};
600 mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
604 DISABLE_LOG = !( 0 != ( getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE ) );
606 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
607 String cameraDriverStr = sharedPref.getString(getString(
R.string.pref_key_camera_driver), getString(
R.string.pref_default_camera_driver));
608 mCameraDriver = Integer.parseInt(cameraDriverStr);
613 if (!PermissionHelper.hasPermission(
this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
614 PermissionHelper.requestPermission(
this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
619 Intent intent = getIntent();
620 String
action = intent.getAction();
621 String
type = intent.getType();
623 if (Intent.ACTION_SEND.equals(action) &&
type !=
null) {
624 if (
"application/octet-stream".equals(
type)) {
625 Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
626 if (imageUri !=
null) {
628 Log.i(
TAG,
"Intent received: " + imageUri.getPath() +
" Name:" + fileName);
629 if(fileName.endsWith(
".db"))
634 mToast.makeText(
this, fileName +
" already exists in RTAB-Map's library! Cannot be copied.",
mToast.LENGTH_LONG).show();
638 copy(imageUri, file);
644 }
else if (Intent.ACTION_SEND_MULTIPLE.equals(action) &&
type !=
null) {
645 if (
type.startsWith(
"application/")) {
646 ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
647 if (imageUris !=
null) {
648 boolean added =
false;
649 for(Uri imageUri: imageUris)
652 Log.i(
TAG,
"Intent received: " + imageUri.getPath() +
" Name:" + fileName);
653 if(fileName.endsWith(
".db"))
658 copy(imageUri, file);
663 Log.e(
TAG, fileName +
" already exists in RTAB-Map's library! Cannot be copied.");
680 public void copy(File src, File
dst)
throws IOException {
681 InputStream in =
new FileInputStream(src);
682 OutputStream
out =
new FileOutputStream(
dst);
685 byte[] buf =
new byte[1024];
687 while ((
len = in.read(buf)) > 0) {
694 public void copy(Uri uri, File file)
698 in = getApplicationContext().getContentResolver().openInputStream(uri);
700 OutputStream
out =
new FileOutputStream(
file);
701 byte[] buf =
new byte[1024];
703 while ((
len = in.read(buf)) > 0) {
708 }
catch (IOException
e) {
709 Log.e(TAG,
e.getMessage());
715 if (uri.getScheme().equals(
"content")) {
716 Cursor cursor = getContentResolver().query(uri,
null,
null,
null,
null);
718 if (cursor !=
null && cursor.moveToFirst()) {
719 result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
727 int cut =
result.lastIndexOf(
'/');
739 Log.i(TAG,
"postCreate()");
742 if(files.length == 0)
744 mButtonLibrary.setVisibility(View.INVISIBLE);
750 Log.i(TAG, String.format(
"updateCameraDriverSettings() mCameraDriver=%d RTABMapLib.isBuiltWith(%d)=%d", mCameraDriver, mCameraDriver,
RTABMapLib.
isBuiltWith(nativeApplication, mCameraDriver)?1:0));
752 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
753 String cameraDriverStr = sharedPref.getString(getString(
R.string.pref_key_camera_driver), getString(
R.string.pref_default_camera_driver));
754 mCameraDriver = Integer.parseInt(cameraDriverStr);
756 if(mCameraDriver == -1)
760 SharedPreferences.Editor editor = sharedPref.edit();
761 editor.putString(getString(
R.string.pref_key_camera_driver),
"0");
765 if(mCameraDriver == 0 && (!CheckTangoCoreVersion(MIN_TANGO_CORE_VERSION) || !
RTABMapLib.
isBuiltWith(nativeApplication, 0)))
769 SharedPreferences.Editor editor = sharedPref.edit();
770 editor.putString(getString(
R.string.pref_key_camera_driver),
"2");
773 else if(mIsARCoreAvailable)
775 SharedPreferences.Editor editor = sharedPref.edit();
776 editor.putString(getString(
R.string.pref_key_camera_driver),
"3");
780 else if(((mCameraDriver == 1 && (!
RTABMapLib.
isBuiltWith(nativeApplication, 0) || !mIsARCoreAvailable)) ||
781 (mCameraDriver == 3 && !mIsARCoreAvailable)))
785 SharedPreferences.Editor editor = sharedPref.edit();
786 editor.putString(getString(
R.string.pref_key_camera_driver),
"0");
791 SharedPreferences.Editor editor = sharedPref.edit();
792 editor.putString(getString(
R.string.pref_key_camera_driver),
"2");
796 else if(mCameraDriver == 2 && (!mIsAREngineAvailable || !
RTABMapLib.
isBuiltWith(nativeApplication, 2)))
800 SharedPreferences.Editor editor = sharedPref.edit();
801 editor.putString(getString(
R.string.pref_key_camera_driver),
"0");
804 else if(mIsARCoreAvailable)
806 SharedPreferences.Editor editor = sharedPref.edit();
807 editor.putString(getString(
R.string.pref_key_camera_driver),
"3");
814 ArCoreApk.Availability availability = ArCoreApk.getInstance().checkAvailability(
this);
815 if (availability.isTransient()) {
817 new Handler().postDelayed(
new Runnable() {
824 if (availability.isSupported()) {
825 Log.i(TAG,
"ARCore supported");
826 mIsARCoreAvailable =
true;
827 updateCameraDriverSettings();
829 Log.i(TAG,
"ARCore supported");
830 mIsARCoreAvailable =
false;
831 updateCameraDriverSettings();
837 AREnginesApk.ARAvailability availability = AREnginesApk.checkAvailability(
this);
838 if (availability.isTransient()) {
840 new Handler().postDelayed(
new Runnable() {
843 isArEngineAvailable();
847 if (availability.isSupported()) {
848 Log.i(TAG,
"AREngine supported");
849 mIsAREngineAvailable =
true;
850 updateCameraDriverSettings();
852 Log.i(TAG,
"AREngine not supported");
853 mIsAREngineAvailable =
false;
854 updateCameraDriverSettings();
857 catch(UnsatisfiedLinkError
e)
859 Log.i(TAG,
"AREngine not supported");
860 mIsAREngineAvailable =
false;
869 if(!DISABLE_LOG) Log.d(TAG,
"onDestroy()");
871 synchronized (
this) {
873 nativeApplication = 0;
879 if(event.sensor == mAccelerometer || event.sensor == mMagnetometer)
881 if (event.sensor == mAccelerometer) {
882 System.arraycopy(event.values, 0, mLastAccelerometer, 0, event.values.length);
883 mLastAccelerometerSet =
true;
884 }
else if (event.sensor == mMagnetometer) {
885 System.arraycopy(event.values, 0, mLastMagnetometer, 0, event.values.length);
886 mLastMagnetometerSet =
true;
888 if (mLastAccelerometerSet && mLastMagnetometerSet) {
889 SensorManager.getRotationMatrix(mR,
null, mLastAccelerometer, mLastMagnetometer);
891 mNewR.setConcat(mRMat, mDeviceToCamera) ;
893 SensorManager.getOrientation(mR, mOrientation);
894 mCompassDeg = mOrientation[0] * 180.0f/(
float)Math.PI;
897 mCompassDeg += 360.0f;
901 else if(event.sensor == mAmbientTemperature)
903 mLastEnvSensors[1] =
event.values[0];
904 mLastEnvSensorsSet[1] =
true;
907 else if(event.sensor == mAmbientAirPressure)
909 mLastEnvSensors[2] =
event.values[0];
910 mLastEnvSensorsSet[2] =
true;
913 else if(event.sensor == mAmbientLight)
915 mLastEnvSensors[3] =
event.values[0];
916 mLastEnvSensorsSet[3] =
true;
919 else if(event.sensor == mAmbientRelativeHumidity)
921 mLastEnvSensors[4] =
event.values[0];
922 mLastEnvSensorsSet[4] =
true;
935 int resourceId = getResources().getIdentifier(
"status_bar_height",
"dimen",
"android");
936 if (resourceId > 0) {
937 result = getResources().getDimensionPixelSize(resourceId);
943 TypedValue tv =
new TypedValue();
944 if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv,
true))
946 result = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
955 super.onWindowFocusChanged(hasFocus);
959 mRenderer.
setOffset(!hasFocus?-mStatusBarHeight:0);
965 int newVis = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
966 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
967 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
969 newVis |= View.SYSTEM_UI_FLAG_LOW_PROFILE
970 | View.SYSTEM_UI_FLAG_FULLSCREEN
971 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
972 | View.SYSTEM_UI_FLAG_IMMERSIVE;
973 mRenderer.
setOffset(!hasWindowFocus()?-mStatusBarHeight:0);
977 mRenderer.
setOffset(-mStatusBarHeight-mActionBarHeight);
981 mDecorView.setSystemUiVisibility(newVis);
987 if (requestCode == Tango.TANGO_INTENT_ACTIVITYCODE) {
989 if (resultCode == RESULT_CANCELED) {
990 mToast.makeText(
this,
"Motion Tracking Permissions Required!", mToast.LENGTH_SHORT).show();
993 else if (requestCode == SKETCHFAB_ACTIVITY_CODE) {
995 if (resultCode == RESULT_OK) {
996 mAuthToken =
data.getStringExtra(RTABMAP_AUTH_TOKEN_KEY);
1004 return super.onMenuOpened(featureId, menu);
1009 mMenuOpened =
false;
1015 Date
t =
new Date();
1016 boolean doubleBack =
t.getTime() - mBackClickedTime.getTime() < 3500;
1017 mBackClickedTime =
t;
1030 super.onBackPressed();
1034 mToast.makeText(
this,
"Press Back once more to exit", mToast.LENGTH_LONG).show();
1039 closeVisualization();
1048 stopDisconnectTimer();
1050 if(!DISABLE_LOG) Log.i(TAG,
"onPause()");
1062 mLocationManager.removeUpdates(mLocationListener);
1063 mSensorManager.unregisterListener(
this);
1064 mLastAccelerometerSet =
false;
1065 mLastMagnetometerSet=
false;
1066 mLastEnvSensorsSet[0] = mLastEnvSensorsSet[1]= mLastEnvSensorsSet[2]= mLastEnvSensorsSet[3]= mLastEnvSensorsSet[4]=
false;
1071 mOnPauseStamp = System.currentTimeMillis()/1000;
1079 Log.i(TAG,
"update preferences...");
1080 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
1081 mUpdateRate = sharedPref.getString(getString(
R.string.pref_key_update_rate), getString(
R.string.pref_default_update_rate));
1082 String maxSpeed = sharedPref.getString(getString(
R.string.pref_key_max_speed), getString(
R.string.pref_default_max_speed));
1083 mTimeThr = sharedPref.getString(getString(
R.string.pref_key_time_thr), getString(
R.string.pref_default_time_thr));
1084 String memThr = sharedPref.getString(getString(
R.string.pref_key_mem_thr), getString(
R.string.pref_default_mem_thr));
1085 mLoopThr = sharedPref.getString(getString(
R.string.pref_key_loop_thr), getString(
R.string.pref_default_loop_thr));
1086 String simThr = sharedPref.getString(getString(
R.string.pref_key_sim_thr), getString(
R.string.pref_default_sim_thr));
1087 mMinInliers = sharedPref.getString(getString(
R.string.pref_key_min_inliers), getString(
R.string.pref_default_min_inliers));
1088 mMaxOptimizationError = sharedPref.getString(getString(
R.string.pref_key_opt_error), getString(
R.string.pref_default_opt_error));
1089 float maxOptimizationError =
Float.parseFloat(mMaxOptimizationError);
1090 if(maxOptimizationError >0 && maxOptimizationError<1)
1092 Log.w(TAG,
"Migration of " + getString(
R.string.pref_key_opt_error) +
" from " + mMaxOptimizationError +
" to " + getString(
R.string.pref_default_opt_error)) ;
1093 SharedPreferences.Editor editor = sharedPref.edit();
1094 editor.putString(getString(
R.string.pref_key_opt_error), getString(
R.string.pref_default_opt_error));
1096 mMaxOptimizationError = getString(
R.string.pref_default_opt_error);
1098 mMaxFeatures = sharedPref.getString(getString(
R.string.pref_key_features_voc), getString(
R.string.pref_default_features_voc));
1099 String maxFeaturesLoop = sharedPref.getString(getString(
R.string.pref_key_features), getString(
R.string.pref_default_features));
1100 String featureType = sharedPref.getString(getString(
R.string.pref_key_features_type), getString(
R.string.pref_default_features_type));
1101 boolean keepAllDb = sharedPref.getBoolean(getString(
R.string.pref_key_keep_all_db),
Boolean.parseBoolean(getString(
R.string.pref_default_keep_all_db)));
1102 boolean optimizeFromGraphEnd = sharedPref.getBoolean(getString(
R.string.pref_key_optimize_end),
Boolean.parseBoolean(getString(
R.string.pref_default_optimize_end)));
1103 String optimizer = sharedPref.getString(getString(
R.string.pref_key_optimizer), getString(
R.string.pref_default_optimizer));
1104 String markerDetection = sharedPref.getString(getString(
R.string.pref_key_marker_detection), getString(
R.string.pref_default_marker_detection));
1105 String markerDetectionDepthError = sharedPref.getString(getString(
R.string.pref_key_marker_detection_depth_error), getString(
R.string.pref_default_marker_detection_depth_error));
1106 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)));
1109 mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mLocationListener);
1110 mSensorManager.registerListener(
this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
1111 mSensorManager.registerListener(
this, mMagnetometer, SensorManager.SENSOR_DELAY_UI);
1113 mEnvSensorsSaved = sharedPref.getBoolean(getString(
R.string.pref_key_env_sensors_saved),
Boolean.parseBoolean(getString(
R.string.pref_default_env_sensors_saved)));
1114 if(mEnvSensorsSaved)
1116 mSensorManager.registerListener(
this, mAmbientTemperature, SensorManager.SENSOR_DELAY_NORMAL);
1117 mSensorManager.registerListener(
this, mAmbientAirPressure, SensorManager.SENSOR_DELAY_NORMAL);
1118 mSensorManager.registerListener(
this, mAmbientLight, SensorManager.SENSOR_DELAY_NORMAL);
1119 mSensorManager.registerListener(
this, mAmbientRelativeHumidity, SensorManager.SENSOR_DELAY_NORMAL);
1120 mEnvSensorsTimer.schedule(
new TimerTask() {
1124 WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
1126 if(wifiInfo !=
null && (dbm = wifiInfo.getRssi()) > -127)
1128 mLastEnvSensors[0] = (
float)dbm;
1129 mLastEnvSensorsSet[0] =
true;
1137 Log.i(TAG,
"set mapping parameters");
1138 RTABMapLib.
setOnlineBlending(nativeApplication, sharedPref.getBoolean(getString(
R.string.pref_key_blending),
Boolean.parseBoolean(getString(
R.string.pref_default_blending))));
1139 RTABMapLib.
setNodesFiltering(nativeApplication, sharedPref.getBoolean(getString(
R.string.pref_key_nodes_filtering),
Boolean.parseBoolean(getString(
R.string.pref_default_nodes_filtering))));
1140 RTABMapLib.
setRawScanSaved(nativeApplication, sharedPref.getBoolean(getString(
R.string.pref_key_raw_scan_saved),
Boolean.parseBoolean(getString(
R.string.pref_default_raw_scan_saved))));
1141 RTABMapLib.
setFullResolution(nativeApplication, sharedPref.getBoolean(getString(
R.string.pref_key_resolution),
Boolean.parseBoolean(getString(
R.string.pref_default_resolution))));
1142 RTABMapLib.
setSmoothing(nativeApplication, sharedPref.getBoolean(getString(
R.string.pref_key_smoothing),
Boolean.parseBoolean(getString(
R.string.pref_default_smoothing))));
1143 RTABMapLib.
setDepthFromMotion(nativeApplication, sharedPref.getBoolean(getString(
R.string.pref_key_depth_from_motion),
Boolean.parseBoolean(getString(
R.string.pref_default_depth_from_motion))));
1144 RTABMapLib.
setCameraColor(nativeApplication, !sharedPref.getBoolean(getString(
R.string.pref_key_fisheye),
Boolean.parseBoolean(getString(
R.string.pref_default_fisheye))));
1145 RTABMapLib.
setAppendMode(nativeApplication, sharedPref.getBoolean(getString(
R.string.pref_key_append),
Boolean.parseBoolean(getString(
R.string.pref_default_append))));
1163 if(Integer.parseInt(markerDetection) == -1)
1175 Log.i(TAG,
"set exporting parameters...");
1176 RTABMapLib.
setCloudDensityLevel(nativeApplication, Integer.parseInt(sharedPref.getString(getString(
R.string.pref_key_density), getString(
R.string.pref_default_density))));
1177 RTABMapLib.
setMaxCloudDepth(nativeApplication,
Float.parseFloat(sharedPref.getString(getString(
R.string.pref_key_depth), getString(
R.string.pref_default_depth))));
1178 RTABMapLib.
setMinCloudDepth(nativeApplication,
Float.parseFloat(sharedPref.getString(getString(
R.string.pref_key_min_depth), getString(
R.string.pref_default_min_depth))));
1179 RTABMapLib.
setPointSize(nativeApplication,
Float.parseFloat(sharedPref.getString(getString(
R.string.pref_key_point_size), getString(
R.string.pref_default_point_size))));
1181 RTABMapLib.
setMeshTriangleSize(nativeApplication, Integer.parseInt(sharedPref.getString(getString(
R.string.pref_key_triangle), getString(
R.string.pref_default_triangle))));
1182 float bgColor =
Float.parseFloat(sharedPref.getString(getString(
R.string.pref_key_background_color), getString(
R.string.pref_default_background_color)));
1186 Log.i(TAG,
"set rendering parameters...");
1187 RTABMapLib.
setClusterRatio(nativeApplication,
Float.parseFloat(sharedPref.getString(getString(
R.string.pref_key_cluster_ratio), getString(
R.string.pref_default_cluster_ratio))));
1188 RTABMapLib.
setMaxGainRadius(nativeApplication,
Float.parseFloat(sharedPref.getString(getString(
R.string.pref_key_gain_max_radius), getString(
R.string.pref_default_gain_max_radius))));
1189 RTABMapLib.
setRenderingTextureDecimation(nativeApplication, Integer.parseInt(sharedPref.getString(getString(
R.string.pref_key_rendering_texture_decimation), getString(
R.string.pref_default_rendering_texture_decimation))));
1191 if(mItemRenderingPointCloud !=
null)
1193 int renderingType = sharedPref.getInt(getString(
R.string.pref_key_rendering), Integer.parseInt(getString(
R.string.pref_default_rendering)));
1194 if(renderingType == 0)
1196 mItemRenderingPointCloud.setChecked(
true);
1198 else if(renderingType == 1)
1200 mItemRenderingMesh.setChecked(
true);
1204 mItemRenderingTextureMesh.setChecked(
true);
1208 mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
1209 mItemRenderingTextureMesh.isChecked());
1211 mButtonBackfaceShown.setVisibility(mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked()?View.VISIBLE:View.INVISIBLE);
1216 Log.e(TAG,
"Error parsing preferences: " +
e.getMessage());
1217 mToast.makeText(
this, String.format(
"Error parsing preferences: "+
e.getMessage()), mToast.LENGTH_LONG).show();
1225 setAndroidOrientation();
1227 updateCameraDriverSettings();
1228 updatePreferences();
1232 String message =
new String();
1235 if(System.currentTimeMillis()/1000 - mOnPauseStamp < 1)
1237 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.");
1241 message = String.format(
"Hold Tight! Initializing Camera Service...");
1243 mToast.makeText(
this,
"Mapping is paused!", mToast.LENGTH_LONG).show();
1247 message = String.format(
"Hold Tight! Initializing Camera Service...\nTip: If the camera is still drifting just after the mapping has started, do \"Reset\".");
1249 startCamera(message);
1253 if(!DISABLE_LOG) Log.i(TAG, String.format(
"onResume()"));
1260 switch (requestCode) {
1263 if (
results.length > 0 &&
results[0] == PackageManager.PERMISSION_GRANTED) {
1270 Toast.makeText(
this,
"Storage read/write permissions are needed to run this application", Toast.LENGTH_LONG).show();
1279 if (
results.length > 0 &&
results[0] == PackageManager.PERMISSION_GRANTED) {
1281 startCamera(String.format(
"Hold Tight! Initializing Camera Service...\n"
1282 +
"Tip: If the camera is still drifting just after the mapping has started, do \"Reset\"."));
1284 Toast.makeText(
this,
"Camera permission is needed for scanning and motion tracking.", Toast.LENGTH_LONG).show();
1292 if (
results.length > 0 &&
results[0] == PackageManager.PERMISSION_GRANTED) {
1295 Toast.makeText(
this,
"Internet permission is needed to share scans.", Toast.LENGTH_LONG).show();
1314 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
1315 String cameraDriverStr = sharedPref.getString(getString(
R.string.pref_key_camera_driver), getString(
R.string.pref_default_camera_driver));
1316 final boolean depthFromMotion = sharedPref.getBoolean(getString(
R.string.pref_key_depth_from_motion),
Boolean.parseBoolean(getString(
R.string.pref_default_depth_from_motion)));
1317 mCameraDriver = Integer.parseInt(cameraDriverStr);
1319 Log.i(TAG, String.format(
"startCamera() driver=%d", mCameraDriver));
1320 if(mCameraDriver == 0)
1323 if (!CheckTangoCoreVersion(MIN_TANGO_CORE_VERSION)) {
1324 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();
1328 if (!Tango.hasPermission(
this, Tango.PERMISSIONTYPE_MOTION_TRACKING)) {
1329 if(!DISABLE_LOG) Log.i(TAG, String.format(
"Asking for motion tracking permission"));
1330 startActivityForResult(
1331 Tango.getRequestPermissionIntent(Tango.PERMISSIONTYPE_MOTION_TRACKING),
1332 Tango.TANGO_INTENT_ACTIVITYCODE);
1338 if(mCameraServiceConnectionUsed)
1340 mProgressDialog.setTitle(
"");
1341 mProgressDialog.setMessage(message);
1342 mProgressDialog.show();
1343 resetNoTouchTimer(
true);
1349 mToast.makeText(
this,
"Current camera driver selected is Tango, but Tango service binding failed. Abort scanning...", mToast.LENGTH_LONG).show();
1353 else if(mCameraDriver == 1 || mCameraDriver == 2 || mCameraDriver == 3)
1355 if((mCameraDriver == 1 || mCameraDriver == 3) && !mIsARCoreAvailable)
1357 mToast.makeText(
this,
"ARCore not supported on this phone! Cannot start a new scan.", mToast.LENGTH_LONG).show();
1360 if(mCameraDriver == 2 && !mIsAREngineAvailable)
1362 mToast.makeText(
this,
"AREngine not supported on this phone! Cannot start a new scan.", mToast.LENGTH_LONG).show();
1367 if(mCameraDriver==1 && !mItemRenderingPointCloud.isChecked())
1369 mItemRenderingPointCloud.setChecked(
true);
1371 Thread bindThread =
new Thread(
new Runnable() {
1374 if(mCameraDriver==1 && !depthFromMotion)
1378 mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
1379 mItemRenderingTextureMesh.isChecked());
1381 final boolean cameraStartSucess =
RTABMapLib.
startCamera(nativeApplication,
null, getApplicationContext(), getActivity(), mCameraDriver);
1382 runOnUiThread(
new Runnable() {
1384 boolean localSuccess = cameraStartSucess;
1385 if(cameraStartSucess && mCameraDriver == 3)
1387 synchronized (
this) {
1390 mProgressDialog.setTitle(
"");
1391 mProgressDialog.setMessage(message);
1392 mProgressDialog.show();
1395 mToast.makeText(getActivity(),
"Current camera driver selected is ARCore Java, but initialization failed. Abort scanning...", mToast.LENGTH_LONG).show();
1396 mArCoreCamera =
null;
1397 localSuccess =
false;
1404 mItemRenderingPointCloud.setChecked(
true);
1406 mToast.makeText(getApplicationContext(),
"Depth camera not found, only poses and RGB images can be recorded.", mToast.LENGTH_LONG).show();
1412 mProgressDialog.dismiss();
1415 mToast.makeText(getApplicationContext(),
1416 String.format(
"Failed to intialize Camera!"), mToast.LENGTH_LONG).show();
1422 mToast.makeText(getApplicationContext(),
"Currently ARCore NDK driver doesn't support depth, only poses, RGB images and 3d features can be recorded.", mToast.LENGTH_LONG).show();
1438 mToast.makeText(
this,
"Supported camera driver not found! Cannot start a new scan.", mToast.LENGTH_LONG).show();
1444 if(!DISABLE_LOG) Log.i(TAG, String.format(
"called setCamera(type=%d);",
type));
1447 long freeMemory = getFreeMemory();
1448 mStatusTexts[1] = getString(
R.string.memory)+String.valueOf(mFreeMemoryOnStart>freeMemory?mFreeMemoryOnStart-freeMemory:0);
1449 mStatusTexts[2] = getString(
R.string.free_memory)+String.valueOf(freeMemory);
1450 updateStatusTexts();
1454 mSeekBarOrthoCut.setVisibility(
type!=3?View.INVISIBLE:View.VISIBLE);
1455 mSeekBarGrid.setVisibility(mSeekBarGrid.isEnabled() &&
type==3?View.VISIBLE:View.INVISIBLE);
1458 mSeekBarOrthoCut.setMax(120);
1459 mSeekBarOrthoCut.setProgress(80);
1466 switch (
v.getId()) {
1467 case R.id.gl_surface_view:
1469 case R.id.start_button:
1472 case R.id.stop_button:
1482 case R.id.light_button:
1485 case R.id.backface_button:
1488 case R.id.wireframe_button:
1491 case R.id.close_visualization_button:
1492 closeVisualization();
1495 case R.id.button_saveOnDevice:
1498 case R.id.button_shareToSketchfab:
1501 case R.id.button_library:
1504 case R.id.button_new_scan:
1510 resetNoTouchTimer();
1515 if(mSavedRenderingType==0)
1517 mItemRenderingPointCloud.setChecked(
true);
1519 else if(mSavedRenderingType==1)
1521 mItemRenderingMesh.setChecked(
true);
1525 mItemRenderingTextureMesh.setChecked(
true);
1532 resetNoTouchTimer();
1536 resetNoTouchTimer();
1540 Display display = getWindowManager().getDefaultDisplay();
1541 Camera.CameraInfo colorCameraInfo =
new Camera.CameraInfo();
1542 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
1543 boolean fisheye = sharedPref.getBoolean(getString(
R.string.pref_key_fisheye),
Boolean.parseBoolean(getString(
R.string.pref_default_fisheye)));
1544 Camera.getCameraInfo(fisheye?1:0, colorCameraInfo);
1548 class DoubleTapGestureDetector
extends GestureDetector.SimpleOnGestureListener {
1551 public boolean onDoubleTap(MotionEvent event) {
1552 if(!DISABLE_LOG) Log.i(TAG,
"onDoubleTap");
1553 float normalizedX =
event.getX(0) / mScreenSize.x;
1554 float normalizedY =
event.getY(0) / mScreenSize.y;
1555 RTABMapLib.
onTouchEvent(nativeApplication, 3, event.getActionMasked(), normalizedX, normalizedY, 0.0f, 0.0f);
1559 public boolean onSingleTapConfirmed(MotionEvent event) {
1560 if(!DISABLE_LOG) Log.i(TAG,
"onSingleTapConfirmed");
1563 notouchHandler.removeCallbacks(notouchCallback);
1564 notouchHandler.postDelayed(notouchCallback, 0);
1568 resetNoTouchTimer(
true);
1576 if(!DISABLE_LOG) Log.i(TAG,
"called onCreateOptionsMenu;");
1578 MenuInflater inflater = getMenuInflater();
1579 inflater.inflate(
R.menu.optionmenu, menu);
1581 getActionBar().setDisplayShowHomeEnabled(
true);
1582 getActionBar().setIcon(
R.drawable.ic_launcher);
1584 mItemSave = menu.findItem(
R.id.save);
1585 mItemOpen = menu.findItem(
R.id.open);
1586 mItemNewScan = menu.findItem(
R.id.new_scan);
1587 mItemPostProcessing = menu.findItem(
R.id.post_processing);
1588 mItemExport = menu.findItem(
R.id.export);
1589 mItemSettings = menu.findItem(
R.id.settings);
1590 mItemModes = menu.findItem(
R.id.modes);
1591 mItemResume = menu.findItem(
R.id.resume);
1592 mItemLocalizationMode = menu.findItem(
R.id.localization_mode);
1593 mItemTrajectoryMode = menu.findItem(
R.id.trajectory_mode);
1594 mItemRenderingPointCloud = menu.findItem(
R.id.point_cloud);
1595 mItemRenderingMesh = menu.findItem(
R.id.mesh);
1596 mItemRenderingTextureMesh = menu.findItem(
R.id.texture_mesh);
1597 mItemDataRecorderMode = menu.findItem(
R.id.data_recorder);
1598 mItemStatusVisibility = menu.findItem(
R.id.status);
1599 mItemDebugVisibility = menu.findItem(
R.id.debug);
1600 mItemSave.setEnabled(
false);
1601 mItemNewScan.setEnabled(
true);
1602 mItemExport.setEnabled(
false);
1603 mItemOpen.setEnabled(
true);
1604 mItemPostProcessing.setEnabled(
false);
1605 mItemDataRecorderMode.setEnabled(
false);
1609 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
1610 int renderingType = sharedPref.getInt(getString(
R.string.pref_key_rendering), Integer.parseInt(getString(
R.string.pref_default_rendering)));
1611 if(renderingType == 0)
1613 mItemRenderingPointCloud.setChecked(
true);
1615 else if(renderingType == 1)
1617 mItemRenderingMesh.setChecked(
true);
1621 mItemRenderingTextureMesh.setChecked(
true);
1625 mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
1626 mItemRenderingTextureMesh.isChecked());
1628 if(mButtonBackfaceShown !=
null)
1630 mButtonBackfaceShown.setVisibility(mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked()?View.VISIBLE:View.INVISIBLE);
1635 Log.e(TAG,
"Error parsing rendering preferences: " +
e.getMessage());
1636 mToast.makeText(
this, String.format(
"Error parsing rendering preferences: "+
e.getMessage()), mToast.LENGTH_LONG).show();
1639 updateState(mState);
1646 MemoryInfo mi =
new MemoryInfo();
1647 ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
1648 activityManager.getMemoryInfo(mi);
1649 return mi.availMem / 0x100000
L;
1654 if(mItemStatusVisibility !=
null && mItemDebugVisibility !=
null)
1666 mRenderer.
updateTexts(Arrays.copyOfRange(mStatusTexts, 0, STATUS_TEXTS_POSE_INDEX-1));
1668 else if(mItemDebugVisibility.isChecked())
1670 mRenderer.
updateTexts(Arrays.copyOfRange(mStatusTexts, STATUS_TEXTS_POSE_INDEX-1, mStatusTexts.length));
1677 if(mGLView.getRenderMode() == GLSurfaceView.RENDERMODE_WHEN_DIRTY)
1679 mGLView.requestRender();
1688 float optimizationMaxError,
1689 float optimizationMaxErrorRatio,
1690 boolean fastMovement,
1691 int landmarkDetected,
1692 String[] statusTexts)
1694 for(
int i = 1;
i<mStatusTexts.length &&
i<statusTexts.length; ++
i)
1696 mStatusTexts[
i] = statusTexts[
i];
1700 String updateValue = mUpdateRate.compareTo(
"0")==0?
"Max":mUpdateRate;
1701 mStatusTexts[0] = getString(
R.string.status)+(mItemDataRecorderMode!=
null&&mItemDataRecorderMode.isChecked()?String.format(
"Recording (%s Hz)", updateValue):mItemLocalizationMode!=
null && mItemLocalizationMode.isChecked()?String.format(
"Localization (%s Hz)", updateValue):String.format(
"Mapping (%s Hz)", updateValue));
1704 updateStatusTexts();
1708 if(mButtonStart.getVisibility() != View.VISIBLE)
1711 long memoryFree = getFreeMemory();
1712 long memoryUsed = mFreeMemoryOnStart>memoryFree?mFreeMemoryOnStart-memoryFree:0;
1714 if(memoryFree < 400)
1718 if(mMemoryWarningDialog!=
null)
1720 mMemoryWarningDialog.dismiss();
1721 mMemoryWarningDialog =
null;
1724 mMemoryWarningDialog =
new AlertDialog.Builder(getActivity())
1725 .setTitle(
"Memory is full!")
1726 .setCancelable(
false)
1727 .setMessage(String.format(
"Scanning has been paused because free memory is too "
1728 +
"low (%d MB). You should be able to save the database but some post-processing and exporting options may fail. "
1729 +
"\n\nNote that for large environments, you can save multiple databases and "
1730 +
"merge them with RTAB-Map Desktop version.", memoryUsed))
1731 .setPositiveButton(
"Ok",
new DialogInterface.OnClickListener() {
1732 public void onClick(DialogInterface dialog, int which) {
1733 mMemoryWarningDialog = null;
1736 .setNeutralButton(
"Save",
new DialogInterface.OnClickListener() {
1737 public void onClick(DialogInterface dialog, int which) {
1739 mMemoryWarningDialog = null;
1743 mMemoryWarningDialog.setCanceledOnTouchOutside(
false);
1744 mMemoryWarningDialog.show();
1746 else if(mMemoryWarningDialog ==
null && memoryUsed*3 > memoryFree && (mItemDataRecorderMode ==
null || !mItemDataRecorderMode.isChecked()))
1748 mMemoryWarningDialog =
new AlertDialog.Builder(getActivity())
1749 .setTitle(
"Warning: Memory is almost full!")
1750 .setCancelable(
false)
1751 .setMessage(String.format(
"Free memory (%d MB) should be at least 3 times the "
1752 +
"memory used (%d MB) so that some post-processing and exporting options "
1753 +
"have enough memory to work correctly. If you just want to save the database "
1754 +
"after scanning, you can continue until the next warning.\n\n"
1755 +
"Note that showing only point clouds reduces memory needed for rendering.", memoryFree, memoryUsed))
1756 .setPositiveButton(
"Pause",
new DialogInterface.OnClickListener() {
1757 public void onClick(DialogInterface dialog, int which) {
1761 .setNeutralButton(
"Continue",
new DialogInterface.OnClickListener() {
1762 public void onClick(DialogInterface dialog, int which) {
1766 mMemoryWarningDialog.setCanceledOnTouchOutside(
false);
1767 mMemoryWarningDialog.show();
1773 if(mState ==
State.STATE_MAPPING || mState ==
State.STATE_VISUALIZING_CAMERA)
1775 long currentTime = System.currentTimeMillis()/1000;
1776 if(loopClosureId > 0)
1778 if (mToast !=
null) mToast.cancel();
1779 mToast.setText(String.format(
"Loop closure detected! (%d/%d inliers)", inliers, matches));
1782 else if(landmarkDetected != 0)
1784 if (mToast !=
null) mToast.cancel();
1785 mToast.setText(String.format(
"Marker %d detected!", landmarkDetected));
1788 else if(rejected > 0)
1790 if (mToast !=
null) mToast.cancel();
1791 if(inliers >= Integer.parseInt(mMinInliers))
1793 if(optimizationMaxError > 0.0f)
1795 mToast.setText(String.format(
"Loop closure rejected, too high graph optimization error (%.3fm: ratio=%.3f < factor=%sx).", optimizationMaxError, optimizationMaxErrorRatio, mMaxOptimizationError));
1799 mToast.setText(String.format(
"Loop closure rejected, graph optimization failed! You may try a different Graph Optimizer (see Mapping options)."));
1804 mToast.setText(String.format(
"Loop closure rejected, not enough inliers (%d/%d < %s).", inliers, matches, mMinInliers));
1808 else if(fastMovement)
1810 if(currentTime - mLastFastMovementNotificationStamp > 3)
1812 if (mToast !=
null) mToast.cancel();
1813 mToast.setText(
"Move slower... blurry images are not added to map (\"Settings->Mapping...->Maximum Motion Speed\" is enabled).");
1820 mLastFastMovementNotificationStamp = currentTime;
1831 final float updateTime,
1832 final int loopClosureId,
1833 final int highestHypId,
1834 final int databaseMemoryUsed,
1837 final int featuresExtracted,
1838 final float hypothesis,
1839 final int nodesDrawn,
1842 final float rehearsalValue,
1843 final float optimizationMaxError,
1844 final float optimizationMaxErrorRatio,
1845 final float distanceTravelled,
1846 final int fastMovement,
1847 final int landmarkDetected,
1855 if(!DISABLE_LOG) Log.i(TAG, String.format(
"updateStatsCallback()"));
1857 final String[] statusTexts =
new String[STATUS_TEXTS_SIZE];
1859 long memoryFree = getFreeMemory();
1860 statusTexts[1] = getString(
R.string.memory)+(mFreeMemoryOnStart>memoryFree?mFreeMemoryOnStart-memoryFree:0);
1861 statusTexts[2] = getString(
R.string.free_memory)+memoryFree;
1863 if(loopClosureId > 0)
1865 ++mTotalLoopClosures;
1872 if(mLastKnownLocation !=
null)
1874 long millisec = System.currentTimeMillis() - mLastKnownLocation.getTime();
1877 statusTexts[3] = getString(
R.string.gps)+String.format(
"[too old, %d ms]", millisec);
1881 statusTexts[3] = getString(
R.string.gps)+
1882 String.format(
"%.2f %.2f %.2fm %.0fdeg %.0fm",
1883 mLastKnownLocation.getLongitude(),
1884 mLastKnownLocation.getLatitude(),
1885 mLastKnownLocation.getAltitude(),
1887 mLastKnownLocation.getAccuracy());
1892 statusTexts[3] = getString(
R.string.gps)+String.format(
"[not yet available, %.0fdeg]", mCompassDeg);
1895 if(mEnvSensorsSaved)
1897 statusTexts[4] = getString(
R.string.env_sensors);
1899 if(mLastEnvSensorsSet[0])
1901 statusTexts[4] += String.format(
" %.0f dbm", mLastEnvSensors[0]);
1902 mLastEnvSensorsSet[0] =
false;
1904 if(mLastEnvSensorsSet[1])
1906 statusTexts[4] += String.format(
" %.1f %cC", mLastEnvSensors[1],
'\u00B0');
1907 mLastEnvSensorsSet[1] =
false;
1909 if(mLastEnvSensorsSet[2])
1911 statusTexts[4] += String.format(
" %.1f hPa", mLastEnvSensors[2]);
1912 mLastEnvSensorsSet[2] =
false;
1914 if(mLastEnvSensorsSet[3])
1916 statusTexts[4] += String.format(
" %.0f lx", mLastEnvSensors[3]);
1917 mLastEnvSensorsSet[3] =
false;
1919 if(mLastEnvSensorsSet[4])
1921 statusTexts[4] += String.format(
" %.0f %%", mLastEnvSensors[4]);
1922 mLastEnvSensorsSet[4] =
false;
1926 String formattedDate =
new SimpleDateFormat(
"HH:mm:ss.SSS").format(
new Date());
1927 statusTexts[5] = getString(
R.string.time)+formattedDate;
1929 int index = STATUS_TEXTS_POSE_INDEX;
1930 statusTexts[index++] = getString(
R.string.nodes)+
nodes+
" (" + nodesDrawn +
" shown)";
1931 statusTexts[index++] = getString(
R.string.words)+words;
1932 statusTexts[index++] = getString(
R.string.database_size)+databaseMemoryUsed;
1933 statusTexts[index++] = getString(
R.string.points)+points;
1934 statusTexts[index++] = getString(
R.string.polygons)+polygons;
1935 statusTexts[index++] = getString(
R.string.update_time)+(
int)(updateTime) +
" / " + (mTimeThr.compareTo(
"0")==0?
"No Limit":mTimeThr);
1936 statusTexts[index++] = getString(
R.string.features)+featuresExtracted +
" / " + (mMaxFeatures.compareTo(
"0")==0?
"No Limit":mMaxFeatures.compareTo(
"-1")==0?
"Disabled":mMaxFeatures);
1937 statusTexts[index++] = getString(
R.string.rehearsal)+(
int)(rehearsalValue*100.0
f);
1938 statusTexts[index++] = getString(
R.string.total_loop)+mTotalLoopClosures;
1939 statusTexts[index++] = getString(
R.string.inliers)+inliers;
1940 statusTexts[index++] = getString(
R.string.hypothesis)+(
int)(hypothesis*100.0
f) +
" / " + (
int)(
Float.parseFloat(mLoopThr)*100.0f) +
" (" + (loopClosureId>0?loopClosureId:highestHypId)+
")";
1941 statusTexts[index++] = getString(
R.string.fps)+(
int)fps+
" Hz";
1942 statusTexts[index++] = getString(
R.string.distance)+(
int)distanceTravelled+
" m";
1943 statusTexts[index++] = String.format(
"Pose (x,y,z): %.2f %.2f %.2f",
x,
y,
z);
1945 runOnUiThread(
new Runnable() {
1947 updateStatsUI(loopClosureId, inliers,
matches, rejected, optimizationMaxError, optimizationMaxErrorRatio, fastMovement!=0, landmarkDetected, statusTexts);
1956 if(!DISABLE_LOG) Log.i(TAG, String.format(
"rtabmapInitEventsUI() status=%d msg=%s", status,
msg));
1958 int optimizedMeshDetected = 0;
1960 if(
msg.equals(
"Loading optimized cloud...done!"))
1962 optimizedMeshDetected = 1;
1964 else if(
msg.equals(
"Loading optimized mesh...done!"))
1966 optimizedMeshDetected = 2;
1968 else if(
msg.equals(
"Loading optimized texture mesh...done!"))
1970 optimizedMeshDetected = 3;
1972 if(optimizedMeshDetected > 0)
1974 resetNoTouchTimer();
1975 mSavedRenderingType = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
1976 if(optimizedMeshDetected==1)
1978 mItemRenderingPointCloud.setChecked(
true);
1980 else if(optimizedMeshDetected==2)
1982 mItemRenderingMesh.setChecked(
true);
1986 mItemRenderingTextureMesh.setChecked(
true);
1990 if(mButtonCameraView.getSelectedItemPosition() == 0)
1994 mToast.makeText(getActivity(), String.format(
"Optimized mesh detected in the database, it is shown while the database is loading..."), mToast.LENGTH_LONG).show();
1995 mProgressDialog.dismiss();
1998 if(mButtonStart!=
null)
2000 mStatusTexts[0] = getString(
R.string.status)+(status == 1 &&
msg.isEmpty()?mState ==
State.
STATE_CAMERA?
"Camera Preview":
"Idle":
msg);
2002 long freeMemory = getFreeMemory();
2003 mStatusTexts[1] = getString(
R.string.memory)+String.valueOf(mFreeMemoryOnStart>freeMemory?mFreeMemoryOnStart-freeMemory:0);
2004 mStatusTexts[2] = getString(
R.string.free_memory)+String.valueOf(freeMemory);
2005 updateStatusTexts();
2014 if(!DISABLE_LOG) Log.i(TAG, String.format(
"rtabmapInitEventCallback()"));
2016 runOnUiThread(
new Runnable() {
2018 rtabmapInitEventUI(status,
msg);
2027 if(!DISABLE_LOG) Log.i(TAG, String.format(
"updateProgressionUI() count=%d max=%s",
count,
max));
2029 mExportProgressDialog.setMax(
max);
2030 mExportProgressDialog.setProgress(
count);
2038 if(!DISABLE_LOG) Log.i(TAG, String.format(
"updateProgressionCallback()"));
2040 runOnUiThread(
new Runnable() {
2064 if(
key.
equals(
"UpstreamRelocationFiltered"))
2066 if(mItemDebugVisibility !=
null && mItemDebugVisibility.isChecked()) {
2067 str = String.
format(
"%s re-localization filtered because an acceleration of %s has been detected, which is over current threshold set in the settings.",
2068 mCameraDriver == 2?
"AREngine":
"ARCore",
value);
2071 else if(
key.
equals(
"TangoServiceException"))
2073 else if(
key.
equals(
"FisheyeOverExposed"))
2075 else if(
key.
equals(
"FisheyeUnderExposed"))
2079 else if(
key.
equals(
"ColorUnderExposed"))
2083 else if(
key.
equals(
"TooFewFeaturesTracked"))
2085 if(!
value.equals(
"0"))
2087 str = String.
format(
"Too few features (%s) were tracked in the fisheye image. This may result in poor odometry!",
value);
2094 str = String.
format(
"Too close! Tip: Scan from at least ~1 meter from surfaces.",
value);
2097 else if(
key.
equals(
"TangoPoseEventNotReceived"))
2099 str = String.
format(
"No valid tango pose event received since %s sec.",
value);
2103 str = String.
format(
"Unknown Camera event detected!? (type=%d, key=%s, value=%s)",
type,
key,
value);
2107 mToast.setText(
str);
2118 runOnUiThread(
new Runnable() {
2126 int versionNumber = 0;
2127 String packageName = TANGO_PACKAGE_NAME;
2129 PackageInfo
pi = getApplicationContext().getPackageManager().getPackageInfo(packageName,
2130 PackageManager.GET_META_DATA);
2131 versionNumber =
pi.versionCode;
2132 }
catch (NameNotFoundException
e) {
2133 e.printStackTrace();
2135 return (minVersion <= versionNumber);
2142 mExportProgressDialog.setTitle(
"Post-Processing");
2143 mExportProgressDialog.setMessage(String.format(
"Please wait while optimizing..."));
2144 mExportProgressDialog.setProgress(0);
2145 mExportProgressDialog.show();
2148 Thread workingThread =
new Thread(
new Runnable() {
2151 runOnUiThread(
new Runnable() {
2154 if(mExportProgressDialog.isShowing())
2156 mExportProgressDialog.dismiss();
2157 if(loopDetected >= 0)
2159 mTotalLoopClosures+=loopDetected;
2160 if(withStandardMeshExport)
2162 export(
true,
true,
false,
true, 200000);
2166 mProgressDialog.setTitle(
"Post-Processing");
2167 mProgressDialog.setMessage(String.format(
"Optimization done! Increasing visual appeal..."));
2168 mProgressDialog.show();
2170 if(mOpenedDatabasePath.isEmpty())
2176 else if(loopDetected < 0)
2178 mToast.makeText(getActivity(), String.format(
"Optimization failed!"), mToast.LENGTH_LONG).show();
2183 mProgressDialog.dismiss();
2184 mToast.makeText(getActivity(), String.format(
"Optimization canceled"), mToast.LENGTH_LONG).show();
2190 workingThread.start();
2193 private Handler notouchHandler =
new Handler(){
2194 public void handleMessage(Message
msg) {
2198 private Runnable notouchCallback =
new Runnable() {
2201 if(!mProgressDialog.isShowing() && !mMenuOpened)
2203 setNavVisibility(
false);
2204 mHudVisible =
false;
2205 updateState(mState);
2209 resetNoTouchTimer();
2215 resetNoTouchTimer(
false);
2222 setNavVisibility(
true);
2223 if(mItemSave !=
null)
2225 updateState(mState);
2229 notouchHandler.removeCallbacks(notouchCallback);
2230 notouchHandler.postDelayed(notouchCallback, NOTOUCH_TIMEOUT);
2232 if(mGLView.getRenderMode() == GLSurfaceView.RENDERMODE_WHEN_DIRTY)
2234 mGLView.requestRender();
2239 notouchHandler.removeCallbacks(notouchCallback);
2240 Timer
timer =
new Timer();
2248 mToast.makeText(getActivity(), String.format(
"Re-adding %d online clouds, this may take some time...", mMapNodes), mToast.LENGTH_LONG).show();
2251 if(!DISABLE_LOG) Log.i(TAG, String.format(
"updateState() state=%s hud=%d",
state.toString(), mHudVisible?1:0));
2252 mStatusTexts[0] =
state.toString();
2257 mButtonLighting.setVisibility(View.INVISIBLE);
2258 mButtonWireframe.setVisibility(View.INVISIBLE);
2259 mButtonCloseVisualization.setVisibility(View.INVISIBLE);
2260 mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
2261 mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
2262 mButtonLibrary.setVisibility(View.INVISIBLE);
2263 mButtonNewScan.setVisibility(View.INVISIBLE);
2264 mItemSave.setEnabled(
false);
2265 mItemExport.setEnabled(
false);
2266 mItemOpen.setEnabled(
false);
2267 mItemNewScan.setEnabled(
true);
2268 mItemPostProcessing.setEnabled(
false);
2269 mItemSettings.setEnabled(
false);
2270 mItemResume.setEnabled(
false);
2272 mItemLocalizationMode.setEnabled(
true);
2273 mItemTrajectoryMode.setEnabled(
true);
2274 mItemDataRecorderMode.setEnabled(
true);
2275 mButtonStart.setVisibility(mState ==
State.
STATE_CAMERA?View.VISIBLE:View.INVISIBLE);
2276 mButtonStop.setVisibility(mHudVisible && mState ==
State.
STATE_MAPPING?View.VISIBLE:View.INVISIBLE);
2278 case STATE_PROCESSING:
2279 mButtonLighting.setVisibility(View.INVISIBLE);
2280 mButtonWireframe.setVisibility(View.INVISIBLE);
2281 mButtonCloseVisualization.setVisibility(View.INVISIBLE);
2282 mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
2283 mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
2284 mButtonLibrary.setVisibility(View.INVISIBLE);
2285 mButtonNewScan.setVisibility(View.INVISIBLE);
2286 mItemSave.setEnabled(
false);
2287 mItemExport.setEnabled(
false);
2288 mItemOpen.setEnabled(
false);
2289 mItemNewScan.setEnabled(
false);
2290 mItemPostProcessing.setEnabled(
false);
2291 mItemSettings.setEnabled(
false);
2292 mItemResume.setEnabled(
false);
2293 mItemModes.setEnabled(
false);
2294 mButtonStart.setVisibility(View.INVISIBLE);
2295 mButtonStop.setVisibility(View.INVISIBLE);
2297 case STATE_VISUALIZING:
2298 case STATE_VISUALIZING_CAMERA:
2299 mButtonLighting.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
2300 mButtonWireframe.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
2302 mButtonCloseVisualization.setEnabled(
true);
2305 mButtonLibrary.setVisibility(View.INVISIBLE);
2306 mButtonNewScan.setVisibility(View.INVISIBLE);
2311 mItemPostProcessing.setEnabled(
false);
2315 mItemLocalizationMode.setEnabled(
true);
2316 mItemTrajectoryMode.setEnabled(
false);
2317 mItemDataRecorderMode.setEnabled(
false);
2318 mButtonStart.setVisibility(View.INVISIBLE);
2321 case STATE_VISUALIZING_WHILE_LOADING:
2322 mButtonLighting.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
2323 mButtonWireframe.setVisibility(mHudVisible && !mItemRenderingPointCloud.isChecked()?View.VISIBLE:View.INVISIBLE);
2324 mButtonCloseVisualization.setVisibility(mHudVisible?View.VISIBLE:View.INVISIBLE);
2325 mButtonCloseVisualization.setEnabled(
false);
2326 mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
2327 mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
2328 mButtonLibrary.setVisibility(View.INVISIBLE);
2329 mButtonNewScan.setVisibility(View.INVISIBLE);
2330 mItemSave.setEnabled(
false);
2331 mItemExport.setEnabled(
false);
2332 mItemOpen.setEnabled(
false);
2333 mItemPostProcessing.setEnabled(
false);
2334 mItemSettings.setEnabled(
false);
2335 mItemResume.setEnabled(
false);
2336 mItemModes.setEnabled(
false);
2337 mButtonStart.setVisibility(View.INVISIBLE);
2338 mButtonStop.setVisibility(View.INVISIBLE);
2341 mButtonLighting.setVisibility(View.INVISIBLE);
2342 mButtonWireframe.setVisibility(View.INVISIBLE);
2343 mButtonCloseVisualization.setVisibility(View.INVISIBLE);
2344 mButtonSaveOnDevice.setVisibility(View.INVISIBLE);
2345 mButtonShareOnSketchfab.setVisibility(View.INVISIBLE);
2346 mButtonLibrary.setVisibility(mState==
State.
STATE_WELCOME?View.VISIBLE:View.INVISIBLE);
2347 mButtonNewScan.setVisibility(mState==
State.
STATE_WELCOME?View.VISIBLE:View.INVISIBLE);
2348 mItemSave.setEnabled(mMapNodes>0);
2349 mItemExport.setEnabled(mMapNodes>0);
2350 mItemOpen.setEnabled(
true);
2351 mItemNewScan.setEnabled(
true);
2352 mItemPostProcessing.setEnabled(mMapNodes>0);
2353 mItemSettings.setEnabled(
true);
2354 mItemResume.setEnabled(mMapNodes>0);
2355 mItemModes.setEnabled(
true);
2356 mButtonStart.setVisibility(View.INVISIBLE);
2357 mButtonStop.setVisibility(View.INVISIBLE);
2358 mItemLocalizationMode.setEnabled(
true);
2359 mItemDataRecorderMode.setEnabled(
true);
2360 mItemTrajectoryMode.setEnabled(
true);
2363 mButtonCameraView.setVisibility(mHudVisible?View.VISIBLE:View.INVISIBLE);
2364 mButtonBackfaceShown.setVisibility(mHudVisible && (mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked())?View.VISIBLE:View.INVISIBLE);
2365 mSeekBarOrthoCut.setVisibility(mHudVisible && mButtonCameraView.getSelectedItemPosition() == 3?View.VISIBLE:View.INVISIBLE);
2366 mSeekBarGrid.setVisibility(mHudVisible && mSeekBarGrid.isEnabled() && mButtonCameraView.getSelectedItemPosition() == 3?View.VISIBLE:View.INVISIBLE);
2370 mGLView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
2371 mGLView.requestRender();
2375 mGLView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
2381 mIntentDbToOpen =
null;
2386 if(!DISABLE_LOG) Log.i(TAG, String.format(
"startMapping()"));
2390 if(mMemoryWarningDialog !=
null)
2392 mMemoryWarningDialog.dismiss();
2393 mMemoryWarningDialog=
null;
2396 mLastFastMovementNotificationStamp = System.currentTimeMillis()/1000;
2398 if(mItemDataRecorderMode.isChecked())
2400 mToast.makeText(getActivity(), String.format(
"Data Recorder Mode: no map is created, only raw data is recorded."), mToast.LENGTH_LONG).show();
2402 else if(mMapNodes>0)
2404 if(mItemLocalizationMode!=
null && mItemLocalizationMode.isChecked())
2406 mToast.makeText(getActivity(), String.format(
"Localization mode"), mToast.LENGTH_LONG).show();
2410 mToast.makeText(getActivity(), String.format(
"On resume, a new map is created. Tip: Try relocalizing in the previous area."), mToast.LENGTH_LONG).show();
2413 else if(mMapNodes==0 && mItemLocalizationMode!=
null && mItemLocalizationMode.isChecked())
2415 mItemLocalizationMode.setChecked(
false);
2417 mToast.makeText(getActivity(), String.format(
"Disabled localization mode as the map is empty, now mapping..."), mToast.LENGTH_LONG).show();
2423 mProgressDialog.setTitle(
"");
2424 mProgressDialog.setMessage(
"Stopping camera...");
2425 mProgressDialog.show();
2444 if(mArCoreCamera !=
null)
2446 synchronized (
this) {
2448 mArCoreCamera.
close();
2449 mArCoreCamera =
null;
2453 Thread stopThread =
new Thread(
new Runnable() {
2455 if(!DISABLE_LOG) Log.i(TAG, String.format(
"stopCamera()"));
2457 if(mCameraServiceConnectionUsed)
2459 if(!DISABLE_LOG) Log.i(TAG, String.format(
"unbindService"));
2460 getActivity().unbindService(mCameraServiceConnection);
2462 mCameraServiceConnectionUsed =
false;
2464 runOnUiThread(
new Runnable() {
2467 if(!DISABLE_LOG) Log.i(TAG, String.format(
"stopMapping(): runOnUiThread"));
2468 mProgressDialog.dismiss();
2478 mProgressDialog.setTitle(
"");
2479 mProgressDialog.setMessage(
"Stopping camera...");
2480 mProgressDialog.show();
2484 if(mArCoreCamera !=
null)
2486 synchronized (
this) {
2488 mArCoreCamera.
close();
2489 mArCoreCamera =
null;
2493 Thread stopThread =
new Thread(
new Runnable() {
2495 if(!DISABLE_LOG) Log.i(TAG, String.format(
"setPausedMapping()"));
2497 if(!DISABLE_LOG) Log.i(TAG, String.format(
"stopCamera()"));
2499 if(mCameraServiceConnectionUsed)
2501 if(!DISABLE_LOG) Log.i(TAG, String.format(
"unbindService"));
2502 getActivity().unbindService(mCameraServiceConnection);
2504 mCameraServiceConnectionUsed =
false;
2506 runOnUiThread(
new Runnable() {
2508 if(!DISABLE_LOG) Log.i(TAG, String.format(
"stopMapping(): runOnUiThread"));
2509 mProgressDialog.dismiss();
2513 long freeMemory = getFreeMemory();
2514 mStatusTexts[1] = getString(
R.string.memory)+String.valueOf(mFreeMemoryOnStart>freeMemory?mFreeMemoryOnStart-freeMemory:0);
2515 mStatusTexts[2] = getString(
R.string.free_memory)+String.valueOf(freeMemory);
2516 updateStatusTexts();
2518 mDateOnPause =
new Date();
2520 long memoryFree = getFreeMemory();
2521 if(!mOnPause && !mItemLocalizationMode.isChecked() && !mItemDataRecorderMode.isChecked() && memoryFree >= 100 && mMapNodes>2)
2523 if(!DISABLE_LOG) Log.i(TAG, String.format(
"Do standard processing>?"));
2525 AlertDialog
d2 =
new AlertDialog.Builder(getActivity())
2526 .setCancelable(
false)
2527 .setTitle(
"Mapping Stopped! Optimize Now?")
2528 .setMessage(
"Do you want to do standard graph and mesh optimizations now? This can be also done later using \"Optimize\" and \"Export\" menus.")
2529 .setNeutralButton(
"Only Graph",
new DialogInterface.OnClickListener() {
2530 public void onClick(DialogInterface dialog, int which) {
2531 standardOptimization(false);
2534 .setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
2535 public void onClick(DialogInterface dialog, int which) {
2536 standardOptimization(true);
2539 .setNegativeButton(
"No",
new DialogInterface.OnClickListener() {
2540 public void onClick(DialogInterface dialog, int which) {
2541 if(mOpenedDatabasePath.isEmpty())
2548 d2.setCanceledOnTouchOutside(
false);
2559 resetNoTouchTimer();
2560 if(!DISABLE_LOG) Log.i(TAG,
"called onOptionsItemSelected; selected item: " + item);
2561 int itemId = item.getItemId();
2562 if (itemId ==
R.id.post_processing_standard)
2564 standardOptimization(
false);
2566 else if (itemId ==
R.id.detect_more_loop_closures)
2568 mProgressDialog.setTitle(
"Post-Processing");
2569 mProgressDialog.setMessage(String.format(
"Please wait while detecting more loop closures..."));
2570 mProgressDialog.show();
2572 Thread workingThread =
new Thread(
new Runnable() {
2575 runOnUiThread(
new Runnable() {
2577 mProgressDialog.dismiss();
2578 if(loopDetected >= 0)
2580 mTotalLoopClosures+=loopDetected;
2581 mToast.makeText(getActivity(), String.format(
"Detection done! %d new loop closure(s) added.", loopDetected), mToast.LENGTH_SHORT).show();
2583 else if(loopDetected < 0)
2585 mToast.makeText(getActivity(), String.format(
"Detection failed!"), mToast.LENGTH_SHORT).show();
2592 workingThread.start();
2594 else if (itemId ==
R.id.global_graph_optimization)
2596 mProgressDialog.setTitle(
"Post-Processing");
2597 mProgressDialog.setMessage(String.format(
"Global graph optimization..."));
2598 mProgressDialog.show();
2600 Thread workingThread =
new Thread(
new Runnable() {
2603 runOnUiThread(
new Runnable() {
2605 mProgressDialog.dismiss();
2608 mToast.makeText(getActivity(), String.format(
"Optimization done!"), mToast.LENGTH_SHORT).show();
2612 mToast.makeText(getActivity(), String.format(
"Optimization failed!"), mToast.LENGTH_SHORT).show();
2619 workingThread.start();
2621 else if (itemId ==
R.id.polygons_filtering)
2623 mProgressDialog.setTitle(
"Post-Processing");
2624 mProgressDialog.setMessage(String.format(
"Noise filtering..."));
2625 mProgressDialog.show();
2628 else if (itemId ==
R.id.gain_compensation_fast)
2630 mProgressDialog.setTitle(
"Post-Processing");
2631 mProgressDialog.setMessage(String.format(
"Adjusting Colors (Fast)..."));
2632 mProgressDialog.show();
2635 else if (itemId ==
R.id.gain_compensation_full)
2637 mProgressDialog.setTitle(
"Post-Processing");
2638 mProgressDialog.setMessage(String.format(
"Adjusting Colors (Full)..."));
2639 mProgressDialog.show();
2642 else if (itemId ==
R.id.bilateral_filtering)
2644 mProgressDialog.setTitle(
"Post-Processing");
2645 mProgressDialog.setMessage(String.format(
"Mesh smoothing..."));
2646 mProgressDialog.show();
2649 else if (itemId ==
R.id.sba)
2651 mProgressDialog.setTitle(
"Post-Processing");
2652 mProgressDialog.setMessage(String.format(
"Bundle adjustement..."));
2653 mProgressDialog.show();
2655 Thread workingThread =
new Thread(
new Runnable() {
2658 runOnUiThread(
new Runnable() {
2660 mProgressDialog.dismiss();
2663 mToast.makeText(getActivity(), String.format(
"Optimization done!"), mToast.LENGTH_SHORT).show();
2667 mToast.makeText(getActivity(), String.format(
"Optimization failed!"), mToast.LENGTH_SHORT).show();
2673 workingThread.start();
2675 else if(itemId ==
R.id.status)
2677 item.setChecked(!item.isChecked());
2678 updateStatusTexts();
2680 else if(itemId ==
R.id.debug)
2682 item.setChecked(!item.isChecked());
2683 updateStatusTexts();
2685 else if(itemId ==
R.id.mesh || itemId ==
R.id.texture_mesh || itemId ==
R.id.point_cloud)
2687 item.setChecked(
true);
2690 mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked(),
2691 mItemRenderingTextureMesh.isChecked());
2693 resetNoTouchTimer();
2698 int type = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
2699 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
2700 SharedPreferences.Editor editor = sharedPref.edit();
2701 editor.putInt(getString(
R.string.pref_key_rendering),
type);
2706 else if(itemId ==
R.id.map_shown)
2708 item.setChecked(!item.isChecked());
2711 else if(itemId ==
R.id.odom_shown)
2713 item.setChecked(!item.isChecked());
2716 else if(itemId ==
R.id.localization_mode)
2718 item.setChecked(!item.isChecked());
2721 else if(itemId ==
R.id.trajectory_mode)
2723 item.setChecked(!item.isChecked());
2725 setCamera(item.isChecked()?2:1);
2727 else if(itemId ==
R.id.graph_optimization)
2729 item.setChecked(!item.isChecked());
2732 else if(itemId ==
R.id.graph_visible)
2734 item.setChecked(!item.isChecked());
2737 else if(itemId ==
R.id.grid_visible)
2739 item.setChecked(!item.isChecked());
2740 mSeekBarGrid.setEnabled(item.isChecked());
2741 mSeekBarGrid.setVisibility(mHudVisible && mSeekBarGrid.isEnabled()&&mButtonCameraView.getSelectedItemPosition() == 3?View.VISIBLE:View.INVISIBLE);
2744 else if (itemId ==
R.id.save)
2748 else if(itemId ==
R.id.resume)
2752 else if(itemId ==
R.id.new_scan)
2756 else if(itemId ==
R.id.data_recorder)
2758 final boolean dataRecorderOldState = item.isChecked();
2759 AlertDialog
d2 =
new AlertDialog.Builder(getActivity())
2760 .setCancelable(
false)
2761 .setTitle(
"Data Recorder Mode")
2762 .setMessage(
"Changing from/to data recorder mode will close the current session. Do you want to continue?")
2763 .setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
2764 public void onClick(DialogInterface dialog, int which) {
2766 mTotalLoopClosures = 0;
2767 int index = STATUS_TEXTS_POSE_INDEX;
2769 mStatusTexts[index++] = getString(R.string.nodes)+0;
2770 mStatusTexts[index++] = getString(R.string.words)+0;
2771 mStatusTexts[index++] = getString(R.string.database_size)+0;
2772 mStatusTexts[index++] = getString(R.string.points)+0;
2773 mStatusTexts[index++] = getString(R.string.polygons)+0;
2774 mStatusTexts[index++] = getString(R.string.update_time)+0;
2775 mStatusTexts[index++] = getString(R.string.features)+0;
2776 mStatusTexts[index++] = getString(R.string.rehearsal)+0;
2777 mStatusTexts[index++] = getString(R.string.total_loop)+0;
2778 mStatusTexts[index++] = getString(R.string.inliers)+0;
2779 mStatusTexts[index++] = getString(R.string.hypothesis)+0;
2780 mStatusTexts[index++] = getString(R.string.fps)+0;
2781 mStatusTexts[index++] = getString(R.string.distance)+0;
2782 mStatusTexts[index++] = String.format(
"Pose (x,y,z): 0 0 0");
2783 updateStatusTexts();
2785 mItemDataRecorderMode.setChecked(!dataRecorderOldState);
2786 RTABMapLib.setDataRecorderMode(nativeApplication, mItemDataRecorderMode.isChecked());
2788 mOpenedDatabasePath =
"";
2789 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
2790 boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory), Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
2791 String tmpDatabase = mWorkingDirectory+RTABMAP_TMP_DB;
2792 RTABMapLib.openDatabase(nativeApplication, tmpDatabase, databaseInMemory, false, true);
2794 mItemLocalizationMode.setEnabled(!mItemDataRecorderMode.isChecked());
2796 if(mItemDataRecorderMode.isChecked())
2798 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();
2802 mToast.makeText(getActivity(), String.format(
"Data recorder mode deactivated!"), mToast.LENGTH_LONG).show();
2806 .setNegativeButton(
"No",
new DialogInterface.OnClickListener() {
2807 public void onClick(DialogInterface dialog, int which) {
2812 d2.setCanceledOnTouchOutside(
false);
2815 else if(itemId ==
R.id.export_point_cloud ||
2816 itemId ==
R.id.export_point_cloud_highrez)
2818 final boolean regenerateCloud = itemId ==
R.id.export_point_cloud_highrez;
2820 export(
false,
false, regenerateCloud,
false, 0);
2822 else if(itemId ==
R.id.export_optimized_mesh ||
2823 itemId ==
R.id.export_optimized_mesh_texture)
2825 final boolean isOBJ = itemId ==
R.id.export_optimized_mesh_texture;
2827 RelativeLayout linearLayout =
new RelativeLayout(
this);
2828 final NumberPicker aNumberPicker =
new NumberPicker(
this);
2829 aNumberPicker.setMaxValue(9);
2830 aNumberPicker.setMinValue(0);
2831 aNumberPicker.setWrapSelectorWheel(
false);
2832 aNumberPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
2833 aNumberPicker.setFormatter(
new NumberPicker.Formatter() {
2835 public String format(int i) {
2840 return String.format(
"%d00 000", i);
2843 aNumberPicker.setValue(2);
2847 Method method = aNumberPicker.getClass().getDeclaredMethod(
"changeValueByOne",
boolean.
class);
2848 method.setAccessible(
true);
2849 method.invoke(aNumberPicker,
true);
2850 }
catch (NoSuchMethodException
e) {
2851 e.printStackTrace();
2852 }
catch (IllegalArgumentException
e) {
2853 e.printStackTrace();
2854 }
catch (IllegalAccessException
e) {
2855 e.printStackTrace();
2856 }
catch (InvocationTargetException
e) {
2857 e.printStackTrace();
2861 RelativeLayout.LayoutParams
params =
new RelativeLayout.LayoutParams(50, 50);
2862 RelativeLayout.LayoutParams numPicerParams =
new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
2863 numPicerParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
2865 linearLayout.setLayoutParams(params);
2866 linearLayout.addView(aNumberPicker,numPicerParams);
2868 AlertDialog ad =
new AlertDialog.Builder(
this)
2869 .setTitle(
"Maximum polygons")
2870 .setView(linearLayout)
2871 .setCancelable(
false)
2872 .setPositiveButton(
"Ok",
2873 new DialogInterface.OnClickListener() {
2874 public void onClick(DialogInterface dialog,
2876 export(isOBJ, true, false, true, aNumberPicker.getValue()*100000);
2879 .setNegativeButton(
"Cancel",
2880 new DialogInterface.OnClickListener() {
2881 public void onClick(DialogInterface dialog,
2886 ad.setCanceledOnTouchOutside(
false);
2889 else if(itemId ==
R.id.open)
2893 else if(itemId ==
R.id.settings)
2895 Intent intent =
new Intent(getActivity(), SettingsActivity.class);
2896 startActivity(intent);
2898 else if(itemId ==
R.id.about)
2900 AboutDialog about =
new AboutDialog(
this);
2901 about.setTitle(
"About RTAB-Map");
2911 startCamera(String.format(
"Hold Tight! Initializing Camera Service...\n"
2912 +
"Tip: If the camera is still drifting just after the mapping has started, do \"Reset\"."));
2920 closeVisualization();
2923 mTotalLoopClosures = 0;
2925 int index = STATUS_TEXTS_POSE_INDEX;
2927 mStatusTexts[index++] = getString(
R.string.nodes)+0;
2928 mStatusTexts[index++] = getString(
R.string.words)+0;
2929 mStatusTexts[index++] = getString(
R.string.database_size)+0;
2930 mStatusTexts[index++] = getString(
R.string.points)+0;
2931 mStatusTexts[index++] = getString(
R.string.polygons)+0;
2932 mStatusTexts[index++] = getString(
R.string.update_time)+0;
2933 mStatusTexts[index++] = getString(
R.string.features)+0;
2934 mStatusTexts[index++] = getString(
R.string.rehearsal)+0;
2935 mStatusTexts[index++] = getString(
R.string.total_loop)+0;
2936 mStatusTexts[index++] = getString(
R.string.inliers)+0;
2937 mStatusTexts[index++] = getString(
R.string.hypothesis)+0;
2938 mStatusTexts[index++] = getString(
R.string.fps)+0;
2939 mStatusTexts[index++] = getString(
R.string.distance)+0;
2940 mStatusTexts[index++] = String.format(
"Pose (x,y,z): 0 0 0");
2941 updateStatusTexts();
2943 mOpenedDatabasePath =
"";
2944 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
2945 boolean databaseInMemory = sharedPref.getBoolean(getString(
R.string.pref_key_db_in_memory),
Boolean.parseBoolean(getString(
R.string.pref_default_db_in_memory)));
2946 final String tmpDatabase = mWorkingDirectory+RTABMAP_TMP_DB;
2948 File newFile =
new File(tmpDatabase);
2949 final int fileSizeMB = (
int)newFile.length()/(1024 * 1024);
2954 AlertDialog
d2 =
new AlertDialog.Builder(getActivity())
2955 .setCancelable(
false)
2956 .setTitle(
"Recovery")
2957 .setMessage(String.format(
"The previous session (%d MB) was not correctly saved, do you want to recover it?", fileSizeMB))
2958 .setNegativeButton(
"Ignore",
new DialogInterface.OnClickListener() {
2959 public void onClick(DialogInterface dialog,
int which) {
2960 (
new File(tmpDatabase)).
delete();
2964 .setNeutralButton(
"Cancel",
new DialogInterface.OnClickListener() {
2965 public void onClick(DialogInterface dialog,
int which) {
2969 .setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
2970 public void onClick(DialogInterface dialog,
int which) {
2971 final String fileName =
new SimpleDateFormat(
"yyMMdd-HHmmss").format(
new Date()) +
".db";
2972 final String outputDbPath = mWorkingDirectory + fileName;
2974 mExportProgressDialog.setTitle(
"Recovering");
2975 mExportProgressDialog.setMessage(String.format(
"Please wait while recovering data..."));
2976 mExportProgressDialog.setProgress(0);
2978 final State previousState = mState;
2980 mExportProgressDialog.show();
2983 Thread exportThread =
new Thread(
new Runnable() {
2986 final long startTime = System.currentTimeMillis()/1000;
2992 runOnUiThread(
new Runnable() {
2994 if(mExportProgressDialog.isShowing())
2998 AlertDialog
d2 =
new AlertDialog.Builder(getActivity())
2999 .setCancelable(
false)
3000 .setTitle(
"Database saved!")
3001 .setMessage(String.format(
"Database \"%s\" (%d MB) successfully saved!", fileName, fileSizeMB))
3002 .setPositiveButton(
"OK",
new DialogInterface.OnClickListener() {
3003 public void onClick(DialogInterface dialog, int which) {
3004 openDatabase(fileName, false);
3008 d2.setCanceledOnTouchOutside(
true);
3013 updateState(previousState);
3014 mToast.makeText(getActivity(), String.format(
"Recovery failed!"), mToast.LENGTH_LONG).show();
3016 mExportProgressDialog.dismiss();
3020 mToast.makeText(getActivity(), String.format(
"Recovery canceled"), mToast.LENGTH_LONG).show();
3021 updateState(previousState);
3027 exportThread.start();
3029 refreshSystemMediaScanDataBase(getActivity(), outputDbPath);
3033 d2.setCanceledOnTouchOutside(
false);
3040 if(!(mState ==
State.STATE_CAMERA || mState ==
State.STATE_MAPPING))
3043 startCamera(String.format(
"Hold Tight! Initializing Camera Service...\n"
3044 +
"Tip: If the camera is still drifting just after the mapping has started, do \"Reset\"."));
3053 if(files.length > 0)
3055 String[] filesWithSize =
new String[files.length];
3056 for(
int i = 0;
i<filesWithSize.length; ++
i)
3058 File filePath =
new File(mWorkingDirectory+files[
i]);
3059 long mb = filePath.length()/(1024*1024);
3060 filesWithSize[
i] = files[
i] +
" ("+mb+
" MB)";
3063 ArrayList<HashMap<String, String> > arrayList =
new ArrayList<HashMap<String, String> >();
3064 for (
int i = 0;
i < filesWithSize.length;
i++) {
3065 HashMap<String, String> hashMap =
new HashMap<String, String>();
3066 hashMap.put(
"name", filesWithSize[
i]);
3067 hashMap.put(
"path", mWorkingDirectory + files[
i]);
3068 arrayList.add(hashMap);
3070 String[] from = {
"name",
"path"};
3071 int[] to = {
R.id.textView,
R.id.imageView};
3074 AlertDialog.Builder builder =
new AlertDialog.Builder(
this);
3075 builder.setCancelable(
true);
3076 builder.setTitle(
"Choose Your File (*.db)");
3077 builder.setNegativeButton(
"Cancel",
new DialogInterface.OnClickListener() {
3078 public void onClick(DialogInterface dialog, int whichIn) {
3082 builder.setAdapter(simpleAdapter,
new DialogInterface.OnClickListener() {
3084 public void onClick(DialogInterface dialog, final int which) {
3087 AlertDialog d2 = new AlertDialog.Builder(getActivity())
3088 .setCancelable(false)
3089 .setTitle(
"Opening database...")
3090 .setMessage(
"Do you want to adjust colors now?\nThis can be done later under Optimize menu.")
3091 .setPositiveButton(
"Yes", new DialogInterface.OnClickListener() {
3092 public void onClick(DialogInterface dialog, int whichIn) {
3093 openDatabase(files[which], true);
3096 .setNeutralButton(
"No", new DialogInterface.OnClickListener() {
3097 public void onClick(DialogInterface dialog, int whichIn) {
3098 openDatabase(files[which], false);
3102 d2.setCanceledOnTouchOutside(false);
3108 final AlertDialog ad = builder.create();
3109 ad.setCanceledOnTouchOutside(
true);
3110 ad.setOnShowListener(
new OnShowListener()
3113 public void onShow(DialogInterface dialog)
3115 ListView lv = ad.getListView();
3116 ad.registerForContextMenu(lv);
3117 lv.setOnCreateContextMenuListener(
new OnCreateContextMenuListener() {
3120 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
3122 if (
v.getId()==ad.getListView().getId()) {
3123 AdapterView.AdapterContextMenuInfo
info = (AdapterView.AdapterContextMenuInfo)menuInfo;
3125 menu.setHeaderTitle(files[position]);
3126 menu.add(Menu.NONE, 0, 0,
"Rename").setOnMenuItemClickListener(
new OnMenuItemClickListener() {
3128 public boolean onMenuItemClick(MenuItem item) {
3129 AlertDialog.Builder builderRename =
new AlertDialog.Builder(getActivity());
3130 builderRename.setCancelable(
false);
3131 builderRename.setTitle(
"RTAB-Map Database Name (*.db):");
3132 final EditText input =
new EditText(getActivity());
3133 input.setInputType(InputType.TYPE_CLASS_TEXT);
3135 input.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
3136 input.setSelectAllOnFocus(
true);
3138 builderRename.setView(input);
3139 builderRename.setPositiveButton(
"OK",
new DialogInterface.OnClickListener() {
3141 public void onClick(DialogInterface dialog, int which)
3143 final String fileName = input.getText().toString();
3145 if(!fileName.isEmpty())
3147 File newFile = new File(mWorkingDirectory + fileName +
".db");
3148 if(newFile.exists())
3150 AlertDialog d2 = new AlertDialog.Builder(getActivity())
3151 .setCancelable(false)
3152 .setTitle(
"File Already Exists")
3153 .setMessage(String.format(
"Name %s already used, choose another name.", fileName))
3155 d2.setCanceledOnTouchOutside(false);
3160 File from = new File(mWorkingDirectory, files[position]);
3161 File to = new File(mWorkingDirectory, fileName +
".db");
3164 long stamp = System.currentTimeMillis();
3165 if(stamp-mSavedStamp < 10000)
3168 Thread.sleep(10000 - (stamp-mSavedStamp));
3170 catch(InterruptedException e){}
3173 refreshSystemMediaScanDataBase(getActivity(), files[position]);
3174 refreshSystemMediaScanDataBase(getActivity(), to.getAbsolutePath());
3177 resetNoTouchTimer(true);
3182 AlertDialog alertToShow = builderRename.create();
3183 alertToShow.setCanceledOnTouchOutside(
false);
3184 alertToShow.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
3189 menu.add(Menu.NONE, 1, 1,
"Delete").setOnMenuItemClickListener(
new OnMenuItemClickListener() {
3191 public boolean onMenuItemClick(MenuItem item) {
3192 DialogInterface.OnClickListener dialogClickListener =
new DialogInterface.OnClickListener() {
3194 public void onClick(DialogInterface dialog,
int which) {
3196 case DialogInterface.BUTTON_POSITIVE:
3197 Log.e(TAG, String.format(
"Yes delete %s!", files[position]));
3198 (
new File(mWorkingDirectory+files[position])).
delete();
3199 refreshSystemMediaScanDataBase(getActivity(), mWorkingDirectory+files[position]);
3201 resetNoTouchTimer(
true);
3204 case DialogInterface.BUTTON_NEGATIVE:
3210 AlertDialog dialog =
new AlertDialog.Builder(getActivity())
3211 .setCancelable(
false)
3212 .setTitle(String.format(
"Delete %s", files[position]))
3213 .setMessage(
"Are you sure?")
3214 .setPositiveButton(
"Yes", dialogClickListener)
3215 .setNegativeButton(
"No", dialogClickListener).create();
3216 dialog.setCanceledOnTouchOutside(
false);
3221 menu.add(Menu.NONE, 2, 2,
"Share").setOnMenuItemClickListener(
new OnMenuItemClickListener() {
3223 public boolean onMenuItemClick(MenuItem item) {
3225 if (!PermissionHelper.hasPermission(getActivity(), Manifest.permission.INTERNET)) {
3226 PermissionHelper.requestPermission(getActivity(), Manifest.permission.INTERNET);
3231 File
f =
new File(mWorkingDirectory+files[position]);
3232 final int fileSizeMB = (
int)
f.length()/(1024 * 1024);
3233 Intent shareIntent =
new Intent();
3234 shareIntent.setAction(Intent.ACTION_SEND);
3235 shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
3236 Uri fileUri = FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() +
".provider", f);
3237 shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
3238 shareIntent.setType(
"application/octet-stream");
3239 startActivity(Intent.createChooser(shareIntent, String.format(
"Sharing database \"%s\" (%d MB)...", files[position], fileSizeMB)));
3241 resetNoTouchTimer(
true);
3254 private void export(
final boolean isOBJ,
final boolean meshing,
final boolean regenerateCloud,
final boolean optimized,
final int optimizedMaxPolygons)
3256 final String extension = isOBJ?
".obj" :
".ply";
3259 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(
this);
3260 final String cloudVoxelSizeStr = sharedPref.getString(getString(
R.string.pref_key_cloud_voxel), getString(
R.string.pref_default_cloud_voxel));
3261 final float cloudVoxelSize =
Float.parseFloat(cloudVoxelSizeStr);
3262 final int textureSize = isOBJ?Integer.parseInt(sharedPref.getString(getString(
R.string.pref_key_texture_size), getString(
R.string.pref_default_texture_size))):0;
3263 final int textureCount = Integer.parseInt(sharedPref.getString(getString(
R.string.pref_key_texture_count), getString(
R.string.pref_default_texture_count)));
3264 final int normalK = Integer.parseInt(sharedPref.getString(getString(
R.string.pref_key_normal_k), getString(
R.string.pref_default_normal_k)));
3265 final float maxTextureDistance =
Float.parseFloat(sharedPref.getString(getString(
R.string.pref_key_max_texture_distance), getString(
R.string.pref_default_max_texture_distance)));
3266 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)));
3267 final float optimizedVoxelSize = cloudVoxelSize;
3268 final int optimizedDepth = Integer.parseInt(sharedPref.getString(getString(
R.string.pref_key_opt_depth), getString(
R.string.pref_default_opt_depth)));
3269 final float optimizedColorRadius =
Float.parseFloat(sharedPref.getString(getString(
R.string.pref_key_opt_color_radius), getString(
R.string.pref_default_opt_color_radius)));
3270 final boolean optimizedCleanWhitePolygons = sharedPref.getBoolean(getString(
R.string.pref_key_opt_clean_white),
Boolean.parseBoolean(getString(
R.string.pref_default_opt_clean_white)));
3271 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)));
3272 final boolean blockRendering = sharedPref.getBoolean(getString(
R.string.pref_key_block_render),
Boolean.parseBoolean(getString(
R.string.pref_default_block_render)));
3275 mExportProgressDialog.setTitle(
"Exporting");
3276 mExportProgressDialog.setMessage(String.format(
"Please wait while preparing data to export..."));
3277 mExportProgressDialog.setProgress(0);
3279 final State previousState = mState;
3281 mExportProgressDialog.show();
3284 Thread exportThread =
new Thread(
new Runnable() {
3287 final long startTime = System.currentTimeMillis()/1000;
3300 optimizedMaxPolygons,
3301 optimizedColorRadius,
3302 optimizedCleanWhitePolygons,
3303 optimizedMinClusterSize,
3305 minTextureClusterSize,
3307 runOnUiThread(
new Runnable() {
3309 if(mExportProgressDialog.isShowing())
3313 if(!meshing && cloudVoxelSize>0.0
f)
3315 mToast.makeText(getActivity(), String.format(
"Cloud assembled and voxelized at %s m.", cloudVoxelSizeStr), mToast.LENGTH_LONG).show();
3318 final long endTime = System.currentTimeMillis()/1000;
3320 if(endTime-startTime > 10)
3324 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
3325 boolean notifySound = sharedPref.getBoolean(getString(
R.string.pref_key_notification_sound),
Boolean.parseBoolean(getString(
R.string.pref_default_notification_sound)));
3326 Notification
n =
new Notification.Builder(getActivity())
3327 .setContentTitle(getString(
R.string.app_name))
3328 .setContentText(
"Data generated and ready to be exported!")
3329 .setSmallIcon(
R.drawable.ic_launcher)
3330 .setDefaults(notifySound?Notification.DEFAULT_SOUND:0)
3331 .setAutoCancel(
true).build();
3333 NotificationManager notificationManager =
3334 (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
3336 notificationManager.notify(0,
n);
3340 resetNoTouchTimer(
true);
3341 mSavedRenderingType = mItemRenderingPointCloud.isChecked()?0:mItemRenderingMesh.isChecked()?1:2;
3344 mItemRenderingPointCloud.setChecked(
true);
3348 mItemRenderingMesh.setChecked(
true);
3352 mItemRenderingTextureMesh.setChecked(
true);
3354 if(!optimizedCleanWhitePolygons)
3356 mButtonLighting.setChecked(
true);
3361 if(mButtonCameraView.getSelectedItemPosition() == 0)
3365 if(mOpenedDatabasePath.isEmpty())
3372 updateState(previousState);
3373 mToast.makeText(getActivity(), String.format(
"Exporting map failed!"), mToast.LENGTH_LONG).show();
3375 mExportProgressDialog.dismiss();
3379 mProgressDialog.dismiss();
3380 mToast.makeText(getActivity(), String.format(
"Export canceled"), mToast.LENGTH_LONG).show();
3381 updateState(previousState);
3387 exportThread.start();
3392 AlertDialog.Builder builder =
new AlertDialog.Builder(
this);
3393 builder.setCancelable(
false);
3394 builder.setTitle(
"RTAB-Map Database Name (*.db):");
3395 final EditText input =
new EditText(
this);
3396 input.setInputType(InputType.TYPE_CLASS_TEXT);
3397 if(mOpenedDatabasePath.isEmpty())
3399 String
timeStamp =
new SimpleDateFormat(
"yyMMdd-HHmmss").format(mDateOnPause);
3404 File
f =
new File(mOpenedDatabasePath);
3405 String
name =
f.getName();
3406 input.setText(
name.substring(0,
name.lastIndexOf(
".")));
3408 input.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
3409 input.setSelectAllOnFocus(
true);
3411 builder.setView(input);
3412 builder.setNegativeButton(
"Cancel",
new DialogInterface.OnClickListener() {
3414 public void onClick(DialogInterface dialog, int which)
3419 builder.setPositiveButton(
"OK",
new DialogInterface.OnClickListener() {
3421 public void onClick(DialogInterface dialog, int which)
3423 final String fileName = input.getText().toString();
3425 if(!fileName.isEmpty())
3427 File newFile = new File(mWorkingDirectory + fileName +
".db");
3428 if(newFile.exists())
3430 AlertDialog d2 = new AlertDialog.Builder(getActivity())
3431 .setCancelable(false)
3432 .setTitle(
"File Already Exists")
3433 .setMessage(
"Do you want to overwrite the existing file?")
3434 .setPositiveButton(
"Yes", new DialogInterface.OnClickListener() {
3435 public void onClick(DialogInterface dialog, int which) {
3436 saveDatabase(fileName);
3439 .setNegativeButton(
"No", new DialogInterface.OnClickListener() {
3440 public void onClick(DialogInterface dialog, int which) {
3442 resetNoTouchTimer(true);
3446 d2.setCanceledOnTouchOutside(false);
3451 saveDatabase(fileName);
3456 AlertDialog alertToShow = builder.create();
3457 alertToShow.setCanceledOnTouchOutside(
false);
3458 alertToShow.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
3468 Intent mediaScanIntent =
new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
3469 Uri contentUri = Uri.fromFile(
new File(docPath));
3470 mediaScanIntent.setData(contentUri);
3471 context.sendBroadcast(mediaScanIntent);
3476 final String newDatabasePath = mWorkingDirectory + fileName +
".db";
3477 final String newDatabasePathHuman = mWorkingDirectoryHuman + fileName +
".db";
3478 mProgressDialog.setTitle(
"Saving");
3479 if(mOpenedDatabasePath.equals(newDatabasePath))
3481 mProgressDialog.setMessage(String.format(
"Please wait while updating \"%s\"...", newDatabasePathHuman));
3485 mProgressDialog.setMessage(String.format(
"Please wait while saving \"%s\"...", newDatabasePathHuman));
3487 mProgressDialog.show();
3488 final State previousState = mState;
3490 Thread saveThread =
new Thread(
new Runnable() {
3493 runOnUiThread(
new Runnable() {
3496 if(mOpenedDatabasePath.equals(newDatabasePath))
3498 msg = String.format(
"Database \"%s\" updated.", newDatabasePathHuman);
3502 refreshSystemMediaScanDataBase(getActivity(), newDatabasePath);
3503 mSavedStamp = System.currentTimeMillis();
3504 msg = String.format(
"Database saved to \"%s\".", newDatabasePathHuman);
3510 PendingIntent pIntent = PendingIntent.getActivity(getActivity(), (
int) System.currentTimeMillis(), intent, 0);
3511 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
3512 boolean notifySound = sharedPref.getBoolean(getString(
R.string.pref_key_notification_sound),
Boolean.parseBoolean(getString(
R.string.pref_default_notification_sound)));
3513 Notification
n =
new Notification.Builder(getActivity())
3514 .setContentTitle(getString(
R.string.app_name))
3515 .setContentText(
msg)
3516 .setSmallIcon(
R.drawable.ic_launcher)
3517 .setContentIntent(pIntent)
3518 .setDefaults(notifySound?Notification.DEFAULT_SOUND:0)
3519 .setAutoCancel(
true).build();
3521 NotificationManager notificationManager =
3522 (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
3524 notificationManager.notify(0,
n);
3526 final File
f =
new File(newDatabasePath);
3527 final int fileSizeMB = (
int)
f.length()/(1024 * 1024);
3529 if(!mItemDataRecorderMode.isChecked())
3531 mOpenedDatabasePath = newDatabasePath;
3533 mProgressDialog.dismiss();
3534 updateState(previousState);
3536 AlertDialog
d2 =
new AlertDialog.Builder(getActivity())
3537 .setCancelable(
false)
3538 .setTitle(
"Database saved!")
3539 .setMessage(String.format(
"Database \"%s\" (%d MB) successfully saved!", newDatabasePathHuman, fileSizeMB))
3540 .setPositiveButton(
"OK",
new DialogInterface.OnClickListener() {
3541 public void onClick(DialogInterface dialog, int which) {
3542 resetNoTouchTimer(true);
3546 d2.setCanceledOnTouchOutside(
true);
3557 AlertDialog.Builder builder =
new AlertDialog.Builder(
this);
3558 builder.setTitle(
"Model Name:");
3559 final EditText input =
new EditText(
this);
3560 input.setInputType(InputType.TYPE_CLASS_TEXT);
3561 builder.setView(input);
3562 if(mOpenedDatabasePath.isEmpty())
3564 String
timeStamp =
new SimpleDateFormat(
"yyMMdd-HHmmss").format(mDateOnPause);
3569 File
f =
new File(mOpenedDatabasePath);
3570 String
name =
f.getName();
3571 input.setText(
name.substring(0,
name.lastIndexOf(
".")));
3573 input.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
3574 input.setSelectAllOnFocus(
true);
3576 builder.setCancelable(
false);
3577 builder.setNegativeButton(
"Cancel",
new DialogInterface.OnClickListener() {
3579 public void onClick(DialogInterface dialog, int which)
3582 resetNoTouchTimer(true);
3585 builder.setPositiveButton(
"Ok",
new DialogInterface.OnClickListener() {
3587 public void onClick(DialogInterface dialog, int which)
3589 final String fileName = input.getText().toString();
3591 if(!fileName.isEmpty())
3593 writeExportedFiles(fileName);
3597 AlertDialog alertToShow = builder.create();
3598 alertToShow.setCanceledOnTouchOutside(
false);
3599 alertToShow.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
3605 Log.i(TAG, String.format(
"Write exported mesh to \"%s\"", fileName));
3607 mProgressDialog.setTitle(
"Exporting");
3608 mProgressDialog.setMessage(String.format(
"Compressing the files..."));
3609 mProgressDialog.show();
3611 Thread workingThread =
new Thread(
new Runnable() {
3613 boolean success =
false;
3615 File tmpDir =
new File(mWorkingDirectory + RTABMAP_TMP_DIR);
3617 String[] fileNames =
Util.
loadFileList(mWorkingDirectory + RTABMAP_TMP_DIR,
false);
3618 if(!DISABLE_LOG) Log.i(TAG, String.format(
"Deleting %d files in \"%s\"", fileNames.length, mWorkingDirectory + RTABMAP_TMP_DIR));
3619 for(
int i=0;
i<fileNames.length; ++
i)
3621 File
f =
new File(mWorkingDirectory + RTABMAP_TMP_DIR +
"/" + fileNames[
i]);
3624 if(!DISABLE_LOG) Log.i(TAG, String.format(
"Deleted \"%s\"",
f.getPath()));
3628 if(!DISABLE_LOG) Log.i(TAG, String.format(
"Failed deleting \"%s\"",
f.getPath()));
3631 File exportDir =
new File(mWorkingDirectory + RTABMAP_EXPORT_DIR);
3634 final String pathHuman = mWorkingDirectoryHuman + RTABMAP_EXPORT_DIR + fileName +
".zip";
3635 final String zipOutput = mWorkingDirectory+RTABMAP_EXPORT_DIR+fileName+
".zip";
3639 if(fileNames.length > 0)
3641 String[] filesToZip =
new String[fileNames.length];
3642 for(
int i=0;
i<fileNames.length; ++
i)
3644 filesToZip[
i] = mWorkingDirectory + RTABMAP_TMP_DIR +
"/" + fileNames[
i];
3647 File toZIPFile =
new File(zipOutput);
3652 Util.
zip(filesToZip, zipOutput);
3655 catch(IOException
e)
3657 final String
msg =
e.getMessage();
3658 runOnUiThread(
new Runnable() {
3660 mToast.makeText(getActivity(), String.format(
"Exporting mesh \"%s\" failed! Error=%s", fileName,
msg), mToast.LENGTH_LONG).show();
3669 runOnUiThread(
new Runnable() {
3671 mProgressDialog.dismiss();
3673 final File
f =
new File(zipOutput);
3674 final int fileSizeMB = (
int)
f.length()/(1024 * 1024);
3676 AlertDialog
d =
new AlertDialog.Builder(getActivity())
3677 .setCancelable(
false)
3678 .setTitle(
"Mesh Saved!")
3679 .setMessage(String.format(
"Mesh \"%s\" (%d MB) successfully exported! Share it?", pathHuman, fileSizeMB))
3680 .setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
3681 public void onClick(DialogInterface dialog,
int which) {
3683 Intent shareIntent =
new Intent();
3684 shareIntent.setAction(Intent.ACTION_SEND);
3685 shareIntent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() +
".provider",
f));
3686 shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
3687 shareIntent.setType(
"application/zip");
3688 startActivity(Intent.createChooser(shareIntent,
"Sharing..."));
3690 resetNoTouchTimer(
true);
3693 .setNegativeButton(
"No",
new DialogInterface.OnClickListener() {
3694 public void onClick(DialogInterface dialog,
int which) {
3695 resetNoTouchTimer(
true);
3698 d.setCanceledOnTouchOutside(
false);
3705 runOnUiThread(
new Runnable() {
3707 mProgressDialog.dismiss();
3708 mToast.makeText(getActivity(), String.format(
"Exporting mesh \"%s\" failed! No files found in tmp directory!? Last export may have failed or have been canceled.", fileName), mToast.LENGTH_LONG).show();
3709 resetNoTouchTimer(
true);
3715 workingThread.start();
3720 mOpenedDatabasePath = mWorkingDirectory + fileName;
3722 Log.i(TAG,
"Open database " + mOpenedDatabasePath);
3724 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
3725 final boolean databaseInMemory = sharedPref.getBoolean(getString(
R.string.pref_key_db_in_memory),
Boolean.parseBoolean(getString(
R.string.pref_default_db_in_memory)));
3727 mProgressDialog.setTitle(
"Loading");
3728 mProgressDialog.setMessage(String.format(
"Opening database \"%s\"...", fileName));
3729 mProgressDialog.show();
3732 Thread openThread =
new Thread(
new Runnable() {
3737 runOnUiThread(
new Runnable() {
3742 mProgressDialog.dismiss();
3743 AlertDialog
d =
new AlertDialog.Builder(getActivity())
3744 .setCancelable(
false)
3746 .setMessage(
"The map is loaded but optimization of the map's graph has "
3747 +
"failed, so the map cannot be shown. Change the Graph Optimizer approach used"
3748 +
" or enable/disable if the graph is optimized from graph "
3749 +
"end in \"Settings -> Mapping...\" and try opening again.")
3750 .setPositiveButton(
"Open Settings",
new DialogInterface.OnClickListener() {
3751 public void onClick(DialogInterface dialog, int which) {
3752 Intent intent = new Intent(getActivity(), SettingsActivity.class);
3753 startActivity(intent);
3756 .setNegativeButton(
"Close",
new DialogInterface.OnClickListener() {
3757 public void onClick(DialogInterface dialog, int which) {
3760 d.setCanceledOnTouchOutside(
false);
3763 else if(status == -2)
3766 mProgressDialog.dismiss();
3767 AlertDialog
d =
new AlertDialog.Builder(getActivity())
3768 .setCancelable(
false)
3770 .setMessage(
"Failed to open database: Out of memory! Try "
3771 +
"again after lowering Point Cloud Density in Settings.")
3772 .setPositiveButton(
"Open Settings",
new DialogInterface.OnClickListener() {
3773 public void onClick(DialogInterface dialog, int which) {
3774 Intent intent = new Intent(getActivity(), SettingsActivity.class);
3775 startActivity(intent);
3778 .setNegativeButton(
"Close",
new DialogInterface.OnClickListener() {
3779 public void onClick(DialogInterface dialog, int which) {
3782 d.setCanceledOnTouchOutside(
false);
3787 if(status >= 1 && status<=3)
3789 mProgressDialog.dismiss();
3790 resetNoTouchTimer(
true);
3792 mToast.makeText(getActivity(), String.format(
"Database loaded!"), mToast.LENGTH_LONG).show();
3794 else if(!mItemTrajectoryMode.isChecked())
3796 if(mButtonCameraView.getSelectedItemPosition() == 0)
3802 mProgressDialog.setTitle(
"Loading");
3803 mProgressDialog.setMessage(String.format(
"Database \"%s\" loaded. Please wait while rendering point clouds and meshes...", fileName));
3823 intent.putExtra(RTABMAP_AUTH_TOKEN_KEY, mAuthToken);
3824 intent.putExtra(RTABMAP_WORKING_DIR_KEY, mWorkingDirectory);
3826 if(mOpenedDatabasePath.isEmpty())
3828 intent.putExtra(RTABMAP_FILENAME_KEY,
new SimpleDateFormat(
"yyMMdd-HHmmss").
format(mDateOnPause));
3832 File
f =
new File(mOpenedDatabasePath);
3833 String
name =
f.getName();
3834 intent.putExtra(RTABMAP_FILENAME_KEY,
name.substring(0,
name.lastIndexOf(
".")));
3837 startActivityForResult(intent, SKETCHFAB_ACTIVITY_CODE);