Get full Unicode input for Android in C / C ++

(Android, NDK, C ++, OpenGL ES)

I need a way to reliably receive text input from a (soft) keyboard. The solution could be through Java, using a subclass of NativeActivity or anything that works. In the end, I need any text that I type, so I can do it myself using OpenGL

Some prerequisites: So far, I have run a soft keyboard by invoking showSoftInput or hideSoftInputFromWindow JNI thought. This has failed. However, the problem is that native activity will not send all characters. Especially some Unicode characters outside the ASCII range, or some soft motion keyboard will not work (AKeyEvent_getKeyCode)

Previously, you could get some of these other Unicode characters that check KeyEvent.ACTION_MULTIPLE and read a character string. But even this will not work more reliably.

So far, I have not been able to find an alternative method. I experimented with the software add EditText, but never got it to work. Even trying to add a simple button made the OpenGL view no longer displayed.

In iOS, I worked around it, having a hidden editing window that I just activated to show the keyboard. Then I read out the edit window and used the line for rendering in OpenGL.

+4
source share
4 answers

, , .

int GetUnicodeChar(struct android_app* app, int eventType, int keyCode, int metaState)
{
JavaVM* javaVM = app->activity->vm;
JNIEnv* jniEnv = app->activity->env;

JavaVMAttachArgs attachArgs;
attachArgs.version = JNI_VERSION_1_6;
attachArgs.name = "NativeThread";
attachArgs.group = NULL;

jint result = javaVM->AttachCurrentThread(&jniEnv, &attachArgs);
if(result == JNI_ERR)
{
    return 0;
}

jclass class_key_event = jniEnv->FindClass("android/view/KeyEvent");
int unicodeKey;

if(metaState == 0)
{
    jmethodID method_get_unicode_char = jniEnv->GetMethodID(class_key_event, "getUnicodeChar", "()I");
    jmethodID eventConstructor = jniEnv->GetMethodID(class_key_event, "<init>", "(II)V");
    jobject eventObj = jniEnv->NewObject(class_key_event, eventConstructor, eventType, keyCode);

    unicodeKey = jniEnv->CallIntMethod(eventObj, method_get_unicode_char);
}

else
{
    jmethodID method_get_unicode_char = jniEnv->GetMethodID(class_key_event, "getUnicodeChar", "(I)I");
    jmethodID eventConstructor = jniEnv->GetMethodID(class_key_event, "<init>", "(II)V");
    jobject eventObj = jniEnv->NewObject(class_key_event, eventConstructor, eventType, keyCode);

    unicodeKey = jniEnv->CallIntMethod(eventObj, method_get_unicode_char, metaState);
}

javaVM->DetachCurrentThread();

LOGI("Unicode key is: %d", unicodeKey);
return unicodeKey;
}

, :

switch (AInputEvent_getType(event))
    {
        case AINPUT_EVENT_TYPE_KEY:
          switch (AKeyEvent_getAction(event))
          {
            case AKEY_EVENT_ACTION_DOWN:
              int key = AKeyEvent_getKeyCode(event);
              int metaState = AKeyEvent_getMetaState(event);
              int uniValue;
              if(metaState != 0)
                  uniValue = GetUnicodeChar(app, AKEY_EVENT_ACTION_DOWN, key, metaState);
              else
                  uniValue = GetUnicodeChar(app, AKEY_EVENT_ACTION_DOWN, key, 0);

, , , . Java KeyEvent, GetUnicodeChar.

+3

, "Character", InputEvent.

: AKeyEvent_getKeyCode KeyCode , "unicode/latin", . @Shammi @eozgonul, KeyEvent, Java, .

, InputQueue ++/Native , (t) > t23 > . , KEYDOWN/KEYUP , Java . ( ).

- Java, dispatchKeyEvent Queue<Integer> queueLastInputCharacter = new ConcurrentLinkedQueue<Integer>();

// [JAVA]
@Override
public boolean dispatchKeyEvent (KeyEvent event)
{
    int metaState = event.getMetaState(); 
    int unichar = event.getUnicodeChar(metaState);

    // We are queuing the Unicode version of the characters for
    // sending to the app during processEvents() call.

    // We Queue the KeyDown and ActionMultiple Event UnicodeCharacters
    if(event.getAction()==KeyEvent.ACTION_DOWN){
        if(unichar != 0){
            queueLastInputCharacter.offer(Integer.valueOf(unichar));
        }
        else{
            unichar = event.getUnicodeChar(); 

            if(unichar != 0){
                queueLastInputCharacter.offer(Integer.valueOf(unichar));
            }
            else if (event.getDisplayLabel() != 0){
                String aText = new String();
                aText = "";
                aText += event.getDisplayLabel();
                queueLastInputCharacter.offer(Integer.valueOf(Character.codePointAt(aText, 0)));
            }
            else
                queueLastInputCharacter.offer(Integer.valueOf(0));
        }
    }
    else if(event.getAction()==KeyEvent.ACTION_MULTIPLE){
        unichar = (Character.codePointAt(event.getCharacters(), 0));
        queueLastInputCharacter.offer(Integer.valueOf(unichar));
    }


    return super.dispatchKeyEvent(event);
}

.

Java, :

// [JAVA]
public int getLastUnicodeChar(){
    if(!queueLastInputCharacter.isEmpty())
        return queueLastInputCharacter.poll().intValue();
    return 0;
}

, , - :

// [C++]
int ident;
int events;
struct android_poll_source* source;

// If not rendering, we will block 250ms waiting for events.
// If animating, we loop until all events are read, then continue
// to draw the next frame of animation.
while ((ident = ALooper_pollAll(((nv_app_status_focused(_lpApp)) ? 1 : 250),
                                NULL,
                                &events,
                                (void**)&source)) >= 0)
{
    // Process this event.
    if (source != NULL)
        source->process(_lpApp, source);

    // Check if we are exiting.  If so, dump out
    if (!nv_app_status_running(_lpApp))
        return;
}

static int modtime = 10; // let not run on every call
if(--modtime == 0) {
    long uniChar = androidUnicodeCharFromKeyEvent();
    while (uniChar != 0) {
        KEvent kCharEvent; // Game engine event
        kCharEvent.ptkKey = K_VK_ERROR;
        kCharEvent.unicodeChar = uniChar;
        kCharEvent.character = uniChar;

        /* Send unicode char */
        kCharEvent.type = K_EVENT_UNICHAR;
        _lpPortableHandler(&kCharEvent);

        if (kCharEvent.character < 127) {
            /* Send ascii char for source compatibility as well */
            kCharEvent.type = K_EVENT_CHAR;
            _lpPortableHandler(&kCharEvent);
        }

        uniChar = androidUnicodeCharFromKeyEvent();
    }
    modtime = 10;
}

androidUnicodeCharFromKeyEvent @Shammi GetStringFromAInputEvent, CallIntMethod, jint.

Key. Android - , AKEYCODE_BACK AKEYCODE_ENTER, ( ).

Editboxes, console .. , , , . , .

+3

Eozgonul . , Java . NativeActivity, , Java. . , KeyEvent.

package com.MyCompany.MyApp;

import android.os.Bundle;
import android.view.inputmethod.InputMethodManager;
import android.content.Context;
import android.view.KeyEvent;

public class MyNativeActivity extends android.app.NativeActivity
{

    // Need this for screen rotation to send configuration changed callbacks to native
    @Override
    public void onConfigurationChanged( android.content.res.Configuration newConfig )
    {
        super.onConfigurationChanged( newConfig );
    }

    public void showKeyboard()
    {
        InputMethodManager imm = ( InputMethodManager )getSystemService( Context.INPUT_METHOD_SERVICE );
        imm.showSoftInput( this.getWindow().getDecorView(), InputMethodManager.SHOW_FORCED );
    }


    public void hideKeyboard()
    {
        InputMethodManager imm = ( InputMethodManager )getSystemService( Context.INPUT_METHOD_SERVICE );
        imm.hideSoftInputFromWindow( this.getWindow().getDecorView().getWindowToken(), 0 );
    }

    public String stringFromKeyCode( long downTime, long eventTime, 
            int eventAction, int keyCode, int repeatCount, int metaState, 
            int deviceId, int scanCode, int flags, int source )
    {
        String strReturn;

        KeyEvent keyEvent = new KeyEvent( downTime, eventTime, eventAction, keyCode, repeatCount, metaState, deviceId, scanCode, flags, source );

        if ( metaState == 0 )
        {
            int unicodeChar = keyEvent.getUnicodeChar();
            if ( eventAction == KeyEvent.ACTION_MULTIPLE && unicodeChar == keyEvent.KEYCODE_UNKNOWN )
            {
                strReturn = keyEvent.getCharacters();
            }
            else
            {
                strReturn = Character.toString( ( char )unicodeChar );
            }
        }
        else
        {
            strReturn = Character.toString( ( char )( keyEvent.getUnicodeChar( metaState ) ) );
        }

        return strReturn;
    }
 }

...

std::string GetStringFromAInputEvent( android_app* pApp, AInputEvent* pInputEvent )
{
    std::string strReturn;

    JavaVM* pJavaVM = pApp->activity->vm;
    JNIEnv* pJNIEnv = pApp->activity->env;

    JavaVMAttachArgs javaVMAttachArgs;
    javaVMAttachArgs.version = JNI_VERSION_1_6;
    javaVMAttachArgs.name = "NativeThread";
    javaVMAttachArgs.group = NULL;

    jint jResult;
    jResult = pJavaVM->AttachCurrentThread( &pJNIEnv, &javaVMAttachArgs );
    if ( jResult != JNI_ERR )
    {
        // Retrieves NativeActivity.
        jobject nativeActivity = pNativeActivity->clazz;
        jclass ClassNativeActivity = pJNIEnv->GetObjectClass( nativeActivity );

        jmethodID MethodStringFromKeyCode = pJNIEnv->GetMethodID( ClassNativeActivity, "stringFromKeyCode", "(JJIIIIIIII)Ljava/lang/String;" );
        jlong jDownTime = AKeyEvent_getDownTime( pInputEvent );
        jlong jEventTime = AKeyEvent_getEventTime( pInputEvent );
        jint jEventAction = AKeyEvent_getAction( pInputEvent );
        jint jKeyCode = AKeyEvent_getKeyCode( pInputEvent );
        jint jRepeatCount = AKeyEvent_getRepeatCount( pInputEvent );
        jint jMetaState = AKeyEvent_getMetaState( pInputEvent );
        jint jDeviceID = AInputEvent_getDeviceId( pInputEvent );
        jint jScanCode = AKeyEvent_getScanCode( pInputEvent );
        jint jFlags = AKeyEvent_getFlags( pInputEvent );
        jint jSource = AInputEvent_getSource( pInputEvent );

        jstring jKeyCodeString = ( jstring )pJNIEnv->CallObjectMethod( nativeActivity, MethodStringFromKeyCode, 
            jDownTime, jEventTime, jEventAction, 
            jKeyCode, jRepeatCount, jMetaState,
            jDeviceID, jScanCode, jFlags, jSource );

        const char* keyCodeString = pJNIEnv->GetStringUTFChars( keyCodeString, nullptr );
        strReturn = std::string( keyCodeString );
        pJNIEnv->ReleaseStringUTFChars( jKeyCodeString, keyCodeString );

        // Finished with the JVM.
        pJavaVM->DetachCurrentThread();
    }

    return strReturn;
}

2 , .

  • , java jni .

  • Java Android, Java-. , java.

+1

This will basically solve the problem.
Cancel NativeActivity onKeyDown ()

But you will have to implement a different method than entering the NDK key to get the onKeyMultiple string event.getCharacters()into your code.

0
source

All Articles