hardware backbutton listeners to move back through different Director screens

Searched in the forums and online but can’t get this working, can someone help?

I have a multi screen app that uses Director. I want the hardware back button on Android devices to function as expected - i.e. from the home_screen if a button opens a new module (screen2) then pressing back should take you back to home_screen. But if on the home_screen, the back button should exit the app.

I’ve read that I need to scope my hardwareKeyEvent listener to outside of each Director scene so here’s what I’ve tried:

main.lua (which scopes onHardwareKeyEvent as a global table)

local director = require("director") local mainGroup = display.newGroup() --====================================================================-- -- GLOBAL -- handle to scope hardwareKeyEvent listeners outside of each Director scene local onHardwareKeyEvent = {} --====================================================================-- -- MAIN FUNCTION local main = function () mainGroup:insert(director.directorView) director:changeScene("home\_screen") return true end --====================================================================-- -- BEGIN --====================================================================-- main()

home_screen.lua (which then defines as a function which returns the default key operation:

            onHardwareKeyEvent = function( event )

              return false         – use default key operation

            end

module(…, package.seeall)

–====================================================================–

– SCENE: Home Screen

–====================================================================–

new = function ( params )

    

    ------------------

    – Constants

    ------------------

    BTN_LABEL = “Open Screen”

    

    ------------------

    – Imports

    ------------------

    

    --import the ui library for buttons

    local ui = require ( “ui” )

    --import the widgets library for buttons

    local widget = require ( “widget” )

    ------------------

    – Groups

    ------------------

    

    local localGroup = display.newGroup()

    

    --====================================================================–

    – BUTTONS

    --====================================================================–

    

    local function onButtonEvent( event )

        local btn = event.target

            if event.phase == “ended” then

                if btn.id == “button1” then

                    director:changeScene( { content=“Hello” } , “screen2”, “fade” )

                end

            end

    end

    

    local button1 = widget.newButton{

        id = “button1”,

        defaultFile = “images/ui/button.png”,

        overFile = “images/ui/button_over.png”,

        width = 200,

        height = 41,

        onEvent = onButtonEvent,

        label = BTN_LABEL,

        font = native.systemFontBold,

        fontSize = 16,

        labelColor = { default={ 205 }, over={ 105 }}

    }

    

    --====================================================================–

    – INITIALIZE

    --====================================================================–

    

    local initVars = function ()

        

        ------------------

        – Inserts

        ------------------

        localGroup:insert( button1 )

        

        ------------------

        – Positions

        ------------------

        button1.x = display.contentCenterX

        button1.y = display.contentCenterY+50

        ------------------

        – Listeners

        ------------------

        

        – hardware key callback

        local resetHardwareKeyListeners = function()

             onHardwareKeyEvent = function( event )

              return false         – use default key operation

            end

        end

        Runtime:removeEventListener( “key”, onHardwareKeyEvent )

        resetHardwareKeyListeners()

        Runtime:addEventListener( “key”, onHardwareKeyEvent )

        

    end – end initVars() function

    

    ------------------

    – Initiate variables

    ------------------

    

    initVars()

    

    ------------------

    – MUST return a display.newGroup()

    ------------------

    

    return localGroup

    

end

screen2.lua   (which then attempts to ‘reset’ the onHardwareKeyEvent function)

module(…, package.seeall)

–====================================================================–

– SCENE: SCREEN 2

–====================================================================–

new = function ( params )

    

    ------------------

    – Imports

    ------------------

        local ui = require(“ui”)

        local widget = require “widget”

    ------------------

    – Params

    ------------------

    local vContent = “no content”

    if type( params ) == “table” then

        

        if type( params.content ) == “string” then

            vContent = params.content

        end

    end

    ------------------

    – Groups

    ------------------

    

    local localGroup = display.newGroup()

    

    ------------------

    – Display Objects

    ------------------

    

    local background = display.newRect(0, 0, display.contentWidth, display.contentHeight)

    local contentText = display.newText( vContent, 0, 0, native.systemFontBold, 16 )

        

    --Setup the back button    

        local function handleBackButtonEvent( event )

                print()

                print(“pressed back”)

                director:changeScene( “home_screen”, “moveFromLeft” )

        end

    local backBtn  = widget.newButton{

            id = “backBtn”,

            defaultFile = “images/ui/backButton.png”,

            overFile = “images/ui/backButton_over.png”,

            width = 60,

            height = 34,

            onRelease = handleBackButtonEvent,

            label = "  Home",

            font = native.systemFontBold,

            fontSize = 12,

            labelColor = { default={ 205 }, over={ 105 }}

        }

    ------------------

    – Listener

    ------------------

    

    local doSlide = function ( event )

        if event.phase == “ended” then

            if event.xStart >= event.x then

                director:changeScene( “home_screen”, “moveFromRight” )

            else

                director:changeScene( “home_screen”, “moveFromLeft” )

            end

        end

    end

    

    --====================================================================–

    – INITIALIZE

    --====================================================================–

    

    local initVars = function ()

        

        ------------------

        – Inserts

        ------------------

        localGroup:insert( background )

        localGroup:insert( contentText )

        localGroup:insert( backBtn )

        

        ------------------

        – Positions

        ------------------

        

        --initial values

        local screenOffsetW, screenOffsetH = display.contentWidth -  display.viewableContentWidth, display.contentHeight - display.viewableContentHeight

        contentText.x = 160

        contentText.y = 240

        

        backBtn.x = math.floor(backBtn.width/2) + screenOffsetW

        backBtn.y = math.floor(display.screenOriginY + 20)

        

        

        ------------------

        – Colors

        ------------------

        

        contentText:setTextColor( 255,255,255 )

        background:setFillColor(77, 77, 77)

        

        ------------------

        – Listeners

        ------------------

        

        – Key listener for hardware keys (android)

        local resetHardwareKeyListeners = function()

             onHardwareKeyEvent = function( event )

                local returnValue = false         – use default key operation

                local phase = event.phase

                local keyName = event.keyName

                if ( phase == “up” and  keyName==“back” ) then

                        director:changeScene( “home_screen”, “moveFromLeft” )

                        returnValue = true         – disable default key operation

                end

                return returnValue

            end

        end

        Runtime:removeEventListener( “key”, onHardwareKeyEvent )

        resetHardwareKeyListeners()

        Runtime:addEventListener( “key”, onHardwareKeyEvent )

        

        background:addEventListener( “touch” , doSlide )

        

    end

    

    ------------------

    – Initiate variables

    ------------------

    

    initVars()

    

    ------------------

    – MUST return a display.newGroup()

    ------------------

    

    return localGroup

    

end

Unfortunately this doesn’t work. On loading the app, on the first screen the back button works as expected - it quits the app. But if you load the app > goto screen2, and press the back button (which takes you back to home_screen), the back button then continues to return to ‘home_screen’ - it never exits.

I just can’t remove the event listener or ‘reset’ the function that the hardware key listener is calling…

Any help much appreciated!

(attached is full test code)

With Storyboard, I can get the current scene name and if I’m not on my menu scene, I goto the menu scene.  If I’m on the menu scene, then I request an exit.

I haven’t tried this with director, but you may need to setup some scene identifier that lets you get the name of the current scene to test against.

I like your thinking! :slight_smile: I think I can work with that… Do you mean something along the lines of:

onHardwareKeyEvent = function( event ) local returnValue = false -- use default key operation local phase = event.phase local keyName = event.keyName if ( phase == "up" and keyName=="back" ) then if sceneName == "scene2" then director:changeScene( "home\_screen", "moveFromLeft" ) returnValue = true -- disable default key operation elseif sceneName == "scene3" then director:changeScene( "scene2", "moveFromLeft" ) returnValue = true -- disable default key operation end end return returnValue end Runtime:addEventListener( "key", onHardwareKeyEvent )

So I assume I’d never have to call ‘removeEventListener’ and would only define the callback function once… but do any Director users out there know where it would be best for me to scope this Runtime listener? In the root of main.lua? Within local main = function () ?

Will try it tomorrow and report back. Thanks for the tip.

Well if you’re on your home scene, then you need to do a native.requestExit() API call so it will go back to the OS.

I would put the code in your main.lua

Thanks again Rob. Works great. For info, here’s what I did.

1/ removed anything to do with the hardware key listener from both home_screen.lua and screen2.lua.

2/ At the top of each of those files (after the module declaration: ‘module(…, package.seeall)’ line) I added a global variable declation:

\_G["screenName"] = require("home\_screen")

and

\_G["screenName"] = require("screen2")

> essentially passing a reference to the current screen to the global table.

3/ In the ‘root’ of main.lua I added the following code:

local onHardwareKeyEvent = function( event ) local returnValue = false -- use default key operation local phase = event.phase local keyName = event.keyName if ( phase == "up" and keyName=="back" ) then if \_G["screenName"]["\_NAME"] == "screen2" then director:changeScene( "home\_screen", "moveFromLeft" ) returnValue = true -- disable default key operation elseif \_G["screenName"]["\_NAME"] == "home\_screen" then native.requestExit() returnValue = true -- disable default key operation end end return returnValue end Runtime:addEventListener( "key", onHardwareKeyEvent )

Full working example attached, for anyone interested.

With Storyboard, I can get the current scene name and if I’m not on my menu scene, I goto the menu scene.  If I’m on the menu scene, then I request an exit.

I haven’t tried this with director, but you may need to setup some scene identifier that lets you get the name of the current scene to test against.

I like your thinking! :slight_smile: I think I can work with that… Do you mean something along the lines of:

onHardwareKeyEvent = function( event ) local returnValue = false -- use default key operation local phase = event.phase local keyName = event.keyName if ( phase == "up" and keyName=="back" ) then if sceneName == "scene2" then director:changeScene( "home\_screen", "moveFromLeft" ) returnValue = true -- disable default key operation elseif sceneName == "scene3" then director:changeScene( "scene2", "moveFromLeft" ) returnValue = true -- disable default key operation end end return returnValue end Runtime:addEventListener( "key", onHardwareKeyEvent )

So I assume I’d never have to call ‘removeEventListener’ and would only define the callback function once… but do any Director users out there know where it would be best for me to scope this Runtime listener? In the root of main.lua? Within local main = function () ?

Will try it tomorrow and report back. Thanks for the tip.

Well if you’re on your home scene, then you need to do a native.requestExit() API call so it will go back to the OS.

I would put the code in your main.lua

Thanks again Rob. Works great. For info, here’s what I did.

1/ removed anything to do with the hardware key listener from both home_screen.lua and screen2.lua.

2/ At the top of each of those files (after the module declaration: ‘module(…, package.seeall)’ line) I added a global variable declation:

\_G["screenName"] = require("home\_screen")

and

\_G["screenName"] = require("screen2")

> essentially passing a reference to the current screen to the global table.

3/ In the ‘root’ of main.lua I added the following code:

local onHardwareKeyEvent = function( event ) local returnValue = false -- use default key operation local phase = event.phase local keyName = event.keyName if ( phase == "up" and keyName=="back" ) then if \_G["screenName"]["\_NAME"] == "screen2" then director:changeScene( "home\_screen", "moveFromLeft" ) returnValue = true -- disable default key operation elseif \_G["screenName"]["\_NAME"] == "home\_screen" then native.requestExit() returnValue = true -- disable default key operation end end return returnValue end Runtime:addEventListener( "key", onHardwareKeyEvent )

Full working example attached, for anyone interested.