I'm trying to build a sample app from Occipital camera, which connects via USB to a Android device.
I imported their "out of box" sample app & attempted to build it.
However, Im getting a build command failed:
ninja: error: 'C:/Users/Fsydn/Desktop/occipital/StructureSDK-CrossPlatform-0.7.3-ROS/Libraries/MyOccipitalTestApp/app/src/main/app/lib/arm64-v8a/libstructure.so', needed by 'C:/Users/Fsydn/Desktop/occipital/StructureSDK-CrossPlatform-0.7.3-ROS/Libraries/MyOccipitalTestApp/app/build/intermediates/cmake/debug/obj/arm64-v8a/libMainActivity.so', missing and no known rule to make it
Below is all the source code & build files.
Java MainActivity:
package com.example.myoccipitaltestapp;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.usb.UsbDeviceConnection;
import android.Manifest;
import android.opengl.GLSurfaceView;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.egl.EGLConfig;
public class MainActivity extends Activity implements com.example.myoccipitaltestapp.StructureCoreUSBHelper.Delegate, ActivityCompat.OnRequestPermissionsResultCallback {
private class GLRenderer implements GLSurfaceView.Renderer {
private MainActivity owner;
private int width;
private int height;
public GLRenderer(MainActivity owner) {
this.owner = owner;
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {}
public void onSurfaceChanged(GL10 gl, int width, int height) {
this.width = width;
this.height = height;
}
public void onDrawFrame(GL10 gl) {
owner.renderFrame(width, height, owner.scaleFactor);
}
}
private class GLView extends GLSurfaceView {
private MainActivity owner;
private boolean mouseDown;
public GLView(Context context, MainActivity owner) {
super(context);
this.owner = owner;
}
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mouseDown = true;
owner.updateMouseState(mouseDown, (int)x, (int)y);
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mouseDown = false;
owner.updateMouseState(mouseDown, (int)x, (int)y);
return true;
case MotionEvent.ACTION_MOVE:
owner.updateMouseState(mouseDown, (int)x, (int)y);
return true;
default:
return false;
}
}
}
private GLView glView;
private volatile float scaleFactor = 1.f;
private com.example.myoccipitaltestapp.StructureCoreUSBHelper usbHelper;
private float computeScaleFactor() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
return metrics.density;
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupApp();
scaleFactor = computeScaleFactor();
glView = new GLView(this, this);
glView.setEGLContextClientVersion(3);
glView.setPreserveEGLContextOnPause(true);
glView.setRenderer(new GLRenderer(this));
glView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
setContentView(glView);
usbHelper = new com.example.myoccipitaltestapp.StructureCoreUSBHelper(this, this);
}
protected void onDestroy() {
super.onDestroy();
teardownApp();
}
public void onConfigurationChanged(Configuration config) {
super.onConfigurationChanged(config);
scaleFactor = computeScaleFactor();
glView.requestRender();
}
protected void onPause() {
super.onPause();
glView.onPause();
}
protected void onResume() {
super.onResume();
glView.onResume();
ActivityCompat.requestPermissions(this, new String[] {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
}, 1);
}
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
}
public void newStructureCoreDevice(UsbDeviceConnection conn) {
plugStructureCoreFileDescriptor(conn.getFileDescriptor());
}
static {
System.loadLibrary("structure");
System.loadLibrary("MainActivity");
}
private native void setupApp();
private native void teardownApp();
private native void renderFrame(int currentWidth, int currentHeight, float scaleFactor);
private native void updateMouseState(boolean down, int x, int y);
private native void plugStructureCoreFileDescriptor(int fd);
}
C++ MainActivity.cpp
// Autogenerated, do not edit
#include "AppInterface.h"
#include <jni.h>
#include <thread>
extern "C" JNIEXPORT void JNICALL Java_com_occipital_MyOccipitalTestApp_MainActivity_setupApp(JNIEnv *, jobject) {
AppInterface::setup();
}
extern "C" JNIEXPORT void JNICALL Java_com_occipital_MyOccipitalTestApp_MainActivity_teardownApp(JNIEnv *, jobject) {
AppInterface::teardown();
}
extern "C" JNIEXPORT void JNICALL Java_com_occipital_MyOccipitalTestApp_MainActivity_renderFrame(JNIEnv *, jobject, jint currentWidth, jint currentHeight, jfloat scaleFactor) {
AppInterface::renderFrame((unsigned)currentWidth, (unsigned)currentHeight, scaleFactor);
}
extern "C" JNIEXPORT void JNICALL Java_com_occipital_MyOccipitalTestApp_MainActivity_updateMouseState(JNIEnv *, jobject, jboolean down, jint x, jint y) {
AppInterface::updateMouseState(down, x, y);
}
extern "C" JNIEXPORT void JNICALL Java_com_occipital_MyOccipitalTestApp_MainActivity_plugStructureCoreFileDescriptor(JNIEnv *, jobject, jint fd) {
AppInterface::plugStructureCoreFileDescriptor(fd);
}
AppInterface.h:
#pragma once
/** These functions define the interface between cross-platform sample
application code and platform-specific wrappers. */
namespace AppInterface {
/** On desktop platforms this function is called in main() before
runUntilWindowClosed(). On Android it is called when the main activity
is created. */
void setup();
/** On desktop platforms this function is called in main() after
runUntilWindowClosed(). On Android it is called when the main activity
is destroyed. */
void teardown();
#if __ANDROID__
/** See Window::renderFrameInGLSurfaceViewContext(). */
void renderFrame(unsigned currentWidth, unsigned currentHeight, float scaleFactor);
/** See Window::updateMouseState(). */
void updateMouseState(bool down, int x, int y);
/** For applications that require Structure Core USB support. The argument
is a file descriptor from the Android UsbDeviceConnection API and should
be passed to ST::registerSensorByUSBFileDescriptor() or equivalent. */
void plugStructureCoreFileDescriptor(int fd);
#else
/** Called in main(). */
void runUntilWindowClosed();
#endif
}
Gradle build:
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.myoccipitaltestapp"
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
sourceSets {
main {
jniLibs.srcDir 'lib'
}
}
ndk{
abiFilters "arm64-v8a", "x86", "x86_64"
}
externalNativeBuild {
cmake {
abiFilters "arm64-v8a", "x86", "x86_64"
arguments '-DANDROID_TOOLCHAIN=g++', '-DANDROID_STL=c++_shared', '-DANDROID_PLATFORM=android-24', '-DANDROID_STL=c++_static'
cppFlags '-std=c++11'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path 'src/main/CMakeLists.txt'
}
}
}
dependencies {
implementation fileTree(dir: 'lib', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
AndroidManifest.xml:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
CMakeLists.txt , The missing .so file is actually listed here in the CMake
# Autogenerated, do not edit
cmake_minimum_required(VERSION 3.6)
project(MainActivity)
add_library(MainActivity SHARED C:/Users/Fsydn/Desktop/occipital/StructureSDK-CrossPlatform-0.7.3-ROS/Libraries/MyOccipitalTestApp/app/src/main/cpp/MainActivity.cpp)
set_target_properties(MainActivity PROPERTIES LINK_FLAGS "-Wl,-rpath,'$ORIGIN'")
target_link_libraries(MainActivity PRIVATE ${PROJECT_SOURCE_DIR}/app/lib/${ANDROID_ABI}/libstructure.so
)
The actual project's file structure seems to correspond to ${PROJECT_SOURCE_DIR}/app/lib/${ANDROID_ABI}/libstructure.so path that is included in the CMake, see below:
How can I build this app successfully?
Thanks a million.