4

I am trying to bundle a small hybrid C#/single page app, developed using VueJS, as an android app. We have some classes, that create and image and are sending it to an ESC printer. Everything works if we load the application from a server:

theView.LoadUrl( "https://our-app.our-server.com" );

However when we load it from file:///android_asset/index.html it sort of starts, but the page is empty.

I see in chrome debugger, that VueJS successfully created some elements:

<div data-app="true" class="application app theme--light">
  <div class="application--wrap">
    <main class="v-content" data-booted="true" style="padding: 0px;">
      <div class="v-content__wrap"></div>
    </main>
  </div>
</div>

There are no errors in the console, just an empty screen.

What am I missing?

3
  • Demo github repo illustrating the problem: github.com/nsimeonov/VueInAndroidWebView Commented Oct 26, 2019 at 19:00
  • How do you open your chrome debugger inside your app? Commented Oct 27, 2019 at 9:04
  • It's not inside the app - once you start the android emulator with the application in it, you can open on your computer chrome://inspect/#devices and you will see the emulator and an inspect link below it. Check this article: developers.google.com/web/tools/chrome-devtools/… Here is another one, which is probably more up to date - at least the screenshots are exact match of what I see (not on google's - probably, because I'm using windows not MacOS) software.intel.com/en-us/articles/… Commented Oct 27, 2019 at 11:16

3 Answers 3

3

There are a couple things going on here. If you are going to serve the webview off your localhost, I suppose your "I figured it out" is the correct solution.

I am trying to use a Vue instance to run offline in the webview. That's where JustinHui's response worked for me. Rather than manually removing the slashes from the index.html that the vue cli generates, I went to vue.config.js and set publicPath to an empty string. That took care of it.

Sign up to request clarification or add additional context in comments.

2 Comments

I tried this, but only serving from localhost worked for me. file:// urls loaded only part of the page and did not execute all the scripts... same happens when I open "index.html" in my browser on the desktop machine
Followup - setting publicPath to ./ didn't help, but setting it to empty string actually worked. Also setting assetsDir to empty string and switching from history mode to hash mode for the router.
2

In your index.html, try taking out the '/' at the beginning of the css and js links

i.e. change:

<script src=/js/chunk-vendors.19bfdd07.js></script>
<script src=/js/app.d04362c5.js></script>

to:

<script src=js/chunk-vendors.19bfdd07.js></script>
<script src=js/app.d04362c5.js></script>

The first instance means that the js files will resolve to

file:///js/chunk-vendors.19bfdd07.js
file:///js/app.d04362c5.js

instead of:

file:///android_asset/js/chunk-vendors.19bfdd07.js
file:///android_asset/js/app.d04362c5.js

You can configure the vue.config.js to not output the slashes

3 Comments

This was the first thing I tried and it didn't work. In order to avoid fixing all URLs manually, I implemented ShouldInterceptRequest to serve the contents from android Assets. When I put a brekpoint it stops there, fixes the URL correctly, but in the end the page is still empty.
to remove the '/' automatically during the build add the publicPath: './' to your vue.config.js in vue cli 3/4 project setup. cli.vuejs.org/config/#publicpath
This turned out to be the correct way to go, however instead of ./ I had to use empty string for publicPath and assetsDir
0

After struggling a bit more it seems, that the root of my problem was in the build, producing absolute paths for the JS files and assets. I changed the two settings in vue.config.js to get it working correctly.

Please note, that previous solution also works, but it's hiding the real problem instead of solving it. Also this workaround works only for android (I didn't find how to do the same on iOS)

Here is what I did: I changed the assetsDir and publicPath to empty string to force the build to produce relative paths.

    module.exports = {
      assetsDir: '',
      publicPath: '',
      .....
    }

Additionally I had to turn off the history mode of the router and define one additional route:

    {
      path: '/index.html',
      name: 'index',
      component: ..........,
    }

This way there are no issues serving from the file system on iOS as well.

------------------- alternative workaround ---------------------

The solution below works only on android:

I figured it out. It's the file:// urls! Change to http://localhost and it works (+ a few lines of code)

The same reason, why some pages just don't work when you open them directly from the file system. I rememberd, that in order to view some web pages, I had to create a local webserver or otherwise I can't see nearly anything, because the browser is way more restrictive when you open a page this way. Once I changed it to http://localhost and fixed my ShouldInterceptRequest to serve any requests to "http://localhost" from android Assets and everything worked.

Not sure why (yet) - I had to load the index this way:

wv.LoadDataWithBaseURL( "http://localhost", new StreamReader( Assets.Open( "index.html" ) ).ReadToEnd(), "text/html", "UTF-8", null );

instead of

wv.LoadUrl( "http://localhost/index.html" );

Also I had to override WebViewClient to serve content from assets when http://localhost is requested:


    // view client to serve the web from assets folder correctly
    public class MyWebViewClient : WebViewClient {

        private AssetManager m_Assets;

        public MyWebViewClient( AssetManager assets ) {
            m_Assets = assets;
        }

        public static string GetMimeType( String url ) {
            String type = null;
            String extension = url.Split( '.' ).Last();
            if ( extension != null ) {
                if ( extension == "css" ) {
                    return "text/css";
                } else if ( extension == "js" ) {
                    return "text/javascript";
                } else if ( extension == "png" ) {
                    return "image/png";
                } else if ( extension == "gif" ) {
                    return "image/gif";
                } else if ( extension == "jpg" ) {
                    return "image/jpeg";
                } else if ( extension == "jpeg" ) {
                    return "image/jpeg";
                } else if ( extension == "woff" ) {
                    return "application/font-woff";
                } else if ( extension == "woff2" ) {
                    return "application/font-woff2";
                } else if ( extension == "ttf" ) {
                    return "application/x-font-ttf";
                } else if ( extension == "eot" ) {
                    return "application/vnd.ms-fontobject";
                } else if ( extension == "svg" ) {
                    return "image/svg+xml";
                }

                type = "text/html";
            }

            return type;
        }

        public override WebResourceResponse ShouldInterceptRequest( WebView view, IWebResourceRequest request ) {

            if ( request.Url != null && request.Url.Path != null && request.Url.Host == "localhost" ) {
                var mimeType = GetMimeType( request.Url.Path );
                var fileUrl = request.Url.Path
                    .Replace( "file://", "" )
                    .Replace( "android_asset/", "" )
                    .Trim( '/' );

                string extension = request.Url.Path.Split( '.' ).Last();;
                string fileEncoding = "application/octet-stream";
                if ( extension.EndsWith( "html" ) || extension.EndsWith( "js" ) || extension.EndsWith( "css" ) )
                    fileEncoding = "UTF-8";

                try {
                    return new WebResourceResponse( mimeType, fileEncoding, m_Assets.Open( fileUrl ) );
                } catch {
                    // ignore
                }
            }

            return base.ShouldInterceptRequest( view, request );
        }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.