Hey, I’ve been working with android a bit and put together a perhaps dirty method of getting some urho3d c++ to java talk working, and it’s rather simple:
The following code will show how to call a java function we will create called ‘gotoGoogle’, which will be tacked onto SDLActivty.java located in Android/src/org/libsdl/app. The Java code will then call a c++ function we will create to inform out app when it’s ready.
Note: There is probably a cleaner way to do this (eg, extending SDLActivity), however I’m not familiar enough with android to know how-- contribute if you do !
[size=200]In SDLActivtiy.java:[/size]
File: Android/src/org/libsdl/app/SDLActivity.java:
Add these imports:
import android.widget.ViewSwitcher;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.github.urho3d.R;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
[ul]
[li]ViewSwitcher is what will allow us to seamlessly switch between our urho application and the WebView (you can add animations, etc.)[/li]
[li]WebView and WebViewClient allow us to embed a browser / handle events in the browser.[/li]
[li]the R class is a machine-generated class that lets us access resources (eg, xml resources in Android/res)[/li]
[li]AnimationUtils lets us load animations from supplied resources[/li][/ul]
[size=150]In SDLActivtiy class:[/size]
Define these members in the SDLActivity class:
protected static WebView webview;
protected static ViewSwitcher layoutSwitcher;
The WebView and ViewSwitcher are like UIElements in Urho3d, it holds the functionality for the widget and renders its contents.
Add these initializers to public static void initialize() in SDLActivity:
layoutSwitcher = null;
mJoystickHandler = null;
At the bottom of protected void onCreate(Bundle savedInstanceState) in SDLActivity, change:
[code]mLayout = new AbsoluteLayout(this);
mLayout.addView(mSurface);
setContentView(mLayout);[/code]
to:
[code]webview = new WebView(this);
mLayout = new AbsoluteLayout(this);
mLayout.addView(mSurface);
layoutSwitcher = new ViewSwitcher(this);
layoutSwitcher.addView(mLayout);
layoutSwitcher.addView(webview);
You will need to set up
Animation animIn, animOut;
animIn = AnimationUtils.loadAnimation(this, R.anim.fadein);
animOut = AnimationUtils.loadAnimation(this, R.anim.fadeout);
layoutSwitcher.setInAnimation(animIn);
layoutSwitcher.setOutAnimation(animOut);
setContentView(layoutSwitcher);[/code]
Note: Later, we will create the resources that R.anim.fadein and R.anim.fadeout reference.
Now the meat of the Java, the function we will call from c++ to switch into the WebView – add these to SDLActivity:
[code]public void gotoGoogle() {
runOnUiThread(new Runnable() {
// Note: You must handle UI events like switching views and working with web view in the UI thread.
@Override
public void run() {
layoutSwitcher.showNext();
webview.getSettings().setJavaScriptEnabled(true); // there can be security concerns with javascript.
webview.getSettings().setBuiltInZoomControls(true);
webview.loadUrl("http://www.google.com");
webview.setWebViewClient(new WebViewClient(){
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon){
super.onPageStarted(view, url, favicon);
// just an example of capturing the page start loading event
Log.d("java urho", "Loading: " + url); // goes to the system console/logcat
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
// We will close the webview as soon as the page is loaded... add your own logic !
returnWebviewUrl(url);
hideWebview();
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// This allows us to follow links in the web view without them being deferred to another browser. You may or may not want this.
view.loadUrl(url);
return true;
}
});
}
});
}
public void hideWebview() {
layoutSwitcher.showPrevious();
}[/code]
And one last important declaration so that we can call a c++ function that we will define:
[size=200]Animation Resources:[/size]
Let’s create the animation resources, Android/res/anim/fadein.xml and Android/res/anim/fadeout.xml:
Android/res/anim/fadein.xml:
[code]<?xml version="1.0" encoding="utf-8"?>
<alpha
android:duration="500"
android:fromAlpha="0.0"
android:toAlpha="1.0" >
</alpha>
[/code]
Android/res/anim/fadeout.xml
[code]<?xml version="1.0" encoding="utf-8"?>
<alpha
android:duration="200"
android:fromAlpha="1.0"
android:toAlpha="0.0" >
</alpha>
[/code]
[size=200]C++:[/size]
Thankfully, we don’t have to worry about setting things up on the C++ away from our main source-- everything works from there.
To call gotoGoogle() from C++, we’ll set up a function that looks like this:
[code]#if defined(ANDROID)
#include “Log.h”
#include <SDL.h>
#include <jni.h>
#include <string.h>
#define JAVA_CHECK_EXCEPTION(env, msg)
while (env->ExceptionCheck())
{
LOGDEBUG(String("Exception:: ") + msg));
env->ExceptionDescribe();
env->ExceptionClear();
}
void JavaGotoGoogle()
{
LOGDEBUG(“JavaGotoGoogle()”);
JNIEnv *env = static_cast<JNIEnv*>(SDL_AndroidGetJNIEnv());
jobject activity = static_cast<jobject>(SDL_AndroidGetActivity());
jclass clazz = env->GetObjectClass(activity);
jmethodID gotoGoogle = env->GetMethodID(clazz, "gotoGoogle", "()V");// Note the signature string..
LOGDEBUG(String((int) loginGoogle)); // inspect that we get a handle -- should do error checking..
JAVA_CHECK_EXCEPTION(env, "Preperation"); // Macro to check for errors. Should really check after each call. Calling before the next bit to clear any errors that may happen earlier.
env->CallVoidMethod(activity, gotoGoogle); // the actual call to java.
JAVA_CHECK_EXCEPTION(env, "env->CallVoidMethod(clazz, gotoGoogle);"); //find out if the call went through- if not what were the errors.
env->DeleteLocalRef(activity);
}
[/code]
To define the ‘returnWebviewUrl’ function we called in Java, we do the following:
extern "C" {
JNIEXPORT jstring JNICALL Java_org_libsdl_app_SDLActivity_returnWebviewUrl (JNIEnv *env, jobject obj, jstring url)
{
LOGDEBUG("Java_org_libsdl_app_SDLActivity_returnWebviewUrl()");
const char* result = env->GetStringUTFChars(url, 0);
if (result != NULL)
{
String s = String(result);
LOGDEBUG(s);
}
env->ReleaseStringUTFChars(url, result);
return env->NewStringUTF("Hello from C++ over JNI!");
}
}
#endif
Note the name of the function, it has to follow a strict naming convention for Java to find it. It should also be in the global scope.
And that’s it ! Pretty simple, although you will need to put your own logic in. Also, currently there’s no way to escape the webview if it gets stuck loading-- so you will need to provide your own options (eg, capture the ‘back’ button, provide GUI elements, etc).