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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Capacitor on Android detected as web platform after redirecting with allowNavigation #7454

Open
1 of 3 tasks
Dylancyclone opened this issue May 7, 2024 · 1 comment
Labels

Comments

@Dylancyclone
Copy link

Capacitor Version

馃拪 Capacitor Doctor 馃拪

Latest Dependencies:

@capacitor/cli: 6.0.0
@capacitor/core: 6.0.0
@capacitor/android: 6.0.0
@capacitor/ios: 6.0.0

Installed Dependencies:

@capacitor/cli: 6.0.0
@capacitor/core: 6.0.0
@capacitor/ios: 6.0.0
@capacitor/android: 6.0.0

[success] iOS looking great! 馃憣
[success] Android looking great! 馃憣

Other API Details

`npm --version`
> 10.7.0

`node --version`
> v20.11.0

Platforms Affected

  • iOS
  • Android
  • Web

Current Behavior

After redirecting to an external URL that is allowed in the server.allowNavigation config setting, an android app reports being on a web platform instead of android.
This can be seen by calling Capacitor.getPlatform() (which returns android before redirecting and web after redirecting) or by trying to use a native API, which will either fallback to the web version of the API, or fail to run at all

All this works on iOS, where after redirecting, the app can still use native iOS APIs

Expected Behavior

After redirecting, the android app should still have access to all the native Android APIs (and report itself as being an Android platform)

Project Reproduction

https://github.com/Dylancyclone/capacitor-android-redirect/tree/main

Additional Information

Reproduction details are in the repo, but the basic reproduction steps are as follows:

  1. Serve a valid capacitor app on your computer (i.e. with npx serve .)
  2. Add your computer's IP to the server.allowNavigation capacitor config setting
  3. Load the application on your (real or simulated) device
  4. Notice that Capacitor.getPlatform() returns android on android and ios on iOS
  5. Redirect to the hosted server (any method works, can be window.location.assign(), window.location.href = "", window.location.replace(), or redirecting through a form
  6. Notice that Capacitor.getPlatform() now returns web on android but still ios on iOS

Possibly related to #5455

Here are videos showing the issue. Even though these were recorded on a simulator, the results are identical to a physical device

android.mov
ios.mov

Our use case is to have one app that connects to different backends on different subdomains, very similar to Slack. So far everything is working except the android app loses access to its native APIs

@Dylancyclone
Copy link
Author

Dylancyclone commented May 8, 2024

So it looks like there are two parts to this issue: redirecting to a local address, and capacitor not injecting the JS bridge into external pages. Again, both of these are only relevant to Android, the iOS side works perfectly

For the first issue, if we try to navigate to a computer on the local network (http, through ip address or [computername].local) we would get the issue described above where the platform would be seen as web and the plugins would use the web version or fail entirely. If we instead redirect to a remotely hosted server (https), we get a slightly different issue.

If we add our remote server to the server.allowNavigation config and redirect to it using the steps above, the platform is reported as android correctly, but all plugins are completely broken:

image

Investigating this issue, we found the root cause to be the JavaScript injection in Bridge.java. We notice that if WebViewFeature.DOCUMENT_START_SCRIPTis supported, the javascript is only injected into the base URL (if it is NOT supported (i.e by commenting the if guard out), the injector always works). If the addDocumentStartJavaScript() function were changed to inject on all allowedOriginRules (the base URL and everything in the server.allowNavigation setting) instead of just the base URL, all plugins work again.

Here's the diff:

Original:

// Start the local web server
JSInjector injector = getJSInjector();
if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
String allowedOrigin = appUrl;
Uri appUri = Uri.parse(appUrl);
if (appUri.getPath() != null) {
if (appUri.getPath().equals("/")) {
allowedOrigin = appUrl.substring(0, appUrl.length() - 1);
} else {
allowedOrigin = appUri.toString().replace(appUri.getPath(), "");
}
}
WebViewCompat.addDocumentStartJavaScript(webView, injector.getScriptString(), Collections.singleton(allowedOrigin));
injector = null;
}
localServer = new WebViewLocalServer(context, this, injector, authorities, html5mode);

New:

        // Start the local web server
        JSInjector injector = getJSInjector();
        if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
            WebViewCompat.addDocumentStartJavaScript(webView, injector.getScriptString(), allowedOriginRules);
            injector = null;
        }
        localServer = new WebViewLocalServer(context, this, injector, authorities, html5mode);

I am new to capacitor and have no idea if this would have any side effects, but this seems to fix our issue completely and bring parity with the iOS version.


So to summarize:

The original issue still exists while redirecting to a computer on a local network (or just over http, I don't have the capability to test that right now)

There also appears to be a bug when redirecting to a remote server (again, maybe just over https) caused by the native bridge not injecting capacitor's Javascript on remote pages. A fix we found was to make the change above, but I'd rather ask if it is the correct approach before creating a pull request.

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

No branches or pull requests

1 participant