How to show A Toast Message from Cocos creator ? [Android]

I’m trying to use the native.reflection.callStaticMethod

But I’m getting lost. In order to call a Toast we need the main activity of the Application.
Is this the best approach ?

Is this of native calls should work right ?

if (sys.isMobile)
{
   let cocosActivity = native.reflection.callStaticMethod("com/cocos/game/AppActivity", "getContext", "()Lcom/cocos/game/AppActivity;");
   let toastClass = native.reflection.callStaticMethod("android/widget/Toast", "makeText", "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;", cocosActivity, message, 0);
   native.reflection.callStaticMethod("android/widget/Toast", "show", "()V", toastClass);            
 }
1 Like

Did you define a static method getContext in the AppActivity.java file?

Related documentation links: Cocos Creator 3.8 Manual - JavaScript and Android Communication with Reflection

JS Call Java

import { _decorator, Component, Label, native, Node, sys } from 'cc';
const { ccclass, property } = _decorator;
let tips_global = null;
//@ts-ignore
window.TestNativeCallJs = function () {
    tips_global.string = "The test was successful...";
}

@ccclass('NativeCallCtrl')
export class NativeCallCtrl extends Component {

    @property(Label)
    tips: Label = null;

    start() {
        tips_global = this.tips;
    }

    onClick () {
        let methodName = "showAlertDialog";
        if (sys.os === sys.OS.ANDROID) {
            let className = "com/cocos/game/AppActivity";
            let methodSignature = "(Ljava/lang/String;Ljava/lang/String;)V";
            native.reflection.callStaticMethod(className, methodName, methodSignature, "Title", "Native Call Test is OK");
        }
    }
}

Java Call JS
AppActivity.java:

package com.cocos.game;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.content.Intent;
import android.content.res.Configuration;

import com.cocos.lib.CocosHelper;
import com.cocos.lib.CocosJavascriptJavaBridge;
import com.cocos.service.SDKWrapper;
import com.cocos.lib.CocosActivity;

public class AppActivity extends CocosActivity {

    private static AppActivity app = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        app = this;
        // DO OTHER INITIALIZATION BELOW
        SDKWrapper.shared().init(this);

    }

    //...... Omit unmodified code here

    public static void showAlertDialog(final String title, final String message) {
        app.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                AlertDialog.Builder builder = new AlertDialog.Builder(app);
                builder.setTitle(title);
                builder.setMessage(message);
                builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        CocosHelper.runOnGameThread(new Runnable() {
                            @Override
                            public void run() {
                                CocosJavascriptJavaBridge.evalString("TestNativeCallJs()");
                            }
                        });
                    }
                });
                AlertDialog alertDialog = builder.create();
                alertDialog.show();
            }
        });
    }
}

This is demo:
59480.zip (863.7 KB)

The modified AppActivity.java is in:

native/engine/android/app/src/com/cocos/game
1 Like

Now I updated the iOS platform method:

Related documnentation links: Cocos Creator 3.8 Manual - JavaScript and iOS/macOS Communication with Reflection

JS Call OC:

    onClick () {
        if (sys.os === sys.OS.ANDROID) {
            let className = "com/cocos/game/AppActivity";
            let methodName = "showAlertDialog";
            let methodSignature = "(Ljava/lang/String;Ljava/lang/String;)V";
            native.reflection.callStaticMethod(className, methodName, methodSignature, "Title", "Native Call Test is OK");
        } else if (sys.os === sys.OS.IOS) {
            let className = "AppDelegate";
            let methodName = "showAlertDialog:withMessage:";
            native.reflection.callStaticMethod(className, methodName, "Title", "Native Call Test is OK");
        }
    }

OC Call JS:
AppDelegate.mm:

#import "AppDelegate.h"
#import "ViewController.h"
#import "View.h"

#include "platform/ios/IOSPlatform.h"
#import "platform/ios/AppDelegateBridge.h"
#import "service/SDKWrapper.h"

#import "cocos/bindings/jswrapper/SeApi.h"

@implementation AppDelegate
static ViewController* rootViewController = nullptr;
@synthesize window;
@synthesize appDelegateBridge;

#pragma mark -
#pragma mark Application lifecycle

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[SDKWrapper shared] application:application didFinishLaunchingWithOptions:launchOptions];
    appDelegateBridge = [[AppDelegateBridge alloc] init];
    
    // Add the view controller's view to the window and display.
    CGRect bounds = [[UIScreen mainScreen] bounds];
    self.window   = [[UIWindow alloc] initWithFrame:bounds];

    // Should create view controller first, cc::Application will use it.
    _viewController                           = [[ViewController alloc] init];
    rootViewController = _viewController;
    _viewController.view                      = [[View alloc] initWithFrame:bounds];
    _viewController.view.contentScaleFactor   = UIScreen.mainScreen.scale;
    _viewController.view.multipleTouchEnabled = true;
    [self.window setRootViewController:_viewController];

    [self.window makeKeyAndVisible];
    [appDelegateBridge application:application didFinishLaunchingWithOptions:launchOptions];
    return YES;
}

//...... Omit unmodified code here

+ (void)showAlertDialog:(NSString *)title withMessage:(NSString *)message {
    UIAlertController* alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
        se::ScriptEngine::getInstance()->evalString("TestNativeCallJs()");
    }];
    [alert addAction:defaultAction];
    [rootViewController presentViewController:alert animated:NO completion:nil];
}
//...... Omit unmodified code here

@end

This is demo:
59480_iOS.zip (1.4 MB)

The modified AppDelegate.mm in:

 native/engine/ios
1 Like

Ok I get the idea. We can only make call and get this data, int, float, boolean,String.
No support for native objects.

The example was even better than what I ask.
The toast message should work, like this then.

Java
public static void showToast(String message, int length)
    {
        app.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(app, message, length).show();
            }
        });
    }

TypeScript

public static showToast (message: string, length:number): void
    {
        if (sys.os === sys.OS.ANDROID)
        {
            let className = "com/cocos/game/AppActivity";
            let methodName = "showToast";
            let methodSignature = "(Ljava/lang/String;I)V";
            native.reflection.callStaticMethod(className, methodName, methodSignature, message, length);
        }
    }

But I’m not sure about this I mean yes this is how works. But the AppActivity.java should be manually change every time the native folder is created?. Is there a way to override the AppActivity when generating the Android project ?

There is no need to re-modify AppActivity.java after each build