Bug? Layout corrupted if device is rotated with IME open

agraham

Expert
Licensed User
Longtime User
This is possibly an Android 'feature' but I have been wrestling to overcome this for several days and have given up and admit the need for help..

The attached trivial project is a primitive code editor. Run the program on a device and rotate it from portrait to landscape and back - all is well. Now tap on the EditText to open the IME and rotate the device with the IME open, whether from portrait to landscape or vice versa. I now get a problem on some of my devices.

On my Samsung Galaxy A3 (Android 8.0) all is well. The IME closes, the layout redraws correctly and the IME remains closed.

On my Unihertz Jelly Pro (Android 8.1) and my Xiaomi Mi Max 3 (Android10) after rotation the IME re-opens and the layout is not as expected - presumably because the IME is interfering with the apparent Activity size.

The visual results seem to differ slightly between keyboards (GBoard, Smart Keyboard Pro, etc) for the portrait to landscape rotation with the IME open but the landscape to portrait rotation with IME open seems to always be the same. I may be missing the blindingly obvious here but my attempts to programmatically close the IME in Activity_Pause to avoid this problem fail. Has anyone else seen this and has a solution?
 

Attachments

  • BasicIDETest.zip
    9.4 KB · Views: 347
Last edited:

JohnC

Expert
Licensed User
Longtime User
It seems the problem is this line in your manifest:

B4X:
SetActivityAttribute(main, android:windowSoftInputMode, adjustResize|stateHidden)

Remarking it out seems to fix the issue on my device.

Your remarks seem to state that this line is required otherwise "AddHeightChangedEvent doesn't fire after an activity pause and resume". But even with this line in the manifest, the "IME_HeightChanged" event still doesn't fire when I rotate the device (without the keyboard displayed) or when the keyboard is shown and I press the home button to switch away (pause) from this apps activity.

I never used this line in any of my apps, so I never saw the "problem" that this line is suppose to fix. And for the few times IME wasn't doing what I wanted (didn't close when it should or something else), then I would just add the code to get it to do what I wanted.
 
Last edited:

JohnC

Expert
Licensed User
Longtime User
Here is a variation of your demo app that placed all the views at the bottom in a separate panel...
 

Attachments

  • BesicIMETesta.zip
    9.5 KB · Views: 318

JohnC

Expert
Licensed User
Longtime User
I noticed a curious effect when I placed a toastmessage in the IME_HeightChanged event to display the NewHeight value on each trigger.

When I rotate it from landscape to portrait, the toast message display like 3-4 values before finally settling at the full screen height.

I'm thinking that even though you set the Activities animation to 0, my OS must be adding an small animation and that's why the numbers are changing before stopping.
 

agraham

Expert
Licensed User
Longtime User
It seems the problem is this line in your manifest:
The demo app is merely a minimal demonstration of the problem. That line is needed to consistently to ensure that the HeightChanged events keep coming which the full app requires to reposition elements to keep them in view when editing. Erel documents this in the IME library tutorial thread.

Remarking it out seems to fix the issue on my device.
I assume that you also see the issue which is reassuring that it is not just me that experiences it.
 

JohnC

Expert
Licensed User
Longtime User
I assume that you also see the issue which is reassuring that it is not just me that experiences it.
The problem I see is when switching from landscape to portrait (with the keyboard displayed), the keyboard does hides, but the various misc views stay positioned in the middle of the screen with the edittext view sized to only half the screen.

When I remove that line from the manifest, then everything positions properly.
 
Last edited:

agraham

Expert
Licensed User
Longtime User
When I remove that line from the manifest, then everything positions properly.
I know, but it isn't satisfactory to do that for the full app. I've tried without that line and the app ceases to get HeightChanged events after a pause and resume just as my comment says.
 

JohnC

Expert
Licensed User
Longtime User
I know, but it isn't satisfactory to do that for the full app. I've tried without that line and the app ceases to get HeightChanged events after a pause and resume just as my comment says.
What actions are you doing to generate this "pause/resume" you mentioned above? Are you changing orientations, or switching to another app and returning to your app?

I'm just curious about this because I have not run into this problem with any of my apps.
 
Last edited:

agraham

Expert
Licensed User
Longtime User
Are you ... switching to another app and returning to your app?
Yes, this is my own Basic language IDE. Although my Basic interpreter is pretty good, making a smoth scrolling syntax highlighting editor in B4A to feed it is not practical at the moment (at least for me) so for big edits I would switch to another code editor (currently QuickEdit+) and back by double tapping the Recent button. On return to the IDE HeightChanged events are no longer received without this line in the manifest.
 
Last edited:

JohnC

Expert
Licensed User
Longtime User
Understood.

Hopefully this issue is reproducible by Erel and he can dig deeper into what's going wrong.
 
Last edited:

ilan

Expert
Licensed User
Longtime User
i also see this bug. the activity height is wrong when you switch from landscape to portrait while the keyboard is open. i tried to hide the keyboard when activity_pause i fired but no luck. removing the line in manifest does fix the issue but as agraham said the HeightChanged is not fired after going home and coming back to the app until you switch again the orientation.

really weird. another thing i noticed is that when you switch from portrait to landscape the keyboard hides but from landscape to portrait it stays even if i call ime.hidekeyboard and even if the activity is recreated. why is it still visible? maybe a bug in android?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
This is a known issue, similar to the issue with switching between full screen activity to non-full screen activity.

A workaround which works on the device I tested:
B4X:
Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Dim IME As IME

    Dim btnRun As Button
    Dim chkBreak As CheckBox
    Dim chkStep As CheckBox
    Dim edtSource As EditText
    Dim btnLineNum As Button
    Dim lblLine As Label
    Dim chkWrap As CheckBox
    Private chkShrink As CheckBox
    Private chkSideBySide As CheckBox
    Private ActivityHeight As Int
    Private ActivityHeightSet As Boolean
End Sub

#Region Activity Events

Sub Activity_Create(FirstTime As Boolean)
    IME.Initialize("IME")
    IME.AddHeightChangedEvent
    ActivityHeight = 100%y
    If FirstTime = False Then
        Sleep(500)
    End If
    ActivityHeightSet = True
    Activity.Height = ActivityHeight
    Activity.LoadLayout("Layout") 
    
    edtSource.Color = Colors.White ' this hides the horizontal line at the bottom of the EditText
    Dim jo As JavaObject
    ' keep edtSource visible in landscape
    jo = edtSource
    jo.RunMethod("setImeOptions", Array(33554432)) ' IME_FLAG_NO_FULLSCREEN
    edtSource.Wrap = False
End Sub

Sub Activity_Pause (UserClosed As Boolean)    
    IME.HideKeyboard
End Sub

Sub IME_HeightChanged (NewHeight As Int, OldHeight As Int)
    Log("HeightChanged: " & NewHeight)
    If ActivityHeightSet = False Then
        Log("Setting activity height: " & NewHeight)
        ActivityHeight = NewHeight
    End If
End Sub
 

agraham

Expert
Licensed User
Longtime User
A workaround which works on the device I tested:
Doesn't work in a real app. The Sleep(500) exits Activity_Create and runs Activity_Resume which immediately bombs trying to set/restore view properties which don't exist because the layout isn't loaded.
 

ilan

Expert
Licensed User
Longtime User
I'm afraid that workaround is a hack too far for me. IME event handling seems sub-optimal to me. I get strange new and old height values in the events on rotations. I'll just live with it for the time being. :(

you could build your own keyboard. i did it once in one of my apps. then you can handle it as any other view and have much more control over it then the build-in android keyboard. it should not take you more than 2 hours to build such a keyboard, much less than the time you spent on trying to fix that issue. ;)
 
Last edited:

agraham

Expert
Licensed User
Longtime User
I've found a better hack localised to a few lines within Activity_Resume. I already had a Process Global IMEIsOpen flag so I used that . This is fine for the few times it will be needed.
B4X:
Sub Activity_Pause (UserClosed As Boolean)
     ' ...
   IME.HideKeyboard
End Sub

Sub Activity_Resume
    ' ...
    If IMEIsOpen Then
        IMEIsOpen = False       
        Activity.Finish
        StartActivity(Me)
    End If
End Sub

Sub IME_HeightChanged (NewHeight As Int, OldHeight As Int)
Log("IME new = " & NewHeight & " old = " & OldHeight & " Act = " & Activity.Height)
    If NewHeight > OldHeight Then 'closed
        IMEIsOpen = False
        ' ...
    Else  if NewHeight <> Activity.Height Then ' opened
        IMEIsOpen = True
        ' ...
    End If
End Sub
 

agraham

Expert
Licensed User
Longtime User
Here is a better way of restarting the Activity. Unlike Finish->StartActivity which restores full screen mode if the app is in split screen this way maintains the split screen mode. Available in API 10 or later.
B4X:
Sub RecreateActivity
    LogColor("Recreating", Colors.Red)
    Dim jo As JavaObject
    jo.InitializeContext
    Return jo.RunMethod("recreate", Null)
End Sub
 
Top