Android 文件选择器未从 Android Webview 调用

Android File Chooser not calling from Android Webview

它是一个带有 Webview 的简单 WebApp,并且有一个带有上传文件选项的注册页面。单击浏览按钮时尝试打开文件选择器但没有响应。我假设我的 gradle 文件中存在一些问题。请帮我调试一下。这是我的代码。

这是我的项目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
    }

这是我的应用程序 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'

这不是 gradle 文件错误。您只需提供如下所示的自定义 WebChromeClient。

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);
    }
}

它在您的网络视图中如下所示

webview.setWebChromeClient(new MyWebChromeClient());

一些其他有用的 stuff/variables 将在全球范围内声明。

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

确保您拥有所有 read/write 权限

更新

使用下面的行提供对存储文件的访问权限。

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

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

更新 2

在 onActivityResult 方法中获取结果。您可以使用下面给出的结果。

@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();
}

此 Kotlin 代码适用于所有版本:

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*/