CODE: Native Alert with callback for iOS and Android

Attached are the files for I build for doing native alerts for Cocos 3.x for iOS and Android. There are two functions that can be used, one with callback and up to 3 buttons, and one without callback and only 1 button (simplified as a replacement for MessageBox).

To use the simplified version just call:

cocos2d::NativeAlert::show( "Alert Title", "This is the message that shows in the alert.", "Close button" );

To use the callback version, your class must implement a callback function:

void onNativeAlertDismissed( int tag, NativeAlert::ButtonType type );

Then show the alert with:

cocos2d::NativeAlert::showWithCallback("Alert Title", "This is the message that shows in the alert.", "Cancel", "Return", "Other button", 404, CC_CALLBACK_2(MyClass::onNativeAlertDismissed, this) );

Files are in the appropriate folder structure to merge in to your project. You will also need to add CCNativeAlert_Android.cpp to the LOCAL_SRC_FILES in jni/Android.mk

Enjoy!

CCNativeAlert.zip (4.8 KB)

4 Likes

Thanks, cool stuff.
The version of Cocos2d-x I use this with has try/catch disabled. Seems like it’s better to leave it that way. So the alert with no callbacks would not work because it uses try/catch. I guess it will not compile without removing try/catch, and removing try/catch will later crash at runtime on tnf = s_AlertViewIDs.at( alertID ); so I had to replace the try/catch with:

if (s_AlertViewIDs.find(alertID) == s_AlertViewIDs.end() ) {
	// not found, no callback
	CCLOG("No callback");
	return;
}
tnf = s_AlertViewIDs.at( alertID );

To have it work.

By the way, I have run into a serious problem with alerts on Android. I cannot for the life of me figure out what’s wrong. I did have my own similar implementation of the native Android alerts and ran into the problem - thought my implementation was bad somewhere so I tried the above one (CCNativeAlert), but it turns out it (or whatever causes it) suffers from the same problem.

The problem: sometime when tapping “ok”/“return”, literally any other button than “cancel” (cancel is always fine!), the app/dialog freezes. Sometimes the app seems to continue for a while in the background, behind the alert, but the alert itself freezes totally and never closes. The app becomes unusable. I think I saw the app crash sometimes too. What’s going on? Why is the alert crashing when a button that is not cancel is tapped??

Why… why… :sob:

Btw I’m on cocos2d-x 3.6.

I’m suspecting that maybe the Java code does not properly call back in the UI thread or something? How can we make sure that?

Some link about it: http://stackoverflow.com/a/20699389/129202

FIXED IT… I think… happy

This fixes the issue for me. Maybe you all should replace that _alertDidDismiss function. Well I cannot totally explain it myself but it’s something with UI thread and we all now that’s nasty.

	public static void _alertDidDismiss( final String alertID, final int index ) {
		// http://stackoverflow.com/a/20699389/129202
		Cocos2dxGLSurfaceView.getInstance().queueEvent(
				new Runnable() {
			  public void run() {
				  alertDidDismiss( alertID, index);
			  }
			});
	}
2 Likes

Thanks! I too was running in to this threading issue, I worked-around on the c++ side, but prefer your solution. I swapped it in and verified that it also works for me.

I put this on github for easy access.

@hawkwood Thank you for the demo. but the bellow method is never executed as log can’t print for iOS .
void HelloWorld::onNativeAlertDismissed(int tag, NativeAlert::ButtonType type)
{
CCLOG("Tag is %i type is %d ", tag, type);

log("Tag is %i type is %d ", tag, type);

}

What is your code to show the native alert?

i am working on creating a alert box with Label, Textbox/EditText and two buttons …any suggestion for that how to implement this??

Add a fullscreen touch trapping layer on top of your scene.

Can’t we put Textbox on the native dialog box by adding few more lines of code to your created classes.

On the native alertView? You should search http://stackoverflow.com for that.

yes !! i am thinking to modify your code and add a Textbox/edittext .

I have a suggestion/question.

This may be a point of taste for some, but to have your alert interface work consistently across iOS and Android I think we want to make the alerts non-cancellable on Android.

builder1.setCancelable(false);

In my game, on a few occasions I have an alert, which waits for the one “OK” button to be tapped, and then the game flow continues.
On Android it was possible to tap outside the alert to just close it without sending any message back to the app, so that the app got stuck there waiting.

So it’s just easier to disable cancelable alerts altogether… because the alerts on iOS can never be cancelled by just tapping outside. Not as far as I know, anyway.

1 Like

Oh, and I request support for Mac :slight_smile: I might try to look at it myself soon.

Ok here is my Mac version, I haven’t tested it much yet:

//
//  CCNativeAlert_Mac.mm
//
//  Created by Jonny Bergstrom on 2015/08/12.
//
//

#include "CCNativeAlert.h"

NS_CC_BEGIN

void NativeAlert::show( std::string title, std::string message, std::string cancelButtonTitle )
{
	NativeAlert::showWithCallback( title, message, cancelButtonTitle, "", "", 0, nullptr );
}

void NativeAlert::showWithCallback( std::string title, std::string message, std::string cancelButtonTitle, std::string returnButtonTitle, std::string otherButtonTitle, int tag, const std::function<void( int tag, ButtonType type)> &callback )
{
	NSAlert* alert = [[NSAlert alloc] init];
	if (! title.empty() ) {
		NSString *nTitle = [NSString stringWithUTF8String: title.c_str() ];
		[alert setMessageText:nTitle];
	}
	if (! message.empty() ) {
		NSString *nMsg = [NSString stringWithUTF8String: message.c_str() ];
		[alert setInformativeText:nMsg];
	}
	if (! cancelButtonTitle.empty() ) {
		NSString *nCBT = [NSString stringWithUTF8String: cancelButtonTitle.c_str() ];
		NSButton* button = [alert addButtonWithTitle: nCBT];
		button.tag = -1;
	}

	if (! returnButtonTitle.empty() ) {
		NSString *nRBT = [NSString stringWithUTF8String: returnButtonTitle.c_str() ];
		NSButton* button = [alert addButtonWithTitle: nRBT];
		button.tag = 1;
	}

	if (! otherButtonTitle.empty() ) {
		NSString *nOBT = [NSString stringWithUTF8String: otherButtonTitle.c_str() ];
		NSButton* button = [alert addButtonWithTitle: nOBT];
		button.tag = 0;
	}
	
	const NSModalResponse RESPONSE = [alert runModal];
	//CCLOG("RESPONSE: %ld", RESPONSE);
	if (callback) {
		cocos2d::NativeAlert::ButtonType type = (cocos2d::NativeAlert::ButtonType)RESPONSE; //cocos2d::NativeAlert::ButtonType::OTHER;

		callback(RESPONSE, type);
	}
}

void NativeAlert::alertDidDismiss( std::string alertID, int buttonIndex)
{
	// This is an Android method only
}

NS_CC_END

Remember to only add this to the desktop target in Xcode.

I don’t know if anyone noticed this, well, I have.

This is another discrepancy between Android alerts and iOS alerts. It can be very important…
The expected way is like it works on iOS: When tapping on a feature in the game which brings up an alert, the alert pops up directly and there is no time/room for the user to do anything else.
On Android: There is a small “time lag” between the tap action and the alert actually shows. This means that a user with fast fingers can press a button several times and then several alerts can pop up. If you have some action tied to the tapping of a button in the alert, then that could lead to all sorts of trouble later because you only expected one of those alerts to be shown at a time.

Summa summarum, or lesson learned, as we say… platform native alerts aren’t really an excellent way to go. These kind of quirks show up. We had better implement alerts native to the rendering engine itself, this time cocos2d-x…

That is good to know that something to work around, but race conditions are generally a problem with User Interaction design, and Cocos is notorious for causing them. For example, if you have multiple buttons that fire their action on TOUCH_UP_INSIDE, then if a user presses on 2 buttons then releases both, how do you stop the second one from firing it’s action? The answer is “programming”.

Great, thank you!

Hi, now i have i little problem by android, i get error:
java.lang.ClassNotFoundException: org/cocos2dx/lib/Cocos2dxNativeAlert

On iOS it works correctly, with or without callback, on android it crashes.