2016-02-15 19:35:24 -05:00
package com.introlab.rtabmap ;
import java.io.File ;
import java.io.FilenameFilter ;
import android.app.Activity ;
import android.app.AlertDialog ;
import android.app.Notification ;
import android.app.NotificationManager ;
import android.app.PendingIntent ;
import android.app.ProgressDialog ;
import android.content.DialogInterface ;
import android.content.Intent ;
import android.content.pm.PackageInfo ;
import android.content.pm.PackageManager ;
import android.content.pm.PackageManager.NameNotFoundException ;
import android.graphics.Point ;
import android.opengl.GLSurfaceView ;
import android.os.Bundle ;
import android.os.Environment ;
import android.os.Handler ;
import android.os.Debug ;
import android.text.Editable ;
import android.text.InputType ;
import android.util.Log ;
import android.view.Display ;
import android.view.Menu ;
import android.view.MenuItem ;
import android.view.MenuInflater ;
import android.view.MotionEvent ;
import android.view.View ;
import android.view.View.OnClickListener ;
2016-04-04 13:03:43 -04:00
import android.view.WindowManager ;
2016-02-15 19:35:24 -05:00
import android.widget.EditText ;
import android.widget.LinearLayout ;
import android.widget.TextView ;
import android.widget.Toast ;
2016-03-17 22:37:59 -04:00
import com.google.atap.tangoservice.Tango ;
2016-02-15 19:35:24 -05:00
// The main activity of the application. This activity shows debug information
// and a glSurfaceView that renders graphic content.
public class RTABMapActivity extends Activity implements OnClickListener {
// Tag for debug logging.
private static final String TAG = RTABMapActivity . class . getSimpleName ( ) ;
// The minimum Tango Core version required from this application.
private static final int MIN_TANGO_CORE_VERSION = 6804 ;
// The package name of Tang Core, used for checking minimum Tango Core version.
private static final String TANGO_PACKAGE_NAME = " com.projecttango.tango " ;
public static final String EXTRA_KEY_PERMISSIONTYPE = " PERMISSIONTYPE " ;
public static final String EXTRA_VALUE_ADF = " ADF_LOAD_SAVE_PERMISSION " ;
// GLSurfaceView and renderer, all of the graphic content is rendered
// through OpenGL ES 2.0 in native code.
private Renderer mRenderer ;
private GLSurfaceView mGLView ;
ProgressDialog mProgressDialog ;
// Screen size for normalizing the touch input for orbiting the render camera.
private Point mScreenSize = new Point ( ) ;
private MenuItem mItemPause ;
private MenuItem mItemSave ;
private MenuItem mItemOpen ;
2016-04-04 13:03:43 -04:00
private MenuItem mItemPostProcessing ;
2016-02-15 19:35:24 -05:00
private MenuItem mItemExport ;
private MenuItem mItemLocalizationMode ;
private MenuItem mItemTrajectoryMode ;
private String mOpenedDatabasePath = " " ;
private String mTempDatabasePath = " " ;
private String mNewDatabasePath = " " ;
private String mWorkingDirectory = " " ;
2016-04-02 15:17:40 -04:00
private int mMaxDepthIndex = 5 ;
2016-04-04 13:03:43 -04:00
private int mMeshAngleToleranceIndex = 1 ;
private int mMeshTriangleSizeIndex = 0 ;
2016-04-02 15:17:40 -04:00
private int mParamUpdateRateHzIndex = 1 ;
private int mParamTimeThrMsIndex = 1 ;
2016-04-04 13:03:43 -04:00
private int mParamMaxFeaturesIndex = 4 ;
private int mParamLoopThrMsIndex = 1 ;
2016-04-02 15:17:40 -04:00
2016-02-15 19:35:24 -05:00
private LinearLayout mLayoutDebug ;
private int mTotalLoopClosures = 0 ;
@Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate ( savedInstanceState ) ;
setTitle ( R . string . menu_name ) ;
// Query screen size, the screen size is used for computing the normalized
// touch point.
Display display = getWindowManager ( ) . getDefaultDisplay ( ) ;
display . getSize ( mScreenSize ) ;
2016-04-04 13:03:43 -04:00
getWindow ( ) . addFlags ( WindowManager . LayoutParams . FLAG_KEEP_SCREEN_ON ) ;
2016-02-15 19:35:24 -05:00
// Setting content view of this activity.
setContentView ( R . layout . activity_rtabmap ) ;
// Buttons for selecting camera view and Set up button click listeners.
findViewById ( R . id . first_person_button ) . setOnClickListener ( this ) ;
findViewById ( R . id . third_person_button ) . setOnClickListener ( this ) ;
findViewById ( R . id . top_down_button ) . setOnClickListener ( this ) ;
// OpenGL view where all of the graphics are drawn.
mGLView = ( GLSurfaceView ) findViewById ( R . id . gl_surface_view ) ;
// Configure OpenGL renderer
mGLView . setEGLContextClientVersion ( 2 ) ;
// Configure the OpenGL renderer.
mRenderer = new Renderer ( ) ;
mGLView . setRenderer ( mRenderer ) ;
mLayoutDebug = ( LinearLayout ) findViewById ( R . id . debug_layout ) ;
mLayoutDebug . setVisibility ( LinearLayout . GONE ) ;
mProgressDialog = new ProgressDialog ( this ) ;
mRenderer . setProgressDialog ( mProgressDialog ) ;
// Check if the Tango Core is out dated.
if ( ! CheckTangoCoreVersion ( MIN_TANGO_CORE_VERSION ) ) {
Toast . makeText ( this , " Tango Core out dated, please update in Play Store " , Toast . LENGTH_LONG ) . show ( ) ;
finish ( ) ;
return ;
2016-03-17 22:37:59 -04:00
}
2016-02-15 19:35:24 -05:00
mOpenedDatabasePath = " " ;
mTempDatabasePath = " " ;
mNewDatabasePath = " " ;
mWorkingDirectory = " " ;
mTotalLoopClosures = 0 ;
if ( Environment . getExternalStorageState ( ) . compareTo ( Environment . MEDIA_MOUNTED ) = = 0 )
{
File extStore = Environment . getExternalStorageDirectory ( ) ;
mWorkingDirectory = extStore . getAbsolutePath ( ) + " / " + getString ( R . string . app_name ) + " / " ;
extStore = new File ( mWorkingDirectory ) ;
extStore . mkdirs ( ) ;
mTempDatabasePath = mWorkingDirectory + " rtabmap.tmp.db " ;
extStore = new File ( mTempDatabasePath ) ;
if ( extStore . exists ( ) )
{
extStore . delete ( ) ;
}
}
else
{
// show warning that data cannot be saved!
Toast . makeText ( getApplicationContext ( ) ,
String . format ( " Failed to get external storage path (SD-CARD, state=%s). Saving disabled. " ,
Environment . getExternalStorageState ( ) ) , Toast . LENGTH_LONG ) . show ( ) ;
}
RTABMapLib . initialize ( this ) ;
RTABMapLib . openDatabase ( mTempDatabasePath ) ;
}
2016-03-17 22:37:59 -04:00
@Override
protected void onActivityResult ( int requestCode , int resultCode , Intent data ) {
// Check which request we're responding to
if ( requestCode = = Tango . TANGO_INTENT_ACTIVITYCODE ) {
// Make sure the request was successful
if ( resultCode = = RESULT_CANCELED ) {
Toast . makeText ( this , " Motion Tracking Permissions Required! " ,
Toast . LENGTH_SHORT ) . show ( ) ;
finish ( ) ;
}
}
}
2016-02-15 19:35:24 -05:00
@Override
protected void onResume ( ) {
super . onResume ( ) ;
2016-03-17 22:37:59 -04:00
Log . i ( TAG , String . format ( " onResume() " ) ) ;
if ( Tango . hasPermission ( this , Tango . PERMISSIONTYPE_MOTION_TRACKING ) ) {
mGLView . onResume ( ) ;
mTotalLoopClosures = 0 ;
if ( mItemOpen ! = null )
{
mItemOpen . setEnabled ( false ) ;
mItemPause . setChecked ( false ) ;
mItemSave . setEnabled ( false ) ;
mItemExport . setEnabled ( false ) ;
2016-04-04 13:03:43 -04:00
mItemPostProcessing . setEnabled ( false ) ;
2016-03-17 22:37:59 -04:00
}
if ( RTABMapLib . onResume ( ) ! = 0 )
{
Toast . makeText ( getApplicationContext ( ) ,
String . format ( " Failed to connect with Tango! " ) , Toast . LENGTH_SHORT ) . show ( ) ;
}
} else {
Log . i ( TAG , String . format ( " Asking for motion tracking permission " ) ) ;
startActivityForResult (
Tango . getRequestPermissionIntent ( Tango . PERMISSIONTYPE_MOTION_TRACKING ) ,
Tango . TANGO_INTENT_ACTIVITYCODE ) ;
2016-02-15 19:35:24 -05:00
}
}
@Override
protected void onPause ( ) {
super . onPause ( ) ;
mGLView . onPause ( ) ;
// Delete all the non-OpenGl resources.
RTABMapLib . onPause ( ) ;
mOpenedDatabasePath = " " ;
RTABMapLib . openDatabase ( mTempDatabasePath ) ;
}
@Override
public void onClick ( View v ) {
// Handle button clicks.
switch ( v . getId ( ) ) {
case R . id . first_person_button :
RTABMapLib . setCamera ( 0 ) ;
break ;
case R . id . third_person_button :
RTABMapLib . setCamera ( 1 ) ;
break ;
case R . id . top_down_button :
RTABMapLib . setCamera ( 2 ) ;
break ;
default :
return ;
}
}
@Override
public boolean onTouchEvent ( MotionEvent event ) {
// Pass the touch event to the native layer for camera control.
// Single touch to rotate the camera around the device.
// Two fingers to zoom in and out.
int pointCount = event . getPointerCount ( ) ;
if ( pointCount = = 1 ) {
float normalizedX = event . getX ( 0 ) / mScreenSize . x ;
float normalizedY = event . getY ( 0 ) / mScreenSize . y ;
RTABMapLib . onTouchEvent ( 1 ,
event . getActionMasked ( ) , normalizedX , normalizedY , 0 . 0f , 0 . 0f ) ;
}
if ( pointCount = = 2 ) {
if ( event . getActionMasked ( ) = = MotionEvent . ACTION_POINTER_UP ) {
int index = event . getActionIndex ( ) = = 0 ? 1 : 0 ;
float normalizedX = event . getX ( index ) / mScreenSize . x ;
float normalizedY = event . getY ( index ) / mScreenSize . y ;
RTABMapLib . onTouchEvent ( 1 ,
MotionEvent . ACTION_DOWN , normalizedX , normalizedY , 0 . 0f , 0 . 0f ) ;
} else {
float normalizedX0 = event . getX ( 0 ) / mScreenSize . x ;
float normalizedY0 = event . getY ( 0 ) / mScreenSize . y ;
float normalizedX1 = event . getX ( 1 ) / mScreenSize . x ;
float normalizedY1 = event . getY ( 1 ) / mScreenSize . y ;
RTABMapLib . onTouchEvent ( 2 , event . getActionMasked ( ) ,
normalizedX0 , normalizedY0 , normalizedX1 , normalizedY1 ) ;
}
}
return true ;
}
@Override
public boolean onCreateOptionsMenu ( Menu menu ) {
Log . i ( TAG , " called onCreateOptionsMenu; " ) ;
MenuInflater inflater = getMenuInflater ( ) ;
inflater . inflate ( R . menu . optionmenu , menu ) ;
mItemPause = menu . findItem ( R . id . pause ) ;
mItemSave = menu . findItem ( R . id . save ) ;
mItemOpen = menu . findItem ( R . id . open ) ;
2016-04-04 13:03:43 -04:00
mItemPostProcessing = menu . findItem ( R . id . post_processing ) ;
2016-02-15 19:35:24 -05:00
mItemExport = menu . findItem ( R . id . export ) ;
mItemLocalizationMode = menu . findItem ( R . id . localization_mode ) ;
mItemTrajectoryMode = menu . findItem ( R . id . trajectory_mode ) ;
mItemSave . setEnabled ( false ) ;
mItemExport . setEnabled ( false ) ;
mItemOpen . setEnabled ( false ) ;
2016-04-04 13:03:43 -04:00
mItemPostProcessing . setEnabled ( false ) ;
2016-02-15 19:35:24 -05:00
return true ;
}
private void updateStatsUI (
int nodes ,
int words ,
int points ,
int polygons ,
float updateTime ,
int loopClosureId ,
int databaseMemoryUsed ,
int inliers ,
int featuresExtracted ,
float hypothesis ,
int nodesDrawn )
{
if ( mItemPause ! = null )
{
( ( TextView ) findViewById ( R . id . status ) ) . setText ( mItemPause . isChecked ( ) ? " Paused " : mItemLocalizationMode . isChecked ( ) ? " Localization " : " Mapping " ) ;
}
( ( TextView ) findViewById ( R . id . points ) ) . setText ( String . valueOf ( points ) ) ;
( ( TextView ) findViewById ( R . id . polygons ) ) . setText ( String . valueOf ( polygons ) ) ;
( ( TextView ) findViewById ( R . id . nodes ) ) . setText ( String . format ( " %d (%d shown) " , nodes , nodesDrawn ) ) ;
( ( TextView ) findViewById ( R . id . words ) ) . setText ( String . valueOf ( words ) ) ;
( ( TextView ) findViewById ( R . id . memory ) ) . setText ( String . valueOf ( Debug . getNativeHeapAllocatedSize ( ) / ( 1024 * 1024 ) ) ) ;
( ( TextView ) findViewById ( R . id . db_size ) ) . setText ( String . valueOf ( databaseMemoryUsed ) ) ;
( ( TextView ) findViewById ( R . id . inliers ) ) . setText ( String . valueOf ( inliers ) ) ;
( ( TextView ) findViewById ( R . id . features ) ) . setText ( String . valueOf ( featuresExtracted ) ) ;
( ( TextView ) findViewById ( R . id . update_time ) ) . setText ( String . format ( " %.3f " , updateTime ) ) ;
( ( TextView ) findViewById ( R . id . hypothesis ) ) . setText ( String . format ( " %.3f (%d) " , hypothesis , loopClosureId ) ) ;
if ( loopClosureId > 0 )
{
+ + mTotalLoopClosures ;
Toast . makeText ( this , " Loop closure detected! " , Toast . LENGTH_SHORT ) . show ( ) ;
}
2016-04-04 13:03:43 -04:00
( ( TextView ) findViewById ( R . id . total_loop ) ) . setText ( String . valueOf ( mTotalLoopClosures ) ) ;
2016-02-15 19:35:24 -05:00
}
// called from jni
public void updateStatsCallback (
final int nodes ,
final int words ,
final int points ,
final int polygons ,
final float updateTime ,
final int loopClosureId ,
final int databaseMemoryUsed ,
final int inliers ,
final int features ,
final float hypothesis ,
final int nodesDrawn )
{
Log . i ( TAG , String . format ( " updateStatsCallback() " ) ) ;
runOnUiThread ( new Runnable ( ) {
public void run ( ) {
updateStatsUI ( nodes , words , points , polygons , updateTime , loopClosureId , databaseMemoryUsed , inliers , features , hypothesis , nodesDrawn ) ;
}
} ) ;
}
private void rtabmapInitEventUI (
int status ,
String msg )
{
Log . i ( TAG , String . format ( " rtabmapInitEventsUI() status=%d msg=%s " , status , msg ) ) ;
( ( TextView ) findViewById ( R . id . status ) ) . setText (
status = = 1 & & msg . isEmpty ( ) ? mItemPause ! = null & & mItemPause . isChecked ( ) ? " Paused " : mItemLocalizationMode ! = null & & mItemLocalizationMode . isChecked ( ) ? " Localization " : " Mapping " : msg ) ;
/ * 0 = kInitializing ,
1 = kInitialized ,
2 = kClosing ,
3 = kClosed ,
4 = kInfo ,
5 = kError * /
if ( status = = 3 )
{
msg = " " ;
if ( ! mNewDatabasePath . isEmpty ( ) )
{
boolean removed = true ;
File outputFile = new File ( mNewDatabasePath ) ;
if ( outputFile . exists ( ) )
{
removed = outputFile . delete ( ) ;
}
if ( removed )
{
File tempFile = new File ( mTempDatabasePath ) ;
if ( tempFile . renameTo ( outputFile ) )
{
msg = String . format ( " Database saved to \" %s \" . " , mNewDatabasePath ) ;
Intent intent = new Intent ( this , RTABMapActivity . class ) ;
// use System.currentTimeMillis() to have a unique ID for the pending intent
PendingIntent pIntent = PendingIntent . getActivity ( this , ( int ) System . currentTimeMillis ( ) , intent , 0 ) ;
// build notification
// the addAction re-use the same intent to keep the example short
Notification n = new Notification . Builder ( this )
. setContentTitle ( getString ( R . string . app_name ) )
. setContentText ( mNewDatabasePath + " saved! " )
. setSmallIcon ( R . drawable . ic_launcher )
. setContentIntent ( pIntent )
. setAutoCancel ( true ) . build ( ) ;
NotificationManager notificationManager =
( NotificationManager ) getSystemService ( NOTIFICATION_SERVICE ) ;
notificationManager . notify ( 0 , n ) ;
}
else
{
msg = String . format ( " Failed to rename temporary database from \" %s \" to \" %s \" . " ,
mTempDatabasePath , mNewDatabasePath ) ;
}
}
else
{
msg = String . format ( " Failed to overwrite the database \" %s \" . The temporary database is still correctly saved at \" %s \" . " ,
mNewDatabasePath , mTempDatabasePath ) ;
}
}
else if ( ! mOpenedDatabasePath . isEmpty ( ) )
{
msg = String . format ( " Database \" %s \" updated. " , mOpenedDatabasePath ) ;
}
if ( ! msg . isEmpty ( ) )
{
Toast . makeText ( this , msg , Toast . LENGTH_LONG ) . show ( ) ;
}
mOpenedDatabasePath = " " ;
mNewDatabasePath = " " ;
//restart a new scan by default
RTABMapLib . openDatabase ( mTempDatabasePath ) ;
( ( TextView ) findViewById ( R . id . points ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . polygons ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . nodes ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . words ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . memory ) ) . setText ( String . valueOf ( Debug . getNativeHeapAllocatedSize ( ) / ( 1024 * 1024 ) ) ) ;
( ( TextView ) findViewById ( R . id . db_size ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . inliers ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . features ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . update_time ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . hypothesis ) ) . setText ( String . valueOf ( 0 ) ) ;
mTotalLoopClosures = 0 ;
( ( TextView ) findViewById ( R . id . total_loop ) ) . setText ( String . valueOf ( mTotalLoopClosures ) ) ;
if ( mItemSave ! = null )
{
if ( mItemPause . isChecked ( ) )
{
mItemPause . setChecked ( false ) ;
mItemOpen . setEnabled ( false ) ;
2016-04-04 13:03:43 -04:00
mItemPostProcessing . setEnabled ( false ) ;
2016-02-15 19:35:24 -05:00
mItemSave . setEnabled ( false ) ;
mItemExport . setEnabled ( false ) ;
RTABMapLib . setPausedMapping ( false ) ; // resume mapping
}
}
mProgressDialog . dismiss ( ) ;
}
}
//called from jni
public void rtabmapInitEventCallback (
final int status ,
final String msg )
{
Log . i ( TAG , String . format ( " rtabmapInitEventCallback() " ) ) ;
runOnUiThread ( new Runnable ( ) {
public void run ( ) {
rtabmapInitEventUI ( status , msg ) ;
}
} ) ;
}
private void tangoEventUI (
int type ,
String key ,
String value )
{
/ * *
*
* " TangoServiceException:X " - The service has encountered an exception , and a text description is given in X .
* " FisheyeOverExposed:X " - the fisheye image is over exposed with average pixel value X px .
* " FisheyeUnderExposed:X " - the fisheye image is under exposed with average pixel value X px .
* " ColorOverExposed:X " - the color image is over exposed with average pixel value X px .
* " ColorUnderExposed:X " - the color image is under exposed with average pixel value X px .
* " TooFewFeaturesTracked:X " - too few features were tracked in the fisheye image . The number of features tracked is X .
* " AreaDescriptionSaveProgress:X " - ADF saving is X * 100 percent complete .
* " Unknown "
* /
if ( key . equals ( " TangoServiceException " ) )
Toast . makeText ( this , String . format ( " Tango service exception: %s " , value ) , Toast . LENGTH_SHORT ) . show ( ) ;
else if ( key . equals ( " FisheyeOverExposed " ) )
; //Toast.makeText(this, String.format("The fisheye image is over exposed with average pixel value %s px.", value), Toast.LENGTH_SHORT).show();
else if ( key . equals ( " FisheyeUnderExposed " ) )
; //Toast.makeText(this, String.format("The fisheye image is under exposed with average pixel value %s px.", value), Toast.LENGTH_SHORT).show();
else if ( key . equals ( " ColorOverExposed " ) )
; //Toast.makeText(this, String.format("The color image is over exposed with average pixel value %s px.", value), Toast.LENGTH_SHORT).show();
else if ( key . equals ( " ColorUnderExposed " ) )
; //Toast.makeText(this, String.format("The color image is under exposed with average pixel value %s px.", value), Toast.LENGTH_SHORT).show();
else if ( key . equals ( " CameraTango " ) )
Toast . makeText ( this , value , Toast . LENGTH_SHORT ) . show ( ) ;
else if ( key . equals ( " TooFewFeaturesTracked " ) )
{
if ( ! value . equals ( " 0 " ) )
Toast . makeText ( this , String . format ( " Too few features (%s) were tracked in the fisheye image. This may result in poor odometry! " , value ) , Toast . LENGTH_SHORT ) . show ( ) ;
}
else
Toast . makeText ( this , String . format ( " Unknown Tango event detected!? (type=%d) " , type ) , Toast . LENGTH_SHORT ) . show ( ) ;
}
//called from jni
public void tangoEventCallback (
final int type ,
final String key ,
final String value )
{
2016-04-03 21:35:30 -04:00
if ( mItemPause ! = null & & ! mItemPause . isChecked ( ) )
{
runOnUiThread ( new Runnable ( ) {
public void run ( ) {
tangoEventUI ( type , key , value ) ;
}
} ) ;
}
2016-02-15 19:35:24 -05:00
}
private boolean CheckTangoCoreVersion ( int minVersion ) {
int versionNumber = 0 ;
String packageName = TANGO_PACKAGE_NAME ;
try {
PackageInfo pi = getApplicationContext ( ) . getPackageManager ( ) . getPackageInfo ( packageName ,
PackageManager . GET_META_DATA ) ;
versionNumber = pi . versionCode ;
} catch ( NameNotFoundException e ) {
e . printStackTrace ( ) ;
}
return ( minVersion < = versionNumber ) ;
}
private RTABMapActivity getActivity ( ) { return this ; }
private String [ ] loadFileList ( String directory ) {
File path = new File ( directory ) ;
String fileList [ ] ;
try {
path . mkdirs ( ) ;
}
catch ( SecurityException e ) {
Log . e ( TAG , " unable to write on the sd card " + e . toString ( ) ) ;
}
if ( path . exists ( ) ) {
FilenameFilter filter = new FilenameFilter ( ) {
@Override
public boolean accept ( File dir , String filename ) {
File sel = new File ( dir , filename ) ;
return filename . endsWith ( " .db " ) | | sel . isDirectory ( ) ;
}
} ;
fileList = path . list ( filter ) ;
}
else {
fileList = new String [ 0 ] ;
}
return fileList ;
}
public boolean onOptionsItemSelected ( MenuItem item ) {
Log . i ( TAG , " called onOptionsItemSelected; selected item: " + item ) ;
int itemId = item . getItemId ( ) ;
if ( itemId = = R . id . pause )
{
item . setChecked ( ! item . isChecked ( ) ) ;
mItemSave . setEnabled ( item . isChecked ( ) ) ;
mItemExport . setEnabled ( item . isChecked ( ) ) ;
mItemOpen . setEnabled ( item . isChecked ( ) ) ;
2016-04-04 13:03:43 -04:00
mItemPostProcessing . setEnabled ( item . isChecked ( ) ) ;
2016-02-15 19:35:24 -05:00
// mItemSave.setEnabled(item.isChecked() && !mWorkingDirectory.isEmpty());
if ( item . isChecked ( ) )
{
RTABMapLib . setPausedMapping ( true ) ;
( ( TextView ) findViewById ( R . id . status ) ) . setText ( " Paused " ) ;
}
else
{
RTABMapLib . setPausedMapping ( false ) ;
( ( TextView ) findViewById ( R . id . status ) ) . setText ( mItemLocalizationMode . isChecked ( ) ? " Localization " : " Mapping " ) ;
}
}
2016-04-04 13:03:43 -04:00
else if ( itemId = = R . id . detect_more_loop_closures )
{
mProgressDialog . setTitle ( " Post-Processing " ) ;
mProgressDialog . setMessage ( String . format ( " Please wait while detecting more loop closures... " ) ) ;
mProgressDialog . show ( ) ;
Thread workingThread = new Thread ( new Runnable ( ) {
public void run ( ) {
final int loopDetected = RTABMapLib . postProcessing ( 2 ) ;
runOnUiThread ( new Runnable ( ) {
public void run ( ) {
mProgressDialog . dismiss ( ) ;
if ( loopDetected > = 0 )
{
Toast . makeText ( getActivity ( ) , String . format ( " Detection done! %d new loop closure(s) added. " , loopDetected ) , Toast . LENGTH_SHORT ) . show ( ) ;
}
else if ( loopDetected < 0 )
{
Toast . makeText ( getActivity ( ) , String . format ( " Detection failed! " ) , Toast . LENGTH_SHORT ) . show ( ) ;
}
}
} ) ;
}
} ) ;
workingThread . start ( ) ;
}
else if ( itemId = = R . id . global_graph_optimization )
{
mProgressDialog . setTitle ( " Post-Processing " ) ;
mProgressDialog . setMessage ( String . format ( " Global graph optimization... " ) ) ;
mProgressDialog . show ( ) ;
Thread workingThread = new Thread ( new Runnable ( ) {
public void run ( ) {
final int value = RTABMapLib . postProcessing ( 0 ) ;
runOnUiThread ( new Runnable ( ) {
public void run ( ) {
mProgressDialog . dismiss ( ) ;
if ( value > = 0 )
{
Toast . makeText ( getActivity ( ) , String . format ( " Optimization done! " ) , Toast . LENGTH_SHORT ) . show ( ) ;
}
else if ( value < 0 )
{
Toast . makeText ( getActivity ( ) , String . format ( " Optimization failed! " ) , Toast . LENGTH_SHORT ) . show ( ) ;
}
}
} ) ;
}
} ) ;
workingThread . start ( ) ;
}
else if ( itemId = = R . id . sba )
{
mProgressDialog . setTitle ( " Post-Processing " ) ;
mProgressDialog . setMessage ( String . format ( " Bundle adjustement... " ) ) ;
mProgressDialog . show ( ) ;
Thread workingThread = new Thread ( new Runnable ( ) {
public void run ( ) {
final int value = RTABMapLib . postProcessing ( 1 ) ;
runOnUiThread ( new Runnable ( ) {
public void run ( ) {
mProgressDialog . dismiss ( ) ;
if ( value > = 0 )
{
Toast . makeText ( getActivity ( ) , String . format ( " Optimization done! " ) , Toast . LENGTH_SHORT ) . show ( ) ;
}
else if ( value < 0 )
{
Toast . makeText ( getActivity ( ) , String . format ( " Optimization failed! " ) , Toast . LENGTH_SHORT ) . show ( ) ;
}
}
} ) ;
}
} ) ;
workingThread . start ( ) ;
}
2016-02-15 19:35:24 -05:00
else if ( itemId = = R . id . debug )
{
item . setChecked ( ! item . isChecked ( ) ) ;
if ( ! item . isChecked ( ) )
{
mLayoutDebug . setVisibility ( LinearLayout . GONE ) ;
}
else
{
mLayoutDebug . setVisibility ( LinearLayout . VISIBLE ) ;
}
}
else if ( itemId = = R . id . mesh_rendering )
{
item . setChecked ( ! item . isChecked ( ) ) ;
RTABMapLib . setMeshRendering ( item . isChecked ( ) ) ;
}
else if ( itemId = = R . id . map_shown )
{
item . setChecked ( ! item . isChecked ( ) ) ;
RTABMapLib . setMapCloudShown ( item . isChecked ( ) ) ;
}
else if ( itemId = = R . id . odom_shown )
{
item . setChecked ( ! item . isChecked ( ) ) ;
RTABMapLib . setOdomCloudShown ( item . isChecked ( ) ) ;
}
else if ( itemId = = R . id . localization_mode )
{
item . setChecked ( ! item . isChecked ( ) ) ;
RTABMapLib . setLocalizationMode ( item . isChecked ( ) ) ;
}
else if ( itemId = = R . id . trajectory_mode )
{
item . setChecked ( ! item . isChecked ( ) ) ;
RTABMapLib . setTrajectoryMode ( item . isChecked ( ) ) ;
}
else if ( itemId = = R . id . graph_optimization )
{
item . setChecked ( ! item . isChecked ( ) ) ;
RTABMapLib . setGraphOptimization ( item . isChecked ( ) ) ;
}
else if ( itemId = = R . id . graph_visible )
{
item . setChecked ( ! item . isChecked ( ) ) ;
RTABMapLib . setGraphVisible ( item . isChecked ( ) ) ;
}
else if ( itemId = = R . id . auto_exposure )
{
item . setChecked ( ! item . isChecked ( ) ) ;
RTABMapLib . setAutoExposure ( item . isChecked ( ) ) ;
}
2016-04-02 15:17:40 -04:00
else if ( itemId = = R . id . resolution )
{
item . setChecked ( ! item . isChecked ( ) ) ;
RTABMapLib . setFullResolution ( item . isChecked ( ) ) ;
}
else if ( itemId = = R . id . max_depth )
{
// get double
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
builder . setTitle ( " Max Depth (m) " ) ;
final String [ ] values = { " 1 " , " 2 " , " 3 " , " 4 " , " 5 " , " No Limit " } ;
builder . setSingleChoiceItems ( values , mMaxDepthIndex , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
dialog . dismiss ( ) ;
if ( which > = 0 & & which < 6 )
{
mMaxDepthIndex = which ;
2016-04-04 13:03:43 -04:00
RTABMapLib . setMaxCloudDepth ( which < 5 ? Float . parseFloat ( values [ which ] ) : 0 ) ;
}
}
} ) ;
builder . show ( ) ;
}
else if ( itemId = = R . id . mesh_angle_tolerance )
{
// get double
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
builder . setTitle ( " Mesh Angle Tolerance (deg) " ) ;
final String [ ] values = { " 5 " , " 10 " , " 15 " , " 20 " , " 25 " , " 30 " } ;
builder . setSingleChoiceItems ( values , mMeshAngleToleranceIndex , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
dialog . dismiss ( ) ;
if ( which > = 0 & & which < 6 )
{
mMeshAngleToleranceIndex = which ;
RTABMapLib . setMeshAngleTolerance ( Float . parseFloat ( values [ which ] ) ) ;
}
}
} ) ;
builder . show ( ) ;
}
else if ( itemId = = R . id . mesh_triangle_size )
{
// get double
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
builder . setTitle ( " Mesh Triangle Size (pixels) " ) ;
final String [ ] values = { " 2 " , " 3 " , " 4 " , " 5 " , " 6 " } ;
builder . setSingleChoiceItems ( values , mMeshTriangleSizeIndex , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
dialog . dismiss ( ) ;
if ( which > = 0 & & which < 5 )
{
mMeshTriangleSizeIndex = which ;
RTABMapLib . setMeshTriangleSize ( Integer . parseInt ( values [ which ] ) ) ;
2016-04-02 15:17:40 -04:00
}
}
} ) ;
builder . show ( ) ;
}
else if ( itemId = = R . id . update_rate )
{
// get double
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
builder . setTitle ( " Update Rate (Hz) " ) ;
final String [ ] values = { " 0.5 " , " 1 " , " 2 " , " Max " } ;
builder . setSingleChoiceItems ( values , mParamUpdateRateHzIndex , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
dialog . dismiss ( ) ;
if ( which > = 0 & & which < 4 )
{
mParamUpdateRateHzIndex = which ;
if ( RTABMapLib . setMappingParameter ( " Rtabmap/DetectionRate " , values [ which ] ) ! = 0 )
{
Toast . makeText ( getActivity ( ) , " Failed to set parameter \" Rtabmap/DetectionRate \" ! " , Toast . LENGTH_LONG ) . show ( ) ;
}
}
}
} ) ;
builder . show ( ) ;
}
else if ( itemId = = R . id . time_threshold )
{
// get double
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
2016-04-04 13:03:43 -04:00
builder . setTitle ( " Time Threshold (ms) " ) ;
2016-04-02 15:17:40 -04:00
final String [ ] values = { " 400 " , " 700 " , " 1400 " , " No Limit " } ;
builder . setSingleChoiceItems ( values , mParamTimeThrMsIndex , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
dialog . dismiss ( ) ;
if ( which > = 0 & & which < 4 )
{
mParamTimeThrMsIndex = which ;
if ( RTABMapLib . setMappingParameter ( " Rtabmap/TimeThr " , which = = 3 ? " 0 " : values [ which ] ) ! = 0 )
{
Toast . makeText ( getActivity ( ) , " Failed to set parameter \" Rtabmap/TimeThr \" ! " , Toast . LENGTH_LONG ) . show ( ) ;
}
}
}
} ) ;
builder . show ( ) ;
}
else if ( itemId = = R . id . features )
{
// get double
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
builder . setTitle ( " Max Features " ) ;
2016-04-04 13:03:43 -04:00
final String [ ] values = { " Disabled " , " 100 " , " 200 " , " 300 " , " 400 " , " 500 " , " 600 " , " 700 " , " 800 " , " 900 " , " 1000 " , " No Limit " } ;
2016-04-02 15:17:40 -04:00
builder . setSingleChoiceItems ( values , mParamMaxFeaturesIndex , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
dialog . dismiss ( ) ;
2016-04-04 13:03:43 -04:00
if ( which > = 0 & & which < 12 )
2016-04-02 15:17:40 -04:00
{
mParamMaxFeaturesIndex = which ;
2016-04-04 13:03:43 -04:00
if ( RTABMapLib . setMappingParameter ( " Kp/MaxFeatures " , which = = 0 ? " -1 " : which = = 11 ? " 0 " : values [ which ] ) ! = 0 )
2016-04-02 15:17:40 -04:00
{
Toast . makeText ( getActivity ( ) , " Failed to set parameter \" Kp/MaxFeatures \" ! " , Toast . LENGTH_LONG ) . show ( ) ;
}
}
}
} ) ;
builder . show ( ) ;
}
2016-04-04 13:03:43 -04:00
else if ( itemId = = R . id . loop_threshold )
{
// get double
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
builder . setTitle ( " Loop Closure Threshold " ) ;
final String [ ] values = { " Disabled " , " 0.11 " , " 0.20 " , " 0.30 " , " 0.40 " , " 0.50 " , " 0.60 " , " 0.70 " , " 0.80 " , " 0.90 " } ;
builder . setSingleChoiceItems ( values , mParamTimeThrMsIndex , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
dialog . dismiss ( ) ;
if ( which > = 0 & & which < 10 )
{
mParamLoopThrMsIndex = which ;
if ( RTABMapLib . setMappingParameter ( " Rtabmap/LoopThr " , which = = 0 ? " 1 " : values [ which ] ) ! = 0 )
{
Toast . makeText ( getActivity ( ) , " Failed to set parameter \" Rtabmap/LoopThr \" ! " , Toast . LENGTH_LONG ) . show ( ) ;
}
}
}
} ) ;
builder . show ( ) ;
}
2016-02-15 19:35:24 -05:00
else if ( itemId = = R . id . save )
{
if ( mOpenedDatabasePath . isEmpty ( ) )
{
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
builder . setTitle ( " RTAB-Map Database Name (*.db): " ) ;
final EditText input = new EditText ( this ) ;
input . setInputType ( InputType . TYPE_CLASS_TEXT ) ;
builder . setView ( input ) ;
builder . setPositiveButton ( " OK " , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which )
{
2016-03-21 20:03:03 -04:00
final String fileName = input . getText ( ) . toString ( ) ;
dialog . dismiss ( ) ;
2016-02-15 19:35:24 -05:00
if ( ! fileName . isEmpty ( ) )
{
File newFile = new File ( mWorkingDirectory + fileName + " .db " ) ;
if ( newFile . exists ( ) )
{
new AlertDialog . Builder ( getActivity ( ) )
. setTitle ( " File Already Exists " )
. setMessage ( " Do you want to overwrite the existing file? " )
. setPositiveButton ( " Yes " , new DialogInterface . OnClickListener ( ) {
public void onClick ( DialogInterface dialog , int which ) {
mNewDatabasePath = mWorkingDirectory + fileName + " .db " ;
mProgressDialog . setTitle ( " Saving " ) ;
mProgressDialog . setMessage ( String . format ( " Please wait while saving \" %s \" ... " , mNewDatabasePath ) ) ;
mProgressDialog . show ( ) ;
RTABMapLib . save ( ) ; // send save event
//disable gui actions
mItemSave . setEnabled ( false ) ;
mItemOpen . setEnabled ( false ) ;
2016-04-04 13:03:43 -04:00
mItemPostProcessing . setEnabled ( false ) ;
2016-02-15 19:35:24 -05:00
mItemExport . setEnabled ( false ) ;
}
} )
. setNegativeButton ( " No " , new DialogInterface . OnClickListener ( ) {
public void onClick ( DialogInterface dialog , int which ) {
dialog . dismiss ( ) ;
}
} )
. show ( ) ;
}
else
{
mNewDatabasePath = mWorkingDirectory + fileName + " .db " ;
mProgressDialog . setTitle ( " Saving " ) ;
mProgressDialog . setMessage ( String . format ( " Please wait while saving \" %s \" ... " , mNewDatabasePath ) ) ;
mProgressDialog . show ( ) ;
RTABMapLib . save ( ) ; // send save event
//disable gui actions
mItemSave . setEnabled ( false ) ;
mItemOpen . setEnabled ( false ) ;
2016-04-04 13:03:43 -04:00
mItemPostProcessing . setEnabled ( false ) ;
2016-02-15 19:35:24 -05:00
mItemExport . setEnabled ( false ) ;
}
}
}
} ) ;
builder . show ( ) ;
}
else
{
mProgressDialog . setTitle ( " Saving " ) ;
mProgressDialog . setMessage ( String . format ( " Please wait while updating \" %s \" ... " , mOpenedDatabasePath ) ) ;
mProgressDialog . show ( ) ;
RTABMapLib . save ( ) ; // send save event
//disable gui actions
mItemSave . setEnabled ( false ) ;
mItemOpen . setEnabled ( false ) ;
2016-04-04 13:03:43 -04:00
mItemPostProcessing . setEnabled ( false ) ;
2016-02-15 19:35:24 -05:00
mItemExport . setEnabled ( false ) ;
}
}
else if ( itemId = = R . id . reset )
{
( ( TextView ) findViewById ( R . id . points ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . polygons ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . nodes ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . words ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . memory ) ) . setText ( String . valueOf ( Debug . getNativeHeapAllocatedSize ( ) / ( 1024 * 1024 ) ) ) ;
( ( TextView ) findViewById ( R . id . db_size ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . inliers ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . features ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . update_time ) ) . setText ( String . valueOf ( 0 ) ) ;
( ( TextView ) findViewById ( R . id . hypothesis ) ) . setText ( String . valueOf ( 0 ) ) ;
mTotalLoopClosures = 0 ;
( ( TextView ) findViewById ( R . id . total_loop ) ) . setText ( String . valueOf ( mTotalLoopClosures ) ) ;
if ( mOpenedDatabasePath . isEmpty ( ) )
{
RTABMapLib . resetMapping ( ) ;
}
else
{
mOpenedDatabasePath = " " ;
RTABMapLib . openDatabase ( mTempDatabasePath ) ;
}
}
2016-04-02 15:17:40 -04:00
else if ( itemId = = R . id . export_obj | | itemId = = R . id . export_ply )
2016-02-15 19:35:24 -05:00
{
2016-04-02 15:17:40 -04:00
final String extension = itemId = = R . id . export_ply ? " .ply " : " .obj " ;
2016-04-04 13:03:43 -04:00
final boolean isOBJ = itemId = = R . id . export_obj ;
2016-04-02 15:17:40 -04:00
2016-02-15 19:35:24 -05:00
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
2016-04-02 15:17:40 -04:00
builder . setTitle ( String . format ( " File Name (*%s): " , extension ) ) ;
2016-02-15 19:35:24 -05:00
final EditText input = new EditText ( this ) ;
input . setInputType ( InputType . TYPE_CLASS_TEXT ) ;
builder . setView ( input ) ;
builder . setPositiveButton ( " OK " , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which )
{
2016-03-21 20:03:03 -04:00
final String fileName = input . getText ( ) . toString ( ) ;
dialog . dismiss ( ) ;
2016-02-15 19:35:24 -05:00
if ( ! fileName . isEmpty ( ) )
{
2016-04-02 15:17:40 -04:00
File newFile = new File ( mWorkingDirectory + fileName + extension ) ;
2016-02-15 19:35:24 -05:00
if ( newFile . exists ( ) )
{
new AlertDialog . Builder ( getActivity ( ) )
. setTitle ( " File Already Exists " )
. setMessage ( " Do you want to overwrite the existing file? " )
. setPositiveButton ( " Yes " , new DialogInterface . OnClickListener ( ) {
public void onClick ( DialogInterface dialog , int which ) {
2016-04-02 15:17:40 -04:00
final String path = mWorkingDirectory + fileName + extension ;
2016-02-15 19:35:24 -05:00
mItemExport . setEnabled ( false ) ;
2016-03-21 20:03:03 -04:00
2016-02-15 19:35:24 -05:00
mProgressDialog . setTitle ( " Exporting " ) ;
2016-04-02 15:17:40 -04:00
mProgressDialog . setMessage ( String . format ( " Please wait while exporting \" %s \" ... " , fileName + extension ) ) ;
2016-02-15 19:35:24 -05:00
mProgressDialog . show ( ) ;
Thread exportThread = new Thread ( new Runnable ( ) {
public void run ( ) {
final boolean success = RTABMapLib . exportMesh ( path ) ;
runOnUiThread ( new Runnable ( ) {
public void run ( ) {
if ( success )
{
2016-04-04 13:03:43 -04:00
if ( isOBJ )
{
Toast . makeText ( getActivity ( ) , String . format ( " Mesh \" %s \" (with textures \" %s/ \" and \" %s \" ) successfully exported! " , path , fileName , fileName + " .mtl " ) , Toast . LENGTH_LONG ) . show ( ) ;
}
else
{
Toast . makeText ( getActivity ( ) , String . format ( " Mesh \" %s \" successfully exported! " , path ) , Toast . LENGTH_LONG ) . show ( ) ;
}
2016-02-15 19:35:24 -05:00
}
else
{
Toast . makeText ( getActivity ( ) , String . format ( " Exporting mesh to \" %s \" failed! " , path ) , Toast . LENGTH_LONG ) . show ( ) ;
}
mItemExport . setEnabled ( true ) ;
mProgressDialog . dismiss ( ) ;
}
} ) ;
}
} ) ;
exportThread . start ( ) ;
}
} )
. setNegativeButton ( " No " , new DialogInterface . OnClickListener ( ) {
public void onClick ( DialogInterface dialog , int which ) {
dialog . dismiss ( ) ;
}
} )
. show ( ) ;
}
else
{
2016-04-02 15:17:40 -04:00
final String path = mWorkingDirectory + fileName + extension ;
2016-02-15 19:35:24 -05:00
mItemExport . setEnabled ( false ) ;
mProgressDialog . setTitle ( " Exporting " ) ;
2016-04-02 15:17:40 -04:00
mProgressDialog . setMessage ( String . format ( " Please wait while exporting \" %s \" ... " , fileName + extension ) ) ;
2016-02-15 19:35:24 -05:00
mProgressDialog . show ( ) ;
Thread exportThread = new Thread ( new Runnable ( ) {
public void run ( ) {
final boolean success = RTABMapLib . exportMesh ( path ) ;
runOnUiThread ( new Runnable ( ) {
public void run ( ) {
if ( success )
{
2016-04-04 13:03:43 -04:00
if ( isOBJ )
{
Toast . makeText ( getActivity ( ) , String . format ( " Mesh \" %s \" (with textures \" %s/ \" and \" %s \" ) successfully exported! " , path , fileName , fileName + " .mtl " ) , Toast . LENGTH_LONG ) . show ( ) ;
}
else
{
Toast . makeText ( getActivity ( ) , String . format ( " Mesh \" %s \" successfully exported! " , path ) , Toast . LENGTH_LONG ) . show ( ) ;
}
2016-02-15 19:35:24 -05:00
}
else
{
Toast . makeText ( getActivity ( ) , String . format ( " Exporting mesh to \" %s \" failed! " , path ) , Toast . LENGTH_LONG ) . show ( ) ;
}
mItemExport . setEnabled ( true ) ;
mProgressDialog . dismiss ( ) ;
}
} ) ;
}
} ) ;
exportThread . start ( ) ;
}
}
}
} ) ;
builder . show ( ) ;
}
else if ( itemId = = R . id . open )
{
final String [ ] files = loadFileList ( mWorkingDirectory ) ;
if ( files . length > 0 )
{
AlertDialog . Builder builder = new AlertDialog . Builder ( this ) ;
builder . setTitle ( " Choose your file " ) ;
builder . setItems ( files , new DialogInterface . OnClickListener ( ) {
public void onClick ( DialogInterface dialog , int which ) {
mOpenedDatabasePath = mWorkingDirectory + files [ which ] ;
if ( ! mItemTrajectoryMode . isChecked ( ) )
{
mProgressDialog . setTitle ( " Loading " ) ;
2016-04-04 13:03:43 -04:00
mProgressDialog . setMessage ( String . format ( " Database \" %s \" loaded. Please wait while creating point clouds and meshes... " , files [ which ] ) ) ;
2016-02-15 19:35:24 -05:00
mProgressDialog . show ( ) ;
}
RTABMapLib . openDatabase ( mOpenedDatabasePath ) ;
RTABMapLib . setCamera ( 1 ) ;
File extStore = new File ( mTempDatabasePath ) ;
if ( extStore . exists ( ) )
{
extStore . delete ( ) ;
}
}
} ) ;
builder . show ( ) ;
}
}
else if ( itemId = = R . id . about )
{
AboutDialog about = new AboutDialog ( this ) ;
about . setTitle ( " About RTAB-Map " ) ;
about . show ( ) ;
}
return true ;
}
}