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

Invisible Notifications / Silent push messages #92

Closed
mzoeller opened this issue Feb 3, 2020 · 10 comments
Closed

Invisible Notifications / Silent push messages #92

mzoeller opened this issue Feb 3, 2020 · 10 comments

Comments

@mzoeller
Copy link
Contributor

mzoeller commented Feb 3, 2020

I'm interested in opening a URL or notifying an app without displaying a notification and without user interaction.

I have this working but I'm not sure about the exact naming for the extras. Maybe something like this?

{
   "client::notification": {
      "silent": true,
      "silentAction": {
         "url": "ab://dfjh",
      }
   },
   "android::notification": {
      "silentAction": {
         "package": "com.package.name",
      }
   }
}
@jmattheis
Copy link
Member

Do you mean with open url, make an api call? I don't think android would allow opening a url in the browser without user interaction. I think notifications with priority 0 are already silent notifications, so I don't think we have to add a silent flag for this.

@mzoeller
Copy link
Contributor Author

mzoeller commented Feb 5, 2020

You are right. For silent notifications setting priority to 0 works fine.

Opening the URL without user interaction actually works fine on Android:

private void showNotification(
    int id, String title, String message, long priority, Map<String, Object> extras) {

        Intent intent;

        String executeUrl = Extras.getNestedValue(String.class, extras, "client::execute", "url");
        if (executeUrl != null) {
            intent = new Intent(Intent.ACTION_VIEW);
            intent.setData(Uri.parse(executeUrl));
            startActivity(intent);
        }
(...)

Payload:

(...)
"priority": 0,
"extras": {
   "client::execute": {
      "url": "http://someurl/test"
   }
}

One could even open an app:

String executeApp = Extras.getNestedValue(String.class, extras, "android::execute", "package");

if (executeApp != null) {
    intent = getPackageManager().getLaunchIntentForPackage(executeApp);
    if (intent != null) {
        startActivity(intent);
    }
}

Payload:

(...)
"priority": 0,
"extras": {
   "android::execute": {
      "package": "com.some.packagename"
   }
}

I don't have a use case for opening apps though.

@jmattheis
Copy link
Member

I tested this with an api 29 emulator. Without setting the FLAG_ACTIVITY_NEW_TASK flag, the intent won't open the browser and the WebSocket will error:

intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(executeUrl));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// ...

Also, opening of the browser only seems to work if the Gotify app is in focus, otherwise nothing happens.

I don't think there are many use-cases for this. It doesn't look like good user experience either, when a message just opens a different app without any interaction.

I think, client::notification.click.url should be used instead.

@mzoeller
Copy link
Contributor Author

mzoeller commented Feb 6, 2020

Thanks for looking into it. Interesting that it does not work in the emulator. I've tested this (without setting any flags) on a few phones, including a fresh Samsung A3 2017, Android 8. Dev tools disabled, non rooted.

I think my use case makes sense, let me explain: I'm building an open source framework for power monitoring at music festivals. If any node in the infrastructure fails I need to notify all android clients in a noisy way. For this I can send Gotify notifications with a click url (I have registered my own URL handler for my app). This works fine and when the user clicks the notification it opens the app and I can handle it there.

But if the app is already open, the user experience is bad - the user has to click on the notification (which should not be displayed at all since the app is already open) in order for the app to display the relevant information.

A silent push notification that opens a url (or app) is a great way to implement server initiated network connections. It could be used for other stuff as well - a server request for current gps location, or battery status, wakeup calls for the app or even it's own notification display and handling (which I'm currently doing).

If you worry about security - obviously none of this works if the user does not install the receiving app (either via it's own protocol handler or calling the app by package name).

@jmattheis
Copy link
Member

But if the app is already open, the user experience is bad - the user has to click on the notification (which should not be displayed at all since the app is already open) in order for the app to display the relevant information.

This doesn't seem to work on newer Android versions:

  • open f.ex https://gotify.net via a notification
  • Browser opens
  • send another notification with a different url
  • Browser doesn't do anything, most likely because the Gotify app is not in focus

Could you try the steps above and your existing use-case with the FLAG_ACTIVITY_NEW_TASK flag? Maybe opening an app via a url has a different behavior. If it does have a different behavior, then I'd be open to merge such a feature.

Here a stacktrace without having FLAG_ACTIVITY_NEW_TASK flag set (Nokia 7 plus, Android 10 not rooted):

02-06 18:52:35.831  4182  4890 E gotify  : WebSocket: failure  Message:
02-06 18:52:35.831  4182  4890 E gotify  : android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
02-06 18:52:35.831  4182  4890 E gotify  :      at android.app.ContextImpl.startActivity(ContextImpl.java:952)
02-06 18:52:35.831  4182  4890 E gotify  :      at android.app.ContextImpl.startActivity(ContextImpl.java:928)
02-06 18:52:35.831  4182  4890 E gotify  :      at android.content.ContextWrapper.startActivity(ContextWrapper.java:383)
02-06 18:52:35.831  4182  4890 E gotify  :      at com.github.gotify.service.WebSocketService.showNotification(WebSocketService.java:231)
02-06 18:52:35.831  4182  4890 E gotify  :      at com.github.gotify.service.WebSocketService.onMessage(WebSocketService.java:175)
02-06 18:52:35.831  4182  4890 E gotify  :      at com.github.gotify.service.WebSocketService.lambda$bv7jSmiJvkZl1XbLRUHEd2vPPFE(Unknown Source:0)
02-06 18:52:35.831  4182  4890 E gotify  :      at com.github.gotify.service.-$$Lambda$WebSocketService$bv7jSmiJvkZl1XbLRUHEd2vPPFE.onSuccess(Unknown Source:4)
02-06 18:52:35.831  4182  4890 E gotify  :      at com.github.gotify.service.WebSocketConnection$Listener.onMessage(WebSocketConnection.java:153)
02-06 18:52:35.831  4182  4890 E gotify  :      at okhttp3.internal.ws.RealWebSocket.onReadMessage(RealWebSocket.java:323)
02-06 18:52:35.831  4182  4890 E gotify  :      at okhttp3.internal.ws.WebSocketReader.readMessageFrame(WebSocketReader.java:219)
02-06 18:52:35.831  4182  4890 E gotify  :      at okhttp3.internal.ws.WebSocketReader.processNextFrame(WebSocketReader.java:105)
02-06 18:52:35.831  4182  4890 E gotify  :      at okhttp3.internal.ws.RealWebSocket.loopReader(RealWebSocket.java:274)
02-06 18:52:35.831  4182  4890 E gotify  :      at okhttp3.internal.ws.RealWebSocket$2.onResponse(RealWebSocket.java:214)
02-06 18:52:35.831  4182  4890 E gotify  :      at okhttp3.RealCall$AsyncCall.execute(RealCall.java:206)
02-06 18:52:35.831  4182  4890 E gotify  :      at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
02-06 18:52:35.831  4182  4890 E gotify  :      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
02-06 18:52:35.831  4182  4890 E gotify  :      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
02-06 18:52:35.831  4182  4890 E gotify  :      at java.lang.Thread.run(Thread.java:919)

@mzoeller
Copy link
Contributor Author

mzoeller commented Feb 8, 2020

I've done some more testing. Opening an app via a protocol handler works when FLAG_ACTIVITY_NEW_TASK is set.

How do you feel about the extra name? I'm not sure about "execute" or even if it should go into ::android. Other names I came up with: "run", "auto", "call", "autoAction"...

@jmattheis
Copy link
Member

Hmm, not sure, I feel like this is special to android. I do not think we can open a browser window without user interaction.

{
   "client::notification": {
      "click": { "url: "https://gotify.net" }
   },
   "android::action": {
      "onReceive": {
         "intentUrl": "https://gotify.net/"
      }
   }
}

with this, we could add stuff like onDismiss or onView.
What do you think? I'm not 100% happy with it, but can't think of something better now.

@mzoeller
Copy link
Contributor Author

mzoeller commented Feb 9, 2020

I like "android::action" and "onReceive", at least it's clear what it does. I'll try and prepare a PR.

@mzoeller
Copy link
Contributor Author

I've submitted a PR: #98

@mzoeller
Copy link
Contributor Author

Merged via #102

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

No branches or pull requests

2 participants