0.11.2: First Google Tango release

This commit is contained in:
matlabbe
2016-02-15 19:35:24 -05:00
parent 8dc01c491b
commit c6a46a7238
415 changed files with 73300 additions and 3193 deletions
@@ -0,0 +1,54 @@
/**
* Original Author: William J. Francis
* http://www.techrepublic.com/blog/software-engineer/a-reusable-about-dialog-for-your-android-apps/
*/
package com.introlab.rtabmap;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.text.Html;
import android.text.util.Linkify;
import android.widget.TextView;
public class AboutDialog extends Dialog{
private static Context mContext = null;
public AboutDialog(Context context) {
super(context);
mContext = context;
}
/**
* This is the standard Android on create method that gets called when the activity initialized.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.about);
TextView tv = (TextView)findViewById(R.id.info_text);
tv.setText(Html.fromHtml(readRawTextFile(R.raw.info)));
tv.setLinkTextColor(Color.WHITE);
Linkify.addLinks(tv, Linkify.ALL);
}
public static String readRawTextFile(int id) {
InputStream inputStream = mContext.getResources().openRawResource(id);
InputStreamReader in = new InputStreamReader(inputStream);
BufferedReader buf = new BufferedReader(in);
String line;
StringBuilder text = new StringBuilder();
try {
while (( line = buf.readLine()) != null) text.append(line);
} catch (IOException e) {
return null;
}
return text.toString();
}
}
@@ -0,0 +1,831 @@
package com.introlab.rtabmap;
import java.io.File;
import java.io.FilenameFilter;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Point;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Debug;
import android.text.Editable;
import android.text.InputType;
import android.util.Log;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
// The main activity of the application. This activity shows debug information
// and a glSurfaceView that renders graphic content.
public class RTABMapActivity extends Activity implements OnClickListener {
// Tag for debug logging.
private static final String TAG = RTABMapActivity.class.getSimpleName();
// The minimum Tango Core version required from this application.
private static final int MIN_TANGO_CORE_VERSION = 6804;
// The package name of Tang Core, used for checking minimum Tango Core version.
private static final String TANGO_PACKAGE_NAME = "com.projecttango.tango";
public static final String EXTRA_KEY_PERMISSIONTYPE = "PERMISSIONTYPE";
public static final String EXTRA_VALUE_ADF = "ADF_LOAD_SAVE_PERMISSION";
// GLSurfaceView and renderer, all of the graphic content is rendered
// through OpenGL ES 2.0 in native code.
private Renderer mRenderer;
private GLSurfaceView mGLView;
ProgressDialog mProgressDialog;
// Screen size for normalizing the touch input for orbiting the render camera.
private Point mScreenSize = new Point();
private MenuItem mItemPause;
private MenuItem mItemSave;
private MenuItem mItemOpen;
private MenuItem mItemExport;
private MenuItem mItemLocalizationMode;
private MenuItem mItemTrajectoryMode;
private String mOpenedDatabasePath = "";
private String mTempDatabasePath = "";
private String mNewDatabasePath = "";
private String mWorkingDirectory = "";
private LinearLayout mLayoutDebug;
private int mTotalLoopClosures = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(R.string.menu_name);
// Query screen size, the screen size is used for computing the normalized
// touch point.
Display display = getWindowManager().getDefaultDisplay();
display.getSize(mScreenSize);
// Setting content view of this activity.
setContentView(R.layout.activity_rtabmap);
// Buttons for selecting camera view and Set up button click listeners.
findViewById(R.id.first_person_button).setOnClickListener(this);
findViewById(R.id.third_person_button).setOnClickListener(this);
findViewById(R.id.top_down_button).setOnClickListener(this);
// OpenGL view where all of the graphics are drawn.
mGLView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
// Configure OpenGL renderer
mGLView.setEGLContextClientVersion(2);
// Configure the OpenGL renderer.
mRenderer = new Renderer();
mGLView.setRenderer(mRenderer);
mLayoutDebug = (LinearLayout) findViewById(R.id.debug_layout);
mLayoutDebug.setVisibility(LinearLayout.GONE);
mProgressDialog = new ProgressDialog(this);
mRenderer.setProgressDialog(mProgressDialog);
// Check if the Tango Core is out dated.
if (!CheckTangoCoreVersion(MIN_TANGO_CORE_VERSION)) {
Toast.makeText(this, "Tango Core out dated, please update in Play Store", Toast.LENGTH_LONG).show();
finish();
return;
}
mOpenedDatabasePath = "";
mTempDatabasePath = "";
mNewDatabasePath = "";
mWorkingDirectory = "";
mTotalLoopClosures = 0;
if(Environment.getExternalStorageState().compareTo(Environment.MEDIA_MOUNTED)==0)
{
File extStore = Environment.getExternalStorageDirectory();
mWorkingDirectory = extStore.getAbsolutePath() + "/" + getString(R.string.app_name) + "/";
extStore = new File(mWorkingDirectory);
extStore.mkdirs();
mTempDatabasePath = mWorkingDirectory + "rtabmap.tmp.db";
extStore = new File(mTempDatabasePath);
if(extStore.exists())
{
extStore.delete();
}
}
else
{
// show warning that data cannot be saved!
Toast.makeText(getApplicationContext(),
String.format("Failed to get external storage path (SD-CARD, state=%s). Saving disabled.",
Environment.getExternalStorageState()), Toast.LENGTH_LONG).show();
}
RTABMapLib.initialize(this);
RTABMapLib.openDatabase(mTempDatabasePath);
}
@Override
protected void onResume() {
super.onResume();
mGLView.onResume();
mTotalLoopClosures = 0;
if(mItemOpen != null)
{
mItemOpen.setEnabled(false);
mItemPause.setChecked(false);
mItemSave.setEnabled(false);
mItemExport.setEnabled(false);
}
RTABMapLib.onResume();
}
@Override
protected void onPause() {
super.onPause();
mGLView.onPause();
// Delete all the non-OpenGl resources.
RTABMapLib.onPause();
mOpenedDatabasePath = "";
RTABMapLib.openDatabase(mTempDatabasePath);
}
@Override
public void onClick(View v) {
// Handle button clicks.
switch (v.getId()) {
case R.id.first_person_button:
RTABMapLib.setCamera(0);
break;
case R.id.third_person_button:
RTABMapLib.setCamera(1);
break;
case R.id.top_down_button:
RTABMapLib.setCamera(2);
break;
default:
return;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Pass the touch event to the native layer for camera control.
// Single touch to rotate the camera around the device.
// Two fingers to zoom in and out.
int pointCount = event.getPointerCount();
if (pointCount == 1) {
float normalizedX = event.getX(0) / mScreenSize.x;
float normalizedY = event.getY(0) / mScreenSize.y;
RTABMapLib.onTouchEvent(1,
event.getActionMasked(), normalizedX, normalizedY, 0.0f, 0.0f);
}
if (pointCount == 2) {
if (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) {
int index = event.getActionIndex() == 0 ? 1 : 0;
float normalizedX = event.getX(index) / mScreenSize.x;
float normalizedY = event.getY(index) / mScreenSize.y;
RTABMapLib.onTouchEvent(1,
MotionEvent.ACTION_DOWN, normalizedX, normalizedY, 0.0f, 0.0f);
} else {
float normalizedX0 = event.getX(0) / mScreenSize.x;
float normalizedY0 = event.getY(0) / mScreenSize.y;
float normalizedX1 = event.getX(1) / mScreenSize.x;
float normalizedY1 = event.getY(1) / mScreenSize.y;
RTABMapLib.onTouchEvent(2, event.getActionMasked(),
normalizedX0, normalizedY0, normalizedX1, normalizedY1);
}
}
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
Log.i(TAG, "called onCreateOptionsMenu;");
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.optionmenu, menu);
mItemPause = menu.findItem(R.id.pause);
mItemSave = menu.findItem(R.id.save);
mItemOpen = menu.findItem(R.id.open);
mItemExport = menu.findItem(R.id.export);
mItemLocalizationMode = menu.findItem(R.id.localization_mode);
mItemTrajectoryMode = menu.findItem(R.id.trajectory_mode);
mItemSave.setEnabled(false);
mItemExport.setEnabled(false);
mItemOpen.setEnabled(false);
return true;
}
private void updateStatsUI(
int nodes,
int words,
int points,
int polygons,
float updateTime,
int loopClosureId,
int databaseMemoryUsed,
int inliers,
int featuresExtracted,
float hypothesis,
int nodesDrawn)
{
if(mItemPause!=null)
{
((TextView)findViewById(R.id.status)).setText(mItemPause.isChecked()?"Paused":mItemLocalizationMode.isChecked()?"Localization":"Mapping");
}
((TextView)findViewById(R.id.points)).setText(String.valueOf(points));
((TextView)findViewById(R.id.polygons)).setText(String.valueOf(polygons));
((TextView)findViewById(R.id.nodes)).setText(String.format("%d (%d shown)", nodes, nodesDrawn));
((TextView)findViewById(R.id.words)).setText(String.valueOf(words));
((TextView)findViewById(R.id.memory)).setText(String.valueOf(Debug.getNativeHeapAllocatedSize()/(1024*1024)));
((TextView)findViewById(R.id.db_size)).setText(String.valueOf(databaseMemoryUsed));
((TextView)findViewById(R.id.inliers)).setText(String.valueOf(inliers));
((TextView)findViewById(R.id.features)).setText(String.valueOf(featuresExtracted));
((TextView)findViewById(R.id.update_time)).setText(String.format("%.3f", updateTime));
((TextView)findViewById(R.id.hypothesis)).setText(String.format("%.3f (%d)", hypothesis, loopClosureId));
if(loopClosureId > 0)
{
++mTotalLoopClosures;
((TextView)findViewById(R.id.total_loop)).setText(String.valueOf(mTotalLoopClosures));
Toast.makeText(this, "Loop closure detected!", Toast.LENGTH_SHORT).show();
}
}
// called from jni
public void updateStatsCallback(
final int nodes,
final int words,
final int points,
final int polygons,
final float updateTime,
final int loopClosureId,
final int databaseMemoryUsed,
final int inliers,
final int features,
final float hypothesis,
final int nodesDrawn)
{
Log.i(TAG, String.format("updateStatsCallback()"));
runOnUiThread(new Runnable() {
public void run() {
updateStatsUI(nodes, words, points, polygons, updateTime, loopClosureId, databaseMemoryUsed, inliers, features, hypothesis, nodesDrawn);
}
});
}
private void rtabmapInitEventUI(
int status,
String msg)
{
Log.i(TAG, String.format("rtabmapInitEventsUI() status=%d msg=%s", status, msg));
((TextView)findViewById(R.id.status)).setText(
status == 1 && msg.isEmpty()?mItemPause!=null&&mItemPause.isChecked()?"Paused":mItemLocalizationMode!=null&&mItemLocalizationMode.isChecked()?"Localization":"Mapping":msg);
/*0=kInitializing,
1=kInitialized,
2=kClosing,
3=kClosed,
4=kInfo,
5=kError*/
if(status == 3)
{
msg = "";
if(!mNewDatabasePath.isEmpty())
{
boolean removed = true;
File outputFile = new File(mNewDatabasePath);
if(outputFile.exists())
{
removed = outputFile.delete();
}
if(removed)
{
File tempFile = new File(mTempDatabasePath);
if(tempFile.renameTo(outputFile))
{
msg = String.format("Database saved to \"%s\".", mNewDatabasePath);
Intent intent = new Intent(this, RTABMapActivity.class);
// use System.currentTimeMillis() to have a unique ID for the pending intent
PendingIntent pIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), intent, 0);
// build notification
// the addAction re-use the same intent to keep the example short
Notification n = new Notification.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setContentText(mNewDatabasePath + " saved!")
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.setAutoCancel(true).build();
NotificationManager notificationManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, n);
}
else
{
msg = String.format("Failed to rename temporary database from \"%s\" to \"%s\".",
mTempDatabasePath, mNewDatabasePath);
}
}
else
{
msg = String.format("Failed to overwrite the database \"%s\". The temporary database is still correctly saved at \"%s\".",
mNewDatabasePath, mTempDatabasePath);
}
}
else if(!mOpenedDatabasePath.isEmpty())
{
msg = String.format("Database \"%s\" updated.", mOpenedDatabasePath);
}
if(!msg.isEmpty())
{
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
mOpenedDatabasePath = "";
mNewDatabasePath = "";
//restart a new scan by default
RTABMapLib.openDatabase(mTempDatabasePath);
((TextView)findViewById(R.id.points)).setText(String.valueOf(0));
((TextView)findViewById(R.id.polygons)).setText(String.valueOf(0));
((TextView)findViewById(R.id.nodes)).setText(String.valueOf(0));
((TextView)findViewById(R.id.words)).setText(String.valueOf(0));
((TextView)findViewById(R.id.memory)).setText(String.valueOf(Debug.getNativeHeapAllocatedSize()/(1024*1024)));
((TextView)findViewById(R.id.db_size)).setText(String.valueOf(0));
((TextView)findViewById(R.id.inliers)).setText(String.valueOf(0));
((TextView)findViewById(R.id.features)).setText(String.valueOf(0));
((TextView)findViewById(R.id.update_time)).setText(String.valueOf(0));
((TextView)findViewById(R.id.hypothesis)).setText(String.valueOf(0));
mTotalLoopClosures = 0;
((TextView)findViewById(R.id.total_loop)).setText(String.valueOf(mTotalLoopClosures));
if(mItemSave!=null)
{
if(mItemPause.isChecked())
{
mItemPause.setChecked(false);
mItemOpen.setEnabled(false);
mItemSave.setEnabled(false);
mItemExport.setEnabled(false);
RTABMapLib.setPausedMapping(false); // resume mapping
}
}
mProgressDialog.dismiss();
}
}
//called from jni
public void rtabmapInitEventCallback(
final int status,
final String msg)
{
Log.i(TAG, String.format("rtabmapInitEventCallback()"));
runOnUiThread(new Runnable() {
public void run() {
rtabmapInitEventUI(status, msg);
}
});
}
private void tangoEventUI(
int type,
String key,
String value)
{
/**
*
* "TangoServiceException:X" - The service has encountered an exception, and a text description is given in X.
* "FisheyeOverExposed:X" - the fisheye image is over exposed with average pixel value X px.
* "FisheyeUnderExposed:X" - the fisheye image is under exposed with average pixel value X px.
* "ColorOverExposed:X" - the color image is over exposed with average pixel value X px.
* "ColorUnderExposed:X" - the color image is under exposed with average pixel value X px.
* "TooFewFeaturesTracked:X" - too few features were tracked in the fisheye image. The number of features tracked is X.
* "AreaDescriptionSaveProgress:X" - ADF saving is X * 100 percent complete.
* "Unknown"
*/
if(key.equals("TangoServiceException"))
Toast.makeText(this, String.format("Tango service exception: %s", value), Toast.LENGTH_SHORT).show();
else if(key.equals("FisheyeOverExposed"))
;//Toast.makeText(this, String.format("The fisheye image is over exposed with average pixel value %s px.", value), Toast.LENGTH_SHORT).show();
else if(key.equals("FisheyeUnderExposed"))
;//Toast.makeText(this, String.format("The fisheye image is under exposed with average pixel value %s px.", value), Toast.LENGTH_SHORT).show();
else if(key.equals("ColorOverExposed"))
;//Toast.makeText(this, String.format("The color image is over exposed with average pixel value %s px.", value), Toast.LENGTH_SHORT).show();
else if(key.equals("ColorUnderExposed"))
;//Toast.makeText(this, String.format("The color image is under exposed with average pixel value %s px.", value), Toast.LENGTH_SHORT).show();
else if(key.equals("CameraTango"))
Toast.makeText(this, value, Toast.LENGTH_SHORT).show();
else if(key.equals("TooFewFeaturesTracked"))
{
if(!value.equals("0"))
Toast.makeText(this, String.format("Too few features (%s) were tracked in the fisheye image. This may result in poor odometry!", value), Toast.LENGTH_SHORT).show();
}
else
Toast.makeText(this, String.format("Unknown Tango event detected!? (type=%d)", type), Toast.LENGTH_SHORT).show();
}
//called from jni
public void tangoEventCallback(
final int type,
final String key,
final String value)
{
Log.i(TAG, String.format("tangoEventCallback()"));
runOnUiThread(new Runnable() {
public void run() {
tangoEventUI(type, key, value);
}
});
}
private boolean CheckTangoCoreVersion(int minVersion) {
int versionNumber = 0;
String packageName = TANGO_PACKAGE_NAME;
try {
PackageInfo pi = getApplicationContext().getPackageManager().getPackageInfo(packageName,
PackageManager.GET_META_DATA);
versionNumber = pi.versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return (minVersion <= versionNumber);
}
private RTABMapActivity getActivity() {return this;}
private String[] loadFileList(String directory) {
File path = new File(directory);
String fileList[];
try {
path.mkdirs();
}
catch(SecurityException e) {
Log.e(TAG, "unable to write on the sd card " + e.toString());
}
if(path.exists()) {
FilenameFilter filter = new FilenameFilter() {
@Override
public boolean accept(File dir, String filename) {
File sel = new File(dir, filename);
return filename.endsWith(".db") || sel.isDirectory();
}
};
fileList = path.list(filter);
}
else {
fileList = new String[0];
}
return fileList;
}
public boolean onOptionsItemSelected(MenuItem item) {
Log.i(TAG, "called onOptionsItemSelected; selected item: " + item);
int itemId = item.getItemId();
if (itemId == R.id.pause)
{
item.setChecked(!item.isChecked());
mItemSave.setEnabled(item.isChecked());
mItemExport.setEnabled(item.isChecked());
mItemOpen.setEnabled(item.isChecked());
// mItemSave.setEnabled(item.isChecked() && !mWorkingDirectory.isEmpty());
if(item.isChecked())
{
RTABMapLib.setPausedMapping(true);
((TextView)findViewById(R.id.status)).setText("Paused");
}
else
{
RTABMapLib.setPausedMapping(false);
((TextView)findViewById(R.id.status)).setText(mItemLocalizationMode.isChecked()?"Localization":"Mapping");
}
}
else if(itemId == R.id.debug)
{
item.setChecked(!item.isChecked());
if(!item.isChecked())
{
mLayoutDebug.setVisibility(LinearLayout.GONE);
}
else
{
mLayoutDebug.setVisibility(LinearLayout.VISIBLE);
}
}
else if(itemId == R.id.mesh_rendering)
{
item.setChecked(!item.isChecked());
RTABMapLib.setMeshRendering(item.isChecked());
}
else if(itemId == R.id.map_shown)
{
item.setChecked(!item.isChecked());
RTABMapLib.setMapCloudShown(item.isChecked());
}
else if(itemId == R.id.odom_shown)
{
item.setChecked(!item.isChecked());
RTABMapLib.setOdomCloudShown(item.isChecked());
}
else if(itemId == R.id.localization_mode)
{
item.setChecked(!item.isChecked());
RTABMapLib.setLocalizationMode(item.isChecked());
}
else if(itemId == R.id.trajectory_mode)
{
item.setChecked(!item.isChecked());
RTABMapLib.setTrajectoryMode(item.isChecked());
}
else if(itemId == R.id.graph_optimization)
{
item.setChecked(!item.isChecked());
RTABMapLib.setGraphOptimization(item.isChecked());
}
else if(itemId == R.id.graph_visible)
{
item.setChecked(!item.isChecked());
RTABMapLib.setGraphVisible(item.isChecked());
}
else if(itemId == R.id.auto_exposure)
{
item.setChecked(!item.isChecked());
RTABMapLib.setAutoExposure(item.isChecked());
}
else if (itemId == R.id.save)
{
if(mOpenedDatabasePath.isEmpty())
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("RTAB-Map Database Name (*.db):");
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
builder.setView(input);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which)
{
final String fileName = input.getText().toString();
if(!fileName.isEmpty())
{
File newFile = new File(mWorkingDirectory + fileName + ".db");
if(newFile.exists())
{
new AlertDialog.Builder(getActivity())
.setTitle("File Already Exists")
.setMessage("Do you want to overwrite the existing file?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mNewDatabasePath = mWorkingDirectory + fileName + ".db";
mProgressDialog.setTitle("Saving");
mProgressDialog.setMessage(String.format("Please wait while saving \"%s\"...", mNewDatabasePath));
mProgressDialog.show();
RTABMapLib.save(); // send save event
//disable gui actions
mItemSave.setEnabled(false);
mItemOpen.setEnabled(false);
mItemExport.setEnabled(false);
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.show();
}
else
{
mNewDatabasePath = mWorkingDirectory + fileName + ".db";
mProgressDialog.setTitle("Saving");
mProgressDialog.setMessage(String.format("Please wait while saving \"%s\"...", mNewDatabasePath));
mProgressDialog.show();
RTABMapLib.save(); // send save event
//disable gui actions
mItemSave.setEnabled(false);
mItemOpen.setEnabled(false);
mItemExport.setEnabled(false);
}
}
}
});
builder.show();
}
else
{
mProgressDialog.setTitle("Saving");
mProgressDialog.setMessage(String.format("Please wait while updating \"%s\"...", mOpenedDatabasePath));
mProgressDialog.show();
RTABMapLib.save(); // send save event
//disable gui actions
mItemSave.setEnabled(false);
mItemOpen.setEnabled(false);
mItemExport.setEnabled(false);
}
}
else if(itemId == R.id.reset)
{
((TextView)findViewById(R.id.points)).setText(String.valueOf(0));
((TextView)findViewById(R.id.polygons)).setText(String.valueOf(0));
((TextView)findViewById(R.id.nodes)).setText(String.valueOf(0));
((TextView)findViewById(R.id.words)).setText(String.valueOf(0));
((TextView)findViewById(R.id.memory)).setText(String.valueOf(Debug.getNativeHeapAllocatedSize()/(1024*1024)));
((TextView)findViewById(R.id.db_size)).setText(String.valueOf(0));
((TextView)findViewById(R.id.inliers)).setText(String.valueOf(0));
((TextView)findViewById(R.id.features)).setText(String.valueOf(0));
((TextView)findViewById(R.id.update_time)).setText(String.valueOf(0));
((TextView)findViewById(R.id.hypothesis)).setText(String.valueOf(0));
mTotalLoopClosures = 0;
((TextView)findViewById(R.id.total_loop)).setText(String.valueOf(mTotalLoopClosures));
if(mOpenedDatabasePath.isEmpty())
{
RTABMapLib.resetMapping();
}
else
{
mOpenedDatabasePath = "";
RTABMapLib.openDatabase(mTempDatabasePath);
}
}
else if(itemId == R.id.export)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("File Name (*.ply):");
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
builder.setView(input);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which)
{
final String fileName = input.getText().toString();
if(!fileName.isEmpty())
{
File newFile = new File(mWorkingDirectory + fileName + ".ply");
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) {
final String path = mWorkingDirectory + fileName + ".ply";
mItemExport.setEnabled(false);
mProgressDialog.setTitle("Exporting");
mProgressDialog.setMessage(String.format("Please wait while exporting \"%s\"...", fileName+".ply"));
mProgressDialog.show();
Thread exportThread = new Thread(new Runnable() {
public void run() {
final boolean success = RTABMapLib.exportMesh(path);
runOnUiThread(new Runnable() {
public void run() {
if(success)
{
Toast.makeText(getActivity(), String.format("Mesh \"%s\" successfully exported!", path), Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(getActivity(), String.format("Exporting mesh to \"%s\" failed!", path), Toast.LENGTH_LONG).show();
}
mItemExport.setEnabled(true);
mProgressDialog.dismiss();
}
});
}
});
exportThread.start();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.show();
}
else
{
final String path = mWorkingDirectory + fileName + ".ply";
mItemExport.setEnabled(false);
mProgressDialog.setTitle("Exporting");
mProgressDialog.setMessage(String.format("Please wait while exporting \"%s\"...", fileName+".ply"));
mProgressDialog.show();
Thread exportThread = new Thread(new Runnable() {
public void run() {
final boolean success = RTABMapLib.exportMesh(path);
runOnUiThread(new Runnable() {
public void run() {
if(success)
{
Toast.makeText(getActivity(), String.format("Mesh \"%s\" successfully exported!", path), Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(getActivity(), String.format("Exporting mesh to \"%s\" failed!", path), Toast.LENGTH_LONG).show();
}
mItemExport.setEnabled(true);
mProgressDialog.dismiss();
}
});
}
});
exportThread.start();
}
}
}
});
builder.show();
}
else if(itemId == R.id.open)
{
final String[] files = loadFileList(mWorkingDirectory);
if(files.length > 0)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Choose your file");
builder.setItems(files, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mOpenedDatabasePath = mWorkingDirectory + files[which];
if(!mItemTrajectoryMode.isChecked())
{
mProgressDialog.setTitle("Loading");
mProgressDialog.setMessage(String.format("Please wait while loading \"%s\"...", files[which]));
mProgressDialog.show();
}
RTABMapLib.openDatabase(mOpenedDatabasePath);
RTABMapLib.setCamera(1);
File extStore = new File(mTempDatabasePath);
if(extStore.exists())
{
extStore.delete();
}
}
});
builder.show();
}
}
else if(itemId == R.id.about)
{
AboutDialog about = new AboutDialog(this);
about.setTitle("About RTAB-Map");
about.show();
}
return true;
}
}
@@ -0,0 +1,67 @@
package com.introlab.rtabmap;
import android.view.KeyEvent;
// Wrapper for native library
public class RTABMapLib
{
static
{
System.loadLibrary("NativeRTABMap");
}
// Initialize the Tango Service, this function starts the communication
// between the application and Tango Service.
// The activity object is used for checking if the API version is outdated.
public static native int initialize(RTABMapActivity activity);
public static native void openDatabase(String databasePath);
public static native int onResume();
// Release all non OpenGl resources that are allocated from the program.
public static native void onPause();
// Allocate OpenGL resources for rendering.
public static native void initGlContent();
// Setup the view port width and height.
public static native void setupGraphic(int width, int height);
// Main render loop.
public static native int render();
// Set the render camera's viewing angle:
// first person, third person, or top down.
public static native void setCamera(int cameraIndex);
// Pass touch events to the native layer.
public static native void onTouchEvent(int touchCount, int event0,
float x0, float y0, float x1, float y1);
public static native void setPausedMapping(boolean paused);
public static native void setMapCloudShown(boolean shown);
public static native void setOdomCloudShown(boolean shown);
public static native void setMeshRendering(boolean enabled);
public static native void setLocalizationMode(boolean enabled);
public static native void setTrajectoryMode(boolean enabled);
public static native void setGraphOptimization(boolean enabled);
public static native void setGraphVisible(boolean visible);
public static native void setAutoExposure(boolean enabled);
public static native void resetMapping();
public static native void save();
public static native boolean exportMesh(String filePath);
public static native String getStatus();
public static native int getTotalNodes();
public static native int getTotalWords();
public static native int getTotalPoints();
public static native float getUpdateTime();
public static native int getLoopClosureId();
}
@@ -0,0 +1,55 @@
/*
* Copyright 2014 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.introlab.rtabmap;
import android.app.ProgressDialog;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
// Renderer renders graphic content. This includes the point cloud,
// ground grid, camera frustum, camera axis, and trajectory based on the Tango
// device's pose.
public class Renderer implements GLSurfaceView.Renderer {
private ProgressDialog mProgressDialog;
public void setProgressDialog(ProgressDialog progressDialog)
{
mProgressDialog = progressDialog;
}
// Render loop of the Gl context.
public void onDrawFrame(GL10 gl) {
int value = RTABMapLib.render();
if(value == 1 && mProgressDialog != null)
{
mProgressDialog.dismiss();
}
}
// Called when the surface size changes.
public void onSurfaceChanged(GL10 gl, int width, int height) {
RTABMapLib.setupGraphic(width, height);
}
// Called when the surface is created or recreated.
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
RTABMapLib.initGlContent();
}
}