Force open url in mobile browser

Hi,

My app is built using UI Builder, and then wrapped into the Backendless Native Mobile App Shell.

I would like to force opening a URL with the native mobile browser rather than in a webview. How can I achieve this ? Even using the parameter true in “Open in another window” still keeps the URL in the webview.

image

Thanks for your guidance.

Hello @Nicolas_REMY,

We are sorry for the inconvenience. An internal ticket was created on this issue - BKNDLSS-31608. Our developer will look into the issue as soon as possible.

Regards
Nazar

No worries, it was not meant as a criticism :slight_smile:

As I experience it right now :

  • Open in another window = false → replaces the webview with the new URL, without any back button, so there is no apparent way to come back.
  • Open in another window = true → replaces the webview with the new URL, with a back button.
    And right now I have not found a way to open the URL in the mobile browser app.

I wonder if it wouldn’t be better if :

  • Open in another window = false → replaces the webview with the new URL, with a back button.
  • Open in another window = true → opens the URL in the mobile browser app.

What do you think ?

@Nicolas_REMY,

Thank you for your detailed description of the current behavior. We really appreciate your suggestions and will be happy to pass them on to our development team.

Regards
Nazar

1 Like

I would like to clarify the current behavior because what I previously said is inaccurate.

On iOS :

  • Open in another window = false → replaces the webview with the new URL, without any back button, so there is no apparent way to come back.
  • Open in another window = true → opens the new URL in a new webview as expected, and with a back button.

On Android :

  • Open in another window = false → replaces the webview with the new URL.
  • Open in another window = true → seems to have the same behavior : the webview is replaced with the new URL. When going back to the previous window, the webapp is reloaded, meaning that App Data is reset and any data stored there is lost (!)

This last behavior is particularly problematic. I would welcome any update on resolution status for this issue.

Hello @Nicolas_REMY

The internal ticket BKNDLSS-31608 is in the support queue, we will let you know as soon as we have any results. Thank you for your patience.

Hello, @Nicolas_REMY.

I checked this case. And for our regret it currently does not work currectly in combination with the native shell. To correct behaviour that take into account the features of native app shell we need to change this block in cloud sdk for ui builder. It can take a time(2-3 weeks in the best forecast).
But we can suggest an around way to achieve this behaviour for your application. You need to implement an own method in the native shell that opens needed url.
It can be look like this(bridge_manager.dart file, executeRequest method, case in switch block):

        case _OPEN_IN_BROWSER:
          {
            try {
              Uri uri = Uri.parse(data['payload']['options']['url']);

              if (await canLaunchUrl(uri)) {
                // Launch the App
                result = await launchUrl(
                  uri,
                );
              }

              return buildResponse(data: requestContainer, response: result);
            } catch (ex) {
              print(ex);
              return buildResponse(data: requestContainer, response: false);
            }
          }

Call method from codeless:

If you have a question let me know pls.

Best Regards, Nikita.

Hi @Nikita_Fedorishchev ,

Thanks for your reply.

It looks as though canLaunchUrl and launchUrl are not recognized.


Could you advise if there is a package I need to add ?

Please clarify, do you have this dependency in your pubspec.yaml file:
url_launcher: ^6.0.10?

We use this package in native shell, and you use it for deep links.
You need to add import in top side of file(bridge_manager.dart), or try focus your mouse on line with this error, IDE should suggest you to add this line.

Regards, Nikita.

Yes, that line is there in pubspec.yaml :

image

But OK, I also needed to add the import, that was the missing step :

Thanks. Now I will try to see if it can work this way.

I hope to use this as a temporary patch until “Open in another window” really opens up a new webview in Android, but also until the images and PDFs can be loaded correctly in Android :

Cheers

1 Like

@Nikita_Fedorishchev
Unfortunately, after having built the app, it is not working as intended : a new window looks like it’s opening, but it stays all black. And I’m not really sure it’s really launching the browser app.

I tested on iOS, everything looked fine. What OS did you test on? Could you make a video of what it looks like for you?

I tested on Android. That’s where I’m having an issue with opening in another window and looking to find a solution. The built-in solution for iOS works fine, so no need there.

I will try to prepare a video.

Here is what it looks like.

Also here is the type of log I am getting in Flutter (the domain has been replaced for confidentiality reasons, but the URLs do work) :

I/flutter (19414): {message: ------- view .docx, messageLevel: 1}
I/flutter (19414): {message: https://my.domain.com/api/files/assets/docs/07/07D7B4DD-4AC6-121A-476A-7B8ADA7C2C5A.docx, messageLevel: 1}
I/UrlLauncher(19414): component name for https://my.domain.com/api/files/assets/docs/07/07D7B4DD-4AC6-121A-476A-7B8ADA7C2C5A.docx is {android/com.android.internal.app.ResolverActivity}
I/InputTransport(19414): Create ARC handle: 0xb9a6a7d0
E/libc    (19414): Access denied finding property "ro.vendor.input.resample_latency_ms"
I/flutter (19414): {message: true, messageLevel: 1}
D/ViewRootImpl[WebViewActivity](19414): controlInsetsForCompatibility: hideByFlags=0x1, showByFlags=0x0, flags=0x1810500, sysUiVis=0x0, matchParent=true, nonAttachedAppWindow=true
D/InsetsSourceConsumer(19414): setRequestedVisible: visible=false, type=0, host=com.domain.my/io.flutter.plugins.urllauncher.WebViewActivity, from=android.view.InsetsSourceConsumer.hide:227 android.view.InsetsController.collectSourceControls:1094 android.view.InsetsController.controlAnimationUnchecked:981 android.view.InsetsController.applyAnimation:1339 android.view.InsetsController.hide:916 android.view.InsetsController.hide:899 android.view.ViewRootImpl.controlInsetsForCompatibility:2223 android.view.ViewRootImpl.performTraversals:2647 android.view.ViewRootImpl.doTraversal:1993 android.view.ViewRootImpl$TraversalRunnable.run:8240 
D/OpenGLRenderer(19414): eglCreateWindowSurface : 0xb3579d40
D/IMGGralloc(19414): Gralloc Register  w:720, h:1555, f:0x1, usage:0xb00, ui64Stamp:181599, sSize:4517888, line = 2335
D/IMGGralloc(19414): Gralloc Register  w:720, h:1555, f:0x1, usage:0xb00, ui64Stamp:181608, sSize:4517888, line = 2335
I/SurfaceControl(19414): release : mNativeObject = 3245865760 - Surface(name=Surface(name=ffc7a4d InputMethod)/@0x2b69213 - animation-leash)/@0xaf87b98 / android.view.-$$Lambda$Rl1VZmNJ0VZDLK0BAbaVGis0rrA.accept:2 android.view.InsetsSourceControl.release:170 android.view.InsetsSourceConsumer.setControl:193 android.view.ImeInsetsSourceConsumer.setControl:151 
I/SurfaceControl(19414): release : mNativeObject = 3245864704 - Surface(name=Surface(name=2f6f4b5 NavigationBar0)/@0xe188dad - animation-leash)/@0x1051ff1 / android.view.-$$Lambda$Rl1VZmNJ0VZDLK0BAbaVGis0rrA.accept:2 android.view.InsetsSourceControl.release:170 android.view.InsetsSourceConsumer.setControl:193 android.view.InsetsController.onControlsChanged:794 
I/SurfaceControl(19414): release : mNativeObject = 2845867424 - Surface(name=Surface(name=12c4abe StatusBar)/@0xa7361d7 - animation-leash)/@0x45037d6 / android.view.-$$Lambda$Rl1VZmNJ0VZDLK0BAbaVGis0rrA.accept:2 android.view.InsetsSourceControl.release:170 android.view.InsetsSourceConsumer.setControl:193 android.view.InsetsController.onControlsChanged:794 
D/InputMethodManager(19414): prepareNavigationBarInfo() DecorView@ce1d50a[WebViewActivity]
D/InputMethodManager(19414): getNavigationBarColor() -16777216
I/SurfaceControl(19414): release : mNativeObject = 3239761856 - Surface(name=Surface(name=ffc7a4d InputMethod)/@0x2b69213 - animation-leash)/@0x3aca2d / android.view.-$$Lambda$Rl1VZmNJ0VZDLK0BAbaVGis0rrA.accept:2 android.view.InsetsSourceControl.release:170 android.view.InsetsSourceConsumer.setControl:193 android.view.ImeInsetsSourceConsumer.setControl:151 
W/OpenGLRenderer(19414): Unable to extract renderTarget info from canvas; aborting GLFunctor draw
I/SurfaceControl(19414): release : mNativeObject = 3239760384 - Surface(name=Surface(name=2f6f4b5 NavigationBar0)/@0xe188dad - animation-leash)/@0xe6ffb62 / android.view.-$$Lambda$Rl1VZmNJ0VZDLK0BAbaVGis0rrA.accept:2 android.view.InsetsSourceControl.release:170 android.view.InsetsSourceConsumer.setControl:193 android.view.InsetsController.onControlsChanged:794 
I/SurfaceControl(19414): release : mNativeObject = 3009163616 - Surface(name=Surface(name=12c4abe StatusBar)/@0xa7361d7 - animation-leash)/@0x41214f3 / android.view.-$$Lambda$Rl1VZmNJ0VZDLK0BAbaVGis0rrA.accept:2 android.view.InsetsSourceControl.release:170 android.view.InsetsSourceConsumer.setControl:193 android.view.InsetsController.onControlsChanged:794 
I/SurfaceControl(19414): release : mNativeObject = 3239762048 - Surface(name=Surface(name=12c4abe StatusBar)/@0xa7361d7 - animation-leash)/@0x50725b0 / android.view.-$$Lambda$Rl1VZmNJ0VZDLK0BAbaVGis0rrA.accept:2 android.view.InsetsSourceControl.release:170 android.view.InsetsAnimationThreadControlRunner.releaseControls:119 android.view.InsetsAnimationThreadControlRunner.access$200:40 
I/SurfaceControl(19414): release : mNativeObject = 3010860608 - Surface(name=SurfaceView - com.domain.my/com.domain.my.MainActivity)/@0x7a6dc29 / android.view.SurfaceControl$Transaction.remove:3169 android.view.SurfaceView$1.positionLost:1373 android.graphics.RenderNode$CompositePositionUpdateListener.positionLost:313 <bottom of call stack> 
I/SurfaceControl(19414): release : mNativeObject = 3010860736 - Surface(name=Background for -SurfaceView - com.domain.my/com.domain.my.MainActivity)/@0x9d66bae / android.view.SurfaceControl$Transaction.remove:3169 android.view.SurfaceView$1.positionLost:1374 android.graphics.RenderNode$CompositePositionUpdateListener.positionLost:313 <bottom of call stack> 
D/OpenGLRenderer(19414): destroyEglSurface : 0xb3578f00
D/IMGGralloc(19414): Gralloc Free  w:720, h:1600, f:0x1, usage:0xb00, ui64Stamp:181488 line = 2441
D/IMGGralloc(19414): Gralloc Free  w:720, h:1600, f:0x1, usage:0xb00, ui64Stamp:181492 line = 2441
D/IMGGralloc(19414): Gralloc Free  w:720, h:1600, f:0x1, usage:0xb00, ui64Stamp:181574 line = 2441
I/SurfaceControl(19414): release : mNativeObject = 3825107200 - Surface(name=null)/@0x584a6dc / android.view.SurfaceControl.assignNativeObject:528 android.view.SurfaceControl.readFromParcel:1073 android.view.IWindowSession$Stub$Proxy.relayout:1734 android.view.ViewRootImpl.relayoutWindow:7498 
I/SurfaceControl(19414): release : mNativeObject = 3065987616 - Surface(name=Bounds for - com.domain.my/com.domain.my.MainActivity)/@0xbaf0d4f / android.view.ViewRootImpl.destroySurface:1886 android.view.ViewRootImpl.relayoutWindow:7533 android.view.ViewRootImpl.performTraversals:2693 android.view.ViewRootImpl.doTraversal:1993 
D/IMGGralloc(19414): Gralloc Register  w:720, h:1555, f:0x1, usage:0xb00, ui64Stamp:181635, sSize:4517888, line = 2335

ok, I resolved this issue, just add mode: LaunchMode.externalApplication to parameters of launchUrl method.

Best Regards, Nikita.

It’s working !

Just for the record, and for those who are not experts in Flutter syntax, it means the function looks like so :

image

And to be complete, I also had to add this :

image