Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for responding to back button on Android #3948

Merged
merged 5 commits into from
Aug 1, 2023

Conversation

andydotxyz
Copy link
Member

@andydotxyz andydotxyz commented Jun 5, 2023

Still todo:

  • Discuss if keyboard code and/or shortcut is the right way to deliver
  • Make sure that apps by default will handle it correctly, unless app uses the event...

Fixes #2910

Checklist:

  • Tests included. <- we can't test this native lifecycle yet
  • Lint and formatter run with no errors.
  • Tests all pass.
  • Public APIs match existing style and have Since: line.

Current usage:

	w.Canvas().SetOnTypedKey(func(ev *fyne.KeyEvent) {
		if ev.Name == mobile.KeyBack {
			log.Println("Intercepted and issued back")
			a.Driver().(mobile.Driver).GoBack()
		}
	})

@andydotxyz andydotxyz marked this pull request as draft June 5, 2023 09:39
@andydotxyz
Copy link
Member Author

Marking as work in progress as I think the two remaining TODO items do need discussion to get it right.

@coveralls
Copy link

coveralls commented Jun 5, 2023

Coverage Status

coverage: 66.174% (-0.01%) from 66.186% when pulling 38ad3a2 on andydotxyz:feature/backbutton into eacbaf6 on fyne-io:develop.

@andydotxyz
Copy link
Member Author

@MatejMagat305 which part are you trying to simplify?

One of the reasons it is not simple is because the back button is a specific event with it's own callback handler in the app on the Java side...

@MatejMagat305
Copy link
Contributor

MatejMagat305 commented Jun 5, 2023

sorry, my mistake, I thought that:

static int32_t handle_input(struct android_app* app, AInputEvent* event) {
  if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY)
  {
    int key_val = AKeyEvent_getKeyCode(event);
    switch(key_val){
      case AKEYCODE_BACK:
        LOGI("Back Button hit");
        break;
    }
    LOGI("Input Recieved %d", key_val);
  }
  return 1;
} 

in stackoverflow AKeyEvent_getKeyCode(event); is already in code and is not nesesery to add special handler ...,
I m sorry for hide coment, the GPT code is here, but probably I was wrong

@MatejMagat305
Copy link
Contributor

MatejMagat305 commented Jul 11, 2023

it can be done like that too andydotxyz#2

@andydotxyz
Copy link
Member Author

it can be done like that toohttps://github.com/andydotxyz/pull/2

Are you sure about this? If you don't add:

    @Override
    public void onBackPressed() {

then the default back behaviour will occur.

And without the ability to call the GoBack behaviour then someone intercepting the key cannot decide to go back from an activity vs issuing some other behaviour...

@MatejMagat305
Copy link
Contributor

MatejMagat305 commented Jul 11, 2023

it can be done like that toohttps://github.com/andydotxyz/pull/2

Are you sure about this? If you don't add:

    @Override
    public void onBackPressed() {

then the default back behaviour will occur.

And without the ability to call the GoBack behaviour then someone intercepting the key cannot decide to go back from an activity vs issuing some other behaviour...

I was try it with fyne/main and my changes and it catch it (do not look to error it was course by mismash version ...):
back

@MatejMagat305
Copy link
Contributor

MatejMagat305 commented Jul 11, 2023

we can also insert if callback == nill {return 0} and finish you already have so ANativeActivity_finish(...) is not needed

@andydotxyz
Copy link
Member Author

Are you certain you're using the released fyne command that matches the base library version for this change, and not one that I was working on before? This code is inserted into the tool, not added in the library in the compiler.

The issue with going backwards isn't just if there is a callback or not, its so that the callback can decide whether or not to permit the back action... If the developer wants to display a confirmation before going back then how can you do it without providing the GoBack call?

@MatejMagat305
Copy link
Contributor

well take or do not ...
return 0 is default behaviour, return 1 is not allow ...., if you want that behaviour you can done that callback will return boolean reprezentation of allow default ..., or call "finish" activity ...

@andydotxyz
Copy link
Member Author

or call "finish" activity ...

How will the Go code be able to call finish activity without the bridge code exposing that API?

@MatejMagat305
Copy link
Contributor

MatejMagat305 commented Jul 21, 2023

or call "finish" activity ...

How will the Go code be able to call finish activity without the bridge code exposing that API?

change 89 row in android.c to
finish_method = find_method(env, current_class, "finish", "()V"); - https://github.com/andydotxyz/fyne/pull/2/files#diff-502ce6455a7147cc204b49413063d0d317df0dd787d6f9c3917798199c260cbbR82
and it will close activity (without any other change in C or GO)...., I released one principial problem when I try this (even for other functionality s):

you call setCurrentContext in on_create it is clewer, but when I was close activity and resume it context is probably new and you have not actual context, well you can refuse this (back button) sugestion, but it would be good fix context to new ...

for more contrete: https://github.com/andydotxyz/fyne/pull/2/files#diff-5d878dec0c0c4e09096aef345b55e1b73598999dae4a4a73f851ac1c47b7cd33R123 - re-set context on start and resume, btw why not use Quit interface instead of GoBack interface? https://github.com/andydotxyz/fyne/pull/2/files#diff-f51d860903d6227f965ed690092de5b8677b954e250fe77eff66c9c174309829R129 - Quit as finish ...

make your origin like this:

    w.Canvas().SetOnTypedKey(func(ev *fyne.KeyEvent) {
		if ev.Name == mobile.KeyBack {
			log.Println("Intercepted and issued back")
			a.Driver().Quit()
		}
	})

my trying (working) code was:

package main

import (
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/widget"
)

func main() {
	a := app.New()
	w := a.NewWindow("Hello")

	hello := widget.NewLabel("Hello Fyne!")
	w.SetContent(container.NewVBox(
		hello,
		widget.NewButton("Hi!", func() {
			hello.SetText("Welcome :)")
		}),
		widget.NewButton("exit", func() {
			a.Driver().Quit()
		}),
	))

	w.ShowAndRun()
}

@andydotxyz
Copy link
Member Author

make your origin like this:

    w.Canvas().SetOnTypedKey(func(ev *fyne.KeyEvent) {
		if ev.Name == mobile.KeyBack {
			log.Println("Intercepted and issued back")
			a.Driver().Quit()
		}
	})

Unfortunately this is an over-simplification. Going "back" is not the same as quitting an app.

Also because our keyboard handlers don't return whether or not something was intercepted the underlying driver does not know if you are going to handle the back button or not.
I appreciate you working on this, but you haven't convinced me there is a simpler way than what I have proposed.

@MatejMagat305
Copy link
Contributor

MatejMagat305 commented Jul 23, 2023

make your origin like this:

    w.Canvas().SetOnTypedKey(func(ev *fyne.KeyEvent) {
		if ev.Name == mobile.KeyBack {
			log.Println("Intercepted and issued back")
			a.Driver().Quit()
		}
	})

Unfortunately this is an over-simplification. Going "back" is not the same as quitting an app.

Also because our keyboard handlers don't return whether or not something was intercepted the underlying driver does not know if you are going to handle the back button or not. I appreciate you working on this, but you haven't convinced me there is a simpler way than what I have proposed.

well, it is not my library ..., but please look at context at start and resume ..., that is hide and it would cause problems in futures ...

and if the biggest problem is handle it only we want we can do https://github.com/andydotxyz/fyne/pull/3/files#diff-5d878dec0c0c4e09096aef345b55e1b73598999dae4a4a73f851ac1c47b7cd33R615 and https://github.com/andydotxyz/fyne/pull/3/files#diff-5d878dec0c0c4e09096aef345b55e1b73598999dae4a4a73f851ac1c47b7cd33R149

but again you are shef here and the most important for me is actual (jobject) context and jvm on resume

@andydotxyz
Copy link
Member Author

but please look at context at start and resume ..., that is hide and it would cause problems in futures ...

I'm sorry I don't understand this. The lifecycle code already handles the context on resumed apps?

@MatejMagat305
Copy link
Contributor

MatejMagat305 commented Jul 23, 2023

but please look at context at start and resume ..., that is hide and it would cause problems in futures ...

I'm sorry I don't understand this. The lifecycle code already handles the context on resumed apps?

well I release that, if I close app (put on background) and open it again, the pointers to context and vm (clazz and jvm) are not actual ... (not work if you want call for example finish() on activity) the solution would be use function to set actual pointers: https://github.com/andydotxyz/fyne/pull/2/files#diff-5d878dec0c0c4e09096aef345b55e1b73598999dae4a4a73f851ac1c47b7cd33R123, again your solution is good and probably this error has not efekt to them, but for example bluetooth which I prepare it is important to have actual pointers ...

if you want to try it (what I mean), you can:

  1. copy code with Quit() (above)
  2. copy https://github.com/MatejMagat305/fyne/tree/feature/backbutton to vendor
  3. remove command C.set_global() from functions onResume and onStart: https://github.com/andydotxyz/fyne/pull/2/files#diff-5d878dec0c0c4e09096aef345b55e1b73598999dae4a4a73f851ac1c47b7cd33R123
  4. compile and install to android
  5. open and try exit - it will work
  6. open again (from bg)
  7. try to trap exit - it will probably not working because the pointers to clazz and vm are different

It is probably to next P.R., but on this case is evident why it should be done

@andydotxyz
Copy link
Member Author

I still don't understand. I am using RunNative to call functions into Android. The VM and env parameters are passed in then so why would they be stale?

@MatejMagat305
Copy link
Contributor

MatejMagat305 commented Jul 23, 2023

I still don't understand. I am using RunNative to call functions into Android. The VM and env parameters are passed in then so why would they be stale?

please try it (my code), my pure search show me that the VM and CTX working(in general), but in some situation when I need request permision or do with actual pointers ... (but not in specific cases)

or look the code in onCreate(...) save VM and CTX into global variables, after app moving into background (close app and reopen from bg) and re-start app will get in onResume(...) new VM and CTX which are different from global variables which are not make actual, this would be hiden error in some case ...

tomorow I will share videos ...

@andydotxyz
Copy link
Member Author

or look the code in onCreate(...) save VM and CTX into global variables, after app moving into background (close app and reopen from bg) and re-start app will get in onResume(...) new VM and CTX which are different from global variables which are not make actual, this would be hiden error in some case ...

Yes you cannot (and should not) save these to global variables and assume they work forever.

@MatejMagat305
Copy link
Contributor

MatejMagat305 commented Jul 24, 2023

or look the code in onCreate(...) save VM and CTX into global variables, after app moving into background (close app and reopen from bg) and re-start app will get in onResume(...) new VM and CTX which are different from global variables which are not make actual, this would be hiden error in some case ...

Yes you cannot (and should not) save these to global variables and assume they work forever.

finaly we are at same wave, but I m not certian, therefore I put video here ..., as you see at video when the global variables (which are at package mabileinit) are not actualiting at onResume(...) something is wrong when I try call finish(), so can I create new PR. to fix that? or you will fix at this PR?

@andydotxyz
Copy link
Member Author

I don't think we are on the same page. You can see that in the Fyne code internal/driver/mobile/app/android.c makes a call to setCorrentContext every time the activity is re-created.

So the context available in the package variables is always up to date...

@andydotxyz andydotxyz changed the title Add initial support for responding to back button on Android Support for responding to back button on Android Jul 29, 2023
@andydotxyz
Copy link
Member Author

are not actualiting at onResume(...) something is wrong when I try call finish()

My apologies I finally saw this happen - it seemed to not always be a problem.

Hopefully 8b545a8 resolves that aspect of this PR?

@andydotxyz
Copy link
Member Author

As promised @Jacalz here is a video of the work :)

2023-07-29.10-44-11.mp4

@andydotxyz andydotxyz marked this pull request as ready for review July 29, 2023 14:49
@andydotxyz
Copy link
Member Author

Just for record the app in the video above is just this:

// Package main loads a very basic Hello World graphical application.
package main

import (
	"fmt"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/driver/mobile"
	"fyne.io/fyne/v2/widget"
)

func main() {
	a := app.New()
	w := a.NewWindow("Hello")

	count := 0
	hello := widget.NewLabel("Trapped back 0 times")
	w.Canvas().SetOnTypedKey(func(ev *fyne.KeyEvent) {
		if ev.Name == mobile.KeyBack {
			count++
			hello.SetText(fmt.Sprintf("Trapped back %d times", count))
		}
	})
	back := widget.NewButton("Go Back", nil)
	if drv, ok := a.Driver().(mobile.Driver); ok {
		back.OnTapped = func() {
			drv.(mobile.Driver).GoBack()
		}
	} else {
		back.Disable()
	}
	w.SetContent(container.NewVBox(
		hello,
		back,
	))

	w.ShowAndRun()
}

Copy link
Member

@Jacalz Jacalz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Well done. This looks great :)

@MatejMagat305
Copy link
Contributor

are not actualiting at onResume(...) something is wrong when I try call finish()

My apologies I finally saw this happen - it seemed to not always be a problem.

Hopefully 8b545a8 resolves that aspect of this PR?

nice, thank ...

maybe call origin function OnResume(...) in go is useless but thank for this fix

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants