2016-02-15 19:35:24 -05:00
package com.introlab.rtabmap ;
2017-01-26 16:46:24 -05:00
import java.io.BufferedInputStream ;
import java.io.BufferedOutputStream ;
2016-02-15 19:35:24 -05:00
import java.io.File ;
2017-01-26 16:46:24 -05:00
import java.io.FileInputStream ;
import java.io.FileOutputStream ;
2016-02-15 19:35:24 -05:00
import java.io.FilenameFilter ;
2017-01-26 16:46:24 -05:00
import java.io.IOException ;
2017-02-05 17:24:45 -05:00
import java.io.InputStream ;
import java.io.OutputStream ;
2017-02-28 21:50:50 -05:00
import java.lang.reflect.InvocationTargetException ;
import java.lang.reflect.Method ;
2016-11-04 16:39:52 -04:00
import java.text.SimpleDateFormat ;
2016-11-04 17:03:06 -04:00
import java.util.Arrays ;
2016-11-04 16:39:52 -04:00
import java.util.Date ;
2017-01-26 16:46:24 -05:00
import java.util.List ;
import java.util.zip.ZipEntry ;
import java.util.zip.ZipOutputStream ;
2016-02-15 19:35:24 -05:00
import android.app.Activity ;
2017-02-05 17:24:45 -05:00
import android.app.ActivityManager ;
import android.app.ActivityManager.MemoryInfo ;
2016-02-15 19:35:24 -05:00
import android.app.AlertDialog ;
2017-01-26 16:46:24 -05:00
import android.app.Dialog ;
2016-02-15 19:35:24 -05:00
import android.app.Notification ;
import android.app.NotificationManager ;
import android.app.PendingIntent ;
import android.app.ProgressDialog ;
2016-05-12 14:34:51 -04:00
import android.content.ComponentName ;
2017-01-26 16:46:24 -05:00
import android.content.Context ;
2016-02-15 19:35:24 -05:00
import android.content.DialogInterface ;
import android.content.Intent ;
2016-05-12 14:34:51 -04:00
import android.content.ServiceConnection ;
2017-01-26 16:46:24 -05:00
import android.content.SharedPreferences ;
2017-03-12 21:21:09 -04:00
import android.content.pm.ApplicationInfo ;
2016-02-15 19:35:24 -05:00
import android.content.pm.PackageInfo ;
import android.content.pm.PackageManager ;
import android.content.pm.PackageManager.NameNotFoundException ;
2017-02-05 17:24:45 -05:00
import android.content.res.Configuration ;
2017-01-26 16:46:24 -05:00
import android.graphics.Bitmap ;
2017-02-22 09:48:45 -05:00
import android.graphics.Canvas ;
import android.graphics.Color ;
import android.graphics.Paint ;
import android.graphics.Typeface ;
2017-02-05 17:24:45 -05:00
import android.hardware.Camera ;
2016-02-15 19:35:24 -05:00
import android.graphics.Point ;
2017-02-05 17:24:45 -05:00
import android.hardware.display.DisplayManager ;
import android.net.ConnectivityManager ;
import android.net.NetworkInfo ;
2017-01-26 16:46:24 -05:00
import android.net.Uri ;
2016-02-15 19:35:24 -05:00
import android.opengl.GLSurfaceView ;
2017-01-26 16:46:24 -05:00
import android.os.AsyncTask ;
2016-02-15 19:35:24 -05:00
import android.os.Bundle ;
import android.os.Environment ;
import android.os.Handler ;
import android.os.Debug ;
2016-05-12 14:34:51 -04:00
import android.os.IBinder ;
2017-06-03 20:06:45 -04:00
import android.os.Message ;
2017-01-26 16:46:24 -05:00
import android.preference.PreferenceManager ;
2016-02-15 19:35:24 -05:00
import android.text.Editable ;
2017-02-27 16:53:05 -05:00
import android.text.Html ;
2016-02-15 19:35:24 -05:00
import android.text.InputType ;
2017-02-27 16:53:05 -05:00
import android.text.SpannableString ;
2017-02-22 09:48:45 -05:00
import android.text.TextPaint ;
2017-02-27 16:53:05 -05:00
import android.text.method.LinkMovementMethod ;
import android.text.util.Linkify ;
2016-02-15 19:35:24 -05:00
import android.util.Log ;
import android.view.Display ;
2017-04-17 21:42:01 -04:00
import android.view.GestureDetector ;
2016-02-15 19:35:24 -05:00
import android.view.Menu ;
import android.view.MenuItem ;
import android.view.MenuInflater ;
import android.view.MotionEvent ;
2017-02-05 17:24:45 -05:00
import android.view.Surface ;
2016-02-15 19:35:24 -05:00
import android.view.View ;
import android.view.View.OnClickListener ;
2017-04-17 21:42:01 -04:00
import android.view.View.OnTouchListener ;
2016-04-04 13:03:43 -04:00
import android.view.WindowManager ;
2017-02-15 19:07:10 -05:00
import android.view.inputmethod.EditorInfo ;
2017-01-26 16:46:24 -05:00
import android.webkit.WebView ;
import android.webkit.WebViewClient ;
import android.widget.Button ;
2016-02-15 19:35:24 -05:00
import android.widget.EditText ;
import android.widget.LinearLayout ;
2017-02-28 21:50:50 -05:00
import android.widget.NumberPicker ;
import android.widget.RelativeLayout ;
2017-06-12 15:36:33 -04:00
import android.widget.SeekBar ;
import android.widget.SeekBar.OnSeekBarChangeListener ;
2016-02-15 19:35:24 -05:00
import android.widget.TextView ;
import android.widget.Toast ;
2017-01-26 16:46:24 -05:00
import android.widget.ToggleButton ;
2016-02-15 19:35:24 -05:00
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 {
2017-01-26 16:46:24 -05:00
// Tag for debug logging.
public static final String TAG = RTABMapActivity . class . getSimpleName ();
2017-03-12 21:21:09 -04:00
public static boolean DISABLE_LOG = true ;
2016-02-15 19:35:24 -05:00
2017-01-26 16:46:24 -05:00
// The minimum Tango Core version required from this application.
private static final int MIN_TANGO_CORE_VERSION = 9377 ;
2016-02-15 19:35:24 -05:00
2017-01-26 16:46:24 -05:00
// The package name of Tang Core, used for checking minimum Tango Core version.
private static final String TANGO_PACKAGE_NAME = "com.google.tango" ;
2016-02-15 19:35:24 -05:00
2017-01-26 16:46:24 -05:00
public static final String EXTRA_KEY_PERMISSIONTYPE = "PERMISSIONTYPE" ;
public static final String EXTRA_VALUE_ADF = "ADF_LOAD_SAVE_PERMISSION" ;
2017-02-05 17:24:45 -05:00
public static final String RTABMAP_TMP_DB = "rtabmap.tmp.db" ;
2017-06-10 21:45:51 -04:00
public static final String RTABMAP_TMP_DIR = "tmp" ;
2017-02-15 19:07:10 -05:00
public static final String RTABMAP_TMP_FILENAME = "map" ;
2017-02-22 09:48:45 -05:00
public static final String RTABMAP_SDCARD_PATH = "/sdcard/" ;
2017-03-05 13:05:26 -05:00
public static final String RTABMAP_EXPORT_DIR = "Export/" ;
2016-05-12 14:34:51 -04:00
2017-02-15 19:07:10 -05:00
public static final String RTABMAP_AUTH_TOKEN_KEY = "com.introlab.rtabmap.AUTH_TOKEN" ;
public static final String RTABMAP_FILENAME_KEY = "com.introlab.rtabmap.FILENAME" ;
public static final String RTABMAP_OPENED_DB_PATH_KEY = "com.introlab.rtabmap.OPENED_DB_PATH" ;
public static final String RTABMAP_EXPORTED_OBJ_KEY = "com.introlab.rtabmap.EXPORTED_OBJ" ;
public static final String RTABMAP_WORKING_DIR_KEY = "com.introlab.rtabmap.WORKING_DIR" ;
public static final int SKETCHFAB_ACTIVITY_CODE = 999 ;
2017-01-26 16:46:24 -05:00
private String mAuthToken ;
2017-06-03 20:06:45 -04:00
public static final long NOTOUCH_TIMEOUT = 5000 ; // 5 sec
private boolean mHudVisible = true ;
2017-06-12 15:36:33 -04:00
private boolean mTipOrthoShown_ = false ;
2017-06-12 19:08:51 -04:00
private int mSavedRenderingType = 0 ;
2016-02-15 19:35:24 -05:00
2017-01-26 16:46:24 -05:00
// UI states
private static enum State {
STATE_IDLE ,
STATE_PROCESSING ,
STATE_VISUALIZING
}
State mState = State . STATE_IDLE ;
2016-02-15 19:35:24 -05:00
2017-01-26 16:46:24 -05:00
// GLSurfaceView and renderer, all of the graphic content is rendered
// through OpenGL ES 2.0 in native code.
private Renderer mRenderer ;
private GLSurfaceView mGLView ;
2016-02-15 19:35:24 -05:00
2017-01-26 16:46:24 -05:00
ProgressDialog mProgressDialog ;
2017-02-28 21:50:50 -05:00
ProgressDialog mExportProgressDialog ;
2016-02-15 19:35:24 -05:00
2017-01-26 16:46:24 -05:00
// Screen size for normalizing the touch input for orbiting the render camera.
private Point mScreenSize = new Point ();
2017-02-05 17:24:45 -05:00
private long mOnPauseStamp = 0 ;
2017-01-26 16:46:24 -05:00
private boolean mOnPause = false ;
2017-02-15 19:07:10 -05:00
private Date mDateOnPause = new Date ();
2017-02-27 16:53:05 -05:00
private boolean mBlockBack = true ;
2016-02-15 19:35:24 -05:00
2017-01-26 16:46:24 -05:00
private MenuItem mItemSave ;
private MenuItem mItemOpen ;
private MenuItem mItemPostProcessing ;
private MenuItem mItemExport ;
private MenuItem mItemSettings ;
private MenuItem mItemModes ;
private MenuItem mItemReset ;
private MenuItem mItemLocalizationMode ;
private MenuItem mItemTrajectoryMode ;
private MenuItem mItemRenderingPointCloud ;
private MenuItem mItemRenderingMesh ;
private MenuItem mItemRenderingTextureMesh ;
private MenuItem mItemDataRecorderMode ;
2017-02-22 09:48:45 -05:00
private MenuItem mItemStatusVisibility ;
private MenuItem mItemDebugVisibility ;
2016-09-08 11:55:44 -04:00
2017-01-26 16:46:24 -05:00
private ToggleButton mButtonFirst ;
private ToggleButton mButtonThird ;
private ToggleButton mButtonTop ;
private ToggleButton mButtonPause ;
private ToggleButton mButtonLighting ;
2017-06-12 19:08:51 -04:00
private ToggleButton mButtonWireframe ;
2017-02-15 19:07:10 -05:00
private ToggleButton mButtonBackfaceShown ;
2017-01-27 11:47:57 -05:00
private Button mButtonCloseVisualization ;
2017-02-15 19:07:10 -05:00
private Button mButtonSaveOnDevice ;
private Button mButtonShareOnSketchfab ;
2017-06-12 15:36:33 -04:00
private SeekBar mSeekBarFov ;
2017-01-26 16:46:24 -05:00
private String mOpenedDatabasePath = "" ;
private String mWorkingDirectory = "" ;
2017-02-22 09:48:45 -05:00
private String mWorkingDirectoryHuman = "" ;
2017-01-26 16:46:24 -05:00
private String mUpdateRate ;
private String mTimeThr ;
private String mMaxFeatures ;
private String mLoopThr ;
2017-02-15 19:07:10 -05:00
private String mMinInliers ;
private String mMaxOptimizationError ;
2017-01-26 16:46:24 -05:00
private int mTotalLoopClosures = 0 ;
private boolean mMapIsEmpty = false ;
2017-02-15 19:07:10 -05:00
private boolean mExportedOBJ = false ;
2017-03-12 21:21:09 -04:00
int mMapNodes = 0 ;
2017-01-26 16:46:24 -05:00
private Toast mToast = null ;
2017-02-15 19:07:10 -05:00
2017-02-22 09:48:45 -05:00
private AlertDialog mMemoryWarningDialog = null ;
private String [] mStatusTexts = new String [ 16 ] ;
2017-04-17 21:42:01 -04:00
GestureDetector mGesDetect = null ;
2017-01-26 16:46:24 -05:00
//Tango Service connection.
ServiceConnection mTangoServiceConnection = new ServiceConnection () {
2017-02-05 17:24:45 -05:00
public void onServiceConnected ( ComponentName name , final IBinder service ) {
Thread bindThread = new Thread ( new Runnable () {
public void run () {
if ( ! RTABMapLib . onTangoServiceConnected ( service ))
{
runOnUiThread ( new Runnable () {
public void run () {
mToast . makeText ( getApplicationContext (),
String . format ( "Failed to intialize Tango!" ), mToast . LENGTH_LONG ). show ();
}
});
}
}
});
bindThread . start ();
2017-01-26 16:46:24 -05:00
}
public void onServiceDisconnected ( ComponentName name ) {
// Handle this if you need to gracefully shutdown/retry
// in the event that Tango itself crashes/gets upgraded while running.
mToast . makeText ( getApplicationContext (),
2017-02-05 17:24:45 -05:00
String . format ( "Tango disconnected!" ), mToast . LENGTH_LONG ). show ();
2017-01-26 16:46:24 -05:00
}
};
@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 );
getWindow (). addFlags ( WindowManager . LayoutParams . FLAG_KEEP_SCREEN_ON );
// Setting content view of this activity.
setContentView ( R . layout . activity_rtabmap );
// Make sure to initialize all default values
SettingsActivity settings ;
// Buttons for selecting camera view and Set up button click listeners.
mButtonFirst = ( ToggleButton ) findViewById ( R . id . first_person_button );
mButtonThird = ( ToggleButton ) findViewById ( R . id . third_person_button );
mButtonTop = ( ToggleButton ) findViewById ( R . id . top_down_button );
mButtonPause = ( ToggleButton ) findViewById ( R . id . pause_button );
mButtonLighting = ( ToggleButton ) findViewById ( R . id . light_button );
2017-06-12 19:08:51 -04:00
mButtonWireframe = ( ToggleButton ) findViewById ( R . id . wireframe_button );
2017-02-15 19:07:10 -05:00
mButtonBackfaceShown = ( ToggleButton ) findViewById ( R . id . backface_button );
2017-01-27 11:47:57 -05:00
mButtonCloseVisualization = ( Button ) findViewById ( R . id . close_visualization_button );
2017-02-15 19:07:10 -05:00
mButtonSaveOnDevice = ( Button ) findViewById ( R . id . button_saveOnDevice );
mButtonShareOnSketchfab = ( Button ) findViewById ( R . id . button_shareToSketchfab );
2017-01-26 16:46:24 -05:00
mButtonFirst . setOnClickListener ( this );
mButtonThird . setOnClickListener ( this );
mButtonTop . setOnClickListener ( this );
mButtonPause . setOnClickListener ( this );
mButtonLighting . setOnClickListener ( this );
2017-06-12 19:08:51 -04:00
mButtonWireframe . setOnClickListener ( this );
2017-02-15 19:07:10 -05:00
mButtonBackfaceShown . setOnClickListener ( this );
2017-01-27 11:47:57 -05:00
mButtonCloseVisualization . setOnClickListener ( this );
2017-02-15 19:07:10 -05:00
mButtonSaveOnDevice . setOnClickListener ( this );
mButtonShareOnSketchfab . setOnClickListener ( this );
2017-01-26 16:46:24 -05:00
mButtonFirst . setChecked ( true );
2017-04-09 20:00:23 -04:00
mButtonLighting . setChecked ( false );
2017-01-26 16:46:24 -05:00
mButtonLighting . setVisibility ( View . INVISIBLE );
2017-06-12 19:08:51 -04:00
mButtonWireframe . setChecked ( false );
mButtonWireframe . setVisibility ( View . INVISIBLE );
2017-01-27 11:47:57 -05:00
mButtonCloseVisualization . setVisibility ( View . INVISIBLE );
2017-02-15 19:07:10 -05:00
mButtonSaveOnDevice . setVisibility ( View . INVISIBLE );
mButtonShareOnSketchfab . setVisibility ( View . INVISIBLE );
if ( mItemRenderingMesh != null && mItemRenderingTextureMesh != null )
{
mButtonBackfaceShown . setVisibility ( mItemRenderingMesh . isChecked () || mItemRenderingTextureMesh . isChecked () ? View . VISIBLE : View . INVISIBLE );
}
2017-06-12 15:36:33 -04:00
mSeekBarFov = ( SeekBar ) findViewById ( R . id . seekBar_fov );
mSeekBarFov . setMax ( 45 );
mSeekBarFov . setProgress ( 20 );
mSeekBarFov . setOnSeekBarChangeListener ( new OnSeekBarChangeListener () {
@Override
public void onProgressChanged ( SeekBar seekBar , int progressValue , boolean fromUser ) {
if ( mButtonFirst . isChecked ())
{
RTABMapLib . setFOV (( float ) progressValue + 45 . 0f );
}
else if ( mButtonTop . isChecked ())
{
2017-06-12 19:08:51 -04:00
RTABMapLib . setOrthoCropFactor (( float )( 120 - progressValue ) / 20 . 0f - 3 . 0f );
2017-06-12 15:36:33 -04:00
}
resetNoTouchTimer ();
}
@Override
public void onStartTrackingTouch ( SeekBar seekBar ) {
}
@Override
public void onStopTrackingTouch ( SeekBar seekBar ) {
}
});
2017-01-26 16:46:24 -05:00
mToast = Toast . makeText ( getApplicationContext (), "" , Toast . LENGTH_SHORT );
// OpenGL view where all of the graphics are drawn.
mGLView = ( GLSurfaceView ) findViewById ( R . id . gl_surface_view );
2017-04-17 21:42:01 -04:00
mGesDetect = new GestureDetector ( this , new DoubleTapGestureDetector ());
2017-01-26 16:46:24 -05:00
// Configure OpenGL renderer
mGLView . setEGLContextClientVersion ( 2 );
2017-04-17 21:42:01 -04:00
mGLView . setEGLConfigChooser ( 8 , 8 , 8 , 8 , 24 , 0 );
mGLView . setOnTouchListener ( new OnTouchListener () {
@Override
public boolean onTouch ( View v , MotionEvent event ) {
2017-06-03 20:06:45 -04:00
resetNoTouchTimer ();
2017-04-17 21:42:01 -04:00
mGesDetect . onTouchEvent ( 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 ;
}
});
2017-01-26 16:46:24 -05:00
// Configure the OpenGL renderer.
2017-02-05 17:24:45 -05:00
mRenderer = new Renderer ( this );
2017-01-26 16:46:24 -05:00
mGLView . setRenderer ( mRenderer );
mProgressDialog = new ProgressDialog ( this );
mProgressDialog . setCanceledOnTouchOutside ( false );
mRenderer . setProgressDialog ( mProgressDialog );
2017-02-22 09:48:45 -05:00
mRenderer . setToast ( mToast );
2017-02-28 21:50:50 -05:00
mExportProgressDialog = new ProgressDialog ( this );
mExportProgressDialog . setCanceledOnTouchOutside ( false );
mExportProgressDialog . setCancelable ( false );
mExportProgressDialog . setProgressStyle ( ProgressDialog . STYLE_HORIZONTAL );
mExportProgressDialog . setProgressNumberFormat ( null );
mExportProgressDialog . setProgressPercentFormat ( null );
mExportProgressDialog . setButton ( DialogInterface . BUTTON_NEGATIVE , "Cancel" , new DialogInterface . OnClickListener () {
@Override
public void onClick ( DialogInterface dialog , int which ) {
RTABMapLib . cancelProcessing ();
}
});
2017-01-26 16:46:24 -05:00
// Check if the Tango Core is out dated.
if ( ! CheckTangoCoreVersion ( MIN_TANGO_CORE_VERSION )) {
mToast . makeText ( this , "Tango Core out dated, please update in Play Store" , mToast . LENGTH_LONG ). show ();
finish ();
return ;
}
mOpenedDatabasePath = "" ;
mWorkingDirectory = "" ;
2017-02-22 09:48:45 -05:00
mWorkingDirectoryHuman = "" ;
2017-01-26 16:46:24 -05:00
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 ();
2017-02-22 09:48:45 -05:00
mWorkingDirectoryHuman = RTABMAP_SDCARD_PATH + getString ( R . string . app_name ) + "/" ;
2017-01-26 16:46:24 -05:00
}
else
{
// show warning that data cannot be saved!
mToast . makeText ( getApplicationContext (),
String . format ( "Failed to get external storage path (SD-CARD, state=%s). Saving disabled." ,
Environment . getExternalStorageState ()), mToast . LENGTH_LONG ). show ();
}
RTABMapLib . onCreate ( this );
2017-02-05 17:24:45 -05:00
String tmpDatabase = mWorkingDirectory + RTABMAP_TMP_DB ;
( new File ( tmpDatabase )). delete ();
SharedPreferences sharedPref = PreferenceManager . getDefaultSharedPreferences ( this );
boolean databaseInMemory = sharedPref . getBoolean ( getString ( R . string . pref_key_db_in_memory ), Boolean . parseBoolean ( getString ( R . string . pref_default_db_in_memory )));
RTABMapLib . openDatabase ( tmpDatabase , databaseInMemory , false );
DisplayManager displayManager = ( DisplayManager ) getSystemService ( DISPLAY_SERVICE );
if ( displayManager != null ) {
displayManager . registerDisplayListener ( new DisplayManager . DisplayListener () {
@Override
public void onDisplayAdded ( int displayId ) {
}
@Override
public void onDisplayChanged ( int displayId ) {
synchronized ( this ) {
setAndroidOrientation ();
2017-04-17 21:42:01 -04:00
Display display = getWindowManager (). getDefaultDisplay ();
display . getSize ( mScreenSize );
2017-02-05 17:24:45 -05:00
}
}
@Override
public void onDisplayRemoved ( int displayId ) {}
}, null );
}
2017-03-12 21:21:09 -04:00
DISABLE_LOG = ! ( 0 != ( getApplicationInfo (). flags & ApplicationInfo . FLAG_DEBUGGABLE ) );
2017-01-26 16:46:24 -05: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 ) {
mToast . makeText ( this , "Motion Tracking Permissions Required!" ,
mToast . LENGTH_SHORT ). show ();
finish ();
}
}
2017-02-15 19:07:10 -05:00
else if ( requestCode == SKETCHFAB_ACTIVITY_CODE ) {
// Make sure the request was successful
if ( resultCode == RESULT_OK ) {
mAuthToken = data . getStringExtra ( RTABMAP_AUTH_TOKEN_KEY );
}
}
2017-01-26 16:46:24 -05:00
}
2017-02-05 17:24:45 -05:00
2017-02-27 16:53:05 -05:00
@Override
public void onBackPressed () {
if ( mBlockBack )
{
mToast . makeText ( this , "Press Back once more to exit" , mToast . LENGTH_LONG ). show ();
mBlockBack = false ;
}
else
{
super . onBackPressed ();
}
}
2017-02-05 17:24:45 -05:00
@Override
protected void onPause () {
super . onPause ();
2017-06-03 20:06:45 -04:00
stopDisconnectTimer ();
2017-02-05 17:24:45 -05:00
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . i ( TAG , "onPause()" );
2017-02-05 17:24:45 -05:00
mOnPause = true ;
// This deletes OpenGL context!
mGLView . onPause ();
RTABMapLib . onPause ();
unbindService ( mTangoServiceConnection );
if ( ! mButtonPause . isChecked ())
{
mButtonPause . setChecked ( true );
pauseMapping ();
}
mOnPauseStamp = System . currentTimeMillis () / 1000 ;
}
2017-01-26 16:46:24 -05:00
@Override
protected void onResume () {
super . onResume ();
2017-01-26 17:34:15 -05:00
2017-02-05 17:24:45 -05:00
mProgressDialog . setTitle ( "" );
if ( mOnPause )
2017-01-26 16:46:24 -05:00
{
2017-02-05 17:24:45 -05:00
if ( System . currentTimeMillis () / 1000 - mOnPauseStamp < 1 )
2017-01-26 16:46:24 -05:00
{
2017-02-05 17:24:45 -05:00
mProgressDialog . setMessage ( String . format ( "RTAB-Map has been interrupted by another application, Tango should be re-initialized! Set your phone/tablet in Airplane mode if this happens often." ));
2017-01-26 16:46:24 -05:00
}
else
{
2017-02-05 17:24:45 -05:00
mProgressDialog . setMessage ( String . format ( "Hold Tight! Initializing Tango Service..." ));
2017-01-26 16:46:24 -05:00
}
mToast . makeText ( this , "Mapping is paused!" , mToast . LENGTH_LONG ). show ();
}
else
{
2017-02-05 17:24:45 -05:00
mProgressDialog . setMessage ( String . format ( "Hold Tight! Initializing Tango Service...\nTip: If the camera is still drifting just after the mapping has started, do \"Reset\"." ));
2017-01-26 16:46:24 -05:00
}
2017-02-05 17:24:45 -05:00
mProgressDialog . show ();
2017-01-26 16:46:24 -05:00
mOnPause = false ;
2017-02-05 17:24:45 -05:00
setAndroidOrientation ();
2017-01-26 16:46:24 -05:00
2017-02-05 17:24:45 -05:00
// update preferences
try
{
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . d ( TAG , "update preferences..." );
2017-02-05 17:24:45 -05:00
SharedPreferences sharedPref = PreferenceManager . getDefaultSharedPreferences ( this );
mUpdateRate = sharedPref . getString ( getString ( R . string . pref_key_update_rate ), getString ( R . string . pref_default_update_rate ));
mTimeThr = sharedPref . getString ( getString ( R . string . pref_key_time_thr ), getString ( R . string . pref_default_time_thr ));
String memThr = sharedPref . getString ( getString ( R . string . pref_key_mem_thr ), getString ( R . string . pref_default_mem_thr ));
mLoopThr = sharedPref . getString ( getString ( R . string . pref_key_loop_thr ), getString ( R . string . pref_default_loop_thr ));
String simThr = sharedPref . getString ( getString ( R . string . pref_key_sim_thr ), getString ( R . string . pref_default_sim_thr ));
2017-02-15 19:07:10 -05:00
mMinInliers = sharedPref . getString ( getString ( R . string . pref_key_min_inliers ), getString ( R . string . pref_default_min_inliers ));
mMaxOptimizationError = sharedPref . getString ( getString ( R . string . pref_key_opt_error ), getString ( R . string . pref_default_opt_error ));
2017-02-05 17:24:45 -05:00
mMaxFeatures = sharedPref . getString ( getString ( R . string . pref_key_features_voc ), getString ( R . string . pref_default_features_voc ));
String maxFeaturesLoop = sharedPref . getString ( getString ( R . string . pref_key_features ), getString ( R . string . pref_default_features ));
String featureType = sharedPref . getString ( getString ( R . string . pref_key_features_type ), getString ( R . string . pref_default_features_type ));
boolean keepAllDb = sharedPref . getBoolean ( getString ( R . string . pref_key_keep_all_db ), Boolean . parseBoolean ( getString ( R . string . pref_default_keep_all_db )));
2017-02-15 19:07:10 -05:00
boolean optimizeFromGraphEnd = sharedPref . getBoolean ( getString ( R . string . pref_key_optimize_end ), Boolean . parseBoolean ( getString ( R . string . pref_default_optimize_end )));
String optimizer = sharedPref . getString ( getString ( R . string . pref_key_optimizer ), getString ( R . string . pref_default_optimizer ));
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . d ( TAG , "set mapping parameters" );
2017-04-17 21:42:01 -04:00
RTABMapLib . setOnlineBlending ( sharedPref . getBoolean ( getString ( R . string . pref_key_blending ), Boolean . parseBoolean ( getString ( R . string . pref_default_blending ))));
2017-02-05 17:24:45 -05:00
RTABMapLib . setNodesFiltering ( sharedPref . getBoolean ( getString ( R . string . pref_key_nodes_filtering ), Boolean . parseBoolean ( getString ( R . string . pref_default_nodes_filtering ))));
RTABMapLib . setRawScanSaved ( sharedPref . getBoolean ( getString ( R . string . pref_key_raw_scan_saved ), Boolean . parseBoolean ( getString ( R . string . pref_default_raw_scan_saved ))));
RTABMapLib . setFullResolution ( sharedPref . getBoolean ( getString ( R . string . pref_key_resolution ), Boolean . parseBoolean ( getString ( R . string . pref_default_resolution ))));
2017-02-22 09:48:45 -05:00
RTABMapLib . setSmoothing ( sharedPref . getBoolean ( getString ( R . string . pref_key_smoothing ), Boolean . parseBoolean ( getString ( R . string . pref_default_smoothing ))));
2017-03-13 20:51:59 -04:00
RTABMapLib . setCameraColor ( ! sharedPref . getBoolean ( getString ( R . string . pref_key_fisheye ), Boolean . parseBoolean ( getString ( R . string . pref_default_fisheye ))));
2017-02-05 17:24:45 -05:00
RTABMapLib . setAppendMode ( sharedPref . getBoolean ( getString ( R . string . pref_key_append ), Boolean . parseBoolean ( getString ( R . string . pref_default_append ))));
RTABMapLib . setMappingParameter ( "Rtabmap/DetectionRate" , mUpdateRate );
RTABMapLib . setMappingParameter ( "Rtabmap/TimeThr" , mTimeThr );
RTABMapLib . setMappingParameter ( "Rtabmap/MemoryThr" , memThr );
RTABMapLib . setMappingParameter ( "Mem/RehearsalSimilarity" , simThr );
RTABMapLib . setMappingParameter ( "Kp/MaxFeatures" , mMaxFeatures );
RTABMapLib . setMappingParameter ( "Vis/MaxFeatures" , maxFeaturesLoop );
2017-02-15 19:07:10 -05:00
RTABMapLib . setMappingParameter ( "Vis/MinInliers" , mMinInliers );
2017-02-05 17:24:45 -05:00
RTABMapLib . setMappingParameter ( "Rtabmap/LoopThr" , mLoopThr );
2017-02-15 19:07:10 -05:00
RTABMapLib . setMappingParameter ( "RGBD/OptimizeMaxError" , mMaxOptimizationError );
2017-02-05 17:24:45 -05:00
RTABMapLib . setMappingParameter ( "Kp/DetectorStrategy" , featureType );
RTABMapLib . setMappingParameter ( "Vis/FeatureType" , featureType );
RTABMapLib . setMappingParameter ( "Mem/NotLinkedNodesKept" , String . valueOf ( keepAllDb ));
2017-02-15 19:07:10 -05:00
RTABMapLib . setMappingParameter ( "RGBD/OptimizeFromGraphEnd" , String . valueOf ( optimizeFromGraphEnd ));
RTABMapLib . setMappingParameter ( "Optimizer/Strategy" , optimizer );
2017-02-05 17:24:45 -05:00
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . d ( TAG , "set exporting parameters..." );
2017-03-15 17:05:19 -04:00
RTABMapLib . setCloudDensityLevel ( Integer . parseInt ( sharedPref . getString ( getString ( R . string . pref_key_density ), getString ( R . string . pref_default_density ))));
2017-02-05 17:24:45 -05:00
RTABMapLib . setMaxCloudDepth ( Float . parseFloat ( sharedPref . getString ( getString ( R . string . pref_key_depth ), getString ( R . string . pref_default_depth ))));
2017-04-01 14:14:36 -04:00
RTABMapLib . setMinCloudDepth ( Float . parseFloat ( sharedPref . getString ( getString ( R . string . pref_key_min_depth ), getString ( R . string . pref_default_min_depth ))));
2017-02-05 17:24:45 -05:00
RTABMapLib . setPointSize ( Float . parseFloat ( sharedPref . getString ( getString ( R . string . pref_key_point_size ), getString ( R . string . pref_default_point_size ))));
RTABMapLib . setMeshAngleTolerance ( Float . parseFloat ( sharedPref . getString ( getString ( R . string . pref_key_angle ), getString ( R . string . pref_default_angle ))));
RTABMapLib . setMeshTriangleSize ( Integer . parseInt ( sharedPref . getString ( getString ( R . string . pref_key_triangle ), getString ( R . string . pref_default_triangle ))));
2017-04-17 21:42:01 -04:00
float bgColor = Float . parseFloat ( sharedPref . getString ( getString ( R . string . pref_key_background_color ), getString ( R . string . pref_default_background_color )));
RTABMapLib . setBackgroundColor ( bgColor );
2017-04-18 17:30:14 -04:00
mRenderer . setTextColor ( bgColor >= 0 . 6f ? 0 . 0f : 1 . 0f );
2017-02-05 17:24:45 -05:00
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . d ( TAG , "set rendering parameters..." );
RTABMapLib . setClusterRatio ( Float . parseFloat ( sharedPref . getString ( getString ( R . string . pref_key_cluster_ratio ), getString ( R . string . pref_default_cluster_ratio ))));
2017-02-05 17:24:45 -05:00
RTABMapLib . setMaxGainRadius ( Float . parseFloat ( sharedPref . getString ( getString ( R . string . pref_key_gain_max_radius ), getString ( R . string . pref_default_gain_max_radius ))));
2017-03-12 21:21:09 -04:00
RTABMapLib . setRenderingTextureDecimation ( Integer . parseInt ( sharedPref . getString ( getString ( R . string . pref_key_rendering_texture_decimation ), getString ( R . string . pref_default_rendering_texture_decimation ))));
2017-02-05 17:24:45 -05:00
if ( mItemRenderingPointCloud != null )
{
int renderingType = sharedPref . getInt ( getString ( R . string . pref_key_rendering ), Integer . parseInt ( getString ( R . string . pref_default_rendering )));
if ( renderingType == 0 )
{
mItemRenderingPointCloud . setChecked ( true );
}
else if ( renderingType == 1 )
{
mItemRenderingMesh . setChecked ( true );
}
else
{
mItemRenderingTextureMesh . setChecked ( true );
}
RTABMapLib . setMeshRendering (
mItemRenderingMesh . isChecked () || mItemRenderingTextureMesh . isChecked (),
mItemRenderingTextureMesh . isChecked ());
2017-02-15 19:07:10 -05:00
mButtonBackfaceShown . setVisibility ( mItemRenderingMesh . isChecked () || mItemRenderingTextureMesh . isChecked () ? View . VISIBLE : View . INVISIBLE );
2017-02-05 17:24:45 -05:00
}
}
catch ( Exception e )
{
Log . e ( TAG , "Error parsing preferences: " + e . getMessage ());
mToast . makeText ( this , String . format ( "Error parsing preferences: " + e . getMessage ()), mToast . LENGTH_LONG ). show ();
}
2017-01-26 16:46:24 -05:00
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . i ( TAG , String . format ( "onResume()" ));
2017-01-26 16:46:24 -05:00
if ( Tango . hasPermission ( this , Tango . PERMISSIONTYPE_MOTION_TRACKING )) {
mGLView . onResume ();
} else {
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . i ( TAG , String . format ( "Asking for motion tracking permission" ));
2017-01-26 16:46:24 -05:00
startActivityForResult (
Tango . getRequestPermissionIntent ( Tango . PERMISSIONTYPE_MOTION_TRACKING ),
Tango . TANGO_INTENT_ACTIVITYCODE );
}
2017-02-05 17:24:45 -05:00
TangoInitializationHelper . bindTangoService ( getActivity (), mTangoServiceConnection );
2017-06-03 20:06:45 -04:00
resetNoTouchTimer ();
2017-01-26 16:46:24 -05:00
}
2017-02-05 17:24:45 -05:00
2017-01-26 16:46:24 -05:00
private void setCamera ( int type )
2017-06-12 15:36:33 -04:00
{
if ( ! DISABLE_LOG ) Log . i ( TAG , String . format ( "called setCamera(type=%d);" , type ));
2017-02-22 09:48:45 -05:00
// for convenience, for a refresh of the memory used
mStatusTexts [ 1 ] = getString ( R . string . memory ) + String . valueOf ( Debug . getNativeHeapAllocatedSize () / ( 1024 * 1024 ));
mStatusTexts [ 2 ] = getString ( R . string . free_memory ) + String . valueOf ( getFreeMemory ());
updateStatusTexts ();
2017-06-12 15:36:33 -04:00
2017-01-26 16:46:24 -05:00
RTABMapLib . setCamera ( type );
mButtonFirst . setChecked ( type == 0 );
mButtonThird . setChecked ( type == 1 );
2017-06-12 15:36:33 -04:00
mButtonTop . setChecked ( type == 2 || type == 3 );
mButtonTop . setText ( type == 3 ? "Ortho" : "Top" );
mButtonTop . setTextOn ( type == 3 ? "Ortho" : "Top" );
mButtonTop . setTextOff ( type == 3 ? "Ortho" : "Top" );
mSeekBarFov . setVisibility ( type != 0 && type != 3 ? View . INVISIBLE : View . VISIBLE );
if ( type == 0 )
{
mSeekBarFov . setMax ( 45 );
mSeekBarFov . setProgress ( 20 );
}
if ( type == 3 )
{
mSeekBarFov . setMax ( 120 );
2017-06-12 19:08:51 -04:00
mSeekBarFov . setProgress ( 80 );
2017-06-12 15:36:33 -04:00
}
if ( type == 2 && ! mTipOrthoShown_ )
{
mToast . makeText ( this , "Tip: Click again on Top to set Ortho mode." , mToast . LENGTH_LONG ). show ();
mTipOrthoShown_ = true ;
}
2016-09-08 11:55:44 -04:00
}
2017-01-26 16:46:24 -05:00
@Override
public void onClick ( View v ) {
// Handle button clicks.
switch ( v . getId ()) {
case R . id . first_person_button :
setCamera ( 0 );
break ;
case R . id . third_person_button :
setCamera ( 1 );
break ;
case R . id . top_down_button :
2017-06-12 15:36:33 -04:00
setCamera ( mButtonFirst . isChecked () || mButtonThird . isChecked () || mButtonTop . getTextOn (). equals ( "Ortho" ) ? 2 : 3 );
2017-01-26 16:46:24 -05:00
break ;
case R . id . pause_button :
pauseMapping ();
break ;
case R . id . light_button :
RTABMapLib . setLighting ( mButtonLighting . isChecked ());
break ;
2017-02-15 19:07:10 -05:00
case R . id . backface_button :
RTABMapLib . setBackfaceCulling ( ! mButtonBackfaceShown . isChecked ());
break ;
2017-06-12 19:08:51 -04:00
case R . id . wireframe_button :
RTABMapLib . setWireframe ( mButtonWireframe . isChecked ());
break ;
2017-01-27 11:47:57 -05:00
case R . id . close_visualization_button :
2017-06-12 19:08:51 -04:00
if ( mSavedRenderingType == 0 )
{
mItemRenderingPointCloud . setChecked ( true );
}
else if ( mSavedRenderingType == 1 )
{
mItemRenderingMesh . setChecked ( true );
}
else
{
mItemRenderingTextureMesh . setChecked ( true );
}
2017-01-27 11:47:57 -05:00
updateState ( State . STATE_IDLE );
2017-06-10 21:45:51 -04:00
RTABMapLib . postExportation ( false );
2017-01-27 11:47:57 -05:00
break ;
2017-02-15 19:07:10 -05:00
case R . id . button_saveOnDevice :
saveOnDevice ();
break ;
case R . id . button_shareToSketchfab :
shareToSketchfab ();
break ;
2017-01-26 16:46:24 -05:00
default :
return ;
}
2017-06-03 20:06:45 -04:00
resetNoTouchTimer ();
2017-01-26 16:46:24 -05:00
}
2017-02-05 17:24:45 -05:00
private void setAndroidOrientation () {
Display display = getWindowManager (). getDefaultDisplay ();
Camera . CameraInfo colorCameraInfo = new Camera . CameraInfo ();
2017-03-13 20:51:59 -04:00
SharedPreferences sharedPref = PreferenceManager . getDefaultSharedPreferences ( this );
boolean fisheye = sharedPref . getBoolean ( getString ( R . string . pref_key_fisheye ), Boolean . parseBoolean ( getString ( R . string . pref_default_fisheye )));
Camera . getCameraInfo ( fisheye ? 1 : 0 , colorCameraInfo );
2017-02-05 17:24:45 -05:00
RTABMapLib . setScreenRotation ( display . getRotation (), colorCameraInfo . orientation );
}
2017-04-17 21:42:01 -04:00
class DoubleTapGestureDetector extends GestureDetector . SimpleOnGestureListener {
2017-01-26 16:46:24 -05:00
2017-04-17 21:42:01 -04:00
@Override
public boolean onDoubleTap ( MotionEvent event ) {
float normalizedX = event . getX ( 0 ) / mScreenSize . x ;
2017-01-26 16:46:24 -05:00
float normalizedY = event . getY ( 0 ) / mScreenSize . y ;
2017-04-17 21:42:01 -04:00
RTABMapLib . onTouchEvent ( 3 , event . getActionMasked (), normalizedX , normalizedY , 0 . 0f , 0 . 0f );
return true ;
}
}
2017-01-26 16:46:24 -05:00
@Override
public boolean onCreateOptionsMenu ( Menu menu ) {
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . i ( TAG , "called onCreateOptionsMenu;" );
2017-01-26 16:46:24 -05:00
MenuInflater inflater = getMenuInflater ();
inflater . inflate ( R . menu . optionmenu , menu );
2017-02-05 17:24:45 -05:00
getActionBar (). setDisplayShowHomeEnabled ( true );
getActionBar (). setIcon ( R . drawable . ic_launcher );
2017-01-26 16:46:24 -05:00
mItemSave = menu . findItem ( R . id . save );
mItemOpen = menu . findItem ( R . id . open );
mItemPostProcessing = menu . findItem ( R . id . post_processing );
mItemExport = menu . findItem ( R . id . export );
mItemSettings = menu . findItem ( R . id . settings );
mItemModes = menu . findItem ( R . id . modes );
mItemReset = menu . findItem ( R . id . reset );
mItemLocalizationMode = menu . findItem ( R . id . localization_mode );
mItemTrajectoryMode = menu . findItem ( R . id . trajectory_mode );
mItemRenderingPointCloud = menu . findItem ( R . id . point_cloud );
mItemRenderingMesh = menu . findItem ( R . id . mesh );
mItemRenderingTextureMesh = menu . findItem ( R . id . texture_mesh );
mItemDataRecorderMode = menu . findItem ( R . id . data_recorder );
2017-02-22 09:48:45 -05:00
mItemStatusVisibility = menu . findItem ( R . id . status );
mItemDebugVisibility = menu . findItem ( R . id . debug );
2017-01-26 16:46:24 -05:00
mItemSave . setEnabled ( false );
mItemExport . setEnabled ( false );
mItemOpen . setEnabled ( false );
mItemPostProcessing . setEnabled ( false );
mItemDataRecorderMode . setEnabled ( false );
2017-02-05 17:24:45 -05:00
try
2017-01-26 16:46:24 -05:00
{
2017-02-05 17:24:45 -05:00
SharedPreferences sharedPref = PreferenceManager . getDefaultSharedPreferences ( this );
int renderingType = sharedPref . getInt ( getString ( R . string . pref_key_rendering ), Integer . parseInt ( getString ( R . string . pref_default_rendering )));
if ( renderingType == 0 )
{
mItemRenderingPointCloud . setChecked ( true );
}
else if ( renderingType == 1 )
{
mItemRenderingMesh . setChecked ( true );
}
else
{
mItemRenderingTextureMesh . setChecked ( true );
}
RTABMapLib . setMeshRendering (
mItemRenderingMesh . isChecked () || mItemRenderingTextureMesh . isChecked (),
mItemRenderingTextureMesh . isChecked ());
2017-02-15 19:07:10 -05:00
if ( mButtonBackfaceShown != null )
{
mButtonBackfaceShown . setVisibility ( mItemRenderingMesh . isChecked () || mItemRenderingTextureMesh . isChecked () ? View . VISIBLE : View . INVISIBLE );
}
2017-01-26 16:46:24 -05:00
}
2017-02-05 17:24:45 -05:00
catch ( Exception e )
2017-01-26 16:46:24 -05:00
{
2017-02-05 17:24:45 -05:00
Log . e ( TAG , "Error parsing rendering preferences: " + e . getMessage ());
mToast . makeText ( this , String . format ( "Error parsing rendering preferences: " + e . getMessage ()), mToast . LENGTH_LONG ). show ();
2017-01-26 16:46:24 -05:00
}
updateState ( mState );
return true ;
}
2017-02-05 17:24:45 -05:00
private long getFreeMemory ()
{
MemoryInfo mi = new MemoryInfo ();
ActivityManager activityManager = ( ActivityManager ) getSystemService ( ACTIVITY_SERVICE );
activityManager . getMemoryInfo ( mi );
return mi . availMem / 0x100000L ; // MB
}
2017-02-22 09:48:45 -05:00
private void updateStatusTexts ()
2016-09-08 11:55:44 -04:00
{
2017-02-22 09:48:45 -05:00
if ( mItemStatusVisibility != null && mItemDebugVisibility != null )
2017-01-26 16:46:24 -05:00
{
2017-02-22 09:48:45 -05:00
if ( mItemStatusVisibility . isChecked () && mItemDebugVisibility . isChecked ())
2017-01-26 16:46:24 -05:00
{
2017-02-22 09:48:45 -05:00
mRenderer . updateTexts ( mStatusTexts );
}
else if ( mItemStatusVisibility . isChecked ())
{
mRenderer . updateTexts ( Arrays . copyOfRange ( mStatusTexts , 0 , 3 ));
}
else if ( mItemDebugVisibility . isChecked ())
{
mRenderer . updateTexts ( Arrays . copyOfRange ( mStatusTexts , 4 , mStatusTexts . length ));
2017-01-26 16:46:24 -05:00
}
else
{
2017-02-22 09:48:45 -05:00
mRenderer . updateTexts ( null );
}
}
}
private void updateStatsUI (
int processMemoryUsed ,
int loopClosureId ,
int inliers ,
int matches ,
int rejected ,
float optimizationMaxError ,
String [] statusTexts )
{
mStatusTexts = statusTexts ;
updateStatusTexts ();
if ( mButtonPause != null )
{
if ( ! mButtonPause . isChecked ())
{
//check if we are low in memory
long memoryUsed = processMemoryUsed ;
long memoryFree = getFreeMemory ();
2017-03-12 21:21:09 -04:00
if ( memoryFree < 200 )
2017-02-22 09:48:45 -05:00
{
mButtonPause . setChecked ( true );
pauseMapping ();
if ( mMemoryWarningDialog != null )
{
mMemoryWarningDialog . dismiss ();
mMemoryWarningDialog = null ;
}
mMemoryWarningDialog = new AlertDialog . Builder ( getActivity ())
. setTitle ( "Memory is full!" )
. setCancelable ( false )
. setMessage ( String . format ( "Scanning has been paused because free memory is too "
+ "low (%d MB). You should be able to save the database but some post-processing and exporting options may fail. "
+ "\n\nNote that for large environments, you can save multiple databases and "
+ "merge them with RTAB-Map Desktop version." , memoryUsed ))
. setPositiveButton ( "Ok" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
mMemoryWarningDialog = null ;
}
})
2017-02-28 21:50:50 -05:00
. setNeutralButton ( "Save" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
saveOnDevice ();
mMemoryWarningDialog = null ;
}
})
2017-02-22 09:48:45 -05:00
. create ();
mMemoryWarningDialog . show ();
}
else if ( mMemoryWarningDialog == null && memoryUsed * 3 > memoryFree && ( mItemDataRecorderMode == null || ! mItemDataRecorderMode . isChecked ()))
{
mMemoryWarningDialog = new AlertDialog . Builder ( getActivity ())
. setTitle ( "Warning: Memory is almost full!" )
. setCancelable ( false )
. setMessage ( String . format ( "Free memory (%d MB) should be at least 3 times the "
+ "memory used (%d MB) so that some post-processing and exporting options "
+ "have enough memory to work correctly. If you just want to save the database "
+ "after scanning, you can continue until the next warning.\n\n"
+ "Note that showing only point clouds reduces memory needed for rendering." , memoryFree , memoryUsed ))
. setPositiveButton ( "Pause" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
mButtonPause . setChecked ( true );
pauseMapping ();
}
})
. setNeutralButton ( "Continue" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
}
})
. create ();
mMemoryWarningDialog . show ();
}
2017-01-26 16:46:24 -05:00
}
}
2017-02-22 09:48:45 -05:00
2017-01-26 16:46:24 -05:00
if ( mButtonPause != null && ! mButtonPause . isChecked ())
{
if ( loopClosureId > 0 )
{
2017-02-15 19:07:10 -05:00
mToast . setText ( String . format ( "Loop closure detected! (%d/%d inliers)" , inliers , matches ));
2017-01-26 16:46:24 -05:00
mToast . show ();
}
2017-02-15 19:07:10 -05:00
else if ( rejected > 0 )
2017-01-26 16:46:24 -05:00
{
2017-02-15 19:07:10 -05:00
if ( inliers >= Integer . parseInt ( mMinInliers ))
{
2017-03-05 21:48:18 -05:00
if ( optimizationMaxError > 0 . 0f )
{
mToast . setText ( String . format ( "Loop closure rejected, too high graph optimization error (%.3fm > %sm)." , optimizationMaxError , mMaxOptimizationError ));
}
else
{
mToast . setText ( String . format ( "Loop closure rejected, graph optimization failed! You may try a different Graph Optimizer (see Mapping options)." ));
}
2017-02-15 19:07:10 -05:00
}
else
{
mToast . setText ( String . format ( "Loop closure rejected, not enough inliers (%d/%d < %s)." , inliers , matches , mMinInliers ));
}
2017-01-26 16:46:24 -05:00
mToast . show ();
}
}
2016-09-08 11:55:44 -04:00
}
2016-03-17 22:37:59 -04:00
2017-01-26 16:46: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 highestHypId ,
2017-02-22 09:48:45 -05:00
final int processMemoryUsed ,
2017-01-26 16:46:24 -05:00
final int databaseMemoryUsed ,
final int inliers ,
2017-02-15 19:07:10 -05:00
final int matches ,
2017-02-22 09:48:45 -05:00
final int featuresExtracted ,
2017-01-26 16:46:24 -05:00
final float hypothesis ,
final int nodesDrawn ,
final float fps ,
2017-02-05 17:24:45 -05:00
final int rejected ,
2017-02-15 19:07:10 -05:00
final float rehearsalValue ,
final float optimizationMaxError )
2017-01-26 16:46:24 -05:00
{
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . i ( TAG , String . format ( "updateStatsCallback()" ));
2016-03-17 22:37:59 -04:00
2017-02-22 09:48:45 -05:00
final String [] statusTexts = new String [ 16 ] ;
2017-03-12 21:21:09 -04:00
if ( mButtonPause != null && ! mButtonPause . isChecked ())
2017-02-22 09:48:45 -05:00
{
2017-03-12 21:21:09 -04:00
String updateValue = mUpdateRate . compareTo ( "0" ) == 0 ? "Max" : mUpdateRate ;
statusTexts [ 0 ] = getString ( R . string . status ) + ( mItemDataRecorderMode != null && mItemDataRecorderMode . isChecked () ? String . format ( "Recording (%s Hz)" , updateValue ): mItemLocalizationMode != null && mItemLocalizationMode . isChecked () ? String . format ( "Localization (%s Hz)" , updateValue ): String . format ( "Mapping (%s Hz)" , updateValue ));
2017-02-22 09:48:45 -05:00
}
else
{
2017-03-12 21:21:09 -04:00
statusTexts [ 0 ] = mStatusTexts [ 0 ] ;
2017-02-22 09:48:45 -05:00
}
// getNativeHeapAllocatedSize() is too slow, so we need to use the estimate.
// Multiply by 3/2 to match getNativeHeapAllocatedSize()
final int adjustedMemoryUsed = ( processMemoryUsed * 3 ) / 2 ;
2017-03-12 21:21:09 -04:00
if ( mButtonPause != null )
{
if ( ! mButtonPause . isChecked ())
{
statusTexts [ 1 ] = getString ( R . string . memory ) + adjustedMemoryUsed ;
}
else if ( mState == State . STATE_PROCESSING )
{
// This request is long to do, only do it when processing.
statusTexts [ 1 ] = getString ( R . string . memory ) + String . valueOf ( Debug . getNativeHeapAllocatedSize () / ( 1024 * 1024 ));
}
else
{
statusTexts [ 1 ] = mStatusTexts [ 1 ] ;
}
}
else
{
statusTexts [ 1 ] = mStatusTexts [ 1 ] ;
}
2017-02-22 09:48:45 -05:00
statusTexts [ 2 ] = getString ( R . string . free_memory ) + getFreeMemory ();
if ( loopClosureId > 0 )
{
++ mTotalLoopClosures ;
}
2017-03-12 21:21:09 -04:00
mMapNodes = nodes ;
2017-02-22 09:48:45 -05:00
int index = 4 ;
statusTexts [ index ++] = getString ( R . string . nodes ) + nodes + " (" + nodesDrawn + " shown)" ;
statusTexts [ index ++] = getString ( R . string . words ) + words ;
statusTexts [ index ++] = getString ( R . string . database_size ) + databaseMemoryUsed ;
statusTexts [ index ++] = getString ( R . string . points ) + points ;
statusTexts [ index ++] = getString ( R . string . polygons ) + polygons ;
statusTexts [ index ++] = getString ( R . string . update_time ) + ( int )( updateTime ) + " / " + ( mTimeThr . compareTo ( "0" ) == 0 ? "No Limit" : mTimeThr );
statusTexts [ index ++] = getString ( R . string . features ) + featuresExtracted + " / " + ( mMaxFeatures . compareTo ( "0" ) == 0 ? "No Limit" : mMaxFeatures . compareTo ( "-1" ) == 0 ? "Disabled" : mMaxFeatures );
statusTexts [ index ++] = getString ( R . string . rehearsal ) + ( int )( rehearsalValue * 100 . 0f );
statusTexts [ index ++] = getString ( R . string . total_loop ) + mTotalLoopClosures ;
statusTexts [ index ++] = getString ( R . string . inliers ) + inliers ;
statusTexts [ index ++] = getString ( R . string . hypothesis ) + ( int )( hypothesis * 100 . 0f ) + " / " + ( int )( Float . parseFloat ( mLoopThr ) * 100 . 0f ) + " (" + ( loopClosureId > 0 ? loopClosureId : highestHypId ) + ")" ;
statusTexts [ index ++] = getString ( R . string . fps ) + ( int ) fps + " Hz" ;
2017-01-26 16:46:24 -05:00
runOnUiThread ( new Runnable () {
2017-02-22 09:48:45 -05:00
public void run () {
updateStatsUI ( adjustedMemoryUsed , loopClosureId , inliers , matches , rejected , optimizationMaxError , statusTexts );
}
2016-02-15 19:35:24 -05:00
});
}
2017-01-26 16:46:24 -05:00
private void rtabmapInitEventUI (
int status ,
String msg )
{
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . i ( TAG , String . format ( "rtabmapInitEventsUI() status=%d msg=%s" , status , msg ));
2017-01-26 16:46:24 -05:00
if ( mButtonPause != null )
{
if ( mButtonPause . isChecked ())
{
2017-02-22 09:48:45 -05:00
mStatusTexts [ 0 ] = getString ( R . string . status ) + ( status == 1 && msg . isEmpty () ? "Paused" : msg );
2017-01-26 16:46:24 -05:00
}
2017-03-12 21:21:09 -04:00
else if ( mItemLocalizationMode != null && mItemDataRecorderMode != null )
2017-01-26 16:46:24 -05:00
{
2017-03-12 21:21:09 -04:00
mStatusTexts [ 0 ] = getString ( R . string . status ) + ( status == 1 && msg . isEmpty () ? ( mItemDataRecorderMode != null && mItemDataRecorderMode . isChecked () ? "Recording" : mItemLocalizationMode != null && mItemLocalizationMode . isChecked () ? "Localization" : "Mapping" ): msg );
2017-01-26 16:46:24 -05:00
}
2017-02-22 09:48:45 -05:00
mStatusTexts [ 1 ] = getString ( R . string . memory ) + String . valueOf ( Debug . getNativeHeapAllocatedSize () / ( 1024 * 1024 ));
mStatusTexts [ 2 ] = getString ( R . string . free_memory ) + String . valueOf ( getFreeMemory ());
updateStatusTexts ();
2017-01-26 16:46:24 -05:00
}
}
//called from jni
public void rtabmapInitEventCallback (
final int status ,
final String msg )
{
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . i ( TAG , String . format ( "rtabmapInitEventCallback()" ));
2017-01-26 16:46:24 -05:00
runOnUiThread ( new Runnable () {
public void run () {
rtabmapInitEventUI ( status , msg );
}
});
}
2017-02-28 21:50:50 -05:00
private void updateProgressionUI (
int count ,
int max )
{
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . i ( TAG , String . format ( "updateProgressionUI() count=%d max=%s" , count , max ));
2017-02-28 21:50:50 -05:00
mExportProgressDialog . setMax ( max );
mExportProgressDialog . setProgress ( count );
}
//called from jni
public void updateProgressionCallback (
final int count ,
final int max )
{
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . i ( TAG , String . format ( "updateProgressionCallback()" ));
2017-02-28 21:50:50 -05:00
runOnUiThread ( new Runnable () {
public void run () {
updateProgressionUI ( count , max );
}
});
}
2017-01-26 16:46:24 -05:00
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"
*/
String str = null ;
if ( key . equals ( "TangoServiceException" ))
str = String . format ( "Tango service exception: %s" , value );
else if ( key . equals ( "FisheyeOverExposed" ))
; //str = String.format("The fisheye image is over exposed with average pixel value %s px.", value);
else if ( key . equals ( "FisheyeUnderExposed" ))
; //str = String.format("The fisheye image is under exposed with average pixel value %s px.", value);
else if ( key . equals ( "ColorOverExposed" ))
; //str = String.format("The color image is over exposed with average pixel value %s px.", value);
else if ( key . equals ( "ColorUnderExposed" ))
; //str = String.format("The color image is under exposed with average pixel value %s px.", value);
else if ( key . equals ( "CameraTango" ))
str = value ;
else if ( key . equals ( "TooFewFeaturesTracked" ))
{
if ( ! value . equals ( "0" ))
{
str = String . format ( "Too few features (%s) were tracked in the fisheye image. This may result in poor odometry!" , value );
}
}
else
{
str = String . format ( "Unknown Tango event detected!? (type=%d)" , type );
}
if ( str != null )
{
mToast . setText ( str );
mToast . show ();
}
}
//called from jni
public void tangoEventCallback (
final int type ,
final String key ,
final String value )
{
if ( mButtonPause != null && ! mButtonPause . isChecked ())
{
runOnUiThread ( new Runnable () {
public void run () {
tangoEventUI ( type , key , value );
}
2016-09-02 22:37:12 -04:00
});
2017-01-26 16:46: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 ;}
2017-06-10 21:45:51 -04:00
private String [] loadFileList ( String directory , final boolean databasesOnly ) {
2017-01-26 16:46:24 -05:00
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 );
2017-06-10 21:45:51 -04:00
if ( databasesOnly )
{
return filename . compareTo ( RTABMAP_TMP_DB ) != 0 && filename . endsWith ( ".db" );
}
else
{
return sel . isFile ();
}
2017-01-26 16:46:24 -05:00
}
};
fileList = path . list ( filter );
Arrays . sort ( fileList );
}
else {
fileList = new String [ 0 ] ;
}
return fileList ;
}
private void standardOptimization () {
2017-02-28 21:50:50 -05:00
mExportProgressDialog . setTitle ( "Post-Processing" );
mExportProgressDialog . setMessage ( String . format ( "Please wait while optimizing..." ));
mExportProgressDialog . setProgress ( 0 );
mExportProgressDialog . show ();
2017-01-26 16:46:24 -05:00
updateState ( State . STATE_PROCESSING );
Thread workingThread = new Thread ( new Runnable () {
public void run () {
final int loopDetected = RTABMapLib . postProcessing ( - 1 );
runOnUiThread ( new Runnable () {
public void run () {
2017-02-28 21:50:50 -05:00
if ( mExportProgressDialog . isShowing ())
2017-01-26 16:46:24 -05:00
{
2017-02-28 21:50:50 -05:00
mExportProgressDialog . dismiss ();
if ( loopDetected >= 0 )
{
mTotalLoopClosures += loopDetected ;
mProgressDialog . setTitle ( "Post-Processing" );
mProgressDialog . setMessage ( String . format ( "Optimization done! Increasing visual appeal..." ));
mProgressDialog . show ();
}
else if ( loopDetected < 0 )
{
mToast . makeText ( getActivity (), String . format ( "Optimization failed!" ), mToast . LENGTH_LONG ). show ();
}
2017-01-26 16:46:24 -05:00
}
2017-02-28 21:50:50 -05:00
else
2017-01-26 16:46:24 -05:00
{
2017-02-28 21:50:50 -05:00
mToast . makeText ( getActivity (), String . format ( "Optimization canceled" ), mToast . LENGTH_LONG ). show ();
2017-01-26 16:46:24 -05:00
}
updateState ( State . STATE_IDLE );
}
});
}
});
workingThread . start ();
}
2017-06-03 20:06:45 -04:00
private Handler notouchHandler = new Handler (){
public void handleMessage ( Message msg ) {
}
};
private Runnable notouchCallback = new Runnable () {
@Override
public void run () {
mHudVisible = false ;
updateState ( mState );
}
};
public void resetNoTouchTimer (){
if ( ! mHudVisible )
{
mHudVisible = true ;
updateState ( mState );
}
mHudVisible = true ;
notouchHandler . removeCallbacks ( notouchCallback );
notouchHandler . postDelayed ( notouchCallback , NOTOUCH_TIMEOUT );
}
public void stopDisconnectTimer (){
notouchHandler . removeCallbacks ( notouchCallback );
}
2017-03-12 21:21:09 -04:00
2017-01-26 16:46:24 -05:00
private void updateState ( State state )
2017-02-15 19:07:10 -05:00
{
2017-03-12 21:21:09 -04:00
if ( mState == State . STATE_VISUALIZING && state == State . STATE_IDLE && mMapNodes > 100 )
{
mToast . makeText ( getActivity (), String . format ( "Re-adding %d online clouds, this may take some time..." , mMapNodes ), mToast . LENGTH_LONG ). show ();
}
2017-01-26 16:46:24 -05:00
mState = state ;
switch ( state )
{
case STATE_PROCESSING :
mButtonLighting . setVisibility ( View . INVISIBLE );
2017-06-12 19:08:51 -04:00
mButtonWireframe . setVisibility ( View . INVISIBLE );
2017-01-27 11:47:57 -05:00
mButtonCloseVisualization . setVisibility ( View . INVISIBLE );
2017-02-15 19:07:10 -05:00
mButtonSaveOnDevice . setVisibility ( View . INVISIBLE );
mButtonShareOnSketchfab . setVisibility ( View . INVISIBLE );
2017-01-26 16:46:24 -05:00
mItemSave . setEnabled ( false );
mItemExport . setEnabled ( false );
mItemOpen . setEnabled ( false );
mItemPostProcessing . setEnabled ( false );
mItemSettings . setEnabled ( false );
mItemReset . setEnabled ( false );
mItemModes . setEnabled ( false );
2017-02-15 19:07:10 -05:00
mButtonPause . setVisibility ( View . INVISIBLE );
2017-01-26 16:46:24 -05:00
break ;
case STATE_VISUALIZING :
2017-06-12 19:08:51 -04:00
mButtonLighting . setVisibility ( mHudVisible && ! mItemRenderingPointCloud . isChecked () ? View . VISIBLE : View . INVISIBLE );
mButtonWireframe . setVisibility ( mHudVisible && ! mItemRenderingPointCloud . isChecked () ? View . VISIBLE : View . INVISIBLE );
2017-06-03 20:06:45 -04:00
mButtonCloseVisualization . setVisibility ( mHudVisible ? View . VISIBLE : View . INVISIBLE );
mButtonSaveOnDevice . setVisibility ( mHudVisible ? View . VISIBLE : View . INVISIBLE );
mButtonShareOnSketchfab . setVisibility ( mHudVisible ? View . VISIBLE : View . INVISIBLE );
2017-01-26 16:46:24 -05:00
mItemSave . setEnabled ( mButtonPause . isChecked ());
mItemExport . setEnabled ( mButtonPause . isChecked () && ! mItemDataRecorderMode . isChecked ());
2017-03-04 19:34:40 -05:00
mItemOpen . setEnabled ( false );
mItemPostProcessing . setEnabled ( false );
2017-01-26 16:46:24 -05:00
mItemSettings . setEnabled ( true );
mItemReset . setEnabled ( true );
mItemModes . setEnabled ( true );
2017-02-15 19:07:10 -05:00
mButtonPause . setVisibility ( View . INVISIBLE );
2017-01-26 16:46:24 -05:00
mItemDataRecorderMode . setEnabled ( mButtonPause . isChecked ());
break ;
default :
mButtonLighting . setVisibility ( View . INVISIBLE );
2017-06-12 19:08:51 -04:00
mButtonWireframe . setVisibility ( View . INVISIBLE );
2017-01-27 11:47:57 -05:00
mButtonCloseVisualization . setVisibility ( View . INVISIBLE );
2017-02-15 19:07:10 -05:00
mButtonSaveOnDevice . setVisibility ( View . INVISIBLE );
mButtonShareOnSketchfab . setVisibility ( View . INVISIBLE );
2017-01-26 16:46:24 -05:00
mItemSave . setEnabled ( mButtonPause . isChecked ());
mItemExport . setEnabled ( mButtonPause . isChecked () && ! mItemDataRecorderMode . isChecked ());
mItemOpen . setEnabled ( mButtonPause . isChecked () && ! mItemDataRecorderMode . isChecked ());
mItemPostProcessing . setEnabled ( mButtonPause . isChecked () && ! mItemDataRecorderMode . isChecked ());
mItemSettings . setEnabled ( true );
mItemReset . setEnabled ( true );
mItemModes . setEnabled ( true );
2017-06-03 20:06:45 -04:00
mButtonPause . setVisibility ( mHudVisible ? View . VISIBLE : View . INVISIBLE );
2017-01-27 11:47:57 -05:00
mItemDataRecorderMode . setEnabled ( mButtonPause . isChecked ());
2017-01-26 16:46:24 -05:00
break ;
}
2017-06-03 20:06:45 -04:00
mButtonFirst . setVisibility ( mHudVisible ? View . VISIBLE : View . INVISIBLE );
mButtonThird . setVisibility ( mHudVisible ? View . VISIBLE : View . INVISIBLE );
mButtonTop . setVisibility ( mHudVisible ? View . VISIBLE : View . INVISIBLE );
mButtonBackfaceShown . setVisibility ( mHudVisible && ( mItemRenderingMesh . isChecked () || mItemRenderingTextureMesh . isChecked ()) ? View . VISIBLE : View . INVISIBLE );
2017-06-12 15:36:33 -04:00
mSeekBarFov . setVisibility ( mHudVisible && ( mButtonFirst . isChecked () || ( mButtonTop . isChecked () && mButtonTop . getTextOn (). equals ( "Ortho" ))) ? View . VISIBLE : View . INVISIBLE );
2017-01-26 16:46:24 -05:00
}
private void pauseMapping () {
updateState ( State . STATE_IDLE );
if ( mButtonPause . isChecked ())
{
RTABMapLib . setPausedMapping ( true );
2017-02-22 09:48:45 -05:00
mStatusTexts [ 0 ] = getString ( R . string . status ) + "Paused" ;
mStatusTexts [ 1 ] = getString ( R . string . memory ) + String . valueOf ( Debug . getNativeHeapAllocatedSize () / ( 1024 * 1024 ));
mStatusTexts [ 2 ] = getString ( R . string . free_memory ) + String . valueOf ( getFreeMemory ());
updateStatusTexts ();
2017-01-26 16:46:24 -05:00
mMapIsEmpty = false ;
2017-02-15 19:07:10 -05:00
mDateOnPause = new Date ();
2017-01-26 16:46:24 -05:00
2017-02-22 09:48:45 -05:00
long memoryFree = getFreeMemory ();
if ( ! mOnPause && ! mItemLocalizationMode . isChecked () && ! mItemDataRecorderMode . isChecked () && memoryFree >= 100 )
2017-01-26 16:46:24 -05:00
{
// Do standard post processing?
new AlertDialog . Builder ( getActivity ())
. setTitle ( "Mapping Paused! Optimize Now?" )
. setMessage ( "Do you want to do standard map optimization now? This can be also done later using \"Optimize\" menu." )
. setPositiveButton ( "Yes" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
standardOptimization ();
}
})
. setNegativeButton ( "No" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
// do nothing...
}
})
. show ();
}
}
else
{
2017-02-22 09:48:45 -05:00
if ( mMemoryWarningDialog != null )
{
mMemoryWarningDialog . dismiss ();
mMemoryWarningDialog = null ;
}
2017-01-26 16:46:24 -05:00
RTABMapLib . setPausedMapping ( false );
2017-02-22 09:48:45 -05:00
2017-01-26 16:46:24 -05:00
if ( mItemDataRecorderMode . isChecked ())
{
mToast . makeText ( getActivity (), String . format ( "Data Recorder Mode: no map is created, only raw data is recorded." ), mToast . LENGTH_LONG ). show ();
}
else if ( ! mMapIsEmpty )
{
mToast . makeText ( getActivity (), String . format ( "On resume, a new map is created. Tip: Try relocalizing in the previous area." ), mToast . LENGTH_LONG ). show ();
}
}
}
public boolean onOptionsItemSelected ( MenuItem item ) {
2017-03-12 21:21:09 -04:00
if ( ! DISABLE_LOG ) Log . i ( TAG , "called onOptionsItemSelected; selected item: " + item );
2017-01-26 16:46:24 -05:00
int itemId = item . getItemId ();
if ( itemId == R . id . post_processing_standard )
{
standardOptimization ();
}
else if ( itemId == R . id . detect_more_loop_closures )
{
2016-04-04 13:03:43 -04:00
mProgressDialog . setTitle ( "Post-Processing" );
mProgressDialog . setMessage ( String . format ( "Please wait while detecting more loop closures..." ));
mProgressDialog . show ();
2017-01-26 16:46:24 -05:00
updateState ( State . STATE_PROCESSING );
2016-04-04 13:03:43 -04:00
Thread workingThread = new Thread ( new Runnable () {
2017-01-26 16:46:24 -05:00
public void run () {
final int loopDetected = RTABMapLib . postProcessing ( 2 );
runOnUiThread ( new Runnable () {
public void run () {
mProgressDialog . dismiss ();
if ( loopDetected >= 0 )
{
mTotalLoopClosures += loopDetected ;
mToast . makeText ( getActivity (), String . format ( "Detection done! %d new loop closure(s) added." , loopDetected ), mToast . LENGTH_SHORT ). show ();
}
else if ( loopDetected < 0 )
{
mToast . makeText ( getActivity (), String . format ( "Detection failed!" ), mToast . LENGTH_SHORT ). show ();
}
updateState ( State . STATE_IDLE );
}
});
}
2016-04-04 13:03:43 -04:00
});
workingThread . start ();
2017-01-26 16:46:24 -05:00
}
else if ( itemId == R . id . global_graph_optimization )
{
2016-04-04 13:03:43 -04:00
mProgressDialog . setTitle ( "Post-Processing" );
mProgressDialog . setMessage ( String . format ( "Global graph optimization..." ));
mProgressDialog . show ();
2017-01-26 16:46:24 -05:00
updateState ( State . STATE_PROCESSING );
2016-04-04 13:03:43 -04:00
Thread workingThread = new Thread ( new Runnable () {
2017-01-26 16:46:24 -05:00
public void run () {
final int value = RTABMapLib . postProcessing ( 0 );
runOnUiThread ( new Runnable () {
public void run () {
mProgressDialog . dismiss ();
if ( value >= 0 )
{
mToast . makeText ( getActivity (), String . format ( "Optimization done!" ), mToast . LENGTH_SHORT ). show ();
}
else if ( value < 0 )
{
mToast . makeText ( getActivity (), String . format ( "Optimization failed!" ), mToast . LENGTH_SHORT ). show ();
}
updateState ( State . STATE_IDLE );
}
});
}
2016-04-04 13:03:43 -04:00
});
workingThread . start ();
2017-01-26 16:46:24 -05:00
}
else if ( itemId == R . id . polygons_filtering )
{
mProgressDialog . setTitle ( "Post-Processing" );
mProgressDialog . setMessage ( String . format ( "Noise filtering..." ));
mProgressDialog . show ();
RTABMapLib . postProcessing ( 4 );
}
else if ( itemId == R . id . gain_compensation_fast )
{
mProgressDialog . setTitle ( "Post-Processing" );
2017-04-01 14:14:36 -04:00
mProgressDialog . setMessage ( String . format ( "Adjusting Colors (Fast)..." ));
2017-01-26 16:46:24 -05:00
mProgressDialog . show ();
RTABMapLib . postProcessing ( 5 );
}
else if ( itemId == R . id . gain_compensation_full )
{
mProgressDialog . setTitle ( "Post-Processing" );
2017-04-01 14:14:36 -04:00
mProgressDialog . setMessage ( String . format ( "Adjusting Colors (Full)..." ));
2017-01-26 16:46:24 -05:00
mProgressDialog . show ();
RTABMapLib . postProcessing ( 6 );
}
else if ( itemId == R . id . bilateral_filtering )
{
mProgressDialog . setTitle ( "Post-Processing" );
mProgressDialog . setMessage ( String . format ( "Mesh smoothing..." ));
mProgressDialog . show ();
RTABMapLib . postProcessing ( 7 );
}
else if ( itemId == R . id . sba )
{
2016-04-04 13:03:43 -04:00
mProgressDialog . setTitle ( "Post-Processing" );
mProgressDialog . setMessage ( String . format ( "Bundle adjustement..." ));
mProgressDialog . show ();
2017-01-26 16:46:24 -05:00
2016-04-04 13:03:43 -04:00
Thread workingThread = new Thread ( new Runnable () {
2017-01-26 16:46:24 -05:00
public void run () {
final int value = RTABMapLib . postProcessing ( 1 );
runOnUiThread ( new Runnable () {
public void run () {
mProgressDialog . dismiss ();
if ( value >= 0 )
{
mToast . makeText ( getActivity (), String . format ( "Optimization done!" ), mToast . LENGTH_SHORT ). show ();
}
else if ( value < 0 )
{
mToast . makeText ( getActivity (), String . format ( "Optimization failed!" ), mToast . LENGTH_SHORT ). show ();
}
}
});
}
2016-04-04 13:03:43 -04:00
});
workingThread . start ();
2017-01-26 16:46:24 -05:00
}
2017-02-22 09:48:45 -05:00
else if ( itemId == R . id . status )
{
item . setChecked ( ! item . isChecked ());
updateStatusTexts ();
}
2017-01-26 16:46:24 -05:00
else if ( itemId == R . id . debug )
{
item . setChecked ( ! item . isChecked ());
2017-02-22 09:48:45 -05:00
updateStatusTexts ();
2017-01-26 16:46:24 -05:00
}
else if ( itemId == R . id . mesh || itemId == R . id . texture_mesh || itemId == R . id . point_cloud )
{
item . setChecked ( true );
RTABMapLib . setMeshRendering (
mItemRenderingMesh . isChecked () || mItemRenderingTextureMesh . isChecked (),
mItemRenderingTextureMesh . isChecked ());
2017-02-15 19:07:10 -05:00
2017-06-12 19:08:51 -04:00
resetNoTouchTimer ();
if ( mState != State . STATE_VISUALIZING )
{
// save preference
int type = mItemRenderingPointCloud . isChecked () ? 0 : mItemRenderingMesh . isChecked () ? 1 : 2 ;
SharedPreferences sharedPref = PreferenceManager . getDefaultSharedPreferences ( this );
SharedPreferences . Editor editor = sharedPref . edit ();
editor . putInt ( getString ( R . string . pref_key_rendering ), type );
// Commit the edits!
editor . commit ();
}
2017-01-26 16:46:24 -05:00
}
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 ());
2017-01-30 12:29:34 -05:00
setCamera ( item . isChecked () ? 2 : 1 );
2017-01-26 16:46:24 -05:00
}
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 . grid_visible )
{
item . setChecked ( ! item . isChecked ());
RTABMapLib . setGridVisible ( item . isChecked ());
}
else if ( itemId == R . id . save )
{
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 );
if ( mOpenedDatabasePath . isEmpty ())
{
2017-02-15 19:07:10 -05:00
String timeStamp = new SimpleDateFormat ( "yyMMdd-HHmmss" ). format ( mDateOnPause );
2017-01-26 16:46:24 -05:00
input . setText ( timeStamp );
}
else
{
File f = new File ( mOpenedDatabasePath );
String name = f . getName ();
input . setText ( name . substring ( 0 , name . lastIndexOf ( "." )));
}
2017-02-15 19:07:10 -05:00
input . setImeOptions ( EditorInfo . IME_FLAG_NO_EXTRACT_UI );
2017-01-26 16:46:24 -05:00
input . setSelectAllOnFocus ( true );
input . selectAll ();
builder . setView ( input );
builder . setPositiveButton ( "OK" , new DialogInterface . OnClickListener () {
@Override
public void onClick ( DialogInterface dialog , int which )
{
final String fileName = input . getText (). toString ();
dialog . dismiss ();
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 ) {
2017-02-22 09:48:45 -05:00
saveDatabase ( fileName );
2016-11-21 17:09:04 -05:00
}
2017-01-26 16:46:24 -05:00
})
. setNegativeButton ( "No" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
dialog . dismiss ();
}
})
. show ();
}
else
{
2017-02-22 09:48:45 -05:00
saveDatabase ( fileName );
2017-01-26 16:46:24 -05:00
}
}
}
});
2017-02-15 19:07:10 -05:00
AlertDialog alertToShow = builder . create ();
alertToShow . getWindow (). setSoftInputMode ( WindowManager . LayoutParams . SOFT_INPUT_STATE_VISIBLE );
alertToShow . show ();
2017-01-26 16:46:24 -05:00
}
else if ( itemId == R . id . reset )
{
mTotalLoopClosures = 0 ;
2017-02-22 09:48:45 -05:00
int index = 4 ;
2017-03-12 21:21:09 -04:00
mMapNodes = 0 ;
2017-02-22 09:48:45 -05:00
mStatusTexts [ index ++] = getString ( R . string . nodes ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . words ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . database_size ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . points ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . polygons ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . update_time ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . features ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . rehearsal ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . total_loop ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . inliers ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . hypothesis ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . fps ) + 0 ;
updateStatusTexts ();
2016-11-21 17:09:04 -05:00
2017-02-05 17:24:45 -05:00
mOpenedDatabasePath = "" ;
SharedPreferences sharedPref = PreferenceManager . getDefaultSharedPreferences ( this );
boolean databaseInMemory = sharedPref . getBoolean ( getString ( R . string . pref_key_db_in_memory ), Boolean . parseBoolean ( getString ( R . string . pref_default_db_in_memory )));
String tmpDatabase = mWorkingDirectory + RTABMAP_TMP_DB ;
( new File ( tmpDatabase )). delete ();
RTABMapLib . openDatabase ( tmpDatabase , databaseInMemory , false );
2017-01-26 16:46:24 -05:00
mMapIsEmpty = true ;
mItemSave . setEnabled ( false );
mItemExport . setEnabled ( false );
mItemPostProcessing . setEnabled ( false );
2017-02-05 17:24:45 -05:00
updateState ( State . STATE_IDLE );
2017-01-26 16:46:24 -05:00
}
else if ( itemId == R . id . data_recorder )
{
final boolean dataRecorderOldState = item . isChecked ();
new AlertDialog . Builder ( getActivity ())
. setTitle ( "Data Recorder Mode" )
. setMessage ( "Changing from/to data recorder mode will close the current session. Do you want to continue?" )
. setPositiveButton ( "Yes" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
// reset
mTotalLoopClosures = 0 ;
2017-02-22 09:48:45 -05:00
int index = 4 ;
2017-03-12 21:21:09 -04:00
mMapNodes = 0 ;
2017-02-22 09:48:45 -05:00
mStatusTexts [ index ++] = getString ( R . string . nodes ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . words ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . database_size ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . points ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . polygons ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . update_time ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . features ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . rehearsal ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . total_loop ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . inliers ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . hypothesis ) + 0 ;
mStatusTexts [ index ++] = getString ( R . string . fps ) + 0 ;
updateStatusTexts ();
2016-02-15 19:35:24 -05:00
2017-01-26 16:46:24 -05:00
mItemDataRecorderMode . setChecked ( ! dataRecorderOldState );
RTABMapLib . setDataRecorderMode ( mItemDataRecorderMode . isChecked ());
mOpenedDatabasePath = "" ;
2017-02-05 17:24:45 -05:00
SharedPreferences sharedPref = PreferenceManager . getDefaultSharedPreferences ( getActivity ());
boolean databaseInMemory = sharedPref . getBoolean ( getString ( R . string . pref_key_db_in_memory ), Boolean . parseBoolean ( getString ( R . string . pref_default_db_in_memory )));
String tmpDatabase = mWorkingDirectory + RTABMAP_TMP_DB ;
( new File ( tmpDatabase )). delete ();
RTABMapLib . openDatabase ( tmpDatabase , databaseInMemory , false );
2017-01-26 16:46:24 -05:00
mItemOpen . setEnabled ( ! mItemDataRecorderMode . isChecked () && mButtonPause . isChecked ());
mItemPostProcessing . setEnabled ( ! mItemDataRecorderMode . isChecked () && mButtonPause . isChecked ());
mItemExport . setEnabled ( ! mItemDataRecorderMode . isChecked () && mButtonPause . isChecked ());
mItemLocalizationMode . setEnabled ( ! mItemDataRecorderMode . isChecked ());
if ( mItemDataRecorderMode . isChecked ())
{
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 ();
}
else
{
mToast . makeText ( getActivity (), String . format ( "Data recorder mode deactivated!" ), mToast . LENGTH_LONG ). show ();
}
}
})
. setNegativeButton ( "No" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
dialog . dismiss ();
}
})
. show ();
}
2017-02-22 09:48:45 -05:00
else if ( itemId == R . id . export_point_cloud ||
2017-04-17 21:42:01 -04:00
itemId == R . id . export_point_cloud_highrez )
2017-01-26 16:46:24 -05:00
{
2017-02-22 09:48:45 -05:00
final boolean regenerateCloud = itemId == R . id . export_point_cloud_highrez ;
2017-01-26 16:46:24 -05:00
2017-04-17 21:42:01 -04:00
export ( false , false , regenerateCloud , false , 0 );
2017-02-28 21:50:50 -05:00
}
else if ( itemId == R . id . export_optimized_mesh ||
itemId == R . id . export_optimized_mesh_texture )
{
final boolean isOBJ = itemId == R . id . export_optimized_mesh_texture ;
2017-02-15 19:07:10 -05:00
2017-02-28 21:50:50 -05:00
RelativeLayout linearLayout = new RelativeLayout ( this );
final NumberPicker aNumberPicker = new NumberPicker ( this );
aNumberPicker . setMaxValue ( 9 );
aNumberPicker . setMinValue ( 0 );
aNumberPicker . setWrapSelectorWheel ( false );
aNumberPicker . setDescendantFocusability ( NumberPicker . FOCUS_BLOCK_DESCENDANTS );
aNumberPicker . setFormatter ( new NumberPicker . Formatter () {
@Override
public String format ( int i ) {
if ( i == 0 )
{
return "No Limit" ;
}
return String . format ( "%d00 000" , i );
}
});
2017-03-14 10:54:23 -04:00
aNumberPicker . setValue ( 2 );
2017-01-26 16:46:24 -05:00
2017-02-28 21:50:50 -05:00
// Fix to correctly show value on first render
try {
Method method = aNumberPicker . getClass (). getDeclaredMethod ( "changeValueByOne" , boolean . class );
method . setAccessible ( true );
method . invoke ( aNumberPicker , true );
} catch ( NoSuchMethodException e ) {
e . printStackTrace ();
} catch ( IllegalArgumentException e ) {
e . printStackTrace ();
} catch ( IllegalAccessException e ) {
e . printStackTrace ();
} catch ( InvocationTargetException e ) {
e . printStackTrace ();
}
RelativeLayout . LayoutParams params = new RelativeLayout . LayoutParams ( 50 , 50 );
RelativeLayout . LayoutParams numPicerParams = new RelativeLayout . LayoutParams ( RelativeLayout . LayoutParams . WRAP_CONTENT , RelativeLayout . LayoutParams . WRAP_CONTENT );
numPicerParams . addRule ( RelativeLayout . CENTER_HORIZONTAL );
linearLayout . setLayoutParams ( params );
linearLayout . addView ( aNumberPicker , numPicerParams );
AlertDialog . Builder alertDialogBuilder = new AlertDialog . Builder ( this );
alertDialogBuilder . setTitle ( "Maximum polygons" );
alertDialogBuilder . setView ( linearLayout );
alertDialogBuilder
. setCancelable ( false )
. setPositiveButton ( "Ok" ,
new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog ,
int id ) {
export ( isOBJ , true , false , true , aNumberPicker . getValue () * 100000 );
}
})
. setNegativeButton ( "Cancel" ,
new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog ,
int id ) {
dialog . cancel ();
}
});
AlertDialog alertDialog = alertDialogBuilder . create ();
alertDialog . show ();
}
else if ( itemId == R . id . open )
{
2017-06-10 21:45:51 -04:00
final String [] files = loadFileList ( mWorkingDirectory , true );
2017-02-28 21:50:50 -05:00
if ( files . length > 0 )
{
String [] filesWithSize = new String [ files . length ] ;
for ( int i = 0 ; i < filesWithSize . length ; ++ i )
{
File filePath = new File ( mWorkingDirectory + files [ i ] );
long mb = filePath . length () / ( 1024 * 1024 );
filesWithSize [ i ] = files [ i ] + " (" + mb + " MB)" ;
}
AlertDialog . Builder builder = new AlertDialog . Builder ( this );
builder . setTitle ( "Choose Your File (*.db)" );
builder . setItems ( filesWithSize , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , final int which ) {
// Adjust color now?
new AlertDialog . Builder ( getActivity ())
. setTitle ( "Opening database..." )
. setMessage ( "Do you want to adjust colors now?\nThis can be done later under Optimize menu." )
. setPositiveButton ( "Yes" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int whichIn ) {
openDatabase ( files [ which ] , true );
}
})
. setNeutralButton ( "No" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int whichIn ) {
openDatabase ( files [ which ] , false );
}
})
. show ();
return ;
}
});
builder . show ();
}
}
else if ( itemId == R . id . settings )
{
Intent intent = new Intent ( getActivity (), SettingsActivity . class );
startActivity ( intent );
mBlockBack = true ;
}
else if ( itemId == R . id . about )
{
AboutDialog about = new AboutDialog ( this );
about . setTitle ( "About RTAB-Map" );
about . show ();
}
return true ;
}
private void export ( final boolean isOBJ , final boolean meshing , final boolean regenerateCloud , final boolean optimized , final int optimizedMaxPolygons )
{
final String extension = isOBJ ? ".obj" : ".ply" ;
// get Export settings
SharedPreferences sharedPref = PreferenceManager . getDefaultSharedPreferences ( this );
final String cloudVoxelSizeStr = sharedPref . getString ( getString ( R . string . pref_key_cloud_voxel ), getString ( R . string . pref_default_cloud_voxel ));
final float cloudVoxelSize = Float . parseFloat ( cloudVoxelSizeStr );
final int textureSize = isOBJ ? Integer . parseInt ( sharedPref . getString ( getString ( R . string . pref_key_texture_size ), getString ( R . string . pref_default_texture_size ))): 0 ;
2017-06-10 21:45:51 -04:00
final int textureCount = Integer . parseInt ( sharedPref . getString ( getString ( R . string . pref_key_texture_count ), getString ( R . string . pref_default_texture_count )));
2017-02-28 21:50:50 -05:00
final int normalK = Integer . parseInt ( sharedPref . getString ( getString ( R . string . pref_key_normal_k ), getString ( R . string . pref_default_normal_k )));
final float maxTextureDistance = Float . parseFloat ( sharedPref . getString ( getString ( R . string . pref_key_max_texture_distance ), getString ( R . string . pref_default_max_texture_distance )));
2017-03-04 19:34:40 -05:00
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 )));
2017-02-28 21:50:50 -05:00
final float optimizedVoxelSize = cloudVoxelSize ;
final int optimizedDepth = Integer . parseInt ( sharedPref . getString ( getString ( R . string . pref_key_opt_depth ), getString ( R . string . pref_default_opt_depth )));
final float optimizedColorRadius = Float . parseFloat ( sharedPref . getString ( getString ( R . string . pref_key_opt_color_radius ), getString ( R . string . pref_default_opt_color_radius )));
final boolean optimizedCleanWhitePolygons = sharedPref . getBoolean ( getString ( R . string . pref_key_opt_clean_white ), Boolean . parseBoolean ( getString ( R . string . pref_default_opt_clean_white )));
final boolean optimizedColorWhitePolygons = false ; //sharedPref.getBoolean("pref_key_opt_color_white", false); // not used
final boolean blockRendering = sharedPref . getBoolean ( getString ( R . string . pref_key_block_render ), Boolean . parseBoolean ( getString ( R . string . pref_default_block_render )));
mExportProgressDialog . setTitle ( "Exporting" );
mExportProgressDialog . setMessage ( String . format ( "Please wait while preparing data to export..." ));
mExportProgressDialog . setProgress ( 0 );
final State previousState = mState ;
mExportProgressDialog . show ();
updateState ( State . STATE_PROCESSING );
2017-06-10 21:45:51 -04:00
final String tmpPath = mWorkingDirectory + RTABMAP_TMP_DIR + "/" + RTABMAP_TMP_FILENAME + extension ;
2017-02-28 21:50:50 -05:00
File tmpDir = new File ( mWorkingDirectory + RTABMAP_TMP_DIR );
tmpDir . mkdirs ();
2017-06-10 21:45:51 -04:00
String [] fileNames = loadFileList ( mWorkingDirectory + RTABMAP_TMP_DIR , false );
if ( ! DISABLE_LOG ) Log . i ( TAG , String . format ( "Deleting %d files in \"%s\"" , fileNames . length , mWorkingDirectory + RTABMAP_TMP_DIR ));
for ( int i = 0 ; i < fileNames . length ; ++ i )
{
File f = new File ( mWorkingDirectory + RTABMAP_TMP_DIR + "/" + fileNames [ i ] );
if ( f . delete ())
{
if ( ! DISABLE_LOG ) Log . i ( TAG , String . format ( "Deleted \"%s\"" , f . getPath ()));
}
else
{
if ( ! DISABLE_LOG ) Log . i ( TAG , String . format ( "Failed deleting \"%s\"" , f . getPath ()));
}
}
2017-03-05 13:05:26 -05:00
File exportDir = new File ( mWorkingDirectory + RTABMAP_EXPORT_DIR );
exportDir . mkdirs ();
2017-02-28 21:50:50 -05:00
Thread exportThread = new Thread ( new Runnable () {
public void run () {
2017-03-12 21:21:09 -04:00
final long startTime = System . currentTimeMillis () / 1000 ;
2017-02-28 21:50:50 -05:00
final boolean success = RTABMapLib . exportMesh (
tmpPath ,
cloudVoxelSize ,
regenerateCloud ,
meshing ,
textureSize ,
2017-06-10 21:45:51 -04:00
textureCount ,
2017-02-28 21:50:50 -05:00
normalK ,
optimized ,
optimizedVoxelSize ,
optimizedDepth ,
optimizedMaxPolygons ,
optimizedColorRadius ,
optimizedCleanWhitePolygons ,
optimizedColorWhitePolygons ,
2017-03-04 19:34:40 -05:00
maxTextureDistance ,
minTextureClusterSize ,
2017-02-28 21:50:50 -05:00
blockRendering );
runOnUiThread ( new Runnable () {
public void run () {
if ( mExportProgressDialog . isShowing ())
{
2017-02-15 19:07:10 -05:00
if ( success )
{
2017-02-22 09:48:45 -05:00
if ( ! meshing && cloudVoxelSize > 0 . 0f )
{
mToast . makeText ( getActivity (), String . format ( "Cloud assembled and voxelized at %s m." , cloudVoxelSizeStr ), mToast . LENGTH_LONG ). show ();
}
2017-03-12 21:21:09 -04:00
final long endTime = System . currentTimeMillis () / 1000 ;
2017-02-27 16:53:05 -05:00
2017-02-15 19:07:10 -05:00
// Visualize the result?
2017-02-27 16:53:05 -05:00
AlertDialog d = new AlertDialog . Builder ( getActivity ())
2017-02-15 19:07:10 -05:00
. setCancelable ( false )
2017-03-12 21:21:09 -04:00
. setTitle ( "Export Successful! (" + ( endTime - startTime ) + " sec)" )
2017-02-27 16:53:05 -05:00
. setMessage ( Html . fromHtml ( "Do you want visualize the result before saving to file or sharing to <a href=\"https://sketchfab.com/about\">Sketchfab</a>?" ))
2017-02-15 19:07:10 -05:00
. setPositiveButton ( "Yes" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
mExportedOBJ = isOBJ ;
2017-06-03 20:06:45 -04:00
resetNoTouchTimer ();
2017-06-12 19:08:51 -04:00
mSavedRenderingType = ! meshing ? 0 : ! isOBJ ? 1 : 2 ;
if ( ! meshing )
{
mItemRenderingPointCloud . setChecked ( true );
}
else if ( ! isOBJ )
{
mItemRenderingMesh . setChecked ( true );
}
else // isOBJ
{
mItemRenderingTextureMesh . setChecked ( true );
}
2017-02-15 19:07:10 -05:00
updateState ( State . STATE_VISUALIZING );
RTABMapLib . postExportation ( true );
if ( mButtonFirst . isChecked ())
{
setCamera ( 2 );
2017-01-26 16:46:24 -05:00
}
2017-02-15 19:07:10 -05:00
}
})
. setNegativeButton ( "No" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
mExportedOBJ = isOBJ ;
updateState ( State . STATE_IDLE );
RTABMapLib . postExportation ( false );
2017-02-27 16:53:05 -05:00
AlertDialog d2 = new AlertDialog . Builder ( getActivity ())
2017-02-15 19:07:10 -05:00
. setCancelable ( false )
. setTitle ( "Save to..." )
2017-02-27 16:53:05 -05:00
. setMessage ( Html . fromHtml ( "Do you want to share to <a href=\"https://sketchfab.com/about\">Sketchfab</a> or save it on device?" ))
2017-02-15 19:07:10 -05:00
. setPositiveButton ( "Share to Sketchfab" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
shareToSketchfab ();
}
})
. setNegativeButton ( "Save on device" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
saveOnDevice ();
}
})
2017-02-22 09:48:45 -05:00
. setNeutralButton ( "Cancel" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
}
})
2017-02-27 16:53:05 -05:00
. create ();
d2 . show ();
// Make the textview clickable. Must be called after show()
(( TextView ) d2 . findViewById ( android . R . id . message )). setMovementMethod ( LinkMovementMethod . getInstance ());
2017-02-15 19:07:10 -05:00
}
})
2017-02-27 16:53:05 -05:00
. create ();
d . show ();
// Make the textview clickable. Must be called after show()
(( TextView ) d . findViewById ( android . R . id . message )). setMovementMethod ( LinkMovementMethod . getInstance ());
2017-02-15 19:07:10 -05:00
}
else
{
updateState ( State . STATE_IDLE );
mToast . makeText ( getActivity (), String . format ( "Exporting map failed!" ), mToast . LENGTH_LONG ). show ();
}
2017-02-28 21:50:50 -05:00
mExportProgressDialog . dismiss ();
}
else
{
mToast . makeText ( getActivity (), String . format ( "Export canceled" ), mToast . LENGTH_LONG ). show ();
updateState ( previousState );
2017-01-26 16:46:24 -05:00
}
}
});
2017-02-28 21:50:50 -05:00
}
});
exportThread . start ();
2017-01-26 16:46:24 -05:00
}
2017-02-05 17:24:45 -05:00
2017-02-22 09:48:45 -05:00
private void saveDatabase ( String fileName )
{
final String newDatabasePath = mWorkingDirectory + fileName + ".db" ;
final String newDatabasePathHuman = mWorkingDirectoryHuman + fileName + ".db" ;
mProgressDialog . setTitle ( "Saving" );
if ( mOpenedDatabasePath . equals ( newDatabasePath ))
{
mProgressDialog . setMessage ( String . format ( "Please wait while updating \"%s\"..." , newDatabasePathHuman ));
}
else
{
mProgressDialog . setMessage ( String . format ( "Please wait while saving \"%s\"..." , newDatabasePathHuman ));
}
mProgressDialog . show ();
updateState ( State . STATE_PROCESSING );
Thread saveThread = new Thread ( new Runnable () {
public void run () {
RTABMapLib . save ( newDatabasePath ); // save
runOnUiThread ( new Runnable () {
public void run () {
if ( mOpenedDatabasePath . equals ( newDatabasePath ))
{
mToast . makeText ( getActivity (), String . format ( "Database \"%s\" updated." , newDatabasePathHuman ), mToast . LENGTH_LONG ). show ();
}
else
{
mToast . makeText ( getActivity (), String . format ( "Database saved to \"%s\"." , newDatabasePathHuman ), mToast . LENGTH_LONG ). show ();
Intent intent = new Intent ( getActivity (), RTABMapActivity . class );
// use System.currentTimeMillis() to have a unique ID for the pending intent
PendingIntent pIntent = PendingIntent . getActivity ( getActivity (), ( int ) System . currentTimeMillis (), intent , 0 );
// build notification
// the addAction re-use the same intent to keep the example short
Notification n = new Notification . Builder ( getActivity ())
. setContentTitle ( getString ( R . string . app_name ))
. setContentText ( newDatabasePathHuman + " saved!" )
. setSmallIcon ( R . drawable . ic_launcher )
. setContentIntent ( pIntent )
. setAutoCancel ( true ). build ();
NotificationManager notificationManager =
( NotificationManager ) getSystemService ( NOTIFICATION_SERVICE );
notificationManager . notify ( 0 , n );
}
if ( ! mItemDataRecorderMode . isChecked ())
{
mOpenedDatabasePath = newDatabasePath ;
}
mProgressDialog . dismiss ();
updateState ( State . STATE_IDLE );
}
});
}
});
saveThread . start ();
}
2017-02-15 19:07:10 -05:00
private void saveOnDevice ()
{
AlertDialog . Builder builder = new AlertDialog . Builder ( this );
final String extension = mExportedOBJ ? ".obj" : ".ply" ;
builder . setTitle ( String . format ( "File Name (*%s):" , extension ));
final EditText input = new EditText ( this );
input . setInputType ( InputType . TYPE_CLASS_TEXT );
builder . setView ( input );
if ( mOpenedDatabasePath . isEmpty ())
{
String timeStamp = new SimpleDateFormat ( "yyMMdd-HHmmss" ). format ( mDateOnPause );
input . setText ( timeStamp );
}
else
{
File f = new File ( mOpenedDatabasePath );
String name = f . getName ();
input . setText ( name . substring ( 0 , name . lastIndexOf ( "." )));
}
input . setImeOptions ( EditorInfo . IME_FLAG_NO_EXTRACT_UI );
input . setSelectAllOnFocus ( true );
input . selectAll ();
builder . setCancelable ( false );
builder . setNegativeButton ( "Cancel" , new DialogInterface . OnClickListener () {
@Override
public void onClick ( DialogInterface dialog , int which )
{
dialog . dismiss ();
}
});
builder . setPositiveButton ( "Ok" , new DialogInterface . OnClickListener () {
@Override
public void onClick ( DialogInterface dialog , int which )
{
final String fileName = input . getText (). toString ();
dialog . dismiss ();
if ( ! fileName . isEmpty ())
{
2017-03-05 13:05:26 -05:00
File newFile = new File ( mWorkingDirectory + RTABMAP_EXPORT_DIR + fileName + ( mExportedOBJ ? ".zip" : ".ply" ));
2017-02-15 19:07:10 -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 ) {
writeExportedFiles ( fileName , mExportedOBJ );
}
})
. setNegativeButton ( "No" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
saveOnDevice ();
}
})
. show ();
}
else
{
writeExportedFiles ( fileName , mExportedOBJ );
}
}
}
});
AlertDialog alertToShow = builder . create ();
alertToShow . getWindow (). setSoftInputMode ( WindowManager . LayoutParams . SOFT_INPUT_STATE_VISIBLE );
alertToShow . show ();
}
private void writeExportedFiles ( String fileName , boolean isOBJ )
2017-03-05 13:05:26 -05:00
{
String pathHuman ;
2017-02-15 19:07:10 -05:00
boolean success = true ;
if ( mExportedOBJ )
{
2017-03-05 13:05:26 -05:00
final String zipOutput = mWorkingDirectory + RTABMAP_EXPORT_DIR + fileName + ".zip" ;
pathHuman = mWorkingDirectoryHuman + RTABMAP_EXPORT_DIR + fileName + ".zip" ;
2017-06-10 21:45:51 -04:00
String [] fileNames = loadFileList ( mWorkingDirectory + RTABMAP_TMP_DIR , false );
if ( fileNames . length > 0 )
2017-02-15 19:07:10 -05:00
{
2017-06-10 21:45:51 -04:00
String [] filesToZip = new String [ fileNames . length ] ;
for ( int i = 0 ; i < fileNames . length ; ++ i )
{
filesToZip [ i ] = mWorkingDirectory + RTABMAP_TMP_DIR + "/" + fileNames [ i ] ;
}
File toZIPFile = new File ( zipOutput );
toZIPFile . delete ();
try
{
Util . zip ( filesToZip , zipOutput );
mToast . makeText ( getActivity (), String . format ( "Mesh \"%s\" successfully exported!" , pathHuman ), mToast . LENGTH_LONG ). show ();
}
catch ( IOException e )
{
mToast . makeText ( getActivity (), String . format ( "Exporting mesh \"%s\" failed! Error=%s" , pathHuman , e . getMessage ()), mToast . LENGTH_LONG ). show ();
success = false ;
}
2017-02-15 19:07:10 -05:00
}
2017-06-10 21:45:51 -04:00
else
2017-02-15 19:07:10 -05:00
{
2017-06-12 15:36:33 -04:00
mToast . makeText ( getActivity (), String . format ( "Exporting mesh \"%s\" failed! No files found in tmp directory!? Last export may have failed or have been canceled." , pathHuman ), mToast . LENGTH_LONG ). show ();
2017-02-15 19:07:10 -05:00
success = false ;
}
}
else
{
2017-03-05 13:05:26 -05:00
final String path = mWorkingDirectory + RTABMAP_EXPORT_DIR + fileName + ".ply" ;
pathHuman = mWorkingDirectoryHuman + RTABMAP_EXPORT_DIR + fileName + ".ply" ;
2017-02-22 09:48:45 -05:00
File toPLYFile = new File ( path );
2017-02-15 19:07:10 -05:00
toPLYFile . delete ();
2017-06-10 21:45:51 -04:00
File fromPLYFile = new File ( mWorkingDirectory + RTABMAP_TMP_DIR + "/" + RTABMAP_TMP_FILENAME + ".ply" );
2017-02-15 19:07:10 -05:00
try
{
copy ( fromPLYFile , toPLYFile );
2017-02-22 09:48:45 -05:00
mToast . makeText ( getActivity (), String . format ( "Mesh/point cloud \"%s\" successfully exported!" , pathHuman ), mToast . LENGTH_LONG ). show ();
2017-02-15 19:07:10 -05:00
}
catch ( Exception e )
{
2017-02-22 09:48:45 -05:00
mToast . makeText ( getActivity (), String . format ( "Exporting mesh/point cloud \"%s\" failed! Error=%s" , pathHuman , e . getMessage ()), mToast . LENGTH_LONG ). show ();
2017-02-15 19:07:10 -05:00
success = false ;
}
}
if ( success )
{
Intent intent = new Intent ( getActivity (), RTABMapActivity . class );
// use System.currentTimeMillis() to have a unique ID for the pending intent
PendingIntent pIntent = PendingIntent . getActivity ( getActivity (), ( int ) System . currentTimeMillis (), intent , 0 );
// build notification
// the addAction re-use the same intent to keep the example short
Notification n = new Notification . Builder ( getActivity ())
. setContentTitle ( getString ( R . string . app_name ))
2017-02-22 09:48:45 -05:00
. setContentText ( pathHuman + " exported!" )
2017-02-15 19:07:10 -05:00
. setSmallIcon ( R . drawable . ic_launcher )
. setContentIntent ( pIntent )
. setAutoCancel ( true ). build ();
NotificationManager notificationManager =
( NotificationManager ) getSystemService ( NOTIFICATION_SERVICE );
notificationManager . notify ( 0 , n );
}
}
2017-02-22 09:48:45 -05:00
private void openDatabase ( final String fileName , final boolean optimize )
2017-02-05 17:24:45 -05:00
{
mOpenedDatabasePath = mWorkingDirectory + fileName ;
SharedPreferences sharedPref = PreferenceManager . getDefaultSharedPreferences ( getActivity ());
2017-02-22 09:48:45 -05:00
final boolean databaseInMemory = sharedPref . getBoolean ( getString ( R . string . pref_key_db_in_memory ), Boolean . parseBoolean ( getString ( R . string . pref_default_db_in_memory )));
2017-02-15 19:07:10 -05:00
2017-02-22 09:48:45 -05:00
mProgressDialog . setTitle ( "Loading" );
mProgressDialog . setMessage ( String . format ( "Opening database \"%s\"..." , fileName ));
mProgressDialog . show ();
updateState ( State . STATE_PROCESSING );
Thread openThread = new Thread ( new Runnable () {
public void run () {
2017-03-12 21:21:09 -04:00
final String tmpDatabase = mWorkingDirectory + RTABMAP_TMP_DB ;
( new File ( tmpDatabase )). delete ();
try {
copy ( new File ( mOpenedDatabasePath ), new File ( tmpDatabase ));
}
catch ( IOException e )
{
mToast . makeText ( getActivity (), String . format ( "Failed to create temp database from %s!" , mOpenedDatabasePath ), mToast . LENGTH_LONG ). show ();
updateState ( State . STATE_IDLE );
mProgressDialog . dismiss ();
return ;
}
2017-02-22 09:48:45 -05:00
final int status = RTABMapLib . openDatabase ( tmpDatabase , databaseInMemory , optimize );
runOnUiThread ( new Runnable () {
public void run () {
setCamera ( 1 );
updateState ( State . STATE_IDLE );
if ( status == - 1 )
{
2017-03-26 13:40:25 -04:00
mProgressDialog . dismiss ();
2017-02-22 09:48:45 -05:00
new AlertDialog . Builder ( getActivity ())
. setCancelable ( false )
. setTitle ( "Error" )
. setMessage ( "The map is loaded but optimization of the map's graph has "
+ "failed, so the map cannot be shown. Change the Graph Optimizer approach used"
+ " or enable/disable if the graph is optimized from graph "
+ "end in \"Settings -> Mapping...\" and try opening again." )
. setPositiveButton ( "Open Settings" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
Intent intent = new Intent ( getActivity (), SettingsActivity . class );
startActivity ( intent );
2017-02-27 16:53:05 -05:00
mBlockBack = true ;
2017-02-22 09:48:45 -05:00
}
})
. setNegativeButton ( "Close" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
}
})
. show ();
}
2017-04-18 17:30:14 -04:00
else if ( status == - 2 )
{
mProgressDialog . dismiss ();
new AlertDialog . Builder ( getActivity ())
. setCancelable ( false )
. setTitle ( "Error" )
. setMessage ( "Failed to open database: Out of memory! Try "
+ "again after lowering Point Cloud Density in Settings." )
. setPositiveButton ( "Open Settings" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
Intent intent = new Intent ( getActivity (), SettingsActivity . class );
startActivity ( intent );
mBlockBack = true ;
}
})
. setNegativeButton ( "Close" , new DialogInterface . OnClickListener () {
public void onClick ( DialogInterface dialog , int which ) {
}
})
. show ();
}
2017-02-22 09:48:45 -05:00
else
{
// creating meshes...
if ( ! mItemTrajectoryMode . isChecked ())
{
mProgressDialog . setTitle ( "Loading" );
2017-03-12 21:21:09 -04:00
mProgressDialog . setMessage ( String . format ( "Database \"%s\" loaded. Please wait while rendering point clouds and meshes..." , fileName ));
2017-02-22 09:48:45 -05:00
}
}
}
});
}
});
openThread . start ();
2017-02-05 17:24:45 -05:00
}
public void copy ( File src , File dst ) throws IOException {
InputStream in = new FileInputStream ( src );
OutputStream out = new FileOutputStream ( dst );
// Transfer bytes from in to out
byte [] buf = new byte [ 1024 ] ;
int len ;
while (( len = in . read ( buf )) > 0 ) {
out . write ( buf , 0 , len );
}
in . close ();
out . close ();
}
2017-02-15 19:07:10 -05:00
private void shareToSketchfab ()
2017-01-26 16:46:24 -05:00
{
2017-02-15 19:07:10 -05:00
Intent intent = new Intent ( getActivity (), SketchfabActivity . class );
intent . putExtra ( RTABMAP_AUTH_TOKEN_KEY , mAuthToken );
intent . putExtra ( RTABMAP_EXPORTED_OBJ_KEY , mExportedOBJ );
intent . putExtra ( RTABMAP_WORKING_DIR_KEY , mWorkingDirectory );
if ( mOpenedDatabasePath . isEmpty ())
2017-01-26 16:46:24 -05:00
{
2017-02-15 19:07:10 -05:00
intent . putExtra ( RTABMAP_FILENAME_KEY , new SimpleDateFormat ( "yyMMdd-HHmmss" ). format ( mDateOnPause ));
2017-01-26 16:46:24 -05:00
}
else
{
2017-02-15 19:07:10 -05:00
File f = new File ( mOpenedDatabasePath );
String name = f . getName ();
intent . putExtra ( RTABMAP_FILENAME_KEY , name . substring ( 0 , name . lastIndexOf ( "." )));
2017-02-05 17:24:45 -05:00
}
2017-02-15 19:07:10 -05:00
startActivityForResult ( intent , SKETCHFAB_ACTIVITY_CODE );
2017-02-27 16:53:05 -05:00
mBlockBack = true ;
2017-02-05 17:24:45 -05:00
}
2016-02-15 19:35:24 -05:00
}