community icon indicating copy to clipboard operation
community copied to clipboard

Added proper handling for back button on Android

Open inclement opened this issue 7 years ago • 26 comments

With this PR, the back button will exit to the homescreen without stopping Kivy, just like the home button.

I think this is correct behaviour because, although SDL2 maps the back button to the esc keycode, it doesn't have the same implications as on the desktop. That said, I just threw this together from the code in my own app and have made the PR to raise discussion, the best solution may not be the same as this.

Something definitely needs changing, as right now kivy halts without informing Android, so it appears to have crashed and will raise an error popup when the user navigates back to the app.

Edit: I also haven't tested this exact code, even if you really love it then that needs doing before merging.

inclement avatar Oct 14 '16 22:10 inclement

I'm not sure if this is a good idea, because it reminds me some of the games made with Unity which didn't have back button implemented at all and the only thing that could be done was using home button, which only paused the game and left a lot of stuff in memory (not sure for how long or if it is removed later, I just purged it with force). With Kivy there's always packaged the whole interpreter, so what would happen if there were more Kivy apps on a phone with this behavior launched one after another and exited only with home button (the new back button behavior)?

Will be everything collected from memory, or will it hang there and if, for how long? Leaving app paused is a good thing (with home button as an intentional pause), yet I expect a back button to exit the app, not just put it in background.

KeyWeeUsr avatar Oct 14 '16 23:10 KeyWeeUsr

With Kivy there's always packaged the whole interpreter, so what would happen if there were more Kivy apps on a phone with this behavior launched one after another and exited only with home button (the new back button behavior)?

Android would quietly halt the app and reclaim the memory if it ever needs it. This is the recommended Android activity model.

inclement avatar Oct 14 '16 23:10 inclement

What about data loss while paused and the memory would get reclaimed? I haven't really poked into how Java apps handle the back button, but if there's no real danger making the back button behave like that, I'm for that. Except the potential memory & data issues I mentioned it can only bring the good stuff such as quicker loading (if in memory) and converging to the Java Android app model. 👍

KeyWeeUsr avatar Oct 14 '16 23:10 KeyWeeUsr

When the app is paused, it gets a few seconds to store data (via onPause), but after that Android provides no guarantee that it will ever run again. It is expected as part of this execution model that if you don't save data, it may be lost.

On 15/10/16 00:32, Peter Badida wrote:

What about data loss while paused and the memory would get reclaimed? I haven't really poked into how Java apps handle the back button, but if there's no real danger making the back button behave like that, I'm for that. Except the potential memory & data issues I mentioned it can only bring the good stuff such as quicker loading (if in memory) and converging to the Java Android app model. 👍

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/kivy/kivy/pull/4670#issuecomment-253943807, or mute the thread https://github.com/notifications/unsubscribe-auth/ABNQmxldiTq7jR40bIJSmANJIDSMNyvOks5q0BEegaJpZM4KXgqr.

inclement avatar Oct 14 '16 23:10 inclement

Will it be possible to cleanly exit without pausing when the back button is pressed?

dessant avatar Oct 15 '16 02:10 dessant

@inclement in the standard Android Activity model, when you press back button, Activity's onDestroy() is called and activity is killed. Only when you'll override onBackPressed(), you can achieve behavior from the PR. While I like it, it is not the default that Android activities are working like.

@dessant to achieve that you'll need to bind to Window's on_keyboard, and from there call app.stop() when key 27 is pressed.

ghost avatar Oct 15 '16 09:10 ghost

I again feel this is not a good idea mainly because of Activity lifecycle:

There are a few scenarios in which your activity is destroyed due to normal app behavior, such as when the user presses the Back button or your activity signals its own destruction by calling finish().

When your activity is destroyed because the user presses Back or the activity finishes itself, the system's concept of that Activity instance is gone forever because the behavior indicates the activity is no longer needed.

These quotes show that the current behavior of back button in Kivy app is actually the most accurate if compared to the Java Android app.

KeyWeeUsr avatar Oct 16 '16 09:10 KeyWeeUsr

@inclement, @KeyWeeUsr: so, was it settled? 😋

dessant avatar Dec 11 '16 06:12 dessant

Well, all apps seem to have back button to exit/close an app, even android seems to do so in its docs, so I don't feel it's a good change. Let's leave this behavior to Home button.

KeyWeeUsr avatar Dec 13 '16 23:12 KeyWeeUsr

I realise I was wrong about this being the most correct thing to do according to the docs, but I still think we should consider it - it seems that one reason for the java model is that the java ui can be quickly regenerated, but restarting a Kivy app means completely restarting the python process which is very slow. I think apps like games often override the back button in the same way, presumably for the same reason

inclement avatar Dec 13 '16 23:12 inclement

But this way everything remains as if the app was paused, which just kills everyone else's app that might depend on cleaning the environment properly each time. Maybe a better approach would be a property set in App.build() or in config.ini?

KeyWeeUsr avatar Dec 16 '16 20:12 KeyWeeUsr

It should be toggleable, but I'm not sure there's really a concern about killing other peoples apps - it depends on how Android decides what to halt, but it seems more likely that the Kivy app would be the first one killed if it's consuming resources disproportionately.

On 16/12/16 20:24, Peter Badida wrote:

But this way everything remains as if the app was paused, which just kills everyone else's app that might depend on cleaning the environment properly each time. Maybe a better approach would be a property set in |App.build()| or in |config.ini|?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/kivy/kivy/pull/4670#issuecomment-267687688, or mute the thread https://github.com/notifications/unsubscribe-auth/ABNQm0KbBgc2bwUcfQOF9PC5ZOIV8DZVks5rIvOGgaJpZM4KXgqr.

inclement avatar Dec 16 '16 20:12 inclement

Ah, bad wording. Let's say you make a casual application, not a game. You have there some variables that need to reset each time an App starts e.g. you fetch some token, get a key from url or something. This change doesn't allow the app to exit properly, only relies on Android taking care of it and

... that if you don't save data, it may be lost.

in the worst case Android might even restore the data e.g. if you have on_pause enabled, press home button and quickly run the app again. I see the same thing I left it when paused. When a user works with files it might be even less desirable just as out-of-box feature. That's why if it should be included, let user decide how to handle this stuff with Config.

KeyWeeUsr avatar Dec 16 '16 20:12 KeyWeeUsr

I think this idea makes sense since Kivy app starts much longer then Java-based Android apps.

On other hand if app uses to process files, this PR right now may lead to crush.

germn avatar Dec 28 '16 07:12 germn

What about Config.set('kivy', 'pause_on_minimize', 1)? Wouldn't it be better to reuse this functionality to pause the app instead of making back button behave as the second home button?

KeyWeeUsr avatar Jan 25 '17 12:01 KeyWeeUsr

@KeyWeeUsr that's more about pausing the event loop or keeping it runnig, but you don't have a choice for that on mobile afaik. Respecting exit_on_escape might be a better fit.

dessant avatar Jan 25 '17 12:01 dessant

I meant something like this:

if WindowBase.on_keyboard.exit_on_escape:
    if key == 27 or all([is_osx, key in [113, 119], modifier == 1024]):
        if not self.dispatch('on_request_close', source='keyboard'):
            if platform == 'android' and Config.getbool('kivy', 'pause_on_minimize'):
                # back button on Android should not be treated
                # the same as desktop's ESC
                from jnius import autoclass, JavaException
                .
                .
                .
            else:
                stopTouchApp()
                self.close()
            return True

Which basically means a user would have an option to toggle this functionality.

KeyWeeUsr avatar Jan 25 '17 12:01 KeyWeeUsr

There isn't a way to signal in java that the app wants to exit now instead of waiting for the process to be stopped by android?

dessant avatar Jan 25 '17 12:01 dessant

@KeyWeeUsr, yeah, but if we do that it just means we hide the bug by default instead of fixing it, just like the pr does in its current form. I agree about this being an undesired change.

dessant avatar Jan 25 '17 12:01 dessant

It's not a bug, it's just Kivy being loaded a little bit more than desired, which I think we can't really do anything about it except attempt to hide it like in this PR or on_pause->return True or another alternative.

I do agree that it's not nice to have hanging app just because it loads long enough for an end-user to notice and if I had some big app with Kivy I'd probably make it pause by default too, but there shouldn't be just a big switch from a common android behavior to pausing everything just because Kivy can become a loading beast similar to Unity if you really want to make it so.

Hence the bool option. Maybe pause_on_minimize isn't the best choice and another variable directly related to this functionality would be better.

Also, about the signal in java, I don't know anything except finishActivity() and system exit(like exit() in py), which probably won't help here.

KeyWeeUsr avatar Jan 25 '17 12:01 KeyWeeUsr

Just came across this during my deep dive into the p4a bootstrap behaviors. As someone who's been using cross-platform toolkits for many years now, I've found that I always want to be given the choice of what to do in my app code. e.g. I'd just like to have some on_back_key event or on_android_back_key to stress that it's an android-specific callback. If the callback isn't handled, then close the app.

The 'close the app' behavior is generally correct in the case when you're at the app's home screen, but, for example, if you're building an ebook reader and the user has started reading a book, users would probably expect the back key to take them back to the book list. It also serves as a "go to previous screen" button. That's how Kindle, Chrome and the Settings app works.

Once that callback is available, devs who don't want their app to fully close on back would have the choice to change that behavior as well. And then if they get complaints about users that back should close the app and free resources, then they can change it back too. ;)

kollivier avatar Jan 28 '17 04:01 kollivier

I just wanted to say; I do not like the idea of Config setting as compared to a method or callback. Callbacks give you the power to be dynamic like in the e-reader example above. But the Config settings are hard coded and can not be changed after loading Kivy.

FeralBytes avatar Feb 25 '17 15:02 FeralBytes

Current setup allows you listen to escape key like so and specify your own behaviour of what to do? Go Back in Screen Higherarchy or to pause app like so

Or to do nothing & app automatically follows the default behaviour of closing the app as the it does for a Java App.

This is the same for on_pause unless you override the app stops, It might feel more intuitive if the app does not do this by default but these parts are critical enough that we want to pull attention of developer towards these to make sure they specifically handle these situations.

I feel the correct solution here is to give a example in doc on how to customise your app to change default behaviour If the developer so chooses and to leave the current behaviour as is.

This conversation in the PR is important enough that it should be retained/moved to the Wiki

At the same time we can't keep these kind of PR's open for long. @inclement if you are ok with it I would like to add examples/Samples to wiki on how to handle this situation along with a link to this conversation and close this pr.

akshayaurora avatar May 01 '19 09:05 akshayaurora

Do you think it would make sense to create an event method that would be fired when the back button is pressed on Android and that the user could overwrite to implement their own handling (just like what is done with on_pause for instance)? I was thinking it could be easier to use, especially since I think handling this back button is a quite common use case @akshayaurora

RobinPicard avatar Aug 29 '22 14:08 RobinPicard

@RobinPicard For custom back button/gesture handling, one way is to bind the keyboard and test for key == 27 , like this https://github.com/Android-for-Python/Android-for-Python-Users#back-button-and-gesture

Btw, I just submitted a PR that addresses a different issue with the back button/gesture. https://github.com/kivy/kivy/pull/7989#issuecomment-1231037983

RobertFlatt avatar Aug 30 '22 02:08 RobertFlatt

#7989 (comment)

I use the binding of the keyboard and the test on test for key == 27 for my own app and it works fine, but I just thought there could be a way of making it more accessible by already doing a part of the work with something like that (before you own modifications):

def on_keyboard(self, key, scancode=None, codepoint=None,
                modifier=None, **kwargs):
    [...]
    if WindowBase.on_keyboard.exit_on_escape:
        if platform == 'android' and key == 27:
            if not self.dispatch('on_request_close', source='keyboard') and not self.dispatch('on_back_key_android'):
                stopTouchApp()
                self.close()
                return True
    [...]

def on_back_key_android(self):
    pass

Then the user would just have to add Window.on_back_key_android = custom_function to handle what happens when the back button is pressed (just like what you'd do with on_enter, on_pause...)

RobinPicard avatar Aug 30 '22 06:08 RobinPicard