This is probably off topic, but I have found this forum to be very helpful in answering all manor of questions. I am just finishing up the hybridization of my app for iOS and android. The only piece left is the ability for a user to launch a URL to a web site from the app. On iOS when a user selects the appropriate cocos2d menu item, I simply call:
Which works nicely within a #ifdefAPPLE statement. What I am looking for is the NDK C*+ equivalent on android. From what I can tell from my searching online it requires that I setup a callback mechanism from within java. Which I have less than 0 knowledge of how to do. I know c/c**/obj-c, but nothing about java. Is there a pure c*+ way to do this from within the NDK?
public static void openURL(String url) {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url)); Cocos2dxActivity.context.startActivity;
}
This compiles, but crashes as soon as the button is pressed. I am very unfamiliar with how activities and contexts work within android… Suggestions as to where I am going wrong?
I FINALLY figured it out. For anyone else attempting to do this here is what needs to be changed from above.
Cocos2dxActivity.java:
Add this line to Cocos2dxActivity: private static Activity me = null;
Remove this line from onCreate: Cocos2dxActivity.context = getApplicationContext();
In its place put: me = this;
In openURL remove this line: Cocos2dxActivity.context.startActivity(i);
In its place put: me.startActivity(i);
If anyone wants the full source of the changes, just let me know. Thanks again all for pointing me in the right direction.
thanks for sharing the tutorial Alan Ide,
I try http://digitalsynapsesblog.blogspot.com/ but I got Activity not Found Exception on the Android compiler
any advice ?
The link still works on my end, however since there have been others that are having the same issue accessing the page, here is a copy of it.
Cocos2d-x: Launching a URL on Android...
Since this is my first blog post ever, let me start by introducing myself. My name is Alan, and I am a game programmer, game player, father, son, husband, lover of all things science, and Futurama's/Stargate's biggest fan. I guess thats about it... Now on to the code!
Cocos2d-x is awesome! However, the framework is not designed to do EVERYTHING for you, thus occasionally we have to break out those programming skills, roll our sleeves up, and get dirty. Thankfully, its not very often. I recently found one of these instances when I attempted to open a URL from inside my app while running on an Android device.
Its a bit of a round about process to get from C++ to Java to Android to another App, especially for someone who has been spoiled by how easy it is to do on iOS devices. The very first step is getting your cocos2d-x project up and running on android. I used the following blog post the first time I did it, and it works pretty well.
http://www.supersuraccoon-cocos2d.com/2011/08/10/cocos2d-x-iphone-androidide-installation-and-setup-under-mac-os/
So I assume you have followed the above instructions and made your way back here with a fully functioning "HelloWorld". Let me be the first to congratulate you! What a pain right! Well the good news is that the hard part is over. So lets get back to trying to open a URL on android from inside our cocos2d-x app.
On iOS you would simply call:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithUTF8String:url]]];
However, since we are using the android NDK to interface from our Cocos2d-x project it takes a few more steps. The first step is to open your Eclipse Project you created using the link above. Inside this project you will find "ProjectName/src/org.cocos2dx.lib/Cocos2dxActivity.java". The beginning of that file should look something like this:
/**********Cocos2dxActivity.java**********/
package org.cocos2dx.lib;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
public class Cocos2dxActivity extends Activity{
public static int screenWidth;
public static int screenHeight;
private static Cocos2dxMusic backgroundMusicPlayer;
private static Cocos2dxSound soundPlayer;
private static Cocos2dxAccelerometer accelerometer;
private static boolean accelerometerEnabled = false;
private static Handler handler;
private final static int HANDLER_SHOW_DIALOG = 1;
private static String packageName;
private static native void nativeSetPaths(String apkPath);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get frame size
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
screenWidth = dm.widthPixels;
screenHeight = dm.heightPixels;
accelerometer = new Cocos2dxAccelerometer(this);
// init media player and sound player
backgroundMusicPlayer = new Cocos2dxMusic(this);
soundPlayer = new Cocos2dxSound(this);
handler = new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
case HANDLER_SHOW_DIALOG:
showDialog(((DialogMessage)msg.obj).title, ((DialogMessage)msg.obj).message);
break;
}
}
};
}
//CLASS METHODS REMOVED FROM EXAMPLE TO SINCE THEY DO NOT CHANGE
}
/********************/
Change the class to look like this. The lines denoted in RED are the changes.
/**********Cocos2dxActivity.java**********/
package org.cocos2dx.lib;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
public class Cocos2dxActivity extends Activity{
public static int screenWidth;
public static int screenHeight;
private static Cocos2dxMusic backgroundMusicPlayer;
private static Cocos2dxSound soundPlayer;
private static Cocos2dxAccelerometer accelerometer;
private static boolean accelerometerEnabled = false;
private static Handler handler;
private final static int HANDLER_SHOW_DIALOG = 1;
private static String packageName;
private static Activity me = null;
private static native void nativeSetPaths(String apkPath);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
me = this;
// get frame size
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
screenWidth = dm.widthPixels;
screenHeight = dm.heightPixels;
accelerometer = new Cocos2dxAccelerometer(this);
// init media player and sound player
backgroundMusicPlayer = new Cocos2dxMusic(this);
soundPlayer = new Cocos2dxSound(this);
handler = new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
case HANDLER_SHOW_DIALOG:
showDialog(((DialogMessage)msg.obj).title, ((DialogMessage)msg.obj).message);
break;
}
}
};
}
public static void openURL(String url) {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
me.startActivity(i);
}
//CLASS METHODS REMOVED FROM EXAMPLE TO SINCE THEY DO NOT CHANGE
}
/********************/
Ok... So what did we just do? Well, we created a self reference "me" to the activity created by Cocos2d-x when "onCreate" is called durring the program initialization phase. Then we added a method named "openURL" that will launch a child activity from "me" that sends an intent to the Android OS to parse the URL and open it with the default web browser.
So, why on earth did we go through all of that just to call "startActivity"? Well, thats because "startActivity" is not a static method, and dealing with static methods from the NDK is WAY easier than dealing with non static methods. Thus creating a reference to "me" is the next best thing. Kind of a hack, but an effective one.
So thats all the Java we need to change. Lets move on to the C++ side. Go to your iOS/Android hybrid project root and open "ProjectName/libs/cocos2dx/platform/android/Cocos2dJni.h" and add the following line to the list of method declarations:
void openURLJNI(const char* url);
Now open "ProjectName/libs/cocos2dx/platform/android/Cocos2dJni.cpp" and add the following method.
void openURLJNI(const char* url)
{
TMethodJNI t;
if (getMethodID(t
, "org/cocos2dx/lib/Cocos2dxActivity"
, "openURL"
, "(Ljava/lang/String;)V"))
{
jstring StringArg1 = t.env->NewStringUTF(url);
t.env->CallStaticVoidMethod(t.classID, t.methodID, StringArg1);
}
}
So what does that do? Well, its just a wrapper for calling the Java function we created earlier from within our C++ project. Now anywhere you want to open a URL you simply call openURLJNI("Http://someplace.com"); Please remember to use the full HTTP:// path when opening your URL since Android is REALLY picky about that.
Obviously that command will NOT work on iOS devices. So my wrapper function looks like this.
void PlatformResolver::openURL(const char* url)
{
#ifdef __APPLE__
PlatformResolver_OBJC::openURL(url);
#endif
#ifdef ANDROID
openURLJNI(url);
#endif
}
Thats it! You can now launch URL links form within your Cocos2d-x application. YAY! Questions, comments, concerns, drop a note below. Thanks!
There is no Cocos2dJni.h file in the last versions of the Cocos2d-x SDK (.10 and .11). Can anybody explain how to proceed or could it possible to update the tutorial ? Thanks !
Sorry, I have not worked with the newer version of Cocos2D, but I downloaded the source this morning and it looks like they have separated the JNI functions into individual files now. Take a look at “cocos2dx/platform/android/jni” in there you will find a shared “JniHelper class” and a series of JNI based files like MessageJni, and SystemInfoJni. You should be able to create a new .h and .cpp using one of these as a template that will encapsulate the functions I created above.
I assume the Java section is still pretty much the same, but I cannot verify at this time as I do not have eclipse installed on this machine. If you still cant figure it out, please let me know and I will try to port this later this week if I can find the time. Sorry I can’t be more help at the moment. Good luck!
Hey guys… I’ve been new to cocos2d-x… Wanna use jni to send mail in Android. Can any 1 give a step by step tutorial for that?
I know android and cocos2d for iOS. But not able to figure how to connect jni files.
If you follow Alan’s instructions for the java part then do the following. #1) Create UrlJni.h && UrlJni.cpp in ‘cocos2dx/platform/android/jni’ #2) Make sure to add the .cpp to the make file. (this got in my way for a moment)
cocos2dx/Android.mk : add ‘platform/android/jni/UrlJni.cpp below the other jni methods (’platform/android/jni/TouchesJni.cpp’) #3) Add this to your UrlJni.h file:
Michael Developer &Alan Ide … Thank You so much for your help, I followed your steps and it is working perfectly.
I have some additional questions I wanted to ask. Can I in the same way display dialog alert or toast message of android??
I wrote this code:
public static void openURL(String url) {
Toast.makeText(me,url,Toast.LENGTH_LONG).show();
}
But its crashing with error: Can’t create handler with thread. Do you know how can I display it correctly?
Thanks once again …
To be completely honest, I have not touched any android code in many months. (I got to go back to my happy place working with platform agnostic OpenGL/C++). So, I would probably less than helpful in answering this question. Just didnt want you to think I ignored you. Anyone else able to answer it?