Useful modifications to the android port (ogles-branch)

Discuss about anything related to the Irrlicht Engine, or read announcements about any significant features or usage changes.
Post Reply
porcus
Posts: 149
Joined: Sun May 27, 2007 6:24 pm
Location: Germany

Useful modifications to the android port (ogles-branch)

Post by porcus »

To make it possible to close the device via closeDevice replace the current implementation with:

Code: Select all

 
void CIrrDeviceAndroid::closeDevice()
{
    ANativeActivity_finish(Android->activity);
}
 
To use a external keyboard or the soft keyboard (you can make it visible via JNI: http://irrlicht.sourceforge.net/forum/v ... =9&t=49242), insert the following code into the CIrrDeviceAndroid::handleInput Method:
(Unfortunately I wasn't able to map all android keycodes to irrlicht keycodes but it works acceptable now.)
(The return button will not exit the app anymore, a KEY_CANCEL Event is issued instead.)

Code: Select all

 
    if( AInputEvent_getType( androidEvent ) == AINPUT_EVENT_TYPE_KEY )
    {
        SEvent irrEvent;
        irrEvent.EventType = EET_KEY_INPUT_EVENT;
        s32 action = AKeyEvent_getAction(androidEvent);
        s32 meta = AKeyEvent_getMetaState(androidEvent);
        irrEvent.KeyInput.Char = 0;
        irrEvent.KeyInput.Control = false;//TODO: Control
        irrEvent.KeyInput.Shift = (meta & AMETA_SHIFT_ON)!=0;
        irrEvent.KeyInput.PressedDown = (action==AKEY_EVENT_ACTION_DOWN);//AKEY_EVENT_ACTION_UP
        s32 key = AKeyEvent_getKeyCode(androidEvent);
/*TODO:
    AKEYCODE_UNKNOWN         = 0,
    AKEYCODE_SOFT_LEFT       = 1,
    AKEYCODE_SOFT_RIGHT      = 2,
    AKEYCODE_CALL            = 5,
    AKEYCODE_ENDCALL         = 6,
    AKEYCODE_DPAD_CENTER     = 23,
    AKEYCODE_VOLUME_UP       = 24,
    AKEYCODE_VOLUME_DOWN     = 25,
    AKEYCODE_POWER           = 26,
    AKEYCODE_CAMERA          = 27,
    AKEYCODE_CLEAR           = 28,
    AKEYCODE_SYM             = 63,
    AKEYCODE_EXPLORER        = 64,
    AKEYCODE_ENVELOPE        = 65,
    AKEYCODE_HEADSETHOOK     = 79,
    AKEYCODE_FOCUS           = 80,   // *Camera* focus
    AKEYCODE_NOTIFICATION    = 83,
    AKEYCODE_SEARCH          = 84,
    AKEYCODE_MEDIA_STOP      = 86,
    AKEYCODE_MEDIA_NEXT      = 87,
    AKEYCODE_MEDIA_PREVIOUS  = 88,
    AKEYCODE_MEDIA_REWIND    = 89,
    AKEYCODE_MEDIA_FAST_FORWARD = 90,
    AKEYCODE_MUTE            = 91,
    AKEYCODE_PICTSYMBOLS     = 94,
    AKEYCODE_SWITCH_CHARSET  = 95,
    AKEYCODE_BUTTON_A        = 96,
    AKEYCODE_BUTTON_B        = 97,
    AKEYCODE_BUTTON_C        = 98,
    AKEYCODE_BUTTON_X        = 99,
    AKEYCODE_BUTTON_Y        = 100,
    AKEYCODE_BUTTON_Z        = 101,
    AKEYCODE_BUTTON_L1       = 102,
    AKEYCODE_BUTTON_R1       = 103,
    AKEYCODE_BUTTON_L2       = 104,
    AKEYCODE_BUTTON_R2       = 105,
    AKEYCODE_BUTTON_THUMBL   = 106,
    AKEYCODE_BUTTON_THUMBR   = 107,
    AKEYCODE_BUTTON_START    = 108,
    AKEYCODE_BUTTON_SELECT   = 109,
    AKEYCODE_BUTTON_MODE     = 110,
    AKEYCODE_NUM             = 78,
*/
        if(key==AKEYCODE_HOME){
            irrEvent.KeyInput.Key = KEY_HOME;
        }else if(key==AKEYCODE_BACK){//the back button will not exit the app anymore, KEY_CANCEL makes sense to me
            irrEvent.KeyInput.Key = KEY_CANCEL;//KEY_BACK;
        }else if(key>=AKEYCODE_0 && key<=AKEYCODE_9){
            irrEvent.KeyInput.Key = (EKEY_CODE)(key-AKEYCODE_0+KEY_KEY_0);
            if(!irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = (wchar_t)(key-AKEYCODE_0)+L'0';
            }else{
                if(key == AKEYCODE_2){
                    irrEvent.KeyInput.Char = L'@';
                }else if(key == AKEYCODE_1){
                    irrEvent.KeyInput.Char = L'!';
                }else if(key == AKEYCODE_3){
                    irrEvent.KeyInput.Char = L'#';
                }else if(key == AKEYCODE_4){
                    irrEvent.KeyInput.Char = L'$';
                }else if(key == AKEYCODE_5){
                    irrEvent.KeyInput.Char = L'%';
                }else if(key == AKEYCODE_6){
                    irrEvent.KeyInput.Char = L'^';
                }else if(key == AKEYCODE_7){
                    irrEvent.KeyInput.Char = L'&';
                }else if(key == AKEYCODE_8){
                    irrEvent.KeyInput.Char = L'*';
                }else if(key == AKEYCODE_9){
                    irrEvent.KeyInput.Char = L'(';
                }else if(key == AKEYCODE_0){
                    irrEvent.KeyInput.Char = L')';
                }
            }
        }else if(key==AKEYCODE_STAR){
            irrEvent.KeyInput.Key = KEY_KEY_8 ;//US Keyboard
            irrEvent.KeyInput.Char = L'*';
        }else if(key==AKEYCODE_POUND){
            irrEvent.KeyInput.Key = KEY_KEY_3;//British Keyboard
            irrEvent.KeyInput.Char = L'£';
        }else if(key==AKEYCODE_DPAD_UP){
            irrEvent.KeyInput.Key = KEY_UP;
        }else if(key==AKEYCODE_DPAD_DOWN){
            irrEvent.KeyInput.Key = KEY_DOWN;
        }else if(key==AKEYCODE_DPAD_LEFT){
            irrEvent.KeyInput.Key = KEY_LEFT;
        }else if(key==AKEYCODE_DPAD_RIGHT){
            irrEvent.KeyInput.Key = KEY_RIGHT;
        }else if(key>=AKEYCODE_A && key<=AKEYCODE_Z){
            irrEvent.KeyInput.Key = (EKEY_CODE)(key-AKEYCODE_A+KEY_KEY_A);
            if(!irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = (wchar_t)(key-AKEYCODE_A)+L'a';
            }else{
                irrEvent.KeyInput.Char = (wchar_t)(key-AKEYCODE_A)+L'A';
            }
        }else if(key==AKEYCODE_COMMA){
            irrEvent.KeyInput.Key = KEY_COMMA;
            if(irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = L'<';
            }else{
                irrEvent.KeyInput.Char = L',';
            }
        }else if(key==AKEYCODE_PERIOD){
            irrEvent.KeyInput.Key = KEY_PERIOD;
            if(irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = L'>';
            }else{
                irrEvent.KeyInput.Char = L'.';
            }
        }else if(key==AKEYCODE_ALT_LEFT){
            irrEvent.KeyInput.Key = KEY_LMENU;
        }else if(key==AKEYCODE_ALT_RIGHT){
            irrEvent.KeyInput.Key = KEY_RMENU;
        }else if(key==AKEYCODE_SHIFT_LEFT){
            irrEvent.KeyInput.Key = KEY_LSHIFT;
        }else if(key==AKEYCODE_SHIFT_RIGHT){
            irrEvent.KeyInput.Key = KEY_RSHIFT;
        }else if(key==AKEYCODE_TAB){
            irrEvent.KeyInput.Key = KEY_TAB;
            irrEvent.KeyInput.Char = L'\t';
        }else if(key==AKEYCODE_SPACE){
            irrEvent.KeyInput.Key = KEY_SPACE;
            irrEvent.KeyInput.Char = L' ';
        }else if(key==AKEYCODE_ENTER){
            irrEvent.KeyInput.Key = KEY_RETURN;
            irrEvent.KeyInput.Char = L'\n';
        }else if(key==AKEYCODE_DEL){
            irrEvent.KeyInput.Key = KEY_BACK;
        }else if(key==AKEYCODE_MINUS){
            irrEvent.KeyInput.Key = KEY_MINUS;
            if(irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = L'_';
            }else{
                irrEvent.KeyInput.Char = L'-';
            }
        }else if(key==AKEYCODE_EQUALS){
            irrEvent.KeyInput.Key = KEY_PLUS;//US Keyboard
            if(irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = L'+';
            }else{
                irrEvent.KeyInput.Char = L'=';
            }
        }else if(key==AKEYCODE_LEFT_BRACKET){
            irrEvent.KeyInput.Key = KEY_OEM_4;//US Keyboard
            if(irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = L'{';
            }else{
                irrEvent.KeyInput.Char = L'[';
            }
        }else if(key==AKEYCODE_RIGHT_BRACKET){
            irrEvent.KeyInput.Key = KEY_OEM_6;//US Keyboard
            if(irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = L'}';
            }else{
                irrEvent.KeyInput.Char = L']';
            }
        }else if(key==AKEYCODE_BACKSLASH){
            irrEvent.KeyInput.Key = KEY_OEM_5;//US Keyboard
            if(irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = L'|';
            }else{
                irrEvent.KeyInput.Char = L'\\';
            }
        }else if(key==AKEYCODE_SEMICOLON){
            irrEvent.KeyInput.Key = KEY_OEM_1;//US Keyboard
            if(irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = L':';
            }else{
                irrEvent.KeyInput.Char = L';';
            }
        }else if(key==AKEYCODE_APOSTROPHE){
            irrEvent.KeyInput.Key = KEY_OEM_7;//US Keyboard
            if(irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = L'\"';
            }else{
                irrEvent.KeyInput.Char = L'\'';
            }
        }else if(key==AKEYCODE_SLASH){
            irrEvent.KeyInput.Key = KEY_OEM_2;//US Keyboard
            if(irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = L'?';
            }else{
                irrEvent.KeyInput.Char = L'/';
            }
        }else if(key==AKEYCODE_AT){
            irrEvent.KeyInput.Key = KEY_KEY_2;//US Keyboard
            irrEvent.KeyInput.Char = L'@';
        }else if(key==AKEYCODE_PLUS){
            irrEvent.KeyInput.Key = KEY_PLUS;
            irrEvent.KeyInput.Char = L'+';
        }else if(key==AKEYCODE_MENU){//Menubutton of the unhidable toolbar
            irrEvent.KeyInput.Key = KEY_MENU;
        }else if(key==AKEYCODE_MEDIA_PLAY_PAUSE){
            irrEvent.KeyInput.Key = KEY_PLAY;//hmmm
        }else if(key==AKEYCODE_PAGE_UP){
            irrEvent.KeyInput.Key = KEY_PRIOR;
        }else if(key==AKEYCODE_PAGE_DOWN){
            irrEvent.KeyInput.Key = KEY_NEXT;
        }else if(key==AKEYCODE_GRAVE){
            irrEvent.KeyInput.Key = KEY_OEM_3;//US Keyboard
            if(irrEvent.KeyInput.Shift){
                irrEvent.KeyInput.Char = L'~';
            }else{
                irrEvent.KeyInput.Char = L'`';
            }
        }else{
            //__android_log_print(ANDROID_LOG_ERROR, "Unhandled Key", "Code: %i Shift: %i\n", key, (int)irrEvent.KeyInput.Shift);
            return 0;
        }
 
        //__android_log_print(ANDROID_LOG_ERROR, "Key", "Code: %i Shift: %i\n", key, (int)irrEvent.KeyInput.Shift);
        Device->postEventFromUser(irrEvent);
        return 1;
    }
 
In my opinion we should also issue MouseEvents, not only MultitouchEvents, which would make it easier to port an existing project to android.
Currently the GUI works with MouseEvents so we can't use the gui without Mouseevents.
Btw you can also connect a real mouse via USB. So it would make sense to support MouseEvents as well.
The following code is replacing the corresponding part in CIrrDeviceAndroid::handleInput:
(I've tested it with touch and a real usb mouse.)

Code: Select all

 
if (AInputEvent_getType(androidEvent) == AINPUT_EVENT_TYPE_MOTION)
    {
        SEvent Event;
        s32 PointerCount = AMotionEvent_getPointerCount(androidEvent);
        s32 EventAction = AMotionEvent_getAction(androidEvent);
 
        bool MultiTouchEvent = true;
        bool Touched = false;
 
        #ifdef _IRR_ANDROID_MOUSE_COMPATIBILITY
        SEvent mouseEvent;
        bool issueMouseEvent = false;
        #endif
 
        switch (EventAction)
        {
        case AMOTION_EVENT_ACTION_DOWN:
            #ifdef _IRR_ANDROID_MOUSE_COMPATIBILITY
            mouseEvent.EventType = EET_MOUSE_INPUT_EVENT;
            mouseEvent.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
            Device->ButtonStates = Device->ButtonStates | EMBSM_LEFT;
            issueMouseEvent = true;
            #endif
            Event.MultiTouchInput.Event = EMTIE_PRESSED_DOWN;
            Touched = true;
            break;
        case AMOTION_EVENT_ACTION_MOVE:
            #ifdef _IRR_ANDROID_MOUSE_COMPATIBILITY
            mouseEvent.EventType = EET_MOUSE_INPUT_EVENT;
            mouseEvent.MouseInput.Event = EMIE_MOUSE_MOVED;
            issueMouseEvent = true;
            #endif
            Event.MultiTouchInput.Event = EMTIE_MOVED;
            Touched = true;
            break;
        case AMOTION_EVENT_ACTION_UP:
            #ifdef _IRR_ANDROID_MOUSE_COMPATIBILITY
            mouseEvent.EventType = EET_MOUSE_INPUT_EVENT;
            mouseEvent.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
            Device->ButtonStates = Device->ButtonStates & (~EMBSM_LEFT);
            issueMouseEvent = true;
            #endif
            Event.MultiTouchInput.Event = EMTIE_LEFT_UP;                    
            break;
        default:
            MultiTouchEvent = false;
            break;
        }
 
        #ifdef _IRR_ANDROID_MOUSE_COMPATIBILITY
        if(issueMouseEvent){
            mouseEvent.MouseInput.X = AMotionEvent_getX(androidEvent, 0);
            mouseEvent.MouseInput.Y = AMotionEvent_getY(androidEvent, 0);
            mouseEvent.MouseInput.ButtonStates = Device->ButtonStates;
            mouseEvent.MouseInput.Control = false;//TODO
            s32 meta = AMotionEvent_getMetaState(androidEvent);
            mouseEvent.MouseInput.Shift = (meta & AMETA_SHIFT_ON)!=0;//TODO: shift does not work
            mouseEvent.MouseInput.Wheel = 0.0f;//TODO
            Device->postEventFromUser(mouseEvent);
            //__android_log_print(ANDROID_LOG_ERROR, "Shift", "meta: %i, Shift: %i\n", meta, (int)mouseEvent.MouseInput.Shift);
        }
        #endif
 
        if (MultiTouchEvent)
        {
            Event.EventType = EET_MULTI_TOUCH_EVENT;
            Event.MultiTouchInput.clear();
 
            for (s32 i = 0; i < PointerCount; ++i)
            {
                if (i >= NUMBER_OF_MULTI_TOUCHES)
                    break;
 
                Event.MultiTouchInput.PrevX[i] = 0; // TODO
                Event.MultiTouchInput.PrevY[i] = 0; // TODO
                Event.MultiTouchInput.X[i] = AMotionEvent_getX(androidEvent, i);
                Event.MultiTouchInput.Y[i] = AMotionEvent_getY(androidEvent, i);
 
                Event.MultiTouchInput.Touched[i] = Touched;
            }
    
            Device->postEventFromUser(Event);
 
            Status = 1;
        }
    }
 
    return Status;
 
This field must be placed in CIrrDeviceAndroid.h:

Code: Select all

 
        s32 ButtonStates;//modified by down and release events
 
Don't forget to add #define _IRR_ANDROID_MOUSE_COMPATIBILITY to the IrrCompileConfig.h.


In CFileSystem.cpp and CGUIFileOpenDialog.cpp were some ifdefs missing for android, so it was impossible explore the filesystem, see download link below.
(Although android uses wchar filesystems the wchar filesystem functions are missing. So currently only non wchar filenames and foldernames are supported.)


Here you can download the modified files: https://dl.dropboxusercontent.com/u/503 ... les.tar.gz (CIrrDeviceAndroid.h|cpp; IrrCompileConfig.h; CFileSystem.cpp and CGUIFileOpenDialog.cpp)

And here's the modified version of the UserInterface example which I've used for testing (don't forget to copy the media files in assets/media):

Code: Select all

 
#include <irrlicht.h>
#if defined(_IRR_ANDROID_PLATFORM_)
    #include <android_native_app_glue.h>
    #include <android/log.h>
    #include <android/native_window.h>
 
//#include <EGL/egl.h>
//#include <GLES/gl.h>
 
#endif
 
using namespace irr;
 
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
 
 
struct SAppContext
{
    IrrlichtDevice *device;
    s32             counter;
    IGUIListBox*    listbox;
};
 
// Define some values that we'll use to identify individual GUI controls.
enum
{
    GUI_ID_QUIT_BUTTON = 101,
    GUI_ID_NEW_WINDOW_BUTTON,
    GUI_ID_FILE_OPEN_BUTTON,
    GUI_ID_TRANSPARENCY_SCROLL_BAR
};
 
void setSkinTransparency(s32 alpha, irr::gui::IGUISkin * skin)
{
    for (s32 i=0; i<irr::gui::EGDC_COUNT ; ++i)
    {
        video::SColor col = skin->getColor((EGUI_DEFAULT_COLOR)i);
        col.setAlpha(alpha);
        skin->setColor((EGUI_DEFAULT_COLOR)i, col);
    }
}
 
class MyEventReceiver : public IEventReceiver
{
public:
    MyEventReceiver(SAppContext & context) : Context(context) { }
 
    virtual bool OnEvent(const SEvent& event)
    {
        if (event.EventType == EET_USER_EVENT){
            __android_log_print(ANDROID_LOG_ERROR, "Reset", "Reset Event");
            //Context.device->getGUIEnvironment()->addImage(Context.device->getVideoDriver()->getTexture("media/irrlichtlogo2.png"), position2d<int>(10,10));
        }else if (event.EventType == EET_GUI_EVENT){
            s32 id = event.GUIEvent.Caller->getID();
            IGUIEnvironment* env = Context.device->getGUIEnvironment();
 
            switch(event.GUIEvent.EventType)
            {
 
            case EGET_SCROLL_BAR_CHANGED:
                if (id == GUI_ID_TRANSPARENCY_SCROLL_BAR)
                {
                    s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
                    setSkinTransparency(pos, env->getSkin());
                }
                break;
 
            case EGET_BUTTON_CLICKED:
                switch(id)
                {
                case GUI_ID_QUIT_BUTTON:
                    Context.device->closeDevice();
                    return true;
 
                case GUI_ID_NEW_WINDOW_BUTTON:
                    {
                    Context.listbox->addItem(L"Window created");
                    Context.counter += 30;
                    if (Context.counter > 200)
                        Context.counter = 0;
 
                    IGUIWindow* window = env->addWindow(
                        rect<s32>(100 + Context.counter, 100 + Context.counter, 300 + Context.counter, 200 + Context.counter),
                        false, // modal?
                        L"Test window");
 
                    env->addStaticText(L"Please close me",
                        rect<s32>(35,35,140,50),
                        true, // border?
                        false, // wordwrap?
                        window);
                    }
                    return true;
 
                case GUI_ID_FILE_OPEN_BUTTON:
                    Context.listbox->addItem(L"File open");
                    //Context.device->getFileSystem()->changeWorkingDirectoryTo("/");
                    env->addFileOpenDialog(L"Please choose a file.", true, 0, -1, true);
                    return true;
 
                default:
                    return false;
                }
                break;
 
            case EGET_FILE_SELECTED:
                {
                    // show the model filename, selected in the file dialog
                    IGUIFileOpenDialog* dialog =
                        (IGUIFileOpenDialog*)event.GUIEvent.Caller;
                    Context.listbox->addItem(dialog->getFileName());
                }
                break;
 
            default:
                break;
            }
        }
 
        return false;
    }
 
private:
    SAppContext & Context;
};
 
#ifdef _IRR_ANDROID_PLATFORM_
//from http://stackoverflow.com/questions/5864790/how-to-show-the-soft-keyboard-on-native-activity
void displayKeyboard(bool pShow, android_app* mApplication, JNIEnv* lJNIEnv) {
    jint lFlags = 0;
 
    // Retrieves NativeActivity.
    jobject lNativeActivity = mApplication->activity->clazz;
    jclass ClassNativeActivity = lJNIEnv->GetObjectClass(lNativeActivity);
 
    // Retrieves Context.INPUT_METHOD_SERVICE.
    jclass ClassContext = lJNIEnv->FindClass("android/content/Context");
    jfieldID FieldINPUT_METHOD_SERVICE = lJNIEnv->GetStaticFieldID(ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;");
    jobject INPUT_METHOD_SERVICE = lJNIEnv->GetStaticObjectField(ClassContext, FieldINPUT_METHOD_SERVICE);
    //jniCheck(INPUT_METHOD_SERVICE);
 
    // Runs getSystemService(Context.INPUT_METHOD_SERVICE).
    jclass ClassInputMethodManager = lJNIEnv->FindClass("android/view/inputmethod/InputMethodManager");
    jmethodID MethodGetSystemService = lJNIEnv->GetMethodID(ClassNativeActivity, "getSystemService","(Ljava/lang/String;)Ljava/lang/Object;");
    jobject lInputMethodManager = lJNIEnv->CallObjectMethod(lNativeActivity, MethodGetSystemService,INPUT_METHOD_SERVICE);
 
    // Runs getWindow().getDecorView().
    jmethodID MethodGetWindow = lJNIEnv->GetMethodID(ClassNativeActivity, "getWindow","()Landroid/view/Window;");
    jobject lWindow = lJNIEnv->CallObjectMethod(lNativeActivity,MethodGetWindow);
    jclass ClassWindow = lJNIEnv->FindClass("android/view/Window");
    jmethodID MethodGetDecorView = lJNIEnv->GetMethodID(ClassWindow, "getDecorView", "()Landroid/view/View;");
    jobject lDecorView = lJNIEnv->CallObjectMethod(lWindow,MethodGetDecorView);
 
    if (pShow) {
        // Runs lInputMethodManager.showSoftInput(...).
        jmethodID MethodShowSoftInput = lJNIEnv->GetMethodID(ClassInputMethodManager, "showSoftInput","(Landroid/view/View;I)Z");
        jboolean lResult = lJNIEnv->CallBooleanMethod(lInputMethodManager, MethodShowSoftInput,lDecorView, lFlags);
    } else {
        // Runs lWindow.getViewToken()
        jclass ClassView = lJNIEnv->FindClass("android/view/View");
        jmethodID MethodGetWindowToken = lJNIEnv->GetMethodID(ClassView, "getWindowToken", "()Landroid/os/IBinder;");
        jobject lBinder = lJNIEnv->CallObjectMethod(lDecorView,MethodGetWindowToken);
 
        // lInputMethodManager.hideSoftInput(...).
        jmethodID MethodHideSoftInput = lJNIEnv->GetMethodID(ClassInputMethodManager, "hideSoftInputFromWindow","(Landroid/os/IBinder;I)Z");
        jboolean lRes = lJNIEnv->CallBooleanMethod(lInputMethodManager, MethodHideSoftInput,lBinder, lFlags);
    }
}
 
void getAndroidScreenSize(int& dwidth, int& dheight, JNIEnv* jnienv, android_app* app){
    jclass CActivity = jnienv->FindClass("android/app/Activity");
    jmethodID MgetWindowManager = jnienv->GetMethodID(CActivity, "getWindowManager", "()Landroid/view/WindowManager;");
    jclass CWindowManager = jnienv->FindClass("android/view/WindowManager");
    jmethodID MgetDefaultDisplay = jnienv->GetMethodID(CWindowManager, "getDefaultDisplay", "()Landroid/view/Display;");
    jclass CDisplay = jnienv->FindClass("android/view/Display");
    jmethodID MgetSize = jnienv->GetMethodID(CDisplay, "getSize", "(Landroid/graphics/Point;)V");
    jclass CPoint = jnienv->FindClass("android/graphics/Point");
    jmethodID MPointNew = jnienv->GetMethodID(CPoint, "<init>", "()V");
    jfieldID FPointX = jnienv->GetFieldID(CPoint, "x", "I");
    jfieldID FPointY = jnienv->GetFieldID(CPoint, "y", "I");
    jobject display = jnienv->CallObjectMethod(jnienv->CallObjectMethod(app->activity->clazz, MgetWindowManager), MgetDefaultDisplay);
    jobject point = jnienv->NewObject(CPoint, MPointNew);
    jnienv->CallVoidMethod(display, MgetSize, point);
    dwidth = (int)(jnienv->GetIntField(point, FPointX));
    dheight = (int)(jnienv->GetIntField(point, FPointY));
}
 
#endif
 
#ifndef _IRR_ANDROID_PLATFORM_
int main(int argc, char *argv[])
#else
void android_main(android_app* app)
#endif
{
 
#if defined(_IRR_ANDROID_PLATFORM_)
    JNIEnv *jnienv = NULL;
    JavaVM *jvm = app->activity->vm;
    //jint rc = jvm->AttachCurrentThread(&jnienv, NULL);
    JavaVMAttachArgs lJavaVMAttachArgs;
    lJavaVMAttachArgs.version = JNI_VERSION_1_6;
    lJavaVMAttachArgs.name = "NativeThread";
    lJavaVMAttachArgs.group = NULL;
    jvm->AttachCurrentThread(&jnienv, &lJavaVMAttachArgs);
    jobject activity = app->activity->clazz;
    app_dummy();
 
    int dwidth, dheight;
    getAndroidScreenSize(dwidth, dheight, jnienv, app);
    __android_log_print(ANDROID_LOG_ERROR, "Screen Width, Height: ", "%i, %i\n", dwidth, dheight);
 
    #if defined(_DEBUG)
    __android_log_print(ANDROID_LOG_ERROR, "Debug", "Debug mode");
    #endif
 
//__android_log_print(ANDROID_LOG_ERROR, "Test", "window: %i\n", app->window?1:0);//leider 0
 
//getCurrentSizeRange erst ab API Level 16
/*
    jmethodID MgetCurrentSizeRange = jnienv->GetMethodID(CDisplay, "getCurrentSizeRange", "(Landroid/graphics/Point;Landroid/graphics/Point;)V");
    jobject point2 = jnienv->NewObject(CPoint, MPointNew);
    __android_log_print(ANDROID_LOG_ERROR, "Test3", "%i, %i, %i\n", point?1:0, point2?1:0, MgetCurrentSizeRange?1:0);
    jnienv->CallVoidMethod(display, MgetCurrentSizeRange, point, point2);
    dwidth = (int)(jnienv->GetIntField(point, FPointX));
    dheight = (int)(jnienv->GetIntField(point, FPointY));
    int dwidth2 = (int)(jnienv->GetIntField(point2, FPointX));
    int dheight2 = (int)(jnienv->GetIntField(point2, FPointY));
    __android_log_print(ANDROID_LOG_ERROR, "Size Range: ", " Width: %i, %i Height: %i, %i\n", dwidth, dwidth2, dheight, dheight2);
*/
 
//curfocus gibts wohl nicht
/*
    jmethodID MgetCurrentFocus = jnienv->GetMethodID(CActivity, "getCurrentFocus", "()Landroid/view/View;");
    jclass CView = jnienv->FindClass("android/view/View");
    jmethodID MgetWidth = jnienv->GetMethodID(CView, "getWidth", "()I");
    jmethodID MgetHeight = jnienv->GetMethodID(CView, "getHeight", "()I");
    jobject curfocus = jnienv->CallObjectMethod(activity, MgetCurrentFocus);
    __android_log_print(ANDROID_LOG_ERROR, "Test2: ", "%i, %i, %i, %i, %i, %i\n", MgetCurrentFocus?1:0, CView?1:0, MgetWidth?1:0, MgetHeight?1:0, curfocus?1:0, app->window?1:0);
    int vwidth = (int)jnienv->CallObjectMethod(curfocus, MgetWidth);
    int vheight = (int)jnienv->CallObjectMethod(curfocus, MgetHeight);
    __android_log_print(ANDROID_LOG_ERROR, "View Width, Height: ", "%i, %i\n", vwidth, vheight);
*/
    //ANativeActivity_showSoftInput(app->activity, 1);
#endif
    SIrrlichtCreationParameters param;
    param.DriverType = EDT_OGLES2;
#if defined(_IRR_ANDROID_PLATFORM_)
    param.PrivateData = app;
    param.WindowSize = dimension2d<u32>(0,0);//dwidth>dheight?dwidth:dheight, dwidth>dheight?dheight:dwidth);
#else
    param.WindowSize = dimension2d<u32>(800,450);
#endif
    param.Bits = 24;
        param.ZBufferBits = 16;
    param.AntiAlias  = 0;
 
    IrrlichtDevice* device = createDeviceEx(param);
 
    if (device == 0) // could not create selected driver.
#if defined(_IRR_ANDROID_PLATFORM_)
        return;
#else
        return 1;
#endif
 
    device->setWindowCaption(L"Irrlicht Engine - User Interface Demo");
    device->setResizable(true);
 
    video::IVideoDriver* driver = device->getVideoDriver();
 
#if defined(_IRR_ANDROID_PLATFORM_)
    dimension2d<u32> size = driver->getScreenSize();
    __android_log_print(ANDROID_LOG_ERROR, "Render Window Width, Height: ", "%i, %i\n", size.Width, size.Height);
#endif
 
    IGUIEnvironment* env = device->getGUIEnvironment();
    ISceneManager* smgr = device->getSceneManager();
 
    IGUISkin* skin = env->getSkin();
    IGUIFont* font = env->getFont("media/fonthaettenschweiler.bmp");
    if (font)
        skin->setFont(font);
 
    skin->setFont(env->getBuiltInFont(), EGDF_TOOLTIP); 
 
    env->addButton(rect<s32>(10,240,110,240 + 32), 0, GUI_ID_QUIT_BUTTON,
            L"Quit", L"Exits Program");
    env->addButton(rect<s32>(10,280,110,280 + 32), 0, GUI_ID_NEW_WINDOW_BUTTON,
            L"New Window", L"Launches a new Window");
    env->addButton(rect<s32>(10,320,110,320 + 32), 0, GUI_ID_FILE_OPEN_BUTTON,
            L"File Open", L"Opens a file");
 
    env->addStaticText(L"Transparent Control:", rect<s32>(150,20,350,40), true);
    IGUIScrollBar* scrollbar = env->addScrollBar(true,
            rect<s32>(150, 45, 350, 60), 0, GUI_ID_TRANSPARENCY_SCROLL_BAR);
    scrollbar->setMax(255);
    scrollbar->setPos(255);
    setSkinTransparency( scrollbar->getPos(), env->getSkin());
 
    // set scrollbar position to alpha value of an arbitrary element
    scrollbar->setPos(env->getSkin()->getColor(EGDC_WINDOW).getAlpha());
 
    env->addStaticText(L"Logging ListBox:", rect<s32>(50,110,250,130), true);
    IGUIListBox * listbox = env->addListBox(rect<s32>(50, 140, 250, 210));
    env->addEditBox(L"Editable Text", rect<s32>(350, 80, 550, 100));
 
    // Store the appropriate data in a context structure.
    SAppContext context;
    context.device = device;
    context.counter = 0;
    context.listbox = listbox;
 
    // Then create the event receiver, giving it that context structure.
    MyEventReceiver receiver(context);
 
    // And tell the device to use our custom event receiver.
    device->setEventReceiver(&receiver);
 
 
    /*
    And at last, we create a nice Irrlicht Engine logo in the top left corner. 
    */
    env->addImage(driver->getTexture("media/irrlichtlogo2.png"),
            position2d<int>(10,10));
 
    IAnimatedMesh* mesh = smgr->getMesh("media/sydney.md2");
    mesh->getMesh(0)->setHardwareMappingHint(EHM_STREAM, EBT_VERTEX_AND_INDEX);
    if(!mesh){
        device->drop();
            return;
    }
 
    IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );
 
    if (node){
        node->setMaterialFlag(EMF_LIGHTING, false);
        node->setMD2Animation(scene::EMAT_STAND);
        node->setMaterialTexture( 0, driver->getTexture("media/sydney.bmp") );
    }
 
    smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));
 
 
    /*
    That's all, we only have to draw everything.
    */
#ifdef _IRR_ANDROID_PLATFORM_
    displayKeyboard(true, app, jnienv);
    __android_log_print(ANDROID_LOG_ERROR, "App", "Started");
#endif
 
    ANativeWindow* oldwin = app->window;
 
    while(device->run() && driver)
    if (device->isWindowActive())
    {
 
        if(oldwin!=app->window){
            __android_log_print(ANDROID_LOG_ERROR, "Win", "window changed");
            oldwin = app->window;
        }
        //__android_log_print(ANDROID_LOG_ERROR, "Test", "window: %i\n", app->window?1:0);
 
        driver->beginScene(true, true, SColor(0,200,200,200));
        smgr->drawAll();
        env->drawAll();
    
        driver->endScene();
    }
 
    device->drop();
 
#if defined(_IRR_ANDROID_PLATFORM_)
    jvm->DetachCurrentThread();
    return;
#else
    return 0;
#endif
}
 
/*
**/
 

Image

Image

I hope those changes will make it into the official ogles branch?

Edit: For compatibility reasons with a CursorControl implementation: https://dl.dropboxusercontent.com/u/503 ... es2.tar.gz
Last edited by porcus on Wed Nov 06, 2013 6:03 pm, edited 1 time in total.
cww
Posts: 27
Joined: Sun Jul 28, 2013 1:29 pm

Re: Useful modifications to the android port (ogles-branch)

Post by cww »

Thanks! Very useful code snippets for me! Cheers!
porcus
Posts: 149
Joined: Sun May 27, 2007 6:24 pm
Location: Germany

Re: Useful modifications to the android port (ogles-branch)

Post by porcus »

I now implemented the CursorControl for Android, because I think it should be there for compatibility reasons (most software will propably not check if the CursorControl is NULL, for example the FPS Camera).
Unfortunately it's afaik not possible to change the visibility and cursor position if an external mouse is connected, but at least the appropriate Events are issued here and it returns the correct cursor position:

In CIrrDeviceAndroid.h:

Code: Select all

 
        //! Android Cursor Control
        class CCursorControl : public gui::ICursorControl
        {
        private:
            core::rect<s32> refrect;
            core::dimension2d<u32> sSize;
            IrrlichtDevice* Device;
            core::position2d<s32> curPos;//only to support the stupid reference in getPosition
 
        public:
            SEvent mevent;
 
            CCursorControl(IrrlichtDevice* device, core::dimension2d<u32> ssize){
                mevent.MouseInput.ButtonStates = 0;
                mevent.MouseInput.Control = false;
                mevent.MouseInput.Event = EMIE_MOUSE_MOVED;
                mevent.MouseInput.Shift = false;
                mevent.MouseInput.Wheel = 0.0f;
                mevent.MouseInput.X = 0;
                mevent.MouseInput.Y = 0;
                sSize = ssize;
                refrect = core::rect<s32>(0,0,sSize.Width,sSize.Height);
                Device = device;
            }
 
            virtual void setVisible(bool visible){}//TODO: external mouse cursor visibility
 
            virtual bool isVisible() const{return false;}
 
            virtual void setPosition(const core::position2d<f32> &pos){
                setPosition(pos.X, pos.Y);
            }
 
            virtual void setPosition(f32 x, f32 y){
                setPosition((s32)(x*refrect.getWidth()), (s32)(y*refrect.getHeight()));
            }
 
            virtual void setPosition(const core::position2d<s32> &pos){
                setPosition(pos.X, pos.Y);
            }
 
            virtual void setPosition(s32 x, s32 y){
                #ifdef _IRR_ANDROID_MOUSE_COMPATIBILITY
                mevent.MouseInput.X = x+refrect.UpperLeftCorner.X;
                mevent.MouseInput.Y = y+refrect.UpperLeftCorner.Y;
                Device->postEventFromUser(mevent);
                #endif
            }
 
            virtual const core::position2d<s32>& getPosition(){
                curPos = core::position2d<s32>(mevent.MouseInput.X-refrect.UpperLeftCorner.X, mevent.MouseInput.Y-refrect.UpperLeftCorner.Y);
                return curPos;
            }
 
            virtual core::position2d<f32> getRelativePosition(){
                return core::position2d<f32>((f32)(mevent.MouseInput.X-refrect.UpperLeftCorner.X)/refrect.getWidth(), (f32)(mevent.MouseInput.Y-refrect.UpperLeftCorner.Y)/refrect.getHeight());
            }
 
            virtual void setReferenceRect(core::rect<s32>* rect=0){
                if(rect){
                    refrect = core::rect<s32>(*rect);
                }else{
                    refrect = core::rect<s32>(0,0,sSize.Width,sSize.Height);
                }
            }
        };
 

And in handleInput:

Code: Select all

 
    if (AInputEvent_getType(androidEvent) == AINPUT_EVENT_TYPE_MOTION)
    {
        SEvent Event;
        s32 PointerCount = AMotionEvent_getPointerCount(androidEvent);
        s32 EventAction = AMotionEvent_getAction(androidEvent);
 
        bool MultiTouchEvent = true;
        bool Touched = false;
 
        #ifdef _IRR_ANDROID_MOUSE_COMPATIBILITY
        SEvent mouseEvent;
        bool issueMouseEvent = false;
        #endif
 
        switch (EventAction)
        {
        case AMOTION_EVENT_ACTION_DOWN:
            #ifdef _IRR_ANDROID_MOUSE_COMPATIBILITY
            mouseEvent.EventType = EET_MOUSE_INPUT_EVENT;
            mouseEvent.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
            Device->ButtonStates = Device->ButtonStates | EMBSM_LEFT;
            issueMouseEvent = true;
            #endif
            Event.MultiTouchInput.Event = EMTIE_PRESSED_DOWN;
            Touched = true;
            break;
        case AMOTION_EVENT_ACTION_MOVE:
            #ifdef _IRR_ANDROID_MOUSE_COMPATIBILITY
            mouseEvent.EventType = EET_MOUSE_INPUT_EVENT;
            mouseEvent.MouseInput.Event = EMIE_MOUSE_MOVED;
            issueMouseEvent = true;
            #endif
            Event.MultiTouchInput.Event = EMTIE_MOVED;
            Touched = true;
            break;
        case AMOTION_EVENT_ACTION_UP:
            #ifdef _IRR_ANDROID_MOUSE_COMPATIBILITY
            mouseEvent.EventType = EET_MOUSE_INPUT_EVENT;
            mouseEvent.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
            Device->ButtonStates = Device->ButtonStates & (~EMBSM_LEFT);
            issueMouseEvent = true;
            #endif
            Event.MultiTouchInput.Event = EMTIE_LEFT_UP;                    
            break;
        default:
            MultiTouchEvent = false;
            break;
        }
 
        #ifdef _IRR_ANDROID_MOUSE_COMPATIBILITY
        if(issueMouseEvent){
            mouseEvent.MouseInput.X = AMotionEvent_getX(androidEvent, 0);
            mouseEvent.MouseInput.Y = AMotionEvent_getY(androidEvent, 0);
            ((CCursorControl*)(Device->CursorControl))->mevent.MouseInput.X = mouseEvent.MouseInput.X;
            ((CCursorControl*)(Device->CursorControl))->mevent.MouseInput.Y = mouseEvent.MouseInput.Y;
            mouseEvent.MouseInput.ButtonStates = Device->ButtonStates;
            mouseEvent.MouseInput.Control = false;//TODO
            s32 meta = AMotionEvent_getMetaState(androidEvent);
            mouseEvent.MouseInput.Shift = (meta & AMETA_SHIFT_ON)!=0;//TODO: shift does not work
            mouseEvent.MouseInput.Wheel = 0.0f;//TODO
            Device->postEventFromUser(mouseEvent);
            //__android_log_print(ANDROID_LOG_ERROR, "Shift", "meta: %i, Shift: %i\n", meta, (int)mouseEvent.MouseInput.Shift);
        }
        #endif
 
        if (MultiTouchEvent)
        {
            Event.EventType = EET_MULTI_TOUCH_EVENT;
            Event.MultiTouchInput.clear();
 
            for (s32 i = 0; i < PointerCount; ++i)
            {
                if (i >= NUMBER_OF_MULTI_TOUCHES)
                    break;
 
                Event.MultiTouchInput.PrevX[i] = 0; // TODO
                Event.MultiTouchInput.PrevY[i] = 0; // TODO
                Event.MultiTouchInput.X[i] = AMotionEvent_getX(androidEvent, i);
                Event.MultiTouchInput.Y[i] = AMotionEvent_getY(androidEvent, i);
 
                Event.MultiTouchInput.Touched[i] = Touched;
            }
    
            Device->postEventFromUser(Event);
 
            Status = 1;
        }
    }
 
Before the SceneManager Creation inside the Constructor:

Code: Select all

 
CursorControl = new CCursorControl(this, VideoDriver->getScreenSize());
 
Here you can download the modified files (including tho ones from the first post): https://dl.dropboxusercontent.com/u/503 ... es2.tar.gz
nabouill
Posts: 4
Joined: Wed Aug 26, 2009 9:00 pm

Re: Useful modifications to the android port (ogles-branch)

Post by nabouill »

Very good job!
thank's a lot
Nabouill
Post Reply