Android device - serious keyboard regression in 5.3.0

Hi folks,

Bug in https://github.com/WeAreROLI/JUCE/commit/4469217b3ed09c99531afb9334ec5236786ec128

Test case - try this on Android: the device keyboard appears and then immediately disappears.

  juce::AlertWindow *w = new juce::AlertWindow(title, message, juce::AlertWindow::NoIcon);
  const juce::String cKeyboardComponentName = "keyboardComponent";
  w->addTextEditor(cKeyboardComponentName, defaultValue, "Enter value", false);
  w->addButton("OK", 1, juce::KeyPress(juce::KeyPress::returnKey, 0, 0));
  w->addButton("Cancel", 0, juce::KeyPress(juce::KeyPress::escapeKey, 0, 0));
  
  auto textEditor = w->getTextEditor(cKeyboardComponentName);
  textEditor->setReadOnly(false);

  auto privateLambda = ([w, theCompletion](int result) {
    
    if (result == 1) {
      auto result = w->getTextEditorContents("text");
    } else {
      auto result = juce::String("");
    }
  });
  auto callback = juce::ModalCallbackFunction::create(privateLambda);
  
  w->enterModalState(true, callback, true);

The only solution I can see for now, is for me to try to plugging-in the old implementation.

HTH!

Pete

The bug is in here:

private class TreeObserver implements ViewTreeObserver.OnGlobalLayoutListener
            {
                @Override
                public void onGlobalLayout()

which fails to account for the view height for the active alert, being much different to the available display size.

My quick work-around is pretty ugly. but sharing with you all in case this digs somebody out of a hole while waiting for an official fix:

  1. make the window display (effectively) full-screen, rather than as a nice alert size
  2. in the lamba, set the focus to another component (otherwise, the keyboard doesn’t disappear)

HTH!

Pete

Thanks Pete for reporting this. I will try to have a look into this as soon as I can today.

Thanks Lukasz; this is blocking a release we’d been hoping to push out today.

My hack doesn’t work in all cases, so I’m relying on a fix from you guys! :slight_smile:

Pete

Just looking into this now. Have you tried on the latest develop? What I am observing is something different - the keyboard does not appear at all (I don’t get the void handleKeyboardHiddenCallback() callback) and when I press on a text editor it then appears. But then when I hide the alert, the keyboard stays on screen (which is what I was fixing but for a different scenario). I need to fix keyboard getting stuck for sure, but do I need to do something extra to repro your particular case? Or does it not happen on the latest develop?

Ok, so the keyboard not showing only happens when the initial text editor content is empty (another subtle bug to fix). Otherwise it shows up correctly and I’ve just made the keyboard go away on AlertWindow dismissal.

Edit: Ok, I think I’m seeing it now after the fixes for the above. Stay tuned…

Cool - thanks so much for helping - it is a tricky one to fix :slight_smile:

OK, the fix is now on develop. It should appear shortly.

Thanks! I’ll give it a go, and let you know if I find any problems!

Best wishes,

Pete

Hi Lukasz,

Alas, that still doesn’t work for me.

To give an idea of the application context: in the attached screenshot, you’ll see a TextEditor area; it isn’t full screen, perhaps that is important

Anyhow, when I give that the focus, the soft keyboard doesn’t appear. This approach works fine on iOS (the soft keyboard appears).

A case which does work OK is if I have TextEditor attached as a single-line editor in an alert - in that case, the keyboard appears just fine.

If you have any suggestions, I’d be grateful!

Pete

Hmmm, yeah I was using you test case with AlertWindow.

Are you observing keyboard showing and disappearing quickly? Or is it not showing up at all? Can you check if handleKeyboardHidden() from private class TreeObserver implements ViewTreeObserver.OnGlobalLayoutListener gets called in your case when TextEditor takes focus?

Also, can you confirm that you get TextEditor::focusGained() callback and that you don’t get TextEditor::focusLost() afterwards?

Hi! I’ll check and get back to you - probably tomorrow now :slight_smile: Best, Pete

Hi Lukasz,

Thanks again for looking into this. Alas, the keyboard still misbehaves :frowning:

To summarise:
A) The soft keyboard works OK from the “large TextEditor” case, provided I haven’t previously tried using from the (single-line) TextEditor in an Alert.
B) if I have the keyboard appear from a (single-line) TextEditor in an Alert, that subsequently prevents the keyboard working in the other case (multi-line TextEditor).
c) I have a question about if it possible to have the Android Soft Keyboard “green tick button” operate as a “DONE” button (please see below)

Test cases:

  1. Start my app, use the “large TextEditor” for which I gave the screenshot, the keyboard shows (thank you!).
    Question: the green tick in the soft keyboard acts as a carriage-return character (I can only hide it by tapping some other component in the display, to have them get key focus). Is there a way to have the Green Tick to work as a “Done” to dimsiss the keyboard, in Juce for Android?

  2. Start my app, and show the TextEditor in a pop-up alert, the keyboard shows and works fine, and the Green Tick in the soft keyboard works fine.
    However if I keep the app running, and now try to use the “large TextEditor” as above: they keyboard doesn’t display in this context.

I hope this feedback helps you to nail this down!

Best wishes,

Pete

Thanks for the feedback Pete. I will investigate some more in a free moment. In the meantime, could you check the sequence of TextEditor::focusGained() and TextEditor::focusLost() as mentioned in my earlier post? If there is a way you could provide a short code snippet showcasing the issue, that might be helpful too.

Hi!

Sure, no problem.

When I enter the screen, focusGained() is called. focusLost() isn’t called.

I see that in checkFocus()

void TextEditor::checkFocus()
{
    if (hasKeyboardFocus (false) && ! isCurrentlyBlockedByAnotherModalComponent())
    {
        wasFocused = true;

        if (auto* peer = getPeer())
            if (! isReadOnly())
                peer->textInputRequired (peer->globalToLocal (getScreenPosition()), *this);
    }
}

I step into here:

    void textInputRequired (Point<int>, TextInputTarget& target) override
    {
        view.callVoidMethod (ComponentPeerView.showKeyboard,
                             javaString (getVirtualKeyboardType (target.getKeyboardType())).get());
    }

And I can see the debugger attempt to show the keyboard.

For some reason when stepping around in showKeyboard with the debugger, the code can crash right now - which makes me wonder if there is some timing-sensitive code in there. Anyhow, clearly the keyboard is getting auto-dismissed in there.

Pete

Hi again!

In case it helps, if I try setting a breakpoint in here on imm.showSoftInput or imm.hideSoftInputFromWindow, the debugger always reports that the app has crashed.

        public void showKeyboard (String type)
        {
            InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE);

            if (imm != null)
            {
                if (type.length() > 0)
                {
                    imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);
                    imm.setInputMethod (getWindowToken(), type);
                    keyboardDismissListener.startListening();
                }
                else
                {
                    imm.hideSoftInputFromWindow (getWindowToken(), 0);
                    keyboardDismissListener.stopListening();
                }
            }
        }

Pete

Hmmm, I have a view with a multi-line text editor and a button to show an AlertWindow. Pressing on a multi-line editor always shows the keyboard correctly and the keyboard also shows fine when showing AlertWindow. There must be an important detail to do with keyboard focus handling, are you able to write a short snippet that demonstrates the issue?

The tick button on the native keyboard sends Enter key code, i.e. the same key code if you pressed enter/return key on a physical keyboard. So if you had:

myEditor.setMultiLine (true);
myEditor.setReturnKeyStartsNewLine (true);

then the tick would simply move caret to a next line.

and if you had instead:

myEditor.setMultiLine (true);
myEditor.onReturnKey = [this] () { juce::Component::unfocusAllComponents(); };

then the tick would dismiss the keyboard but then you loose the ability to jump to a next line with enter/return key, which would also dismiss the keyboard. So it depends on your use case. Note that in order to dismiss the keyboard, the TextEditor must lose focus (you could move it elsewhere if you wanted).

It’s a shame that the tick uses the same key code as enter/return, so we can’t distinguish between them.

Hi Lukasz,

Sorry for the delay in reply - had to get another Wotja release together.

I’m now back looking at this, and found that your fix 0b030331f resolved this problem.

However, I’ve now found another problem with the keyboard on Android.

If I visit a screen with a single-line TextEditor Component, and touch the editor - the Android keyboard shows fine.

However if I’ve previously shown a Juce Alert with a single-line text editor (as in the above discussion), and then go to the screen with the embedded single-line TextEditor component; the soft keyboard never displays. Note that the call here is made (!)

imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT);

I’ve been unable to find a work-around as of yet.

Amazing how hard that Android seems to have made all this! :slight_smile:

Best wishes,

Pete