Develop simultaneously for native and android

Hello every one,

This is my first post. Thanks for all the work on JME !

I’m evaluating JME for developing to both desktop and android.
I have a first demo using a webcam (via OpenCv) running on my desktop (linux 64), and I am now trying to have it work on android.

I started the android port following the instructions of:
https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:android#using_android_specific_functions
which means that I made a CamStream interface (extending AppState) with a getImageData() method. It is implemented by AndroidCamStream and DesktopCamstream, but I’m stuck on the integration:

How do I use the AndroidHarness to set AndroidCamStream as the CamStream instance of my app ?
How do I do the same for the desktop version, i.e. how do I exclude the DesktopCamstream from the common code, and set it up only for desktop build ?

I had a look at the JmeAndroidDemo sources, but if I’m not wrong, it’s an android only application. I also tried my luck in the forum, but I haven’t really found an answer.

Hello everyone,

So after randomly poking into JME’s source code, I came up with the following solution to write an application that uses Android and desktop-specific code. It works, but it does not seem nice. I’d be happy to get some comments on how to make things cleaner.

The idea is that the application is desktop by default, unless “stated” otherwise. To do so, I have a
[java]
public class Main extends SimpleApplication {
public static boolean isDesktop = true;
//…
[/java]

All the desktop specific happens in Main, but checks that isDesktop is true, e.g. :
[java]
public void simpleInitApp() {
if (isDesktop) {
this.camStream = new DesktopCamStream();
}
//…
[/java]

The Android harness takes care of mutating the Main into an Android application:
[java]
public class MainActivity extends AndroidHarness {

static {
    Main.isDesktop = false;
}

//…

private AndroidCamStream camStream;

public MainActivity(){

//…
camStream = new AndroidCamStream();
}

@Override
public void initialize() {
    ((Main) app).camStream = camStream;
    super.initialize();
}

//…
[/java]

Again, it works but it seems ugly… Is there a better way to do ?

Bonus: here is the ugly way of getting the image from the android camera.
For some reason mobile devices expect the camera image to be displayed if its data need to be accessed.
Here is how to display the camera image on a SurfaceView that is present only on the Android version of the application (I don’t need the feedback on the desktop application).

We first need to keep a SurfaceView as member of the AndroidHarness
[java]
public class MainActivity extends AndroidHarness {
private SurfaceView surfaceView;
//…
[/java]

But we initialize it only in the overridden layoutDisplay() (the context does not seem ready earlier)
[java]
@Override
public void layoutDisplay() {
super.layoutDisplay();

    // This crashes if it happens earlier in the application life cycle
    surfaceView = new SurfaceView(this);
    surfaceView.getHolder().addCallback(camStream);
    
    // Hack into the root view's layout and overlay the SurfaceView
    FrameLayout fl = (FrameLayout) getWindow().getDecorView().findViewById(android.R.id.content);
    fl.addView(surfaceView);
    fl.bringChildToFront(surfaceView);
    
    // Resize and place the SurfaceView on the bottom right.
    FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) surfaceView.getLayoutParams();
    layoutParams.width = 160;
    layoutParams.height = 120;
    layoutParams.topMargin = 0;
    layoutParams.leftMargin = 0;
    layoutParams.gravity = Gravity.BOTTOM + Gravity.RIGHT;
}

[/java]

Again, feedbacks/reviews are more than welcome.

Only comments I would make is to be careful when doing things like:

[java]
layoutParams.width = 160;
layoutParams.height = 120;
[/java]

I would scale this based on the dpi or resolution… something along the lines of:

[java]
float baseWidth = 720;
float ratio = (float)getWindow().getAttributes().width/baseWidth;
layoutParams.width = 160ratio;
layoutParams.height = 120
ratio;
[/java]

The other is… calling into your app from the Main Activity is probably not a great idea, as the order in which things are loaded (and the time it takes) will vary. If you do a search on “Android Project Cheatsheet”, it has a description of how to setup communication from your JME app to the Main Activity… once this is setup, you can have your JME app request the camStream if the OS isAndroid i.e.:

[java]
public static boolean isAndroid() {
String OS = System.getProperty(“java.vendor”).toLowerCase();
return (OS.indexOf(“android”) >= 0);
}
[/java]