Notes on the Use of input Tags in Android

Notes on the Use of input Tags in Android

Recently encountered a demand, html page has a <input type="file"/> tag to call the client's photo taking and photo selection function, in iOS can be normally set up to take photos, select photos. But there's no response on Android, and that's what I expected.

Of course, I'll find a way to solve the problem. First, I opened the biying search. All the blogs were written by other people who crawled. All the articles had no value at all. So I went to stack overflow and found one. This answer . Next, I opened the source code of webChormeClient(sdk25) and saw this method. When the input tag of the page clicks, we can intercept it, then process and return the data ourselves. What data format can we return? I was thinking about it at that time, and I saw it later. this Get the answer, base64.

  /**
     * Tell the client to show a file chooser.
     *
     * This is called to handle HTML forms with 'file' input type, in response to the
     * user pressing the "Select File" button.
     * To cancel the request, call <code>filePathCallback.onReceiveValue(null)</code> and
     * return true.
     *
     * @param webView The WebView instance that is initiating the request.
     * @param filePathCallback Invoke this callback to supply the list of paths to files to upload,
     *                         or NULL to cancel. Must only be called if the
     *                         <code>showFileChooser</code> implementations returns true.
     * @param fileChooserParams Describes the mode of file chooser to be opened, and options to be
     *                          used with it.
     * @return true if filePathCallback will be invoked, false to use default handling.
     *
     * @see FileChooserParams
     */
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
            FileChooserParams fileChooserParams) {
        return false;
    }

Ideas:

  • When the input tag of the html page is clicked, we pop up the dialog box to let the user choose to take photos, select photos, etc.
  • User Photo Selection Photo Processing Completed and Transferred to base64 to Return Data to Front End
  • The front end takes the base64 data returned by the client and displays it on the corresponding control.

Processing of Html

Html code

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1, user-scalable=no">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <title></title>
        <style>
            .header {
                height: 44px;
                line-height: 44px;
                text-align: center;
            }

            #capture_img {
                width: 200px;
                height: 200px;
            }

            #video {
                width: 100%;
                height: 200px;
            }
        </style>
    </head>

    <body>
        <header class="header">
            <input type="file" accept="image/*" capture="camera" />
        </header>
        <img id="capture_img" />
        <video id="video" controls="controls" />
    </body>
    <script>
        //*** This method will be called after the Android side takes a picture and selects a photo ****
        function onCaptureFinished(base64String) {
            var img = document.getElementById('capture_img');
            if(base64String) {
                img.src = 'data:image/png;base64,' + base64String;
            }
        }

        //*** Call this method after the Android end has been videotaped ****
        function onRecordFinished(base64String) {
            var video = document.getElementById('video');
            video.src = "data:video/mp4;base64," + base64String;
            setTimeout(function() {
                video.play();
            }, 1000);
        }
    </script>
</html>

Android Processing

1. Initialize WebView

 private void initWebView() {
        mWebView = (WebView) findViewById(R.id.wv_content);
        WebSettings mWebSettings = mWebView.getSettings();
        mWebSettings.setAppCacheEnabled(false);//Prohibit APP caching
        mWebSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);//Prohibit Web Caching
        mWebSettings.setJavaScriptEnabled(true);
        mWebSettings.setSupportZoom(false);//No scaling allowed
        mWebSettings.setAllowFileAccess(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new FileWebChromeClient());
        jsObject = new JavaScriptObject(this);
        //Here is the object that we want to execute the js method
        mWebView.addJavascriptInterface(jsObject, "qfxl");
        mWebView.loadUrl("file:///android_asset/index.html");
    }

2. Rewrite the onShowFileChooser method in WebChromeClient after loading the page with webView

class FileWebChromeClient extends WebChromeClient {
        // This method is called when the page has an input type=file tag
        public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
                                         WebChromeClient.FileChooserParams fileChooserParams) {

            //Here the bouncing frame lets the user choose to take photos, videos, photos, etc.
            showFileChooser();
            return false;
        }
    }

3. Monitor Html's input events, bullet windows

   private void showFileChooser() {
        String[] selectPicTypeStr = {"Photograph", "videotape", "Select from the album"};
        AlertDialog mAlertDialog = new AlertDialog.Builder(MainActivity.this)
                .setItems(selectPicTypeStr,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                                int which) {
                                switch (which) {
                                    // Photograph
                                    case 0:
                                        Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                                        File file = new File(IMG_PATH);
                                        if (!file.exists()) {
                                            try {
                                                file.createNewFile();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                        captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
                                        startActivityForResult(captureIntent, CAPTURE_TAG);
                                        break;
                                    //videotape
                                    case 1:
                                        File videoFile = new File(VIDEO_PATH);
                                        if (!videoFile.exists()) {
                                            try {
                                                videoFile.createNewFile();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                        Intent videoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
                                        videoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(videoFile));
                                        startActivityForResult(videoIntent, CAMERA_TAG);
                                        break;
                                    // Mobile Phone Album
                                    case 2:
                                        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                                        i.addCategory(Intent.CATEGORY_OPENABLE);
                                        i.setType("image/*");
                                        startActivityForResult(Intent.createChooser(i, "Select the photo you want to add"), CHOOSE_TAG);
                                        break;
                                    default:
                                        break;
                                }

                            }
                        }).setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialogInterface) {
                        //DO NOTHING
                    }
                }).show();
    }

4. Processing on Activity Result for Photo Selection

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case CAPTURE_TAG://Photograph
                if (data != null) {//When uri is specified, data is empty
                } else {
                    Bitmap bitmap = BitmapFactory.decodeFile(IMG_PATH);
                    if (bitmap != null) {
                    //Call the js method to convert Bitmap to base64String
                        jsObject.onCaptureFinished(mWebView, castBitmapToBase64(bitmap));
                    } else {
                        Toast.makeText(MainActivity.this, "bitmap is null", Toast.LENGTH_SHORT).show();
                    }
                }
                break;
            case CAMERA_TAG://videotape
                if (data != null) {
                    File file = new File(VIDEO_PATH);
                    if (file.exists()) {
                        try {
                            FileInputStream fin = new FileInputStream(file);
                            byte[] b = new byte[(int) file.length()];
                            fin.read(b);
                            fin.close();
                            String base64String = Base64.encodeToString(b, Base64.DEFAULT);
                            //Calling js method
                            jsObject.onRecordFinished(mWebView, base64String);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
                break;
            case CHOOSE_TAG://Select from the album
                if (data != null) {
                    Uri uri = data.getData();
                    try {
                        Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
                        //Calling js method
                        jsObject.onCaptureFinished(mWebView, castBitmapToBase64(bitmap));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
        }
    }

5,JavaScriptObject

public class JavaScriptObject {
    private Context context;

    public JavaScriptObject(Context context) {
        this.context = context;
    }
    /**
     *
     * Take a picture or choose the end of the picture
     * @param webview
     * @param base64String
     */
    public void onCaptureFinished(WebView webview, String base64String) {
        webview.loadUrl("javascript:onCaptureFinished('" + base64String + "')");
    }

    /**
     * End of Video Recording
     * @param webView
     */
    public void onRecordFinished(WebView webView,String base64String){
        webView.loadUrl("javascript:onRecordFinished('" + base64String + "')");
    }
}

Complete code

public class MainActivity extends AppCompatActivity {
    private WebView mWebView;
    private JavaScriptObject jsObject;
    private final int CAPTURE_TAG = 1;
    private final int CAMERA_TAG = 2;
    private final int CHOOSE_TAG = 3;
    private final String IMG_PATH = Environment.getExternalStorageDirectory() + File.separator + "upload.jpg";
    private final String VIDEO_PATH = Environment.getExternalStorageDirectory() + File.separator + "upload.mp4";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initWebView();
    }

    private void initWebView() {
        mWebView = (WebView) findViewById(R.id.wv_content);
        WebSettings mWebSettings = mWebView.getSettings();
        mWebSettings.setAppCacheEnabled(false);//Prohibit APP caching
        mWebSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);//Prohibit Web Caching
        mWebSettings.setJavaScriptEnabled(true);
        mWebSettings.setSupportZoom(false);//No scaling allowed
        mWebSettings.setAllowFileAccess(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new FileWebChromeClient());
        jsObject = new JavaScriptObject(this);
        mWebView.addJavascriptInterface(jsObject, "qfxl");
        mWebView.loadUrl("file:///android_asset/index.html");
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case CAPTURE_TAG://Photograph
                if (data != null) {//When uri is specified, data is empty
                } else {
                    Bitmap bitmap = BitmapFactory.decodeFile(IMG_PATH);
                    if (bitmap != null) {
                        jsObject.onCaptureFinished(mWebView, castBitmapToBase64(bitmap));
                    } else {
                        Toast.makeText(MainActivity.this, "bitmap is empty", Toast.LENGTH_SHORT).show();
                    }
                }
                break;
            case CAMERA_TAG://videotape
                if (data != null) {
                    File file = new File(VIDEO_PATH);
                    if (file.exists()) {
                        try {
                            FileInputStream fin = new FileInputStream(file);
                            byte[] b = new byte[(int) file.length()];
                            fin.read(b);
                            fin.close();
                            String base64String = Base64.encodeToString(b, Base64.DEFAULT);
                            jsObject.onRecordFinished(mWebView, base64String);
                            Toast.makeText(MainActivity.this, "finished", Toast.LENGTH_SHORT).show();
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
                break;
            case CHOOSE_TAG://Select from the album
                if (data != null) {
                    Uri uri = data.getData();
                    try {
                        Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
                        jsObject.onCaptureFinished(mWebView, castBitmapToBase64(bitmap));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
        }
    }

    /**
     * Converting bitmap to base64
     *
     * @param bitmap
     * @return
     */
    private String castBitmapToBase64(Bitmap bitmap) {
        String base64String = "";
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 30, bos);
            byte[] bitmapBytes = bos.toByteArray();
            bos.flush();
            bos.close();
            base64String = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return base64String;
    }


    class FileWebChromeClient extends WebChromeClient {
        // This method is called when the page has an input type=file tag
        public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
                                         WebChromeClient.FileChooserParams fileChooserParams) {

            showFileChooser();
            return false;
        }

    }

    private void showFileChooser() {
        String[] selectPicTypeStr = {"Photograph", "videotape", "Select from the album"};
        AlertDialog mAlertDialog = new AlertDialog.Builder(MainActivity.this)
                .setItems(selectPicTypeStr,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                                int which) {
                                switch (which) {
                                    // Photograph
                                    case 0:
                                        Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                                        File file = new File(IMG_PATH);
                                        if (!file.exists()) {
                                            try {
                                                file.createNewFile();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                        captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
                                        startActivityForResult(captureIntent, CAPTURE_TAG);
                                        break;
                                    //videotape
                                    case 1:
                                        File videoFile = new File(VIDEO_PATH);
                                        if (!videoFile.exists()) {
                                            try {
                                                videoFile.createNewFile();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                        Intent videoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
                                        videoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(videoFile));
                                        startActivityForResult(videoIntent, CAMERA_TAG);
                                        break;
                                    // Mobile Phone Album
                                    case 2:
                                        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                                        i.addCategory(Intent.CATEGORY_OPENABLE);
                                        i.setType("image/*");
                                        startActivityForResult(Intent.createChooser(i, "Select the photo you want to add"), CHOOSE_TAG);
                                        break;
                                    default:
                                        break;
                                }

                            }
                        }).setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialogInterface) {
                        //DO NOTHING
                    }
                }).show();
    }

}

The process is sorted out.

1. Listen for html file events

2. Client pop-up dialog box lets user choose specific operation

3. Handling callbacks of user operations

4. Return the callback data to html

summary

Blog can not stop updating, good memory is better than bad writing.

Keywords: Android Mobile Javascript iOS

Added by GoSharks on Sat, 13 Jul 2019 22:47:39 +0300