immersiveSticky and display.screenOriginX

Hi,

I’m trying to use immersiveSticky mode (no android soft navigation buttons). My config.lua is pretty close to the one posted here and I’m using the device detection code from here.

main.lua:

– hide the status bar

display.setStatusBar( display.HiddenStatusBar )

local device = require(“device”)

if device.isAndroid then 

  native.setProperty( “androidSystemUiVisibility”, “immersiveSticky” ) 

end

[…]

local function onSystemEvent( event )

  if event.type == “applicationStart” then

  gameconfig.getconfig()

    if device.isAndroid then 

       native.setProperty( “androidSystemUiVisibility”, “immersiveSticky” ) 

    end

    composer.gotoScene( “menu” )

In my menu scene I have a background image that I want to fill the entire screen, so I set:

background.width = display.contentWidth-display.screenOriginX

background.x = display.screenOriginX

in scene:create() and again in scene:show().

When the menu scene is first shown it doesn’t seem to have adjusted yet to the extra screen space - the background doesn’t reach the edges on the left or right.

When I switch to a different scene and back again, the  scene:show() code hits and resizes the background image, it is now shifted across on the left side but doesn’t quite reach the right hand side - it is short by exactly half of the width of the soft buttons.

Is there anything I can do to make the menu screen adjust immediately to the extra space? Also I thought display.contentWidth-display.screenOriginX was pretty standard but it doesn’t appear to be working in this case, am I doing something wrong?

Thanks :slight_smile:

Well if you’re using the config.lua from Moderinzing… then display.contentWidth will be the actual width of the screen. However, when config.lua is being processed it’s before you turn off the bar.  I don’t know if display.screenOriginX is being updated or not depending on the state of the screen.  Can you print out the value of display.actualContentWidth and display.contentWidth and see if it changes depending on the state of the softbar showing?

Rob

Thanks Rob - I’ve figured it out now. display.contentWidth-display.screenOriginX is correct for the right hand edge of the screen but I need the width so display.contentWidth-(display.screenOriginX*2)is right.

Well if you’re using the config.lua from Moderinzing… then display.contentWidth will be the actual width of the screen. However, when config.lua is being processed it’s before you turn off the bar.  I don’t know if display.screenOriginX is being updated or not depending on the state of the screen.  Can you print out the value of display.actualContentWidth and display.contentWidth and see if it changes depending on the state of the softbar showing?

Rob

Thanks Rob - I’ve figured it out now. display.contentWidth-display.screenOriginX is correct for the right hand edge of the screen but I need the width so display.contentWidth-(display.screenOriginX*2)is right.

plz, can you explain I have the same problem.

I am also having this issue. I have a few items on my screen that use screenOriginX as a precondition for their positioning.

For example:

Where in my main, I have:

\_H = display.contentHeight; \_W = display.contentWidth;

and

native.setProperty("androidSystemUiVisibility", "immersiveSticky")

Our config.lua has the following:

application = { content = { width = 320, height = 480, scale = "letterbox", imageSuffix = { ["@2x"] = 1.5, }, }, }

In my Opening Scene, I have:

myButton.x = display.screenOriginX + myButton.width \* 0.5 + 3; myButton.y = \_H - display.screenOriginY - myButton.height\*0.5;

and 

myButton2.x = (\_W - display.screenOriginX) - 55; 

Everything on the screen seems to be sized correctly. And everything aside from these items are positioned correctly. When opening the app on device with immersive mode, these items are now positioned closer to the edge of the screen and are now partially cut off. But, say I go to a new scene and then come back, now they are positioned in their correct positions. 

What in the world is going on? Please help!!

Bump.

Could someone from Corona please advise me on why these items are sometimes positioned incorrectly in immersive mode?

Thank you.

When you set up the screen to be immersive, it causes Android to hide the navigation bar, which causes the screen to be resized.  For example, if you are running a portrait app and you put the device into immersive mode, then the bottom navigation bar will disappear, causing your app window to be resized and become taller (ie: the height increases).  For a letterbox app such as yours, this causes the Corona content scale and screen origin to change.  What you *need* to do is handle the Corona “resize” event and re-layout your content to best-fit the new screen width and height.  Documentation about our “resize” event can be found here…

   https://docs.coronalabs.com/api/event/resize/index.html

It’s just too bad that the resize event doesn’t actually fire when resuming an app from task list and subsequently restoring immersive sticky.  But then again, the view itself never fully resizes itself back to immersive size either (black bar remains at bottom, see:  https://forums.coronalabs.com/topic/54351-problems-with-onsystemeventevent-and-immersivesticky/?hl=immersive) - I’d guess that the two issues are likely related.  (and all still true as of build 2627)

So if you DID read the full immersive screen size on startup after the first resize event, you now think the screen is “too big”. (because no subsequent resize event ever occurred to tell you that immersive failed to properly restore on resume from task list)

That resize event issue that occurs when resuming the app was solved in daily build #2529.

   http://developer.coronalabs.com/release/2015/2529/

That’s a different bug (suspend in one orientation, resume in another) - but didn’t resolve this problem:  no resize event on resume-from-task-list-to-immersive.

Are you sure?  Because I’m pretty sure the bug fix I mentioned above applies to both and has nothing to do with orientation.  The resize event is tied to the Android OpenGL SurfaceView’s resize Java listener.  It was especially need back in the Nook HD days which had a nasty habit of resizing the screen when navigation bar popped in out when going to/from the screen lock… which is the same circumstance that’s happening when you put the app into and out of immersive mode.

Yep, quite sure, even as of yesterday’s build (2627).  The other poster (in the other thread I linked) said they submitted a bug report - I can package up a super-simple test case and resubmit if they didn’t actually submit it.

The whole problem is that the surface itself is NOT being resized after resume-from-task-list.  So no surface resize, no resize event, no actual restoration of immersive mode SIZE.  That is, on system event applicationResume I reissue immersive sticky, and menu disappears, but the SURFACE is never resized to take advantage of it - so a black unoccupied area remains at the bottom of the screen, and display.whateverXY coordinates correspond to the smaller non-immersive area, you can swipe up and menu reappears confirming that the system is in immersive mode, Corona just isn’t USING it, and there’s no resize event or any indication in the display.whateverXY properties that it has failed.

If you restore via the icon (which will additionally fire an applicationOpen event) then the surface is properly restored to full immersive size.  But if you restore via the task list, all you get is applicationResume and the surface is NOT properly resized (and no resize event).

aside:  and while we’re at it, if any engineers happen to be listening for a “feature request”, it’d be nice to have access to a “focused” event.  for example, if you do any native ui like showAlert(), you’ll lose immersive and need to restore it.  yes, you can pepper your code full of calls to native.setProperty() to restore it, but it’d be nicer (and far more thorough) to just do it in one place whenever recovered focus.  or some other equivalent approach, cuz immersive goes away if you so much as sneeze at it – just need some more reliable way to consistently reestablish it.

I just tested the resize event with suspend/resume and immersiveSticky on an Android 5.0 device.  It’s definitely working for me.  The correct screen dimensions are getting reported during the resize event.  Below is the code I used to test this.  You may want to give this a try as well.
 
[config.lua]

application = { content = { width = 320, height = 480, scale = "letterbox", }, } 

[main.lua]

-- Hide the status bar. display.setStatusBar(display.HiddenStatusBar) -- Display a black bacground rectangle in Corona's content region and red in the letterbox region. local backgroundRectangle = display.newRect(0, 0, 0, 0) backgroundRectangle:setFillColor(0, 0, 0) display.setDefault("background", 1, 0, 0) -- Define the rest of our variables. local textObject = nil local textGuideLineTop = nil local textGuideLineBottom = nil local textGuideLineLeft = nil local textGuideLineRight = nil -- Creates a red horizontal line at the given position within the content region. -- It will not draw the line in the letterbox region. local function createHorizontalGuidingLineAt(positionY) local line = display.newLine(0, positionY, display.contentWidth, positionY) line:setStrokeColor(1, 0, 0) line.strokeWidth = 3 \* display.contentScaleY -- Set line width to 3 pixels. return line end -- Creates a red vertical line at the given position within the content region. -- It will not draw the line in the letterbox region. local function createVerticalGuidingLineAt(positionX) local line = display.newLine(positionX, 0, positionX, display.contentHeight) line:setStrokeColor(1, 0, 0) line.strokeWidth = 3 \* display.contentScaleX -- Set line width to 3 pixels. return line end -- Creates and/or updates all display objects to fit the window. local function updatePositions() -- Update the text object. local x = display.contentCenterX local y = display.contentHeight local pixelWidth = display.pixelWidth local pixelHeight = display.pixelHeight if ((system.orientation == "landscapeRight") or (system.orientation == "landscapeLeft")) then local length = pixelWidth pixelWidth = pixelHeight pixelHeight = length end local message = "Window Size: " .. tostring(pixelWidth) .. "x" .. tostring(pixelHeight) .. " pixels" print(message .. ", Orientation: " .. tostring(system.orientation) .. ", Content Scale: " .. tostring(display.contentScaleX)) if textObject then textObject:removeSelf() end local textSettings = { x = display.contentCenterX, y = display.contentCenterY, text = message, align = "center", } textObject = display.newText(textSettings) -- Update the black background rectangle to fit the content region. backgroundRectangle.x = display.contentCenterX backgroundRectangle.y = display.contentCenterY backgroundRectangle.width = display.contentWidth backgroundRectangle.height = display.contentHeight -- Re-position the text object's guiding lines. local bounds = textObject.contentBounds if (textGuideLineTop) then textGuideLineTop:removeSelf() end if (textGuideLineBottom) then textGuideLineBottom:removeSelf() end if (textGuideLineLeft) then textGuideLineLeft:removeSelf() end if (textGuideLineRight) then textGuideLineRight:removeSelf() end textGuideLineTop = createHorizontalGuidingLineAt(bounds.yMin) textGuideLineBottom = createHorizontalGuidingLineAt(bounds.yMax) textGuideLineLeft = createVerticalGuidingLineAt(bounds.xMin) textGuideLineRight = createVerticalGuidingLineAt(bounds.xMax) end updatePositions() -- Called when the window has been resized. Re-layouts the app display objects to fit. local function onResized(event) updatePositions() end Runtime:addEventListener("resize", onResized) -- Called when the screen is tapped on. Enables Android's immersive mode. local function onTap(event) native.setProperty("androidSystemUiVisibility", "immersiveSticky") end Runtime:addEventListener("tap", onTap)

The above app will display the window’s pixel width and height onscreen and update it when a resize event occurs.  The letterbox areas will be highlighted in red and a red guidelines will be displayed around the text object.  The project supports landscape and portrait, so, feel free to enable both and rotate.  I definitely did.  For example, I enabled immersive mode, tapped the application list, rotated, and then tapped to get back in the app.  The resize event was definitely dispatched and re-layed out itself as expected.  Note that this project will also print() the current pixel size of the window when the resize event occurs, proving that the resize event is definitely getting dispatched upon resume.

I did some more testing and I did notice that a black bar is sometimes shown at the bottom of the screen where the navigation bar used to be when resuming the app.  Dave, I believe this is the issue you were talking about, right?  Corona’s surface is definitely being sized correctly as you can tell by look at the pixel dimensions reported onscreen and by the guide lines I’m rendering onscreen in the project.

This looks like a bug in the Android OS.  In fact, I’ve found a bug report about this on Google’s issue tracker here…

   https://code.google.com/p/android/issues/detail?id=170752&q=IMMERSIVE&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars

Since it’s a bug in the operating system and native Android developers have no means of knowing if any OS overlays are on top of the app, your only option is to find a work-around to force the OS to clear the bottom bar it’s rendering.  I’m thinking disabling and re-enabling immersive mode upon resume is your best bet.

Yes, that’s the black bar I’m talking about.  If it’s the OS, oh well, guess I’m out of luck.

It occurs 100% of the time on a Google Nexus 7 2013 5.0.2 when resuming from task list (my app doesn’t support rotation).  Once present, there seems to be no way to remove it during that session (i’ve tried disabling/renabling immersive - nope; timer.performWithDelay in case of some sensitive period following resume - nope; etc)

But if you suspend again and restore via icon (not task list) it goes away (and stays away for that session).

Suspend again, resume via task, it’s back; suspend again, resume via icon, it’s gone; repeat. ad infinitum, 100% repeatable.

My assumption (now proven wrong) was the surface wasn’t being resized, because ask this:  what are the reported dimensions versus what are the visible-by-a-human-using-the-device dimensions?  They don’t agree.  So if I draw at/near the reported bottom of the screen, it’s not visible.

If the bar must remain, then I’d actually rather have it NOT resize to immersive dimensions, and properly report the smaller dimensions, so that at least the area that I can access has proper dimensions.  (I re-read my last reply, and apologize that it’s a bit unclear - it’s a problem description that sort of only makes sense if you’ve seen the problem.  If indeed the surface is being resized, then “flip” the problem description to “properly-resized-surface does not occupy entire screen, reports full dimensions when only partial dimensions are ‘usable’”)

I have numerous other third-party non-Corona-developed immersive apps on that same device that do not exhibit this behaviour at all.  So while it may be the result of an OS issue, perhaps it’s an edge case where it CAN work properly/reliably but perhaps Google’s documentation of all the implementation details are lacking (what?? google’s docs lacking???  never! :D).  Is it perhaps still worth checking to see if there’s still some small detail missing in Corona’s implementation?

Thanks for your consideration.  :)

I tried several things in an attempt to work-around this issue, but I haven’t found a solution yet.  Interestingly enough, you don’t have to be in immersive mode to begin with to reproduce this OS bug.  If you go to the task manager while *not* in immersive mode, return to your app, and then put your app in immersive mode, then the same black bar issue happens at the bottom of the screen.  It’s definitely an OS bug where it is mishandling the navigation bar overlay.  And what makes it so difficult from a native development perspective is that the app doesn’t know that a black bar is overlaid on top of it.

I tried Google’s Chrome app and playing a YouTube video in immersive mode, and I can see what you’re saying.  Google’s app returns back to the correct state.  Wish I knew how they did it.  This is one of those things where it may take a few day of experimentation and trial&error to find a work-around.  Hmm…

Is there a way (or configuration) to use immersive mode without requiring a resize? When I start my app, I see the resize happening in my opening scene. Also, if I put the the device to sleep and then then resume while I’m in the middle of my app, I see the resize happen. 

Is there a way I can have it so my app is always the same size  (ie. the size required while in immersive mode)? (As opposed to… the app always having to resize once immersive mode is recognized). I feel like this resizing feature is not ideal because it affects any and every object that’s dependent upon display.screenOriginX. So, say I used display.screenOriginX as a factor in positioning or sizing my objects… or say I used it as a boundary on a sidescroller for spawning and killing objects. Having to refresh all of this data in all of my scene’s is pretty taxing. I’d have to make some drastic updates to my app.

You can see an example of the desired immersive mode behavior in Sonic Dash. Despite the fact it uses immersive mode, it never resized. Instead, it’s always uses one size.

Thank you!