Android webview画像ファイルのアップロード互換性の問題--アップロードコントロールのクリックが無効な解決方法

18917 ワード

Android webview画像ファイルのアップロード互換性の問題--アップロードコントロールのクリックが無効な解決方法
現象の説明:
Androidのwebviewでは、webページにのコントロールが付いていれば、webviewではこのアップロードコントロールは正常に表示されますが、一部の携帯電話でクリックしても反応しません.
理由の検索
多くの機種の検証で5.0以下が正常に表示され、5.0以上の携帯電話のクリックは反応しなかった.一応Webviewの互換性の問題と判断できます.通常、アップロードコントロールのあるwebviewでアップロードボタンをクリックする流れは、WebViewがアップロードファイルを含むフォームボタンをロードし、HTMLがinputラベルを定義するとともに、inputのtypeタイプがfileであり、指でこのボタンをクリックし、openFileChooserをコールバックする方法(5.0以上のシステムコールバックonShowFileChooser)であるべきである.次に、ファイルセレクタを開いて写真またはファイルを選択します.sdkの比較分析を調べたところ、Android 5.0以下のシステムコールバックopenFileChooser法は、5.0以上のシステムコールバックonShowFileChooser法であるが、システムがonShowFileChooser法を実現していないため、5.0以上のシステムで反応しない現象が発生していることが分かった.
ソリューション
そのため、webchromeClientのonShowFileChooserを書き換え、5.0以上のシステムの互換性を実現する必要があります.まずopenFileChooserとonShowFileChooserのシステムソースopenFileChooserを見てみましょう
/** 
 * Tell the client to open a file chooser. 
 * @param uploadFile A ValueCallback to set the URI of the file to upload. 
 *      onReceiveValue must be called to wake up the thread.a 
 * @param acceptType The value of the 'accept' attribute of the input tag 
 *         associated with this file picker. 
 * @param capture The value of the 'capture' attribute of the input tag 
 *         associated with this file picker. 
 * 
 * @deprecated Use {@link #showFileChooser} instead. 
 * @hide This method was not published in any SDK version. 
 */  
@SystemApi  
@Deprecated  
public void openFileChooser(ValueCallback uploadFile, String acceptType, String capture) {  
    uploadFile.onReceiveValue(null);  
}  

onShowFileChooser

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

この2つの方法を比較すると、openFileChooserに入力されたパラメータValueCallbackインタフェースはUriのオブジェクトを返し、onShowFileChooserはUri[]配列を返すため、onActivity ResultコールバックメソッドでValueCallbackインタフェースメソッドonReceiveValueの入力パラメータを呼び出すには、この2つのメソッドのコールバックが区別されることに特に注意する必要があります.
/** 
 *  onShowFileChooser  ,onReceiveValue  Uri     
 */  
mFilePathCallback.onReceiveValue(new Uri[]{uri});  
/** 
 *  openFileChooser  ,onReceiveValue    Uri   
 */  
mFilePathCallback4.onReceiveValue(uri);  

コードインスタンス
1.openFileChooserとonShowFileChooserメソッドを書き換え、各バージョンに対応
webview.setWebChromeClient(new WebChromeClient() {
            // For Android < 3.0
            public void openFileChooser(ValueCallback uploadMsg) {
                this.openFileChooser(uploadMsg, "*/*");
            }

            // For Android >= 3.0
            public void openFileChooser(ValueCallback uploadMsg,
                    String acceptType) {
                this.openFileChooser(uploadMsg, acceptType, null);
            }

            // For Android >= 4.1
            public void openFileChooser(ValueCallback uploadMsg,
                    String acceptType, String capture) {
                mUploadMessage = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("*/*");
                startActivityForResult(Intent.createChooser(i, "File Browser"),
                        FILECHOOSER_RESULTCODE);
            }

            // For Lollipop 5.0+ Devices
            @TargetApi(Build.VERSION_CODES.LOLLIPOP)
            public boolean onShowFileChooser(WebView mWebView,
                    ValueCallback filePathCallback,
                    WebChromeClient.FileChooserParams fileChooserParams) {
                if (mUploadMessage5 != null) {
                    mUploadMessage5.onReceiveValue(null);
                    mUploadMessage5 = null;
                }
                mUploadMessage5 = filePathCallback;
                Intent intent = fileChooserParams.createIntent();
                try {
                    startActivityForResult(intent,
                            FILECHOOSER_RESULTCODE_FOR_ANDROID_5);
                } catch (ActivityNotFoundException e) {
                    mUploadMessage5 = null;
                    return false;
                }
                return true;
            }
        });

2.onActivity Result()で選択したピクチャの内容をValueCallbackのonReceiveValueメソッドでWebViewに返し、その後jsでアップロードする
 protected void onActivityResult(int requestCode, int resultCode,
            Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (requestCode == FILECHOOSER_RESULTCODE) {
            if (null == mUploadMessage) {
                return;
            }
            Uri result = intent == null || resultCode != Activity.RESULT_OK ? null
                    : intent.getData();
            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;
        } else if (requestCode == FILECHOOSER_RESULTCODE_FOR_ANDROID_5) {
            if (null == mUploadMessage5) {
                return;
            }
            mUploadMessage5.onReceiveValue(WebChromeClient.FileChooserParams
                    .parseResult(resultCode, intent));
            mUploadMessage5 = null;
        }
    }

3.完全なコードインスタンス
package com.example.test;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;

public class MainActivity extends Activity {
    private ValueCallback mUploadMessage;
    private ValueCallback mUploadMessage5;
    public static final int FILECHOOSER_RESULTCODE = 5173;
    public static final int FILECHOOSER_RESULTCODE_FOR_ANDROID_5 = 5174;

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

        WebView webview = (WebView) findViewById(R.id.web_view);
        assert webview != null;
        WebSettings settings = webview.getSettings();
        settings.setUseWideViewPort(true);
        settings.setLoadWithOverviewMode(true);
        settings.setJavaScriptEnabled(true);
        webview.setWebChromeClient(new WebChromeClient() {
            // For Android < 3.0
            public void openFileChooser(ValueCallback uploadMsg){
                this.openFileChooser(uploadMsg, "*/*");
            }

            // For Android >= 3.0
            public void openFileChooser(ValueCallback uploadMsg,
                    String acceptType) {
                this.openFileChooser(uploadMsg, acceptType, null);
            }

            // For Android >= 4.1
            public void openFileChooser(ValueCallback uploadMsg,
                    String acceptType, String capture) {
                mUploadMessage = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("*/*");
                startActivityForResult(Intent.createChooser(i, "File Browser"),
                        FILECHOOSER_RESULTCODE);
            }

            // For Lollipop 5.0+ Devices
            @TargetApi(Build.VERSION_CODES.LOLLIPOP)
            public boolean onShowFileChooser(WebView mWebView,
                    ValueCallback filePathCallback,
                    WebChromeClient.FileChooserParams fileChooserParams) {
                if (mUploadMessage5 != null) {
                    mUploadMessage5.onReceiveValue(null);
                    mUploadMessage5 = null;
                }
                mUploadMessage5 = filePathCallback;
                Intent intent = fileChooserParams.createIntent();
                try {
                    startActivityForResult(intent,
                            FILECHOOSER_RESULTCODE_FOR_ANDROID_5);
                } catch (ActivityNotFoundException e) {
                    mUploadMessage5 = null;
                    return false;
                }
                return true;
            }
        });
        String targetUrl = "file:///android_asset/up.html";
        webview.loadUrl(targetUrl);
    }

    @SuppressLint("NewApi")
    @Override
    protected void onActivityResult(int requestCode, int resultCode,
            Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (requestCode == FILECHOOSER_RESULTCODE) {
            if (null == mUploadMessage) {
                return;
            }
            Uri result = intent == null || resultCode != Activity.RESULT_OK ? null
                    : intent.getData();
            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;
        } else if (requestCode == FILECHOOSER_RESULTCODE_FOR_ANDROID_5) {
            if (null == mUploadMessage5) {
                return;
            }
            mUploadMessage5.onReceiveValue(WebChromeClient.FileChooserParams
                    .parseResult(resultCode, intent));
            mUploadMessage5 = null;
        }
    }
}

コードのgitアドレス、簡単なwebのアップロードボタンページを添付して、assetsフォルダの下でgitアドレスcsdnダウンロードアドレス
後記
会社のプロジェクトで使用されているcordova 3のためです.0.0,同様にこの互換性の問題もあり,CordovaChromeClientに書き換える方法と同じである.またcordovaの最新バージョンを表示すると、この問題が修正されたことがわかります.cordovaをアップグレードすることで、この問題を解決することもできます.最新版cordovaのSystemWebChromeClientにはonShowFileChooserが書き換えられています.具体的には以下の通りです.
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    public boolean onShowFileChooser(WebView webView, final ValueCallback filePathsCallback, final WebChromeClient.FileChooserParams fileChooserParams) {
        Intent intent = fileChooserParams.createIntent();
        try {
            parentEngine.cordova.startActivityForResult(new CordovaPlugin() {
                @Override
                public void onActivityResult(int requestCode, int resultCode, Intent intent) {
                    Uri[] result = WebChromeClient.FileChooserParams.parseResult(resultCode, intent);
                    LOG.d(LOG_TAG, "Receive file chooser URL: " + result);
                    filePathsCallback.onReceiveValue(result);
                }
            }, intent, FILECHOOSER_RESULTCODE);
        } catch (ActivityNotFoundException e) {
            LOG.w("No activity found to handle file chooser intent.", e);
            filePathsCallback.onReceiveValue(null);
        }
        return true;
    }

ソースコードからonActivity Resultの操作もonShowFileChooserにカプセル化されていることがわかりますので、最新版cordovaにアップグレードするとonActivity Resultの操作を自分で書く必要がなく、より便利で手間が省けます.
コードのgitアドレス、簡単なwebのアップロードボタンページを添付して、assetsフォルダの下でgitアドレスcsdnダウンロードアドレス