Android Question B4XPages, press back key when in edittext with Auto-fill showing

RB Smissaert

Well-Known Member
Licensed User
Longtime User
In B4XPages, the back key press (bottom right of the bar at the bottom of the screen) is handled by Main with this Sub:

B4X:
Sub Activity_KeyPress (KeyCode As Int) As Boolean
    Return B4XPages.Delegate.Activity_KeyPress(KeyCode)
End Sub

This then should trigger in the B4XPage this Sub:

B4X:
Private Sub B4XPage_CloseRequest As ResumableSub

I noticed though that if the edittext has the Auto-fill pop-up showing then B4XPage_CloseRequest doesn't run.
Is this by design or is this is a bug or am I overlooking something?
I need to close a custom keyboard via B4XPage_CloseRequest.

RBS
 

drgottjr

Expert
Licensed User
Longtime User
erel's comment:
Activity_KeyPress isn't raised in this case. It is handled by EditText.
got my attention.

not only must the event be trapped by the view with the focus, but - apparently - it goes a step further: to the keyboard itself.

in order to get the back key press into the hands of the activity (for whatever it is you might want to do in that case), you need to know if the keyboard is visible and if the edittext has the focus. if one or both are true, the activity doesn't see the key press.

so what i did was add a listener to the keyboard's visibility. when you tap the back key, the keyboard disappears (as usual), but what happens next is up to you. you can allow normal flow or abnormal flow, and you can toggle the flow on the fly.

the normal flow (in my opinion) would be: tap back key to remove keyboard (as usual) and set the activity to receive the next back key tap. this does not occur now because the edittext still has the focus even though the keyboard is gone. if you wish, you can reset the focus when the app is notified the keyboard is gone.

abnormal flow (in my opinion) would be: tap back key while keyboard is up but have activity receive the key press immediately (which, i think, is what you were looking for). here, you have to decide whether to consume the event or not. but this has nothing to do with my code. to consume or not to consume is always something you have to deal with.

the code is simple enough. you need 2 javaobjects, 1 javaobject call to unset the focus and 1 inline java routine for the listener. you determine which "flow" to use by passing a boolean to the listener. everything is in this post.

B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private xui As XUI
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    Dim jo, edittextjo As JavaObject
    Dim edittext1 As EditText
    Dim normalflow As Boolean
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("Layout")
    jo.InitializeContext
    edittextjo = edittext1
    normalflow = True
    jo.RunMethod("setListener",Array( edittext1,normalflow ))
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub edittext1_FocusChanged (HasFocus As Boolean)
    Log("has focus: " & HasFocus)   
End Sub

Sub kbdtoggle( height As Int )
    If height = 0 Then
        Log("keyboard gone, but edittext still has focus.  it keeps the focus until you clear it below.")
        ' run this to clear the focus
        ' edittextjo.RunMethod("clearFocus",Null)   
    End If
End Sub

Sub activity_KeyPress (KeyCode As Int) As Boolean 'Return True to consume the event
    If KeyCode = KeyCodes.KEYCODE_BACK Then
        If edittextjo.RunMethod("hasFocus",Null) Then
            Log("edittext has focus.  activity blocked. you'll keep coming back here until you clear the focus below.")
    '        if you run this, activity will receive the next back key press
    '        edittextjo.RunMethod("clearFocus",Null)
            Return True            ' up to you to consume or not
        Else
            Log("activity captures back key.  consuming event, but you could fall through if you wanted")
            Return True            ' up to you to consume or not
        End If
    End If
End Sub

B4X:
#if Java
    import android.view.WindowInsets;
    import android.view.View;
    import android.view.WindowInsets.Type;
    import android.view.KeyEvent;
    import android.view.View.OnApplyWindowInsetsListener;
    
    public void setListener( View view, boolean normalFlow ) {
        
        view.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
            @Override
            public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
                  boolean imeVisible = insets.isVisible(WindowInsets.Type.ime());
                  int imeHeight = insets.getInsets(WindowInsets.Type.ime()).bottom;
                BA.Log("height: " + imeHeight);
                if ( (imeHeight == 0) && (normalFlow == false) ) {
                    view.clearFocus();
                    dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
                }
                        
                processBA.raiseEvent(this, "kbdtoggle",new Object[] {imeHeight});
                return null;
            }
        });
    }
#end if

'----------------------------------------------------------------------------------------------------
now the deal breaker: we are supposed to use the jetpack/androidx.core
ViewCompat artifact for the listener. although i have androidx.core package
in the sdk, i have never been able to compile anything that imports any of its
members. this, even though erel has said that it should work with inline java.
whatever. i believe the purpose of the many so-called "Compat" packages
is to maintain backward compatibility with older android devices. i don't
have any, so although i couldn't use ViewCompat, i just used good old
android.view.View class for the listener. i mention this only as a rationale as
to why i used View instead of ViewCompat in case somebody should notice.
whether this or the actual code might knock you off Play, i can't say.
i thought the exercise itself was of interest.
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
erel's comment:

got my attention.

not only must the event be trapped by the view with the focus, but - apparently - it goes a step further: to the keyboard itself.

in order to get the back key press into the hands of the activity (for whatever it is you might want to do in that case), you need to know if the keyboard is visible and if the edittext has the focus. if one or both are true, the activity doesn't see the key press.

so what i did was add a listener to the keyboard's visibility. when you tap the back key, the keyboard disappears (as usual), but what happens next is up to you. you can allow normal flow or abnormal flow, and you can toggle the flow on the fly.

the normal flow (in my opinion) would be: tap back key to remove keyboard (as usual) and set the activity to receive the next back key tap. this does not occur now because the edittext still has the focus even though the keyboard is gone. if you wish, you can reset the focus when the app is notified the keyboard is gone.

abnormal flow (in my opinion) would be: tap back key while keyboard is up but have activity receive the key press immediately (which, i think, is what you were looking for). here, you have to decide whether to consume the event or not. but this has nothing to do with my code. to consume or not to consume is always something you have to deal with.

the code is simple enough. you need 2 javaobjects, 1 javaobject call to unset the focus and 1 inline java routine for the listener. you determine which "flow" to use by passing a boolean to the listener. everything is in this post.

B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private xui As XUI
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    Dim jo, edittextjo As JavaObject
    Dim edittext1 As EditText
    Dim normalflow As Boolean
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("Layout")
    jo.InitializeContext
    edittextjo = edittext1
    normalflow = True
    jo.RunMethod("setListener",Array( edittext1,normalflow ))
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub edittext1_FocusChanged (HasFocus As Boolean)
    Log("has focus: " & HasFocus)  
End Sub

Sub kbdtoggle( height As Int )
    If height = 0 Then
        Log("keyboard gone, but edittext still has focus.  it keeps the focus until you clear it below.")
        ' run this to clear the focus
        ' edittextjo.RunMethod("clearFocus",Null)  
    End If
End Sub

Sub activity_KeyPress (KeyCode As Int) As Boolean 'Return True to consume the event
    If KeyCode = KeyCodes.KEYCODE_BACK Then
        If edittextjo.RunMethod("hasFocus",Null) Then
            Log("edittext has focus.  activity blocked. you'll keep coming back here until you clear the focus below.")
    '        if you run this, activity will receive the next back key press
    '        edittextjo.RunMethod("clearFocus",Null)
            Return True            ' up to you to consume or not
        Else
            Log("activity captures back key.  consuming event, but you could fall through if you wanted")
            Return True            ' up to you to consume or not
        End If
    End If
End Sub

B4X:
#if Java
    import android.view.WindowInsets;
    import android.view.View;
    import android.view.WindowInsets.Type;
    import android.view.KeyEvent;
    import android.view.View.OnApplyWindowInsetsListener;
   
    public void setListener( View view, boolean normalFlow ) {
       
        view.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
            @Override
            public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
                  boolean imeVisible = insets.isVisible(WindowInsets.Type.ime());
                  int imeHeight = insets.getInsets(WindowInsets.Type.ime()).bottom;
                BA.Log("height: " + imeHeight);
                if ( (imeHeight == 0) && (normalFlow == false) ) {
                    view.clearFocus();
                    dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
                }
                       
                processBA.raiseEvent(this, "kbdtoggle",new Object[] {imeHeight});
                return null;
            }
        });
    }
#end if

'----------------------------------------------------------------------------------------------------
now the deal breaker: we are supposed to use the jetpack/androidx.core
ViewCompat artifact for the listener. although i have androidx.core package
in the sdk, i have never been able to compile anything that imports any of its
members. this, even though erel has said that it should work with inline java.
whatever. i believe the purpose of the many so-called "Compat" packages
is to maintain backward compatibility with older android devices. i don't
have any, so although i couldn't use ViewCompat, i just used good old
android.view.View class for the listener. i mention this only as a rationale as
to why i used View instead of ViewCompat in case somebody should notice.
whether this or the actual code might knock you off Play, i can't say.
i thought the exercise itself was of interest.
Thanks, will try this.
My setup is a bit different in that I have a B4XPages project and also I use my own custom keyboard.
I don't think though either should be a problem.
Will report back when I have this working or when I can't get it to work.

RBS
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
Thanks, will try this.
My setup is a bit different in that I have a B4XPages project and also I use my own custom keyboard.
I don't think though either should be a problem.
Will report back when I have this working or when I can't get it to work.

RBS
Actually, it may not be that simple to implement this.
Two problems I can see:

1).
In a B4XPages project

B4X:
Sub activity_KeyPress (KeyCode As Int) As Boolean 'Return True to consume the event

Doesn't run when the edittext has the Auto-fill showing.
I am not sure if this would be taken care of by your Java listener.

2).
I am using a custom keyboard and IME is not used with this.
I noticed you are using it in your Java code.
I am not familiar with Java and wonder if I could instead use a property (getVisible) of my keyboard class.

I am just guessing there might be a simpler way to detect a press of the back key when an Auto-fill popup (or another popup)
is showing in the edittext.

RBS
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
i have a mental block with regard to b4xpages. "default" b4a projects
have an activity and some events, one of which is keypress. erel
answered one of your posts saying that activity_Keypress was not
raised because edittext had the focus. i took this to mean that if
edittext did not have the focus, the "activity" version of b4xpages would
catch the keypress event.

in order for that to occur, 3 things need to happen"
1) keyboard must be down
2) you need a way of knowing this
3) edittext must no long have the focus

if the keyboard is up and/or the edittext has the focus, "activity" cannot
see the back key press.

my code knows when the keyboard is down, and it removes the focus
from edittext. if you know when the keyboard is down and you can remove
the focus, activity will see the key press.

i thought i saw you had routines in java (maybe it was a different thread), so i
thought you were more comfortable with it.

when you tap the back key on a keyboard, it's supposed to go down.
if it's not doing that, then your custom view is not following "standard"
behavior (ie, it's not doing what android thinks it's supposed to do in order
to present a uniform appearance to users). if the keyboard does go down,
you need to be made aware of this. i searched a lot before finding something
which - it claimed - was the way to accomplish it. it was straight out of google's
documentation, not something in stackoverlord. in addition, you need to
understand that causing the keyboard to disappear does not remove the
focus from the edittext. this is what's preventing activity from seeing the key
press. so after the keyboard disappears and tells you it's gone, you need
to clear the focus yourself. once that is done, the next key back press will
go to the activity. when i say clearing the focus has to be done by you, i
mean it can be done in the inline java routine or it can be done in b4a once
the keyboard down event is raised.

i think what i got from your threads is that you were expecting activity to
capture the back key when the keyboard is up. that's what i call abnormal
flow because you bypass 2 events to achieve it. not that there's anything wrong
with that.

i have no particular interest in implementing abnormal flow, so i haven't
put what i've done to rigorous testing. it was more to explain to you why
activity doesn't see the key press and what you can do about it. you may
have to adapt, but i think the process is clear. the clearFocus() method works
and is simple to implement with a javaobject. that leaves figuring out
how to make your keyboard disappear when the back key is pressed. you can't
reasonably poll for this; it has to be with a listener. even if you didn't know the
keyboard was not visible, by simply unsetting the focus on the edittext, you
allow activity to see the next back key press. you just need the keyboard to be
invisible and no focus in the edittext.
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
i have a mental block with regard to b4xpages. "default" b4a projects
have an activity and some events, one of which is keypress. erel
answered one of your posts saying that activity_Keypress was not
raised because edittext had the focus. i took this to mean that if
edittext did not have the focus, the "activity" version of b4xpages would
catch the keypress event.

in order for that to occur, 3 things need to happen"
1) keyboard must be down
2) you need a way of knowing this
3) edittext must no long have the focus

if the keyboard is up and/or the edittext has the focus, "activity" cannot
see the back key press.

my code knows when the keyboard is down, and it removes the focus
from edittext. if you know when the keyboard is down and you can remove
the focus, activity will see the key press.

i thought i saw you had routines in java (maybe it was a different thread), so i
thought you were more comfortable with it.

when you tap the back key on a keyboard, it's supposed to go down.
if it's not doing that, then your custom view is not following "standard"
behavior (ie, it's not doing what android thinks it's supposed to do in order
to present a uniform appearance to users). if the keyboard does go down,
you need to be made aware of this. i searched a lot before finding something
which - it claimed - was the way to accomplish it. it was straight out of google's
documentation, not something in stackoverlord. in addition, you need to
understand that causing the keyboard to disappear does not remove the
focus from the edittext. this is what's preventing activity from seeing the key
press. so after the keyboard disappears and tells you it's gone, you need
to clear the focus yourself. once that is done, the next key back press will
go to the activity. when i say clearing the focus has to be done by you, i
mean it can be done in the inline java routine or it can be done in b4a once
the keyboard down event is raised.

i think what i got from your threads is that you were expecting activity to
capture the back key when the keyboard is up. that's what i call abnormal
flow because you bypass 2 events to achieve it. not that there's anything wrong
with that.

i have no particular interest in implementing abnormal flow, so i haven't
put what i've done to rigorous testing. it was more to explain to you why
activity doesn't see the key press and what you can do about it. you may
have to adapt, but i think the process is clear. the clearFocus() method works
and is simple to implement with a javaobject. that leaves figuring out
how to make your keyboard disappear when the back key is pressed. you can't
reasonably poll for this; it has to be with a listener. even if you didn't know the
keyboard was not visible, by simply unsetting the focus on the edittext, you
allow activity to see the next back key press. you just need the keyboard to be
invisible and no focus in the edittext.
> when you tap the back key on a keyboard, it's supposed to go down.

And it will.
I am talking about the back key of the Android bar at the bottom (right bottom)
only.

If I could pick up if that key was pressed when the edittext had the Auto-fill popup
showing then I think I could work it all out by the removing the focus from the edittext
at the right moment. This I can do with the posted code in the other thread I posted, dealing
with all this. Erel said that in this situation the Android bottom bar back key will be dealt with
by the edittext, but I am not sure how and how to pick the press event up.

RBS
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
we're talking about the same key. when the keyboard is down, the "back" key points "back". when the keyboard is up, the "back" key points "down". some key, different function (depending on whether it's pointing back or down). you got 2 "back" keys? just askin', but why would somebody have 2 back keys? android is confusing enough already.
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
we're talking about the same key. when the keyboard is down, the "back" key points "back". when the keyboard is up, the "back" key points "down". some key, different function (depending on whether it's pointing back or down). you got 2 "back" keys? just askin', but why would somebody have 2 back keys? android is confusing enough already.
That is interesting, never had noticed that the Android back key was pointing differently, depending on the keyboard showing or not!
I am using a custom keyboard and that doesn't alter the Android back key and maybe it should.
Never realized this relation between the Android bottom bar back key and the Android keyboard back key.
Thanks for alerting me to this and I think I now understand a bit better what is going on.

Would it be possible to make my custom keyboard alter the Android bottom bar back key and make it all behave the same as when using
the default Android keyboard?

RBS
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
i think you should determine the value (or key code) of your keyboard "back" key. don't you define these things when creating your custom view? how do you know what's going on?
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
i think you should determine the value (or key code) of your keyboard "back" key. don't you define these things when creating your custom view? how do you know what's going on?
I just hadn't thought about the relation between the keyboard back key and bottom bar back key.
As simple as that. There is no major problem, but I will have to sort this out.

RBS
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
i have a mental block with regard to b4xpages. "default" b4a projects
have an activity and some events, one of which is keypress. erel
answered one of your posts saying that activity_Keypress was not
raised because edittext had the focus. i took this to mean that if
edittext did not have the focus, the "activity" version of b4xpages would
catch the keypress event.

in order for that to occur, 3 things need to happen"
1) keyboard must be down
2) you need a way of knowing this
3) edittext must no long have the focus

if the keyboard is up and/or the edittext has the focus, "activity" cannot
see the back key press.

my code knows when the keyboard is down, and it removes the focus
from edittext. if you know when the keyboard is down and you can remove
the focus, activity will see the key press.

i thought i saw you had routines in java (maybe it was a different thread), so i
thought you were more comfortable with it.

when you tap the back key on a keyboard, it's supposed to go down.
if it's not doing that, then your custom view is not following "standard"
behavior (ie, it's not doing what android thinks it's supposed to do in order
to present a uniform appearance to users). if the keyboard does go down,
you need to be made aware of this. i searched a lot before finding something
which - it claimed - was the way to accomplish it. it was straight out of google's
documentation, not something in stackoverlord. in addition, you need to
understand that causing the keyboard to disappear does not remove the
focus from the edittext. this is what's preventing activity from seeing the key
press. so after the keyboard disappears and tells you it's gone, you need
to clear the focus yourself. once that is done, the next key back press will
go to the activity. when i say clearing the focus has to be done by you, i
mean it can be done in the inline java routine or it can be done in b4a once
the keyboard down event is raised.

i think what i got from your threads is that you were expecting activity to
capture the back key when the keyboard is up. that's what i call abnormal
flow because you bypass 2 events to achieve it. not that there's anything wrong
with that.

i have no particular interest in implementing abnormal flow, so i haven't
put what i've done to rigorous testing. it was more to explain to you why
activity doesn't see the key press and what you can do about it. you may
have to adapt, but i think the process is clear. the clearFocus() method works
and is simple to implement with a javaobject. that leaves figuring out
how to make your keyboard disappear when the back key is pressed. you can't
reasonably poll for this; it has to be with a listener. even if you didn't know the
keyboard was not visible, by simply unsetting the focus on the edittext, you
allow activity to see the next back key press. you just need the keyboard to be
invisible and no focus in the edittext.
> when you tap the back key on a keyboard, it's supposed to go down.

This line is causing some confusion.
What key (on the Android keyboard) are you referring to here?
My custom keyboard behaves exactly as the Android keyboard, except it doesn't alter the Android bottom bar back key.

RBS

RBS
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
i think you should determine the value (or key code) of your keyboard "back" key. don't you define these things when creating your custom view? how do you know what's going on?
All solved now after tidying up some things in the custom keyboard class.
The main thing was to make sure that certain lines in the code were not run before lines above that particular line.
This was done by changing calls to a non-Resumable Sub to calls to a Resumable Sub.

After that it appears that this code will make the edittext not show the Auto-fill popup:

B4X:
NativeMe.RunMethod("disableSoftKeyboard", Array As Object(oEditText))

where NativeMe is set by this code in B4XMainPage:

B4X:
Sub Class_Globals
    Public NativeMe As JavaObject
End Sub

Private Sub B4XPage_Created(Root1 As B4XView)
    If NativeMe.IsInitialized = False Then
        NativeMe.InitializeContext
    End If
End Sub

As to my custom keyboard class not changing the Android bottom bar backkey from < to v (down arrow), I think that is fine
as after all the Android keyboard is not started.
All working fine now and hopefully this clears up some confusion.

RBS
 
Upvote 0
Top