1

I have tried this and have also tested this code. Both work correctly if I need to upload from the file browser but I need to have the option to choose between the File Browser or Camera.

There's plenty of Java examples that I can find that do this, but I cannot find a working version that's done with Kotlin. Here's my current code below for the webview file uploader:

onActivityResult:

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
        {
            if(requestCode == REQUEST_SELECT_FILE){
                if(uploadMessage != null){
                    uploadMessage?.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode,data))
                    uploadMessage = null
                }
            }
        }else if(requestCode == FILECHOOSER_RESULTCODE){
            if(mUploadMessage!=null){
                var result = data?.data
                mUploadMessage?.onReceiveValue(result)
                mUploadMessage = null
            }
        }else{
            Toast.makeText(this,"Failed to open file uploader, please check app permissions.",Toast.LENGTH_LONG).show()
            super.onActivityResult(requestCode, resultCode, data)
        }

setWebChromeClient:

 // For 3.0+ Devices (Start)
            // onActivityResult attached before constructor
            fun openFileChooser(uploadMsg : ValueCallback<Uri>, acceptType:String) {
                mUploadMessage = uploadMsg
                val i = Intent(Intent.ACTION_GET_CONTENT)
                i.addCategory(Intent.CATEGORY_OPENABLE)
                i.type = "*/*"
                startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE)
            }

            // For Lollipop 5.0+ Devices
            override fun onShowFileChooser(mWebView:WebView, filePathCallback:ValueCallback<Array<Uri>>, fileChooserParams:WebChromeClient.FileChooserParams):Boolean {
                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
                    if (uploadMessage != null) {
                        uploadMessage?.onReceiveValue(null)
                        uploadMessage = null
                    }
                    uploadMessage = filePathCallback
                    val intent = fileChooserParams.createIntent()
                    try {
                        startActivityForResult(intent, REQUEST_SELECT_FILE)
                    } catch (e:ActivityNotFoundException) {
                        uploadMessage = null
                        Toast.makeText(getApplicationContext(), "Cannot Open File Chooser", Toast.LENGTH_LONG).show()
                        return false
                    }
                    return true
                }else{
                    return false
                }
            }

            //For Android 4.1 only
            fun openFileChooser(uploadMsg:ValueCallback<Uri>, acceptType:String, capture:String) {
                mUploadMessage = uploadMsg
                val intent = Intent(Intent.ACTION_GET_CONTENT)
                intent.addCategory(Intent.CATEGORY_OPENABLE)
                intent.type = "*/*"
                startActivityForResult(Intent.createChooser(intent, "File Browser"), FILECHOOSER_RESULTCODE)
            }

            fun openFileChooser(uploadMsg:ValueCallback<Uri>) {
                //filePermission()
                mUploadMessage = uploadMsg
                val i = Intent(Intent.ACTION_GET_CONTENT)
                i.addCategory(Intent.CATEGORY_OPENABLE)
                i.type = "*/*"
                startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE)
            }

Does anyone have a working sample of code that allows for uploading via the Camera or File Browser?

Also, the file browser works when selecting images/pdfs that's stored on the device itself but the files from Google Drive via this method, do not upload correctly. Any ideas?

Vegeta ZA
  • 70
  • 4
  • 16

1 Answers1

5

You might be missing adding below code

webview.getSettings().setDomStorageEnabled(true);
webview.getSettings().setAllowContentAccess(true);
webview.getSettings().setAllowFileAccess(true);

Here I have already given the answer in detail. Hope this will help you. If you have any query let me know.

EDIT

Declare below-mentioned variables globally

var requiredPermissions = arrayOf<String>(Permissions.CAMERA, Permissions.WRITE_EXTERNAL_STORAGE, Permissions.READ_EXTERNAL_STORAGE/*, Permissions.WRITE_SETTINGS*/)

val REQUEST_SELECT_FILE = 100
private val FILECHOOSER_RESULTCODE = 1
var uploadMessage: ValueCallback<Array<Uri>>? = null

var link: String? = null
private var mUploadMessage: ValueCallback<*>? = null

Kotlin Code:

@SuppressLint("SetJavaScriptEnabled")
private fun startWebView(url: String) {
    // Create new webview Client to show progress dialog
    // When opening a url or click on link
    // Javascript enabled on webview
    mWebView.settings.javaScriptEnabled = true
    mWebView.settings.builtInZoomControls = true
    mWebView.settings.displayZoomControls = true
    mWebView.settings.domStorageEnabled = true
    mWebView.settings.allowContentAccess = true
    mWebView.settings.setAppCacheEnabled(false)
    mWebView.settings.cacheMode = WebSettings.LOAD_NO_CACHE
    mWebView.settings.setGeolocationEnabled(true)      // life saver, do not remove
    mWebView.addJavascriptInterface(WebAppInterface(this), "Android")
    mWebView.webChromeClient = MyWebChromeClient()
    mWebView.webViewClient = object : WebViewClient() {

        // If you will not use this method url links are open in new browser
        // not in webview
        override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
            view.loadUrl(url)
            return true
        }

        override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                view.loadUrl(request.url.toString())
            }
            return true
        }

        override fun onReceivedError(view: WebView?, errorCode: Int, description: String?, failingUrl: String?) {
            super.onReceivedError(view, errorCode, description, failingUrl)
            util._log(TAG, "onReceivedError ")
        }

        // Show loader on url load
        override fun onLoadResource(view: WebView, url: String) {
        }

        override fun onPageFinished(view: WebView, url: String) {
            super.onPageFinished(view, url)
            progressBar.visibility = View.GONE
        }

        override fun onReceivedHttpError(view: WebView?, request: WebResourceRequest?, errorResponse: WebResourceResponse?) {
            super.onReceivedHttpError(view, request, errorResponse)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                util._log(TAG, "onReceivedHttpError ${errorResponse?.statusCode}")
            }
        }

        override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) {
            super.onReceivedError(view, request, error)
            util._log(TAG, "onReceivedError ")
            WebViewClient.ERROR_AUTHENTICATION
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                util._log(TAG, "error code: ${error.errorCode} " + request.url.toString() + " , " + error.description)
            }
        }

        override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) {
            super.onReceivedSslError(view, handler, error)
            util._log(TAG, "SSl error ")
        }
    }

    // Other webview options
    /*
     * mWebView.getSettings().setLoadWithOverviewMode(true);
     * mWebView.getSettings().setUseWideViewPort(true);
     * mWebView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
     * mWebView.setScrollbarFadingEnabled(false);
     * mWebView.getSettings().setBuiltInZoomControls(true);
     */


    // Load url in webview

    if (NetworkStatus.isOnline(this)) {
        Handler().postDelayed({ mWebView.loadUrl(url) }, 400)
    } else {
        util.showToast(this, getString(R.string.no_internet), true)
    }
}

internal inner class MyWebChromeClient : WebChromeClient() {
    // For 3.0+ Devices (Start)
    // onActivityResult attached before constructor
    protected fun openFileChooser(uploadMsg: ValueCallback<*>, acceptType: String) {
        mUploadMessage = uploadMsg
        val i = Intent(Intent.ACTION_GET_CONTENT)
        i.addCategory(Intent.CATEGORY_OPENABLE)
        i.type = "image/*"
        startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE)
    }

    // For Lollipop 5.0+ Devices
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onShowFileChooser(mWebView: WebView, filePathCallback: ValueCallback<Array<Uri>>, fileChooserParams: FileChooserParams): Boolean {
        if (uploadMessage != null) {
            uploadMessage!!.onReceiveValue(null)
            uploadMessage = null
        }

        uploadMessage = filePathCallback

        val intent = fileChooserParams.createIntent()
        try {
            startActivityForResult(intent, REQUEST_SELECT_FILE)
        } catch (e: Exception) {
            uploadMessage = null
            util.showToast(this@WebLink, "Cannot Open File Chooser")
            return false
        }

        return true
    }

    //For Android 4.1 only
    protected fun openFileChooser(uploadMsg: ValueCallback<Uri>, acceptType: String, capture: String) {
        mUploadMessage = uploadMsg
        val intent = Intent(Intent.ACTION_GET_CONTENT)
        intent.addCategory(Intent.CATEGORY_OPENABLE)
        intent.type = "image/*"
        startActivityForResult(Intent.createChooser(intent, "File Chooser"), FILECHOOSER_RESULTCODE)
    }

    protected fun openFileChooser(uploadMsg: ValueCallback<Uri>) {
        mUploadMessage = uploadMsg
        val i = Intent(Intent.ACTION_GET_CONTENT)
        i.addCategory(Intent.CATEGORY_OPENABLE)
        i.type = "image/*"
        startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE)
    }
}
Rahul Khurana
  • 8,577
  • 7
  • 33
  • 60
  • Thanks for this, I have seen it when I did it in Java for another project. Do you perhaps have a Kotlin version which includes the option to select Camera/File Browser? – Vegeta ZA Aug 17 '20 at 10:27
  • @VegetaZA I have added the kotlin code for your reference. Hope it helps you up to some extent. – Rahul Khurana Aug 18 '20 at 14:15
  • there's nothing for the camera option? My original question is for an option to select between the file browser or camera when uploading files. The code that I have in my original question works correctly for the file browser, just need to know how to make it work with the camera as well. The only issue with the above code is that it does not upload the files from Google Drive, it just works for files saved on the device. – Vegeta ZA Aug 19 '20 at 07:13
  • @VegetaZA I would recommend you **show a popup** for the user to select **option between camera and gallery** in **onShowFileChooser** method. Based on the user input use specific intent to take picture/pick image from the gallery. And then pass the result of the intent to **uploadMessage?.onReceiveValue** method – Rahul Khurana Aug 19 '20 at 13:58