Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing data from Activity to the JavaScript used in the HTML file loaded at webView in the same activity

My Android app objective is to scan a bar/QR code and pass it to the HTML file loaded at webView element.

The app has ONE layout only, that contain a button and the webview element. Once the button is clicked, the app open the bar/QR scanner and return the result to the activity, and I need to pass this result to my HTML file for further processing.

The MainActivity is working correctly, as in this tutorial, and return the result here:

    public void onActivityResult(int requestCode, int resultCode, Intent intent) {

    IntentResult scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
    if (scanningResult != null) {
        String scanContent = scanningResult.getContents();
        String scanFormat = scanningResult.getFormatName();
        formatTxt.setText("FORMAT: " + scanFormat);
        contentTxt.setText("CONTENT: " + scanContent);

     /*** I NEED to SEND the 'scanContent' to the my HTML file loaded at the WebView element   ***/

    }
    else{
        Toast toast = Toast.makeText(getApplicationContext(),
                "No scan data received!", Toast.LENGTH_SHORT);
        toast.show();
    }
}

I read this and this and this and this and thisand got the below below files:

JavaScriptInterface.java

 public class JavaScriptInterface {
    Context mContext;

    /** Instantiate the interface and set the context */
    JavaScriptInterface(Context c) {
        mContext = c;
    }

    /** Show a toast from the web page */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }

}

and the WebViewActivity.java:

public class WebViewActivity extends Activity {

// private WebView webView;

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    WebView webView = (WebView)findViewById(R.id.webview);

    webView.getSettings().setJavaScriptEnabled(true);
    webView.addJavascriptInterface(new JavaScriptInterface(this), "Android");

   /*** I think something wrong in this area **/

    webView.loadUrl("file:///android_asset/myFile.html");
    webView.setWebViewClient(new WebViewClient(){
        public void onPageFinished(WebView view, String url){
            view.loadUrl("javascript:init('" + url + "')");
        }
    });

    //webView.loadUrl("http://www.google.com");

    // String customHtml = "<html><body>" +
    //        ""+
    //        "</body></html>";
    // webView.loadData(customHtml, "text/html", "UTF-8");

}

}

No errors appeared in the Android Studio IDE, and when running the app the button for scanning work, but nothing appear in the webview, only white page!! white page always, from the start up of the up, and after scanning the code!

Any idea where I got lost, and how to fix it!

Note I need to send the data to the JavaScript used in the HTML file loaded at webView in the same activity, not just sending URL to be loaded.

Application tree

like image 797
Hasan A Yousef Avatar asked Jun 15 '16 21:06

Hasan A Yousef


1 Answers

After reading this and this and this, I found my case can be summarized in the below point:

  1. Passing variable from class to another in Android App (i.e. Java Classes)
  2. Calling Android function from JavaScript in the HTML file loaded at the WebView
  3. Sending a parameter back from Android Class to the JavaScript

So, the solution can be summarized in the below:

  1. Create JavaScriptInterface class that can interact with JavaScript commands
  2. Use the @JavascriptInterface annotation before each function required to interact with the JavaScript
  3. Define the name of the JavaScript interaction to be used as module in the JavaScript file as: myWebView.addJavascriptInterface(new JavaScriptInterface(this), "module"); in this statement, the module is the name module to be called in the javaScript file as module.function()
  4. Use getter and setter to communicate between Android classes.

My MainActivity.java file became like this:

package com.fonix.qr;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements OnClickListener {

private Button scanBtn;
private TextView formatTxt, contentTxt;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    scanBtn = (Button) findViewById(R.id.scan_button);
    formatTxt = (TextView) findViewById(R.id.scan_format);
    contentTxt = (TextView) findViewById(R.id.scan_content);
    scanBtn.setOnClickListener(this);

    WebView myWebView = (WebView) findViewById(R.id.webview);
    myWebView.setWebViewClient(new WebViewClient());

    myWebView.addJavascriptInterface(new JavaScriptInterface(this), "fonix");
    WebSettings webSettings = myWebView.getSettings();

    webSettings.setJavaScriptEnabled(true);
    myWebView.loadUrl("file:///android_asset/index.html");
}

public void onClick(View v) {
    if (v.getId() == R.id.scan_button) {
        scanCode();
      //  IntentIntegrator scanIntegrator = new IntentIntegrator(this);
      //  scanIntegrator.initiateScan();
    }
}
public void scanCode(){
    IntentIntegrator scanIntegrator = new IntentIntegrator(this);
    scanIntegrator.initiateScan();
    }

public String scanContent;
public String scanFormat;
public void onActivityResult(int requestCode, int resultCode, Intent intent) {

    IntentResult scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
    if (scanningResult != null) {
        scanContent = scanningResult.getContents();
        scanFormat = scanningResult.getFormatName();
        formatTxt.setText("FORMAT: " + scanFormat);
        contentTxt.setText("CONTENT: " + scanContent);
    } else {
        Toast toast = Toast.makeText(getApplicationContext(),
                "No scan data received!", Toast.LENGTH_SHORT);
        toast.show();
    }

}
public String getContent(){ return scanContent; }

public class JavaScriptInterface {
    Context mContext;

    /* Instantiate the interface and set the context */
    JavaScriptInterface(Context c) {
        mContext = c;
    }

    /* Show a toast from the web page */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }

    @JavascriptInterface
    public void scanJS() {
        scanCode();
    }

    @JavascriptInterface
    public String scanResult() {
         return getContent();
    }
  }
}

and the assets -> index.html file is:

<!DOCTYPE html>
<html>
<head>
   <title>QR and BarCode Scanner using both Android Native and JavaScript Functionality</title>
</head>

<body>
   Hello Android! <br/>
     <br/>
  The above button in Native Android, while the below one is JavaScript button. <br/>
    <br/>
<input type="button" value="Scan from JavaScript button" onClick="scan()" />
    <br/>
    <br/>
    <br/>
 Below button will show the latest scan result, regardless read through Native Android button, or JavaScript button, and will display the result as Native Android Toast.
   <br/>
   <br/>
  <input type="button" value="Show Scan result" onClick="showScanResult()" />
 </body>

 <script type="text/javascript">
    function scan() {
       fonix.scanJS();
    }

    function showScanResult() {
        var scanResult = fonix.scanResult();
        // (!scanResult) equal === null or === undefined
        if(!scanResult)fonix.showToast("Nothing had been scanned");
           else fonix.showToast(fonix.scanResult());
    }
 </script>
</html>

The layout -> activity_main.xml file is:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.fonix.qr.MainActivity">

<Button android:id="@+id/scan_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/scan"
    android:layout_alignParentTop="true"
    android:layout_alignParentStart="true" />

<TextView
    android:id="@+id/scan_format"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textIsSelectable="true"
    android:layout_below="@id/scan_button"
    android:layout_alignEnd="@+id/scan_button" />
<TextView
    android:id="@+id/scan_content"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textIsSelectable="true"
    android:layout_below="@+id/scan_format"
    android:layout_alignEnd="@+id/webview" />

<WebView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/webview"
    android:layout_alignParentStart="true"
    android:layout_alignParentBottom="true"
    android:layout_alignParentEnd="true"
    android:layout_below="@+id/scan_content" />
  </RelativeLayout>

The values -> strings.xml file is:

<resources>
   <string name="app_name">QR and BarCode reader</string>
   <string name="scan">Scan from Native Android button</string>
</resources>

The AndroidManifest.xml file is:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fonix.qr">

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>
<uses-permission android:name="android.permission.INTERNET" />

</manifest>

The Final App app structure is: enter image description here

and when reading This QR code.

like image 161
Hasan A Yousef Avatar answered Sep 28 '22 08:09

Hasan A Yousef