Skip to content

Commit 3521955

Browse files
notif: On Android handle notification taps via Pigeon API
Instead of relying on Flutter's deeplinks implementation for routing the notification URL, handle the Android Intents generated by notification taps ourselves using Pigeon to pass those events over to the Dart layer from the Java layer. The upstream Flutter's deeplinks implementation has a bug where if the deeplink is triggered after the app was killed by the OS when it was in background, the app will get launched again but the route/link will not reach the Flutter's navigation handlers. See: flutter/flutter#178305 In the failure case we seem to be receiving the Android Intent for the notification tap from the OS via `MainActivity.onNewIntent` without any problems. So, to workaround that upstream bug this commit changes the implementation to handle these Android Intents ourselves. Fixes: #1567
1 parent 26e3ba5 commit 3521955

File tree

9 files changed

+567
-70
lines changed

9 files changed

+567
-70
lines changed

android/app/src/main/kotlin/com/zulip/flutter/MainActivity.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
package com.zulip.flutter
22

33
import android.content.Intent
4+
import com.zulip.flutter.notifications.NotificationTapEventListener
5+
import com.zulip.flutter.notifications.NotificationTapEventsStreamHandler
46
import io.flutter.embedding.android.FlutterActivity
57
import io.flutter.embedding.engine.FlutterEngine
68

79
class MainActivity : FlutterActivity() {
810
private var androidIntentEventListener: AndroidIntentEventListener? = null
11+
private var notificationTapEventListener: NotificationTapEventListener? = null
912

1013
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
1114
super.configureFlutterEngine(flutterEngine)
1215

1316
androidIntentEventListener = AndroidIntentEventListener()
1417
AndroidIntentEventsStreamHandler.register(
15-
flutterEngine.dartExecutor.binaryMessenger,
16-
androidIntentEventListener!!
18+
flutterEngine.dartExecutor.binaryMessenger, androidIntentEventListener!!
1719
)
20+
notificationTapEventListener = NotificationTapEventListener()
21+
NotificationTapEventsStreamHandler.register(
22+
flutterEngine.dartExecutor.binaryMessenger, notificationTapEventListener!!
23+
)
24+
1825
maybeHandleIntent(intent)
1926
}
2027

@@ -35,6 +42,17 @@ class MainActivity : FlutterActivity() {
3542
return true
3643
}
3744

45+
Intent.ACTION_VIEW -> {
46+
if (notificationTapEventListener!!.maybeHandleViewNotif(intent)) {
47+
// Notification tapped
48+
return true
49+
}
50+
51+
// Let Flutter handle other intents, in particular the web-auth intents
52+
// have ACTION_VIEW, scheme "zulip", and authority "login".
53+
return false
54+
}
55+
3856
// For other intents, let Flutter handle it.
3957
else -> return false
4058
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.zulip.flutter.notifications
2+
3+
import android.content.Intent
4+
import android.net.Uri
5+
6+
class NotificationTapEventListener : NotificationTapEventsStreamHandler() {
7+
private var eventSink: PigeonEventSink<NotificationTapEvent>? = null
8+
private val buffer = mutableListOf<NotificationTapEvent>()
9+
10+
override fun onListen(p0: Any?, sink: PigeonEventSink<NotificationTapEvent>) {
11+
eventSink = sink
12+
if (buffer.isNotEmpty()) {
13+
buffer.forEach { sink.success(it) }
14+
buffer.clear()
15+
}
16+
}
17+
18+
private fun onNotificationTapEvent(dataUrl: Uri) {
19+
val event = AndroidNotificationTapEvent(dataUrl.toString())
20+
if (eventSink != null) {
21+
eventSink!!.success(event)
22+
} else {
23+
buffer.add(event)
24+
}
25+
}
26+
27+
/**
28+
* Recognize if the ACTION_VIEW intent came from tapping a notification; handle it if so
29+
*
30+
* If the intent is recognized, sends a notification tap event via
31+
* the Pigeon event stream to the Dart layer and returns true.
32+
* Else does nothing and returns false.
33+
*
34+
* Do not call if `intent.action` is not ACTION_VIEW.
35+
*/
36+
fun maybeHandleViewNotif(intent: Intent): Boolean {
37+
assert(intent.action == Intent.ACTION_VIEW)
38+
39+
val url = intent.data
40+
if (url?.scheme == "zulip" && url.authority == "notification") {
41+
onNotificationTapEvent(url)
42+
return true
43+
}
44+
45+
return false
46+
}
47+
}

0 commit comments

Comments
 (0)