12

Its a simple WebApp with a Webview in it and there is a registration page with upload file option. trying to open file chooser when click on browse button but there is not response. i assume that there is some issue in my gradle files. please help me debug it. Here is my Code.

here is my project gradle

    buildscript {

        repositories {
            google()
            jcenter()
            mavenCentral()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:3.1.3'
            classpath 'com.google.gms:google-services:3.2.1'
        }
    }
    allprojects {
        repositories {
            google()
            jcenter()
        }
    }

    task clean(type: Delete) {
        delete rootProject.buildDir
    }

and here is my app gradle

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.androidapp.myApp"
        minSdkVersion 15
        targetSdkVersion 27
        versionCode 2
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        useLibrary 'org.apache.http.legacy'

        multiDexEnabled true
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support:support-v4:27.1.1'
    implementation 'com.google.firebase:firebase-invites:16.0.1'
    implementation 'com.google.firebase:firebase-messaging:17.1.0'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'com.squareup.okhttp3:okhttp:3.8.0'

    implementation 'com.android.support:multidex:1.0.3'


}

apply plugin: 'com.google.gms.google-services'
WaqasArshad
  • 217
  • 1
  • 3
  • 12

2 Answers2

22

It is not the gradle file error. You just need to provide custom WebChromeClient like below.

class MyWebChromeClient extends WebChromeClient {
    // For 3.0+ Devices (Start)
    // onActivityResult attached before constructor
    protected void openFileChooser(ValueCallback uploadMsg, String acceptType) {
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
    }


    // For Lollipop 5.0+ Devices
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        if (uploadMessage != null) {
            uploadMessage.onReceiveValue(null);
            uploadMessage = null;
        }

        uploadMessage = filePathCallback;

        Intent intent = fileChooserParams.createIntent();
        try {
            startActivityForResult(intent, REQUEST_SELECT_FILE);
        } catch (ActivityNotFoundException e) {
            uploadMessage = null;
            Toast.makeText(WebLink.this, "Cannot Open File Chooser", Toast.LENGTH_LONG).show();
            return false;
        }
        return true;
    }

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

    protected void openFileChooser(ValueCallback<Uri> uploadMsg) {
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
    }
}

and it in your webview like below

webview.setWebChromeClient(new MyWebChromeClient());

some other useful stuff/variables to be declared globally.

public ValueCallback<Uri[]> uploadMessage;
private ValueCallback<Uri> mUploadMessage;
public static final int REQUEST_SELECT_FILE = 100;
private final static int FILECHOOSER_RESULTCODE = 1;

make sure you have all the read/write permissions

UPDATE

Use below lines to provide access to files from storage.

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

// EDIT, add this line also
webview.getSettings().setJavaScriptEnabled(true);

UPDATE 2

Get the result in onActivityResult method. You can use the result given like below.

@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        if (requestCode == REQUEST_SELECT_FILE) {
            if (uploadMessage == null)
                return;
            uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
            uploadMessage = null;
        }
    } else if (requestCode == FILECHOOSER_RESULTCODE) {
        if (null == mUploadMessage)
            return;
        // Use MainActivity.RESULT_OK if you're implementing WebView inside Fragment
        // Use RESULT_OK only if you're implementing WebView inside an Activity
        Uri result = intent == null || resultCode != WebLink.RESULT_OK ? null : intent.getData();
        mUploadMessage.onReceiveValue(result);
        mUploadMessage = null;
    } else
        Toast.makeText(WebLink.this, "Failed to Upload Image", Toast.LENGTH_LONG).show();
}
Aakash Sharma
  • 368
  • 1
  • 4
  • 14
Rahul Khurana
  • 8,199
  • 6
  • 33
  • 57
10

This Kotlin code worked for all versions:

webView.settings.javaScriptEnabled = true
webView.settings.loadWithOverviewMode = true
webView.settings.useWideViewPort = true
webView.settings.domStorageEnabled = true
webView.settings.allowFileAccess=true
webView.settings.allowContentAccess=true
webView.settings.allowUniversalAccessFromFileURLs=true
webView.settings.allowFileAccessFromFileURLs=true
webView.settings.javaScriptCanOpenWindowsAutomatically=true
webView.loadUrl(Constants.URL)
webView.webChromeClient = object : WebChromeClient() {
        override fun onShowFileChooser(
            webView: WebView,
            filePathCallback: ValueCallback<Array<Uri>>,
            fileChooserParams: FileChooserParams
        ): Boolean {
            if (mUMA != null) {
                mUMA!!.onReceiveValue(null)
            }
            mUMA = filePathCallback
            var takePictureIntent: Intent? = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
            if (takePictureIntent!!.resolveActivity(this@MainActivity.getPackageManager()) != null) {
                var photoFile: File? = null
                try {
                    photoFile = createImageFile()
                    takePictureIntent.putExtra("PhotoPath", mCM)
                } catch (ex: IOException) {
                    Log.e("Webview", "Image file creation failed", ex)
                }
                if (photoFile != null) {
                    mCM = "file:" + photoFile.getAbsolutePath()
                    takePictureIntent.putExtra(
                        MediaStore.EXTRA_OUTPUT,
                        Uri.fromFile(photoFile)
                    )
                } else {
                    takePictureIntent = null
                }
            }
            val contentSelectionIntent = Intent(Intent.ACTION_GET_CONTENT)
            contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE)
            contentSelectionIntent.type = "*/*"
            val intentArray: Array<Intent>
            intentArray = takePictureIntent?.let { arrayOf(it) } ?: arrayOf<Intent>()
            val chooserIntent = Intent(Intent.ACTION_CHOOSER)
            chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent)
            chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser")
            chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray)
            startActivityForResult(chooserIntent, FCR)
            return true
        }
    }

// Create an image file
@Throws(IOException::class)
private fun createImageFile(): File? {
    @SuppressLint("SimpleDateFormat") val timeStamp: String =
        SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
    val imageFileName = "img_" + timeStamp + "_"
    val storageDir: File =
        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
    return File.createTempFile(imageFileName, ".jpg", storageDir)
}
fun openFileChooser(uploadMsg: ValueCallback<Uri?>?) {
    this.openFileChooser(uploadMsg, "*/*")
}

fun openFileChooser(
    uploadMsg: ValueCallback<Uri?>?,
    acceptType: String?
) {
    this.openFileChooser(uploadMsg, acceptType, null)
}

fun openFileChooser(
    uploadMsg: ValueCallback<Uri?>?,
    acceptType: String?,
    capture: String?
) {
    val i = Intent(Intent.ACTION_GET_CONTENT)
    i.addCategory(Intent.CATEGORY_OPENABLE)
    i.type = "*/*"
    this@MainActivity.startActivityForResult(
        Intent.createChooser(i, "File Browser"),
        FILECHOOSER_RESULTCODE
    )
}

override fun onActivityResult(
    requestCode: Int,
    resultCode: Int,
    intent: Intent?
) {
    super.onActivityResult(requestCode, resultCode, intent)
    if (Build.VERSION.SDK_INT >= 21) {
        var results: Array<Uri>? = null
        //Check if response is positive
        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == FCR) {
                if (null == mUMA) {
                    return
                }
                if (intent == null) { //Capture Photo if no image available
                    if (mCM != null) {
                        results = arrayOf(Uri.parse(mCM))
                    }
                } else {
                    val dataString = intent.dataString
                    if (dataString != null) {
                        results = arrayOf(Uri.parse(dataString))
                    }
                }
            }
        }
        mUMA!!.onReceiveValue(results)
        mUMA = null
    } else {
        if (requestCode == FCR) {
            if (null == mUM) return
            val result =
                if (intent == null || resultCode != Activity.RESULT_OK) null else intent.data
            mUM!!.onReceiveValue(result)
            mUM = null
        }
    }
}

/*needed fileds 
 private var mCM: String? = null
 private var mUM: ValueCallback<Uri>? = null
 private var mUMA: ValueCallback<Array<Uri>>? = null
 private const val FCR = 1*/
  • 2
    While this code may answer the question, [including an explanation](https://meta.stackexchange.com/q/114762) of how and why this solves the problem would help to improve the quality of your answer. Remember that you are also answering the question for readers in the future, not just the person asking now. – herrbischoff Jul 12 '20 at 19:46
  • Yeah so true this provides better result than the above ones – g7pro Oct 12 '20 at 08:15
  • worked liked a charm. Thank you so much. You saved so much of my time. – dennisrufigill Mar 28 '22 at 10:21