0

I have a web app (https://salon.techwithin.in) that will be used on both Browsers and Android apps.

Sample QR Code for my app https://i.postimg.cc/Fsm9bKwT/sample-qr-scan.jpg

To convert this web app to Android App I have created a simple Android application with WebView

public class MainActivity extends AppCompatActivity {
    private WebView mywebView;
    private String userAgent;
    private static final int PERMISSION_REQUEST_CODE = 200;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (checkPermission()) {

        } else {
            requestPermission();
        }

        setContentView(R.layout.activity_main);
        mywebView=findViewById(R.id.webview);
        mywebView.setWebViewClient(new mywebClient());
        
        // TO TEST ON DEVELOPERS DEMO PORTAL I USED THEIR PAGE TO LOAD IN WEBVIEW, BUT SAME ERROR ON THAT PAGE TOO.
        //mywebView.loadUrl("https://blog.minhazav.dev/research/html5-qrcode.html");

        mywebView.loadUrl("https://salon.techwithin.in");

        WebSettings webSettings=mywebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setDomStorageEnabled(true);
        webSettings.setDatabaseEnabled(true);
        webSettings.setPluginState(WebSettings.PluginState.ON);
        webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        webSettings.setLoadWithOverviewMode(false);
        webSettings.setAllowFileAccess(true);
        webSettings.setSupportZoom(true);
        webSettings.setBuiltInZoomControls(false);
        webSettings.setSupportMultipleWindows(true);
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        CookieManager.getInstance().setAcceptCookie(true);

        //userAgent = System.getProperty("http.agent");
        //webSettings.setUserAgentString(webSettings.getUserAgentString().replace("; wv",""));

        mywebView.setWebChromeClient(new WebChromeClient(){
            @Override
            public void onPermissionRequest(final PermissionRequest request) {
                request.grant(request.getResources());
            }
        });
    }

I am getting the following error in logcat, and the QR scanner area remains blank.

2022-09-06 09:05:44.877 2932-2932/in.techwithin.thesalonman I/chromium: [INFO:CONSOLE(2)] "Uncaught ReferenceError: globalThis is not defined", source: https://blog.minhazav.dev/assets/research/html5qrcode/html5-qrcode.min.js (2) 2022-09-06 09:05:44.894 2932-2932/in.techwithin.thesalonman I/Choreographer: Skipped 33 frames! The application may be doing too much work on its main thread. 2022-09-06 09:05:44.975 2932-2932/in.techwithin.thesalonman I/chromium: [INFO:CONSOLE(432)] "Uncaught TypeError: Html5QrcodeScanner is not a constructor", source: https://blog.minhazav.dev/research/html5-qrcode.html (432)

Current Plugin Used to scan qr codes in my web app

https://github.com/mebjas/html5-qrcode

I have tried using the developer's Html5-qrcode demo page in webview which throws the same error. (Check the comments in code sample)

The android app is requesting CAMERA Access properly, To test the camera working I have tested another plugins demo web page

https://nimiq.github.io/qr-scanner/demo/

It opens the camera and scans QR codes properly,

But for now, I won't be able to switch to this working plugin in my web app, hence I need a solution with a current plugin (html5-qrcode) only. My current web app is built in Core PHP and Used html5-qrcode plugin directly in browser without any loader <script src="https://unpkg.com/html5-qrcode" type="text/javascript">

I am new to android app development hence any help will be appreciated.

2 Answers2

0

I am new to android app too.

Based on your code and this answer of this comment html5-qrcode plugin works in my WebView.

Here is the code

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <meta-data android:name="com.onesignal.suppressLaunchURLs" android:value="true"/>


    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.CAMERA2" />
    <uses-permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" />
    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
    <uses-permission android:name="android.permission.VIDEO_CAPTURE" />


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_stat_onesignal_default"
        android:label="PRONOMIO"
        android:roundIcon="@mipmap/ic_stat_onesignal_default_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppCompat.NoActionBar"
        android:usesCleartextTraffic="true"
        android:background="@android:color/black"
        android:name=".PronomioTitanApplicationClass">
        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|screenSize"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing" android:value="false" />
    </application>

</manifest>

MainActivity.java

package com.test.myapp;

import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.util.Log;
import android.view.Window;
import android.webkit.JsResult;
import android.webkit.PermissionRequest;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.widget.Toast;


import com.test.myapp.databinding.ActivityMainBinding;

import java.io.IOException;
import java.io.StringWriter;
import java.lang.annotation.Target;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.view.WindowCompat;

public class MainActivity extends AppCompatActivity {


    private WebView myapp;

    Context context;

    ActivityMainBinding binding;

    private int currentApiVersion;

    @Override
    protected void onResume() {
        super.onResume();
        checkCameraPermission();
    }

    private void checkCameraPermission() {
        int writeExternalStorage = ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA);
        if (writeExternalStorage != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.CAMERA}, 1001);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 1001) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //Do your stuff
                //openWebView();
            } else {
                checkCameraPermissionAndStartWebView();
            }
        }
    }

    private void checkCameraPermissionAndStartWebView() {
        int writeExternalStorage = ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA);
        if (writeExternalStorage != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.CAMERA}, 1001);
        } else {
            //openWebView();
        }
    }




    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        currentApiVersion = android.os.Build.VERSION.SDK_INT;
        final int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
        if (currentApiVersion >= Build.VERSION_CODES.KITKAT) {
            getWindow().getDecorView().setSystemUiVisibility(flags);
            final View decorView = getWindow().getDecorView();
            decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
                @Override
                public void onSystemUiVisibilityChange(int visibility) {
                    if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
                        decorView.setSystemUiVisibility(flags);
                    }
                }
            });
        }

        myapp = (WebView) findViewById(R.id.myapp);


        myapp.setWebViewClient(new WebViewClient() {
            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                myapp.loadUrl("file:///android_asset/error-page.html");

            }
        });

        if(haveNetworkConnection()) {
            myapp.getSettings().setMediaPlaybackRequiresUserGesture(true);
            myapp.getSettings().setSupportZoom(true);
            myapp.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
            myapp.getSettings().setJavaScriptEnabled(true);
            myapp.getSettings().setAllowFileAccessFromFileURLs(true);
            myapp.getSettings().setAllowUniversalAccessFromFileURLs(true);

            myapp.getSettings().setPluginState(WebSettings.PluginState.ON);
            myapp.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
            myapp.getSettings().setLoadWithOverviewMode(false);
            myapp.getSettings().setAllowFileAccess(true);
            myapp.getSettings().setBuiltInZoomControls(false);
            myapp.getSettings().setSupportMultipleWindows(true);
            myapp.setWebChromeClient(new WebChromeClient(){
                @Override
                public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                    //Required functionality here
                    return super.onJsAlert(view, url, message, result);
                }
                @Override
                public void onPermissionRequest(final PermissionRequest request) {
                    request.grant(request.getResources());
                }
            });
            myapp.loadUrl("https://webview.example.com/");
        } else {
            myapp.loadUrl("file:///android_asset/error-page.html");
        }
        myapp.getSettings().setDomStorageEnabled(true);
        myapp.getSettings().setJavaScriptEnabled(true);

    }

    public void onPermissionRequest(final PermissionRequest request) {
        final String[] requestedResources = request.getResources();
        for (String r : requestedResources) {
            if (r.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {
                request.grant(new String[]{PermissionRequest.RESOURCE_VIDEO_CAPTURE});
                break;
            }
        }
    }

    @SuppressLint("NewApi")
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (currentApiVersion >= Build.VERSION_CODES.KITKAT && hasFocus) {
            getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                    View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                    View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        }
    }




    private boolean haveNetworkConnection() {
        boolean haveConnectedWifi = false;
        boolean haveConnectedMobile = false;



        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo[] netInfo = cm.getAllNetworkInfo();

        for (NetworkInfo ni : netInfo) {
            if (ni.getTypeName().equalsIgnoreCase("WIFI"))
                if (ni.isConnected())
                    haveConnectedWifi = true;
            if (ni.getTypeName().equalsIgnoreCase("MOBILE"))
                if (ni.isConnected())
                    haveConnectedMobile = true;
        }
        return haveConnectedWifi || haveConnectedMobile;
    }
}

And in website i have in <head></head>

<script src="https://github.com/mebjas/html5-qrcode" id="scan-qr-js"></script>

In <body></body>

<div class="SCAN-QR-READER-AREA">
    <div id="qr-reader"></div>
    <div id="qr-reader-results"></div>
</div>
<script>
    function docReady(fn) {
        // see if DOM is already available
        if (document.readyState === "complete"
            || document.readyState === "interactive") {
            // call on next available tick
            setTimeout(fn, 1);
        } else {
            document.addEventListener("DOMContentLoaded", fn);
        }
    }
 
    docReady(function () {
            var resultContainer = document.getElementById("qr-reader-results");
            var lastResult, countResults = 0;
            function onScanSuccess(decodedText, decodedResult) {
                if (decodedText !== lastResult) {
                    ++countResults;
                    //lastResult = decodedText;
                    // Handle on success condition with the decoded message.
                    console.log(`Scan result ${decodedText}`, decodedResult);
                    alert(decodedText);
                }
            }

            var html5QrcodeScanner = new Html5QrcodeScanner(
                "qr-reader", { fps: 10, qrbox: 250, rememberLastUsedCamera: false });
                html5QrcodeScanner.render(onScanSuccess);
        
        }
});
</script>

Hope it helps

Sak
  • 87
  • 2
  • 13
0

Problem is not with your android code as per issues list of the project, problem is in html5-qrcode.min.js files after version 2.3.1

so i copied file from following link , and it started working. https://github.com/mebjas/html5-qrcode/releases/tag/v2.3.1