2

Ich möchte den Dateizugriff in einem Android WebView deaktivieren, das ich mit der integrierten WebView-Komponente von react-native erstellt habe.Wie kann ichAllowFileAccess für ein reaktives natives android WebView setzen?

Die Android WebView docs sagen "Dateizugriff ist standardmäßig aktiviert.", Und dies ist ein Sicherheitsrisiko für meine Organisation.

Die react-native 0.31 docs Erwähnung eine getWebViewHandle Methode, die für den Zugriff auf den zugrunde liegenden WebView-Knoten verwendet werden kann; Wenn dies funktioniert, dann könnte ich (vermutlich) schreiben:

import { WebView, Platform } from 'react-native'; 
//... 
var reactWebview = <Webview [props here] /> 
if (Platform.OS === 'android') { 
    var webview = reactWebview.getWebViewHandle(); 
    webview.setAllowFileAccess(false); 
} 

jedoch spätere Versionen der react-native docs nicht getWebViewHandle erwähnen, und wenn ich Code ausführen, wie diese in reagieren-native 0,44 auf einem Android-Gerät, ich Erhalte den Fehler webview.getWebViewHandle is not a function.

Meine Fragen sind:

  1. ist standardmäßig für die Android WebViews durch reagieren-native erstellte Datei-Zugriff aktiviert?

  2. Wenn ja, wie können wir diesen Dateizugriff deaktivieren? Könnten wir dies erreichen, indem wir die WebView-Klasse erweitern, oder müssten wir react-native forkeln und modifizieren?

Danke für Ihre Zeit!

Antwort

2

Fragen 1: Wie aus dem Quellcode von ReactWebViewManager.java zu sehen, ruft RN nicht WebView.setAllowFileAccess, so dass Dateizugriff von Android WebView, nicht von RN aktiviert ist.

Fragen 2: Sie können eine benutzerdefinierte WebView erstellen zu tun, was Sie benötigen, oder einfach nur die Referenz Ihrer WebView von einem Native Module, dann können Sie alle apis von Android WebView zugreifen wie setAllowFileAccess in diesem Native Module.

india Modul

public class WebViewSettingModule extends ReactContextBaseJavaModule { 

    public WebViewSettingModule(ReactApplicationContext reactContext) { 
     super(reactContext); 
    } 

    @Override 
    public String getName() { 
     return "WebViewSetting"; 
    } 

    @ReactMethod 
    public void setWebView() { 

     Activity activity = getCurrentActivity(); 
     //the id for the ReactRootView is always be 1 
     @IdRes int id = 1; 
     View view = activity.findViewById(id); 
     if (view instanceof ReactRootView) { 
      ReactRootView reactRootView = (ReactRootView) view; 
      //make sure the WebView is directly child of ReactRootView 
      reactRootView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() { 
       @Override 
       public void onChildViewAdded(View parent, final View child) { 
        if (child instanceof WebView) { 
         Log.e("onChildViewAdded: ", ((WebView) child).getUrl()); 
         //get the reference to the WebView and setAllowFileAccess 
         ((WebView) child).getSettings().setAllowFileAccess(false); 
        } 
       } 

       @Override 
       public void onChildViewRemoved(View parent, View child) { 

       } 
      }); 
     } 
    } 
} 

index.android.js

import React, {Component} from "react"; 
import {AppRegistry, View, WebView} from "react-native"; 
//the native module 
import MyWebView from "./src/MyWebView"; 

export default class WebViewSetting extends Component { 

    componentDidMount() { 
     //notify native code to modify WebView setting 
     MyWebView.setWebView(); 
    } 

    render() { 
     return (
      <View style={{flex: 1}}> 
       <WebView 
        source={{uri: 'https://github.com/'}} 
        style={{marginTop: 20}}/> 
      </View> 
     ); 
    } 
} 


AppRegistry.registerComponent('WebViewSetting',() => WebViewSetting); 

der gesamte Code kann here

0

ich finden konnte diese Lösung nicht überall gefunden werden, so dachte ich, ich würde teilen, Ich hoffe es hilft ...

Um die Durchsuchen-Schaltfläche zu aktivieren Um zu arbeiten und anschließend den Dateizugriff zu erlauben, können Sie die obigen und diese reagierenden nativen Dateien durch folgende modifizierte Versionen ersetzen:

node_module/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java

/** 
* Copyright (c) 2015-present, Facebook, Inc. 
* All rights reserved. 
* This source code is licensed under the BSD-style license found in the 
* LICENSE file in the root directory of this source tree. An additional grant 
* of patent rights can be found in the PATENTS file in the same directory. 
*/ 

package com.facebook.react.uimanager; 

import javax.annotation.Nullable; 

import android.app.Activity; 
import android.content.Context; 

import com.facebook.react.bridge.ActivityEventListener; 
import com.facebook.react.bridge.ReactApplicationContext; 
import com.facebook.react.bridge.ReactContext; 
import com.facebook.react.bridge.LifecycleEventListener; 

// 

/** 
* Wraps {@link ReactContext} with the base {@link Context} passed into the constructor. 
* It provides also a way to start activities using the viewContext to which RN native views belong. 
* It delegates lifecycle listener registration to the original instance of {@link ReactContext} 
* which is supposed to receive the lifecycle events. At the same time we disallow receiving 
* lifecycle events for this wrapper instances. 
* TODO: T7538544 Rename ThemedReactContext to be in alignment with name of ReactApplicationContext 
*/ 
public class ThemedReactContext extends ReactContext { 

    private final ReactApplicationContext mReactApplicationContext; 

    public ThemedReactContext(ReactApplicationContext reactApplicationContext, Context base) { 
     super(base); 
     initializeWithInstance(reactApplicationContext.getCatalystInstance()); 
     mReactApplicationContext = reactApplicationContext; 
    } 

    @Override 
    public void addLifecycleEventListener(LifecycleEventListener listener) { 
     mReactApplicationContext.addLifecycleEventListener(listener); 
    } 

    @Override 
    public void removeLifecycleEventListener(LifecycleEventListener listener) { 
     mReactApplicationContext.removeLifecycleEventListener(listener); 
    } 

    @Override 
    public void addActivityEventListener(ActivityEventListener listener) { 
     mReactApplicationContext.addActivityEventListener(listener); 
    } 


    @Override 
    public void removeActivityEventListener(ActivityEventListener listener) { 
     mReactApplicationContext.removeActivityEventListener(listener); 
    } 

    @Override 
    public boolean hasCurrentActivity() { 
     return mReactApplicationContext.hasCurrentActivity(); 
    } 

    @Override 
    public 
    @Nullable 
    Activity getCurrentActivity() { 
     return mReactApplicationContext.getCurrentActivity(); 
    } 
} 

Die zweite Datei ist über

0

node_modules/reagieren-native/ReactAndroid/src/main/java/com/Faceboo aufgelistet k/reagieren/Ansichten/ReactWebViewManager.java

/** 
* Copyright (c) 2015-present, Facebook, Inc. 
* All rights reserved. 
* This source code is licensed under the BSD-style license found in the 
* LICENSE file in the root directory of this source tree. An additional grant 
* of patent rights can be found in the PATENTS file in the same directory. 
*/ 

package com.facebook.react.views.webview; 
import javax.annotation.Nullable; 
import java.io.File; 
import java.io.IOException; 
import java.io.UnsupportedEncodingException; 
import java.text.SimpleDateFormat; 
import java.util.Date; 
import java.util.HashMap; 
import java.util.Locale; 
import java.util.Map; 
import android.annotation.SuppressLint; 
import android.app.Activity; 
import android.content.ActivityNotFoundException; 
import android.content.ComponentName; 
import android.content.Intent; 
import android.content.pm.PackageManager; 
import android.graphics.Bitmap; 
import android.graphics.Picture; 
import android.net.Uri; 
import android.os.Build; 
import android.os.Environment; 
import android.provider.MediaStore; 
import android.text.TextUtils; 
import android.util.Log; 
import android.view.ViewGroup.LayoutParams; 
import android.webkit.ConsoleMessage; 
import android.webkit.GeolocationPermissions; 
import android.webkit.WebChromeClient; 
import android.webkit.WebView; 
import android.webkit.WebViewClient; 
import android.webkit.JavascriptInterface; 
import android.webkit.ValueCallback; 
import android.webkit.WebSettings; 
import com.facebook.common.logging.FLog; 
import com.facebook.react.bridge.ActivityEventListener; 
import com.facebook.react.common.ReactConstants; 
import com.facebook.react.bridge.Arguments; 
import com.facebook.react.bridge.LifecycleEventListener; 
import com.facebook.react.bridge.ReactContext; 
import com.facebook.react.bridge.ReadableArray; 
import com.facebook.react.bridge.ReadableMap; 
import com.facebook.react.bridge.ReadableMapKeySetIterator; 
import com.facebook.react.bridge.WritableMap; 
import com.facebook.react.common.MapBuilder; 
import com.facebook.react.common.build.ReactBuildConfig; 
import com.facebook.react.module.annotations.ReactModule; 
import com.facebook.react.uimanager.SimpleViewManager; 
import com.facebook.react.uimanager.ThemedReactContext; 
import com.facebook.react.uimanager.UIManagerModule; 
import com.facebook.react.uimanager.annotations.ReactProp; 
import com.facebook.react.uimanager.events.ContentSizeChangeEvent; 
import com.facebook.react.uimanager.events.Event; 
import com.facebook.react.uimanager.events.EventDispatcher; 
import com.facebook.react.views.webview.events.TopLoadingErrorEvent; 
import com.facebook.react.views.webview.events.TopLoadingFinishEvent; 
import com.facebook.react.views.webview.events.TopLoadingStartEvent; 
import com.facebook.react.views.webview.events.TopMessageEvent; 
import org.json.JSONObject; 
import org.json.JSONException; 
@ReactModule(name = ReactWebViewManager.REACT_CLASS) 
public class ReactWebViewManager extends SimpleViewManager<WebView> { 

    protected static final String REACT_CLASS = "RCTWebView"; 

    private static final String HTML_ENCODING = "UTF-8"; 
    private static final String HTML_MIME_TYPE = "text/html; charset=utf-8"; 
    private static final String BRIDGE_NAME = "__REACT_WEB_VIEW_BRIDGE"; 

    private static final String HTTP_METHOD_POST = "POST"; 

    public static final int COMMAND_GO_BACK = 1; 
    public static final int COMMAND_GO_FORWARD = 2; 
    public static final int COMMAND_RELOAD = 3; 
    public static final int COMMAND_STOP_LOADING = 4; 
    public static final int COMMAND_POST_MESSAGE = 5; 
    public static final int COMMAND_INJECT_JAVASCRIPT = 6; 

    private static final String BLANK_URL = "about:blank"; 
    public static final int INPUT_FILE_REQUEST_GALLERY_IMAGE = 1001; 
    public static final int REQUEST_SELECT_FILE_LEGACY = 1012; 

    private WebViewConfig mWebViewConfig; 
    private 
    @Nullable 
    WebView.PictureListener mPictureListener; 
    private ValueCallback<Uri[]> mFilePathCallbackArr; 
    private ValueCallback<Uri> mFilePathCallback; // Legacy (Android 4.1+) 
    private String mCameraPhotoPath; 

    protected static class ReactWebViewClient extends WebViewClient { 

     private boolean mLastLoadFailed = false; 

     @Override 
     public void onPageFinished(WebView webView, String url) { 
      super.onPageFinished(webView, url); 

      if (!mLastLoadFailed) { 
       ReactWebView reactWebView = (ReactWebView) webView; 
       reactWebView.callInjectedJavaScript(); 
       reactWebView.linkBridge(); 
       emitFinishEvent(webView, url); 
      } 
     } 
     @Override 
     public void onPageStarted(WebView webView, String url, Bitmap favicon) { 
      super.onPageStarted(webView, url, favicon); 
      mLastLoadFailed = false; 

      dispatchEvent(
        webView, 
        new TopLoadingStartEvent(
          webView.getId(), 
          createWebViewEvent(webView, url))); 
     } 
     @Override 
     public boolean shouldOverrideUrlLoading(WebView view, String url) { 
      if (url.startsWith("http://") || url.startsWith("https://") || 
        url.startsWith("file://")) { 
       return false; 
      } else { 
       try { 
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); 
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
        view.getContext().startActivity(intent); 
       } catch (ActivityNotFoundException e) { 
        FLog.w(ReactConstants.TAG, "activity not found to handle uri scheme for: " + url, e); 
       } 
       return true; 
      } 
     } 

     @Override 
     public void onReceivedError(
       WebView webView, 
       int errorCode, 
       String description, 
       String failingUrl) { 
      super.onReceivedError(webView, errorCode, description, failingUrl); 
      mLastLoadFailed = true; 

      emitFinishEvent(webView, failingUrl); 

      WritableMap eventData = createWebViewEvent(webView, failingUrl); 
      eventData.putDouble("code", errorCode); 
      eventData.putString("description", description); 

      dispatchEvent(
        webView, 
        new TopLoadingErrorEvent(webView.getId(), eventData)); 
     } 

     @Override 
     public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) { 
      super.doUpdateVisitedHistory(webView, url, isReload); 

      dispatchEvent(
        webView, 
        new TopLoadingStartEvent(
          webView.getId(), 
          createWebViewEvent(webView, url))); 
     } 

     private void emitFinishEvent(WebView webView, String url) { 
      dispatchEvent(
        webView, 
        new TopLoadingFinishEvent(
          webView.getId(), 
          createWebViewEvent(webView, url))); 
     } 

     private WritableMap createWebViewEvent(WebView webView, String url) { 
      WritableMap event = Arguments.createMap(); 
      event.putDouble("target", webView.getId()); 
      event.putString("url", url); 
      event.putBoolean("loading", !mLastLoadFailed && webView.getProgress() != 100); 
      event.putString("title", webView.getTitle()); 
      event.putBoolean("canGoBack", webView.canGoBack()); 
      event.putBoolean("canGoForward", webView.canGoForward()); 
      return event; 
     } 
    } 

    protected static class ReactWebView extends WebView implements LifecycleEventListener { 
     private 
     @Nullable 
     String injectedJS; 
     private boolean messagingEnabled = false; 

     private class ReactWebViewBridge { 
      ReactWebView mContext; 

      ReactWebViewBridge(ReactWebView c) { 
       mContext = c; 
      } 

      @JavascriptInterface 
      public void postMessage(String message) { 
       mContext.onMessage(message); 
      } 
     } 
     public ReactWebView(ThemedReactContext reactContext) { 
      super(reactContext); 
     } 

     @Override 
     public void onHostResume() { 
      // do nothing 
     } 

     @Override 
     public void onHostPause() { 
      // do nothing 
     } 

     @Override 
     public void onHostDestroy() { 
      cleanupCallbacksAndDestroy(); 
     } 

     public void setInjectedJavaScript(@Nullable String js) { 
      injectedJS = js; 
     } 

     public void setMessagingEnabled(boolean enabled) { 
      if (messagingEnabled == enabled) { 
       return; 
      } 

      messagingEnabled = enabled; 
      if (enabled) { 
       addJavascriptInterface(new ReactWebViewBridge(this), BRIDGE_NAME); 
       linkBridge(); 
      } else { 
       removeJavascriptInterface(BRIDGE_NAME); 
      } 
     } 

     public void callInjectedJavaScript() { 
      if (getSettings().getJavaScriptEnabled() && 
        injectedJS != null && 
        !TextUtils.isEmpty(injectedJS)) { 
       loadUrl("javascript:(function() {\n" + injectedJS + ";\n})();"); 
      } 
     } 

     public void linkBridge() { 
      if (messagingEnabled) { 
       if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 
        // See isNative in lodash 
        String testPostMessageNative = "String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')"; 
        evaluateJavascript(testPostMessageNative, new ValueCallback<String>() { 
         @Override 
         public void onReceiveValue(String value) { 
          if (value.equals("true")) { 
           FLog.w(ReactConstants.TAG, "Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined"); 
          } 
         } 
        }); 
       } 

       loadUrl("javascript:(" + 
         "window.originalPostMessage = window.postMessage," + 
         "window.postMessage = function(data) {" + 
         BRIDGE_NAME + ".postMessage(String(data));" + 
         "}" + 
         ")"); 
      } 
     } 

     public void onMessage(String message) { 
      dispatchEvent(this, new TopMessageEvent(this.getId(), message)); 
     } 

     private void cleanupCallbacksAndDestroy() { 
      setWebViewClient(null); 
      destroy(); 
     } 
    } 

    public ReactWebViewManager() { 
     mWebViewConfig = new WebViewConfig() { 
      public void configWebView(WebView webView) { 
      } 
     }; 
    } 

    public ReactWebViewManager(WebViewConfig webViewConfig) { 
     mWebViewConfig = webViewConfig; 
    } 

    @Override 
    public String getName() { 
     return REACT_CLASS; 
    } 

    @Override 
    protected WebView createViewInstance(final ThemedReactContext reactContext) { 
     ReactWebView webView = new ReactWebView(reactContext); 
     webView.setWebChromeClient(new WebChromeClient() { 
      @Override 
      public boolean onConsoleMessage(ConsoleMessage message) { 
       if (ReactBuildConfig.DEBUG) { 
        return super.onConsoleMessage(message); 
       } 
       return true; 
      } 

      @Override 
      public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { 
       callback.invoke(origin, true, false); 
      } 

      private File createImageFile() throws IOException { 
       String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); 
       String imageFileName = "JPEG_" + timeStamp + "_"; 
       File storageDir = Environment.getExternalStoragePublicDirectory(
         Environment.DIRECTORY_PICTURES); 
       File imageFile = new File(
         storageDir, 
         imageFileName + ".jpg" 
       ); 

       return imageFile; 
      } 


      private Intent getVideoCaptureIntent() { 
       Intent recordVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); 
       recordVideoIntent 
         .resolveActivity(
           reactContext 
            .getCurrentActivity() 
            .getPackageManager() 
         ); 

       recordVideoIntent.putExtra("type", "foobar"); 
       return recordVideoIntent; 
      } 

      public boolean onShowFileChooser(
        WebView webView, 
        ValueCallback<Uri[]> filePathCallback, 
        WebChromeClient.FileChooserParams fileChooserParams 
      ) { 
       if (mFilePathCallbackArr != null) { 
        mFilePathCallbackArr.onReceiveValue(null); 
       } 
       mFilePathCallbackArr = filePathCallback; 

       Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
       ComponentName comp = takePictureIntent 
             .resolveActivity(
               reactContext 
                 .getCurrentActivity() 
                 .getPackageManager() 
             ); 
       if (comp != null) { 
        File photoFile = null; 
        try { 
         photoFile = createImageFile(); 
         takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath); 
        } catch (IOException ex) { 
         FLog.e(ReactConstants.TAG, "Unable to create Image File", ex); 
        } 

        if (photoFile != null) { 
         mCameraPhotoPath = "file:" + photoFile.getAbsolutePath(); 
         takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, 
           Uri.fromFile(photoFile)); 
        } else { 
         takePictureIntent = null; 
        } 
       } 

       Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT); 
       contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE); 
       contentSelectionIntent.setType("*/*"); 

       Intent videoCaptureIntent = getVideoCaptureIntent(); 

       Intent[] intentArray; 
       if (takePictureIntent != null) { 
        intentArray = new Intent[]{takePictureIntent}; 
       } else { 
        intentArray = new Intent[0]; 
       } 

       intentArray = new Intent[]{ 
         intentArray[0], 
         videoCaptureIntent 
       }; 

       Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); 
       chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent); 
       chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser"); 
       chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); 

       reactContext.getCurrentActivity().startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_GALLERY_IMAGE); 

       return true; 
      } 
     }); 


     reactContext.addLifecycleEventListener(webView); 

     reactContext.addActivityEventListener(new ActivityEventListener() { 

      // Android 5+ 
      @Override 
      public void onActivityResult (Activity activity, int requestCode, int resultCode, Intent data) { 
       if(requestCode != INPUT_FILE_REQUEST_GALLERY_IMAGE || mFilePathCallbackArr == null) { 
        return; 
       } 
       Uri[] results = null; 

       if(resultCode == Activity.RESULT_OK) { 
        if(data == null || data.getData() == null) { 
         if(mCameraPhotoPath != null) { 
          results = new Uri[]{Uri.parse(mCameraPhotoPath)}; 
         } 
        } else { 
         String dataString = data.getDataString(); 
         if (dataString != null) { 
          results = new Uri[]{Uri.parse(dataString)}; 
         } 
        } 
       } 

       if(results == null) { 
        mFilePathCallbackArr.onReceiveValue(new Uri[]{}); 
       } 
       else { 
        mFilePathCallbackArr.onReceiveValue(results); 
       } 
       mFilePathCallbackArr = null; 
       return; 
      } 

      @Override 
      public void onNewIntent(Intent intent) {} 
     }); 

     mWebViewConfig.configWebView(webView); 
     webView.getSettings().setBuiltInZoomControls(true); 
     webView.getSettings().setDisplayZoomControls(false); 
     webView.getSettings().setDomStorageEnabled(true); 
     webView.setLayoutParams(
       new LayoutParams(LayoutParams.MATCH_PARENT, 
         LayoutParams.MATCH_PARENT)); 

     if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 
      WebView.setWebContentsDebuggingEnabled(true); 
     } 

     return webView; 
    } 

    @ReactProp(name = "javaScriptEnabled") 
    public void setJavaScriptEnabled(WebView view, boolean enabled) { 
     view.getSettings().setJavaScriptEnabled(enabled); 
    } 

    @ReactProp(name = "scalesPageToFit") 
    public void setScalesPageToFit(WebView view, boolean enabled) { 
     view.getSettings().setUseWideViewPort(!enabled); 
    } 

    @ReactProp(name = "domStorageEnabled") 
    public void setDomStorageEnabled(WebView view, boolean enabled) { 
     view.getSettings().setDomStorageEnabled(enabled); 
    } 

    @ReactProp(name = "userAgent") 
    public void setUserAgent(WebView view, @Nullable String userAgent) { 
     if (userAgent != null) { 
      // TODO(8496850): Fix incorrect behavior when property is unset (uA == null) 
      view.getSettings().setUserAgentString(userAgent); 
     } 
    } 

    @ReactProp(name = "mediaPlaybackRequiresUserAction") 
    public void setMediaPlaybackRequiresUserAction(WebView view, boolean requires) { 
     view.getSettings().setMediaPlaybackRequiresUserGesture(requires); 
    } 

    @ReactProp(name = "allowUniversalAccessFromFileURLs") 
    public void setAllowUniversalAccessFromFileURLs(WebView view, boolean allow) { 
     view.getSettings().setAllowUniversalAccessFromFileURLs(allow); 
    } 

    @ReactProp(name = "injectedJavaScript") 
    public void setInjectedJavaScript(WebView view, @Nullable String injectedJavaScript) { 
     ((ReactWebView) view).setInjectedJavaScript(injectedJavaScript); 
    } 

    @ReactProp(name = "messagingEnabled") 
    public void setMessagingEnabled(WebView view, boolean enabled) { 
     ((ReactWebView) view).setMessagingEnabled(enabled); 
    } 

    @ReactProp(name = "source") 
    public void setSource(WebView view, @Nullable ReadableMap source) { 
    if (source != null) { 
     if (source.hasKey("html")) { 
      String html = source.getString("html"); 
      if (source.hasKey("baseUrl")) { 
       view.loadDataWithBaseURL(
         source.getString("baseUrl"), html, HTML_MIME_TYPE, HTML_ENCODING, null); 
      } else { 
       view.loadData(html, HTML_MIME_TYPE, HTML_ENCODING); 
      } 
      return; 
     } 
     if (source.hasKey("uri")) { 
      String url = source.getString("uri"); 
      String previousUrl = view.getUrl(); 
      if (previousUrl != null && previousUrl.equals(url)) { 
       return; 
      } 
      if (source.hasKey("method")) { 
       String method = source.getString("method"); 
       if (method.equals(HTTP_METHOD_POST)) { 
        byte[] postData = null; 
        if (source.hasKey("body")) { 
         String body = source.getString("body"); 
         try { 
          postData = body.getBytes("UTF-8"); 
         } catch (UnsupportedEncodingException e) { 
          postData = body.getBytes(); 
         } 
        } 
        if (postData == null) { 
         postData = new byte[0]; 
        } 
        view.postUrl(url, postData); 
        return; 
       } 
      } 
      HashMap<String, String> headerMap = new HashMap<>(); 
      if (source.hasKey("headers")) { 
       ReadableMap headers = source.getMap("headers"); 
       ReadableMapKeySetIterator iter = headers.keySetIterator(); 
       while (iter.hasNextKey()) { 
        String key = iter.nextKey(); 
        if ("user-agent".equals(key.toLowerCase(Locale.ENGLISH))) { 
         if (view.getSettings() != null) { 
          view.getSettings().setUserAgentString(headers.getString(key)); 
         } 
        } else { 
         headerMap.put(key, headers.getString(key)); 
        } 
       } 
      } 
      view.loadUrl(url, headerMap); 
      return; 
     } 
    } 
    view.loadUrl(BLANK_URL); 
    } 

    @ReactProp(name = "onContentSizeChange") 
    public void setOnContentSizeChange(WebView view, boolean sendContentSizeChangeEvents) { 
     if (sendContentSizeChangeEvents) { 
      view.setPictureListener(getPictureListener()); 
     } else { 
      view.setPictureListener(null); 
     } 
    } 

    @ReactProp(name = "mixedContentMode") 
    public void setMixedContentMode(WebView view, @Nullable String mixedContentMode) { 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
      if (mixedContentMode == null || "never".equals(mixedContentMode)) { 
       view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW); 
      } else if ("always".equals(mixedContentMode)) { 
       view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); 
      } else if ("compatibility".equals(mixedContentMode)) { 
       view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); 
      } 
     } 
    } 

    @Override 
    protected void addEventEmitters(ThemedReactContext reactContext, WebView view) { 
     view.setWebViewClient(new ReactWebViewClient()); 
    } 
    @Override 
    public 
    @Nullable 
    Map<String, Integer> getCommandsMap() { 
     return MapBuilder.of(
       "goBack", COMMAND_GO_BACK, 
       "goForward", COMMAND_GO_FORWARD, 
       "reload", COMMAND_RELOAD, 
       "stopLoading", COMMAND_STOP_LOADING, 
       "postMessage", COMMAND_POST_MESSAGE, 
       "injectJavaScript", COMMAND_INJECT_JAVASCRIPT 
     ); 
    } 

    @Override 
    public void receiveCommand(WebView root, int commandId, @Nullable ReadableArray args) { 
     switch (commandId) { 
      case COMMAND_GO_BACK: 
       root.goBack(); 
       break; 
      case COMMAND_GO_FORWARD: 
       root.goForward(); 
       break; 
      case COMMAND_RELOAD: 
       root.reload(); 
       break; 
      case COMMAND_STOP_LOADING: 
       root.stopLoading(); 
       break; 
      case COMMAND_POST_MESSAGE: 
       try { 
        JSONObject eventInitDict = new JSONObject(); 
        eventInitDict.put("data", args.getString(0)); 
        root.loadUrl("javascript:(function() {" + 
          "var event;" + 
          "var data = " + eventInitDict.toString() + ";" + 
          "try {" + 
          "event = new MessageEvent('message', data);" + 
          "} catch (e) {" + 
          "event = document.createEvent('MessageEvent');" + 
          "event.initMessageEvent('message', true, true, data.data, data.origin, data.lastEventId, data.source);" + 
          "}" + 
          "document.dispatchEvent(event);" + 
          "})();"); 
       } catch (JSONException e) { 
        throw new RuntimeException(e); 
       } 
       break; 
      case COMMAND_INJECT_JAVASCRIPT: 
       root.loadUrl("javascript:" + args.getString(0)); 
       break; 
     } 
    } 

    @Override 
    public void onDropViewInstance(WebView webView) { 
     super.onDropViewInstance(webView); 
     ((ThemedReactContext) webView.getContext()).removeLifecycleEventListener((ReactWebView) webView); 
     ((ReactWebView) webView).cleanupCallbacksAndDestroy(); 
    } 

    private WebView.PictureListener getPictureListener() { 
     if (mPictureListener == null) { 
      mPictureListener = new WebView.PictureListener() { 
       @Override 
       public void onNewPicture(WebView webView, Picture picture) { 
        dispatchEvent(
          webView, 
          new ContentSizeChangeEvent(
            webView.getId(), 
            webView.getWidth(), 
            webView.getContentHeight())); 
       } 
      }; 
     } 
     return mPictureListener; 
    } 
    private static void dispatchEvent(WebView webView, Event event) { 
     ReactContext reactContext = (ReactContext) webView.getContext(); 
     EventDispatcher eventDispatcher = 
       reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); 
     eventDispatcher.dispatchEvent(event); 
    } 
} 
+0

Das ist eine riesige Menge an Code, um einige fast "Ja"/"Nein" -Fragen zu beantworten. Ich kann nicht einmal sagen, ob das "Wie" darin vergraben ist, denn es gibt so viel! – UKMonkey

Verwandte Themen