llfbandit / app_links

Android App Links, Deep Links, iOs Universal Links and Custom URL schemes handler for Flutter.
https://pub.dev/packages/app_links
Apache License 2.0
176 stars 68 forks source link

_appLinks.uriLinkStream.listen called twice when app link has a redirect #118

Closed ishchhabra closed 1 month ago

ishchhabra commented 2 months ago

Describe the bug

When attempting to handle app links in an Android application using App Links, _appLinks.uriLinkStreams.listen gets called twice when the app link being passed has a redirect. Further investigation revealed that the onNewIntent method is being called twice. The first time, the intent includes the flag FLAG_ACTIVITY_BROUGHT_TO_FRONT, while the second time it does not.

Does it relate to

[X] App Links (Android)

To validate that the issue wasn't specifically with the app link that I had, a simple Python server was set up to always redirect the user.

from http.server import BaseHTTPRequestHandler, HTTPServer

class RedirectHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(302)
        self.send_header('Location', 'http://example.com/applink')
        self.end_headers()

def run(server_class=HTTPServer, handler_class=RedirectHandler, port=8020):
    server_address = ('', port)
    httpd = server_class(server_address, handler_class)
    print(f"Server listening on port {port}")
    httpd.serve_forever()

if __name__ == "__main__":
    run()

The AndroidManifest.xml looks like

<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="http" android:host="example.com" android:pathPrefix="/applink" />
    <data android:scheme="https" />
</intent-filter>
thanhle7 commented 2 months ago

Can you try setting android:launchMode="singleInstance"

ishchhabra commented 2 months ago

I had already tried android:launchMode="singleInstance". The <activity> inside which the intent filter exists looks like:

<activity
    android:name=".MainActivity"
    android:exported="true"
    android:launchMode="singleInstance"
    android:theme="@style/LaunchTheme"
    android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
    android:hardwareAccelerated="true"
    android:windowSoftInputMode="adjustResize">
llfbandit commented 2 months ago

I don't get it. You have a first URL which shouldn't be detected by the system, right? From here, you have a redirect to provide the deeplink?

If so, simplify your filter and/or make it more restrictive:

<data android:scheme="https" android:host="example.com" android:pathPrefix="/applink" />

You current setup allow both schemes "https" and "http" in the same filter. They are merged to include all variants. https://developer.android.com/training/app-links/deep-linking?hl=fr#adding-filters

ishchhabra commented 2 months ago

@llfbandit, I've created a minimal reproducible application at https://github.com/ishchhabra/applinks_debug_app. Hopefully, that helps. I noticed that the URI is the same on both triggers, so I don't believe 'http' or 'https' is the issue.

llfbandit commented 2 months ago

I guess you print it twice because on second attempt your callback is triggered from getInitialAppLink and the stream. You can use allUriLinkStream. This stream merges both previous calls.

ishchhabra commented 2 months ago

I tried adding print("widget.onAppLink callback called from uriLinkStream") in registerIncomingLinks for uriLinkStream.listen's callback. I see the debug console say widget.onAppLink callback called from uriLinkStream twice, so it is not that either.

llfbandit commented 1 month ago

This should be fixed on all platforms with version 6.0. Stop using initial link when it's not needed, the stream is all you need.

ishchhabra commented 1 month ago

@llfbandit I updated the minimal reproducible app I had built to the latest version, and removed using the initial link. I'm still seeing the uriLinkStream.listen being called twice.

thanhle7 commented 1 month ago

I tried adding print("widget.onAppLink callback called from uriLinkStream") in registerIncomingLinks for uriLinkStream.listen's callback. I see the debug console say widget.onAppLink callback called from uriLinkStream twice, so it is not that either.

Try redirect to a different domain. I think the filter works pretty well here.

ishchhabra commented 1 month ago

I tried adding print("widget.onAppLink callback called from uriLinkStream") in registerIncomingLinks for uriLinkStream.listen's callback. I see the debug console say widget.onAppLink callback called from uriLinkStream twice, so it is not that either.

Try redirect to a different domain. I think the filter works pretty well here.

I did try that out as well by changing the redirect URL in my dummy server to 10.0.2.3/applink and updating my AndroidManifest.xml to also handle 10.0.2.3. The issue persists.

On looking into this more, I also noticed that the same error occurs with go_router as well. It could just be a bug with the underlying import io.flutter.plugin.common.PluginRegistry.NewIntentListener or with the way it is being used, so I also opened a similar bug report at https://github.com/flutter/flutter/issues/147848

thanhle7 commented 1 month ago

I tried adding print("widget.onAppLink callback called from uriLinkStream") in registerIncomingLinks for uriLinkStream.listen's callback. I see the debug console say widget.onAppLink callback called from uriLinkStream twice, so it is not that either.

Try redirect to a different domain. I think the filter works pretty well here.

I did try that out as well by changing the redirect URL in my dummy server to 10.0.2.3/applink and updating my AndroidManifest.xml to also handle 10.0.2.3. The issue persists.

On looking into this more, I also noticed that the same error occurs with go_router as well. It could just be a bug with the underlying import io.flutter.plugin.common.PluginRegistry.NewIntentListener or with the way it is being used, so I also opened a similar bug report at flutter/flutter#147848

Coo!l but please try not updating your AndroidManifest.xml to handle 10.0.2.3; or, "pause" the subscriber on receiving the postback link. The problem should be fixed.