Search code examples
codenameone

Splash page hangs if Android back button interrupts timer


I have a splash page that is set to run for a number of seconds then navigate from there to a home page or login etc. I noticed with Android devices if I press the hardware back button while the splash screen is shown and then launch the app again after the timer has completed, the home screen appears briefly then the splash screen opens and hangs just showing the splash screen.

Steps

  1. Launch app to splash screen
  2. Press android back button before the splash screen times out
  3. Open the app again, home screen appears briefly and then opens to the splash screen
  4. Splash screen hangs
  5. Can only recover by closing and opening the app again

This does not happen with iOS devices when using the home button etc.

I made a workaround by simply disabling the button while showing the splash screen but I wonder if there is a better to manage the splash screen and forms than I am using. I have made a small test case of the code below. I have commented out my workaround for setBackCommand

public class TestCase extends Lifecycle {
    @Override
    public void runApp() {
        Form splash = SplashPage();
        splash.show();

        new UITimer(() -> {

            HomePage();

        }).schedule(3000, false, splash);

    }

    public Form SplashPage(){

        Form formSplash = new Form(new BorderLayout());
        formSplash.getToolbar().setUIID("Container");
        formSplash.getToolbar().hideToolbar();

        Container northcnt = new Container(new BoxLayout(BoxLayout.Y_AXIS));
        Container centercnt = new Container(new BoxLayout(BoxLayout.Y_AXIS));
        centercnt.setScrollableY(true);
        Container southcnt = new Container(new BoxLayout(BoxLayout.Y_AXIS));

        
//        formSplash.setBackCommand(new Command("") {
//            public void actionPerformed(ActionEvent ev) {
//                ///DO NOTHING
//                Log.p("Android back button pressed");
//            }
//        });

        centercnt.add(new Label("Splash Page"));

        ((BorderLayout)formSplash.getLayout()).setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_CENTER_ABSOLUTE);
        formSplash.add(BorderLayout.NORTH, northcnt).add(BorderLayout.CENTER, centercnt).add(BorderLayout.SOUTH, southcnt);

        return formSplash;
    }

    public void HomePage(){

        Form formHome = new Form(new BorderLayout());
        formHome.getToolbar().setUIID("Container");
        formHome.getToolbar().hideToolbar();

        Container northcnt = new Container(new BoxLayout(BoxLayout.Y_AXIS));
        Container centercnt = new Container(new BoxLayout(BoxLayout.Y_AXIS));
        centercnt.setScrollableY(true);
        Container southcnt = new Container(new BoxLayout(BoxLayout.Y_AXIS));

        centercnt.add(new Label("Home Page"));

        ((BorderLayout)formHome.getLayout()).setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_CENTER_ABSOLUTE);
        formHome.add(BorderLayout.NORTH, northcnt).add(BorderLayout.CENTER, centercnt).add(BorderLayout.SOUTH, southcnt);

        formHome.show();

    }

    private void hello() {
        Dialog.show("Hello Codename One", "Welcome to Codename One", "OK", null);
    }

}

Solution

  • What happens is this.

    • Your app starts the timer and shows the splash
    • Back button is pressed, your app is minimized
    • You restore the app and it detects the timer elapsed. It shows the new Form
    • We restore the last Form shown in the app which was the Splash

    Since the time is no longer active, that's gone. The Splash will stay forever.

    The "right thing" would be to set the current form to the new form in the lifecycle class too but unfortunately that's a private field. I fixed that in this commit. Ideally we should have a simplified showSplash method in the Lifecycle class.