I got this to work. Its pretty ugly, and probably inefficient. I didnt completely understand how the JNI works, but its not crashing and doesnt impact performance. Use at your own risk. (Adapted from east's code)
first, I extend NativeActivity:
import android.os.Vibrator;
public class MyNativeActivity extends NativeActivity {
Vibrator v;
public boolean hasVibrator(){
v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
return v.hasVibrator();
}
public void vibrate(long ms){
v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(ms);
}
// Load the game's native code:
static {
System.loadLibrary("android_player");
}
}
then, in the AndroidManifest I change the line that sets the main activity to call this one instead. Also, I add vibration permission.
...
<uses-permission android:name="android.permission.VIBRATE" />
...
<activity android:name="com.custom.functions.MyNativeActivity">
vibrate.cpp file:
#include <jni.h>
#include "vibrate.hpp"
#include "jni_util.hpp"
#include "template.h"
bool java_hasVibrator(){
struct JniSetup *setup = GetJNISetup();
jmethodID m_hasVibrator = setup->env->GetMethodID(setup->clazz,"hasVibrator", "()Z");
jboolean ans = setup->env->CallBooleanMethod(setup->thiz, m_hasVibrator);
ReleaseJNI();
return ((bool) ans);
}
void java_vibrate(long ms){
jlong jms = (jlong) ms;
struct JniSetup *setup = GetJNISetup();
jmethodID m_vibrate = setup->env->GetMethodID(setup->clazz,"vibrate", "(J)V"); //returns an ID to method vibrate, with args = J (long) and return = void (V)
setup->env->CallVoidMethod(setup->thiz, m_vibrate, jms);
ReleaseJNI();
}
vibrate.hpp:
#ifndef VIBRATE_HPP
#define VIBRATE_HPP
bool java_hasVibrator();
void java_vibrate(long ms);
#endif
jni_utill.cpp:
#include "jni_util.hpp"
extern ANativeActivity *g_pActivity;
static struct JniSetup _jni_setup = {0};
struct JniSetup* GetJNISetup() {
g_pActivity->vm->AttachCurrentThread(&_jni_setup.env, NULL);
_jni_setup.thiz = g_pActivity->clazz;
_jni_setup.clazz = _jni_setup.env->GetObjectClass(_jni_setup.thiz);
return &_jni_setup;
}
void ReleaseJNI()
{
g_pActivity->vm->DetachCurrentThread();
}
and jni_util.hpp:
extern "C" {
#include <jni.h>
#include <errno.h>
#include <android/log.h>
#include <android_native_app_glue.h>
#include <android/native_activity.h>
#include <unistd.h>
}
#ifndef JNI_UTIL_H
#define JNI_UTIL_H
struct JniSetup{
jobject thiz;
jclass clazz;
JNIEnv* env;
};
struct JniSetup* GetJNISetup();
void ReleaseJNI();
#endif
in the end, I add the cpp files to Android.mk in the jni folder.
I tried a lot to improve this by only attaching the thread once and re-using it but apparently this causes a crash. The java code could be easily improved but im too tired and pissed for this.
Needless to say, these will only compile with the android template.
If anyone could make this work without re-attaching the thread at each call, please share.