Soft keyboard and `textInputNotRequired'


#1

i’m trying to implement the popup soft keyboard on android. this needs to be triggered from the ComponentPeer. so by implementing textInputRequired' inside juce_android_Windowing.cpp i can get a keyboard to appear. but then i have no way to remove it. at first, i thought i could hookdismissPendingTextInput’ but this is not called when there is no “pending text input”, for example if you don’t make an edit to a box.

i put a bit of a hack into my version as follows;

i added virtual void ComponentPeer::textInputNotRequired(); and then implemented this in juce_android_Windowing.cpp

right now i’m triggering this from the Label

void Label::editorAboutToBeHidden (TextEditor*) { ComponentPeer* const peer = getPeer(); if (peer) peer->textInputNotRequired(); }

probably not the ideal place, but it gets there.

here’s also the code to do the soft keyboard,

juce_android_Windowing.cpp:

[code]void textInputRequired (const Point& position)
{
view.callVoidMethod (ComponentPeerView.showKeyboard, true);
}

void textInputNotRequired ()
{
    view.callVoidMethod (ComponentPeerView.showKeyboard, false);
}

void dismissPendingTextInput()
{
view.callVoidMethod (ComponentPeerView.showKeyboard, false);
}

[/code]

Java activity,

[code]import android.view.inputmethod.InputMethodManager;

 public void showKeyboard(boolean v)
    {
        InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
        if (v)
            imm.showSoftInput(this, InputMethodManager.SHOW_FORCED);
        else
            imm.hideSoftInputFromWindow(getWindowToken(), 0);
    }

[/code]

There are probably many problems left with this. firstly, forcing the keyboard on and off is probably not the best way and secondly, i’m not yet checking for the existence of a real keyboard.

also, it’s still wobbly. the keyboard can keep popping up and down if you’ve got a panel with several entry boxes. not ideal.

– hugh.


#2

Did you add your Java activity code as a method of the “JuceAppActivity” class? Or extend that class in some other way? (i.e. without modifying the “JuceAppActivity.java” file). I’m not a big Java jockey but the “final” keyword on JuceAppActivity may not let you extend it. I’m just looking at adding your ideas here to an app that needs keyboard popup / popdown and just trying to figure out how to proceed in a pragmatic way (given my newness to Android and Juce). For example, in IOS, I was able able add some C++/Objective C to inform the main window when the keyboard popped up/down and the keyboard size so the window could resize itself so the keyboard didn’t hide it. I’m trying to figure out how to do the same for Android and your code here looks like a place to start (So thanks for posting this in the first place!)


#3

Hello, i’ve been using this juce hack for a while now and it’s mostly ok.

Yes, there are some other boring changes to make it work, i skipped over. let me see…

juce_ComponentPeer.h

// any OS keyboard not required virtual void textInputNotRequired () {}

juce_android_windowing.cpp. note the hacks to the key codes. i’ve only fixed a few of them so that the basics work.

*** 91,102 ****
--- 94,106 ----
   METHOD (requestFocus,  "requestFocus",     "()Z") \
   METHOD (setVisible,    "setVisible",       "(Z)V") \
   METHOD (isVisible,     "isVisible",        "()Z") \
   METHOD (hasFocus,      "hasFocus",         "()Z") \
   METHOD (invalidate,    "invalidate",       "(IIII)V") \
   METHOD (containsPoint, "containsPoint",    "(II)Z") \
+  METHOD (showKeyboard, "showKeyboard",      "(Z)V") \
   METHOD (createGLView,  "createGLView",     "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$OpenGLView;") \
  
  DECLARE_JNI_CLASS (ComponentPeerView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView");
  #undef JNI_CLASS_MEMBERS
  
  
***************
*** 353,364 ****
--- 357,379 ----
      {
          lastMousePos.setXY ((int) x, (int) y);
          currentModifiers = currentModifiers.withoutMouseButtons();
          handleMouseEvent (0, lastMousePos, currentModifiers, time);
      }
  
+     void handleKeyDownCallback (int k, int kc)
+     {
+         handleKeyPress(k, kc);
+     }
+ 
+     void handleKeyUpCallback (int k, int kc)
+     {
+         //handleKeyUpOrDown(false);
+     }
+ 
+ 
      //==============================================================================
      bool isFocused() const
      {
          return view.callBooleanMethod (ComponentPeerView.hasFocus);
      }
  
***************
*** 374,386 ****
          else
              handleFocusLoss();
      }
  
      void textInputRequired (const Point<int>& position)
      {
!         // TODO
      }
  
      //==============================================================================
      void handlePaintCallback (JNIEnv* env, jobject canvas)
      {
          jobject rect = env->CallObjectMethod (canvas, CanvasMinimal.getClipBounds);
--- 389,411 ----
          else
              handleFocusLoss();
      }
  
      void textInputRequired (const Point<int>& position)
      {
!         view.callVoidMethod (ComponentPeerView.showKeyboard, true);
!     }
! 
!     void textInputNotRequired ()
!     {
!         view.callVoidMethod (ComponentPeerView.showKeyboard, false);
!     }
! 
!     void dismissPendingTextInput()
!     {
!         view.callVoidMethod (ComponentPeerView.showKeyboard, false);
      }
  
      //==============================================================================
      void handlePaintCallback (JNIEnv* env, jobject canvas)
      {
          jobject rect = env->CallObjectMethod (canvas, CanvasMinimal.getClipBounds);
***************
*** 557,568 ****
--- 582,597 ----
  JUCE_VIEW_CALLBACK (void, handleMouseDown,  (JNIEnv* env, jobject view, jfloat x, jfloat y, jlong time),    handleMouseDownCallback ((float) x, (float) y, (int64) time))
  JUCE_VIEW_CALLBACK (void, handleMouseDrag,  (JNIEnv* env, jobject view, jfloat x, jfloat y, jlong time),    handleMouseDragCallback ((float) x, (float) y, (int64) time))
  JUCE_VIEW_CALLBACK (void, handleMouseUp,    (JNIEnv* env, jobject view, jfloat x, jfloat y, jlong time),    handleMouseUpCallback ((float) x, (float) y, (int64) time))
  JUCE_VIEW_CALLBACK (void, viewSizeChanged,  (JNIEnv* env, jobject view),                                    handleMovedOrResized())
  JUCE_VIEW_CALLBACK (void, focusChanged,     (JNIEnv* env, jobject view, jboolean hasFocus),                 handleFocusChangeCallback (hasFocus))
  
+ JUCE_VIEW_CALLBACK (void, handleKeyDown,  (JNIEnv* env, jobject view, jint k, jint kc),  handleKeyDownCallback ((int) k, (int)kc))
+ 
+ JUCE_VIEW_CALLBACK (void, handleKeyUp,  (JNIEnv* env, jobject view, jint k, jint kc),  handleKeyUpCallback ((int) k, (int)kc))
+ 
  //==============================================================================
  ComponentPeer* Component::createNewPeer (int styleFlags, void*)
  {
      return new AndroidComponentPeer (*this, styleFlags);
  }
  
***************
*** 621,637 ****
  
  //==============================================================================
  bool Process::isForegroundProcess()
  {
      return true;      // TODO
  }
- 
  void Process::makeForegroundProcess()
  {
  }
- 
  //==============================================================================
  void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
                                                            const String& title, const String& message,
                                                            Component* associatedComponent)
  {
      android.activity.callVoidMethod (JuceAppActivity.showMessageBox, javaString (title).get(), javaString (message).get(), (jlong) 0);
--- 650,664 ----
***************
*** 752,778 ****
      return juceString (text);
  }
  
  //==============================================================================
  const int extendedKeyModifier       = 0x10000;
  
  const int KeyPress::spaceKey        = ' ';
! const int KeyPress::returnKey       = 0x0d;
! const int KeyPress::escapeKey       = 0x1b;
! const int KeyPress::backspaceKey    = 0x7f;
  const int KeyPress::leftKey         = extendedKeyModifier + 1;
  const int KeyPress::rightKey        = extendedKeyModifier + 2;
  const int KeyPress::upKey           = extendedKeyModifier + 3;
  const int KeyPress::downKey         = extendedKeyModifier + 4;
  const int KeyPress::pageUpKey       = extendedKeyModifier + 5;
  const int KeyPress::pageDownKey     = extendedKeyModifier + 6;
  const int KeyPress::endKey          = extendedKeyModifier + 7;
  const int KeyPress::homeKey         = extendedKeyModifier + 8;
  const int KeyPress::deleteKey       = extendedKeyModifier + 9;
  const int KeyPress::insertKey       = -1;
! const int KeyPress::tabKey          = 9;
  const int KeyPress::F1Key           = extendedKeyModifier + 10;
  const int KeyPress::F2Key           = extendedKeyModifier + 11;
  const int KeyPress::F3Key           = extendedKeyModifier + 12;
  const int KeyPress::F4Key           = extendedKeyModifier + 13;
  const int KeyPress::F5Key           = extendedKeyModifier + 14;
  const int KeyPress::F6Key           = extendedKeyModifier + 16;
--- 779,810 ----
      return juceString (text);
  }
  
  //==============================================================================
  const int extendedKeyModifier       = 0x10000;
  
+     const int KeyPress::returnKey       = 66;
+     const int KeyPress::escapeKey       = 4;
+     const int KeyPress::backspaceKey    = 67;
+     const int KeyPress::tabKey          = 61;
+ 
  const int KeyPress::spaceKey        = ' ';
!     //const int KeyPress::returnKey       = 0x0d;
!     //const int KeyPress::escapeKey       = 0x1b;
!     //const int KeyPress::backspaceKey    = 0x7f;
  const int KeyPress::leftKey         = extendedKeyModifier + 1;
  const int KeyPress::rightKey        = extendedKeyModifier + 2;
  const int KeyPress::upKey           = extendedKeyModifier + 3;
  const int KeyPress::downKey         = extendedKeyModifier + 4;
  const int KeyPress::pageUpKey       = extendedKeyModifier + 5;
  const int KeyPress::pageDownKey     = extendedKeyModifier + 6;
  const int KeyPress::endKey          = extendedKeyModifier + 7;
  const int KeyPress::homeKey         = extendedKeyModifier + 8;
  const int KeyPress::deleteKey       = extendedKeyModifier + 9;
  const int KeyPress::insertKey       = -1;
!     //const int KeyPress::tabKey          = 9;
  const int KeyPress::F1Key           = extendedKeyModifier + 10;
  const int KeyPress::F2Key           = extendedKeyModifier + 11;
  const int KeyPress::F3Key           = extendedKeyModifier + 12;
  const int KeyPress::F4Key           = extendedKeyModifier + 13;
  const int KeyPress::F5Key           = extendedKeyModifier + 14;
  const int KeyPress::F6Key           = extendedKeyModifier + 16;

There are some changes you have to make also to your generated java activity class:

[code]import android.view.inputmethod.InputMethodManager;

private native void handleKeyDown (int keycode, int textchar);
private native void handleKeyUp (int keycode, int textchar);

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event)
    {
     
        handleKeyDown(keyCode, event.getUnicodeChar());
        return true;
    }    

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event)
    {
        handleKeyUp(keyCode, event.getUnicodeChar());
        return true;
    }    

    public void showKeyboard(boolean v)
    {
        InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
        if (v)
            imm.showSoftInput(this, InputMethodManager.SHOW_FORCED);
        else
            imm.hideSoftInputFromWindow(getWindowToken(), 0);
    }

[/code]

i think that might be it,

– hugh.


#4

Hey,

i also tried to get the soft keyboard working on Samsung Galaxy Tab2 10.1 with Android 4.03.

Following things I needed to add in addition to yours:

import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager
        @Override
        public InputConnection onCreateInputConnection(EditorInfo outAttrs)
        {
            outAttrs.actionLabel = "";
            outAttrs.hintText = "";
            outAttrs.initialCapsMode = 0;
            outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
            outAttrs.label = "";
            outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI;        
            outAttrs.inputType = InputType.TYPE_NULL;        

            return  new BaseInputConnection(this, false);       
        } 

#5

Hi,

Thanks for the additions. i’ve not tried in on the samsung tab (nor other samsungs). i’ve tried the nexus7 (4.2) and a no-name gingerbread 2.3.3 phone.


#6

I should add this stuff… Hugh, could you perhaps mail me a copy of whatever your latest files are so I can have a shufty at the changes? Ta!


#7

will do. i’ll send you a changelist against the latest juce tip.

– hugh.


#8

I’m asking here to not open new topic. I am writing app and I need software keyboard. In landscape view everything is ok (I’m testing ok Galaxy Mini II), but in portrait it looks like something is covering keyboard. Do You know how to fix it?


#9

I found solution for keyboard problem on my Samsung Galaxy mini II (GT-S6500) with Android 2.3.6. I had to change outAttrs.inputType = InputType.TYPE_NULL;
to

Without that my android couldn’t find key pattern. After that keyboard was working ok, but it was opening with app, so i had to add to android manifest: