|
@@ -6,12 +6,17 @@ import android.app.DownloadManager;
|
6
|
6
|
import android.content.Context;
|
7
|
7
|
import android.content.Intent;
|
8
|
8
|
import android.graphics.Bitmap;
|
|
9
|
+import android.graphics.Color;
|
9
|
10
|
import android.net.Uri;
|
10
|
11
|
import android.os.Build;
|
11
|
12
|
import android.os.Environment;
|
|
13
|
+import android.support.annotation.RequiresApi;
|
12
|
14
|
import android.text.TextUtils;
|
|
15
|
+import android.view.Gravity;
|
13
|
16
|
import android.view.View;
|
|
17
|
+import android.view.ViewGroup;
|
14
|
18
|
import android.view.ViewGroup.LayoutParams;
|
|
19
|
+import android.view.WindowManager;
|
15
|
20
|
import android.webkit.ConsoleMessage;
|
16
|
21
|
import android.webkit.CookieManager;
|
17
|
22
|
import android.webkit.DownloadListener;
|
|
@@ -24,6 +29,7 @@ import android.webkit.WebResourceRequest;
|
24
|
29
|
import android.webkit.WebSettings;
|
25
|
30
|
import android.webkit.WebView;
|
26
|
31
|
import android.webkit.WebViewClient;
|
|
32
|
+import android.widget.FrameLayout;
|
27
|
33
|
|
28
|
34
|
import com.facebook.react.bridge.Arguments;
|
29
|
35
|
import com.facebook.react.bridge.LifecycleEventListener;
|
|
@@ -106,6 +112,9 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
106
|
112
|
protected static final String BLANK_URL = "about:blank";
|
107
|
113
|
protected WebViewConfig mWebViewConfig;
|
108
|
114
|
|
|
115
|
+ protected RNCWebChromeClient mWebChromeClient = null;
|
|
116
|
+ protected boolean mAllowsFullscreenVideo = false;
|
|
117
|
+
|
109
|
118
|
public RNCWebViewManager() {
|
110
|
119
|
mWebViewConfig = new WebViewConfig() {
|
111
|
120
|
public void configWebView(WebView webView) {
|
|
@@ -137,59 +146,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
137
|
146
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
138
|
147
|
protected WebView createViewInstance(ThemedReactContext reactContext) {
|
139
|
148
|
RNCWebView webView = createRNCWebViewInstance(reactContext);
|
140
|
|
- webView.setWebChromeClient(new WebChromeClient() {
|
141
|
|
- @Override
|
142
|
|
- public boolean onConsoleMessage(ConsoleMessage message) {
|
143
|
|
- if (ReactBuildConfig.DEBUG) {
|
144
|
|
- return super.onConsoleMessage(message);
|
145
|
|
- }
|
146
|
|
- // Ignore console logs in non debug builds.
|
147
|
|
- return true;
|
148
|
|
- }
|
149
|
|
-
|
150
|
|
-
|
151
|
|
- @Override
|
152
|
|
- public void onProgressChanged(WebView webView, int newProgress) {
|
153
|
|
- super.onProgressChanged(webView, newProgress);
|
154
|
|
- WritableMap event = Arguments.createMap();
|
155
|
|
- event.putDouble("target", webView.getId());
|
156
|
|
- event.putString("title", webView.getTitle());
|
157
|
|
- event.putBoolean("canGoBack", webView.canGoBack());
|
158
|
|
- event.putBoolean("canGoForward", webView.canGoForward());
|
159
|
|
- event.putDouble("progress", (float) newProgress / 100);
|
160
|
|
- dispatchEvent(
|
161
|
|
- webView,
|
162
|
|
- new TopLoadingProgressEvent(
|
163
|
|
- webView.getId(),
|
164
|
|
- event));
|
165
|
|
- }
|
166
|
|
-
|
167
|
|
- @Override
|
168
|
|
- public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
|
169
|
|
- callback.invoke(origin, true, false);
|
170
|
|
- }
|
171
|
|
-
|
172
|
|
- protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType) {
|
173
|
|
- getModule(reactContext).startPhotoPickerIntent(filePathCallback, acceptType);
|
174
|
|
- }
|
175
|
|
-
|
176
|
|
- protected void openFileChooser(ValueCallback<Uri> filePathCallback) {
|
177
|
|
- getModule(reactContext).startPhotoPickerIntent(filePathCallback, "");
|
178
|
|
- }
|
179
|
|
-
|
180
|
|
- protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) {
|
181
|
|
- getModule(reactContext).startPhotoPickerIntent(filePathCallback, acceptType);
|
182
|
|
- }
|
183
|
|
-
|
184
|
|
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
185
|
|
- @Override
|
186
|
|
- public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
|
187
|
|
- String[] acceptTypes = fileChooserParams.getAcceptTypes();
|
188
|
|
- boolean allowMultiple = fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE;
|
189
|
|
- Intent intent = fileChooserParams.createIntent();
|
190
|
|
- return getModule(reactContext).startPhotoPickerIntent(filePathCallback, intent, acceptTypes, allowMultiple);
|
191
|
|
- }
|
192
|
|
- });
|
|
149
|
+ setupWebChromeClient(reactContext, webView);
|
193
|
150
|
reactContext.addLifecycleEventListener(webView);
|
194
|
151
|
mWebViewConfig.configWebView(webView);
|
195
|
152
|
WebSettings settings = webView.getSettings();
|
|
@@ -454,6 +411,14 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
454
|
411
|
}
|
455
|
412
|
}
|
456
|
413
|
|
|
414
|
+ @ReactProp(name = "allowsFullscreenVideo")
|
|
415
|
+ public void setAllowsFullscreenVideo(
|
|
416
|
+ WebView view,
|
|
417
|
+ @Nullable Boolean allowsFullscreenVideo) {
|
|
418
|
+ mAllowsFullscreenVideo = allowsFullscreenVideo != null && allowsFullscreenVideo;
|
|
419
|
+ setupWebChromeClient((ReactContext)view.getContext(), view);
|
|
420
|
+ }
|
|
421
|
+
|
457
|
422
|
@ReactProp(name = "allowFileAccess")
|
458
|
423
|
public void setAllowFileAccess(
|
459
|
424
|
WebView view,
|
|
@@ -554,10 +519,67 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
554
|
519
|
((RNCWebView) webView).cleanupCallbacksAndDestroy();
|
555
|
520
|
}
|
556
|
521
|
|
557
|
|
- public RNCWebViewModule getModule(ReactContext reactContext) {
|
|
522
|
+ public static RNCWebViewModule getModule(ReactContext reactContext) {
|
558
|
523
|
return reactContext.getNativeModule(RNCWebViewModule.class);
|
559
|
524
|
}
|
560
|
525
|
|
|
526
|
+ protected void setupWebChromeClient(ReactContext reactContext, WebView webView) {
|
|
527
|
+ if (mAllowsFullscreenVideo) {
|
|
528
|
+ mWebChromeClient = new RNCWebChromeClient(reactContext, webView) {
|
|
529
|
+ @Override
|
|
530
|
+ public void onShowCustomView(View view, CustomViewCallback callback) {
|
|
531
|
+ if (mVideoView != null) {
|
|
532
|
+ callback.onCustomViewHidden();
|
|
533
|
+ return;
|
|
534
|
+ }
|
|
535
|
+
|
|
536
|
+ mVideoView = view;
|
|
537
|
+ mCustomViewCallback = callback;
|
|
538
|
+
|
|
539
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
540
|
+ mVideoView.setSystemUiVisibility(FULLSCREEN_SYSTEM_UI_VISIBILITY);
|
|
541
|
+ mReactContext.getCurrentActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
|
542
|
+ }
|
|
543
|
+
|
|
544
|
+ mVideoView.setBackgroundColor(Color.BLACK);
|
|
545
|
+ getRootView().addView(mVideoView, FULLSCREEN_LAYOUT_PARAMS);
|
|
546
|
+ mWebView.setVisibility(View.GONE);
|
|
547
|
+
|
|
548
|
+ mReactContext.addLifecycleEventListener(this);
|
|
549
|
+ }
|
|
550
|
+
|
|
551
|
+ @Override
|
|
552
|
+ public void onHideCustomView() {
|
|
553
|
+ if (mVideoView == null) {
|
|
554
|
+ return;
|
|
555
|
+ }
|
|
556
|
+
|
|
557
|
+ mVideoView.setVisibility(View.GONE);
|
|
558
|
+ getRootView().removeView(mVideoView);
|
|
559
|
+ mCustomViewCallback.onCustomViewHidden();
|
|
560
|
+
|
|
561
|
+ mVideoView = null;
|
|
562
|
+ mCustomViewCallback = null;
|
|
563
|
+
|
|
564
|
+ mWebView.setVisibility(View.VISIBLE);
|
|
565
|
+
|
|
566
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
567
|
+ mReactContext.getCurrentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
|
568
|
+ }
|
|
569
|
+
|
|
570
|
+ mReactContext.removeLifecycleEventListener(this);
|
|
571
|
+ }
|
|
572
|
+ };
|
|
573
|
+ webView.setWebChromeClient(mWebChromeClient);
|
|
574
|
+ } else {
|
|
575
|
+ if (mWebChromeClient != null) {
|
|
576
|
+ mWebChromeClient.onHideCustomView();
|
|
577
|
+ }
|
|
578
|
+ mWebChromeClient = new RNCWebChromeClient(reactContext, webView);
|
|
579
|
+ webView.setWebChromeClient(mWebChromeClient);
|
|
580
|
+ }
|
|
581
|
+ }
|
|
582
|
+
|
561
|
583
|
protected static class RNCWebViewClient extends WebViewClient {
|
562
|
584
|
|
563
|
585
|
protected boolean mLastLoadFailed = false;
|
|
@@ -655,6 +677,99 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
655
|
677
|
}
|
656
|
678
|
}
|
657
|
679
|
|
|
680
|
+ protected static class RNCWebChromeClient extends WebChromeClient implements LifecycleEventListener {
|
|
681
|
+ protected static final FrameLayout.LayoutParams FULLSCREEN_LAYOUT_PARAMS = new FrameLayout.LayoutParams(
|
|
682
|
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER);
|
|
683
|
+
|
|
684
|
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
|
685
|
+ protected static final int FULLSCREEN_SYSTEM_UI_VISIBILITY = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
|
686
|
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
|
687
|
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
|
688
|
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
|
689
|
+ View.SYSTEM_UI_FLAG_FULLSCREEN |
|
|
690
|
+ View.SYSTEM_UI_FLAG_IMMERSIVE |
|
|
691
|
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
|
692
|
+
|
|
693
|
+ protected ReactContext mReactContext;
|
|
694
|
+ protected View mWebView;
|
|
695
|
+
|
|
696
|
+ protected View mVideoView;
|
|
697
|
+ protected WebChromeClient.CustomViewCallback mCustomViewCallback;
|
|
698
|
+
|
|
699
|
+ public RNCWebChromeClient(ReactContext reactContext, WebView webView) {
|
|
700
|
+ this.mReactContext = reactContext;
|
|
701
|
+ this.mWebView = webView;
|
|
702
|
+ }
|
|
703
|
+
|
|
704
|
+ @Override
|
|
705
|
+ public boolean onConsoleMessage(ConsoleMessage message) {
|
|
706
|
+ if (ReactBuildConfig.DEBUG) {
|
|
707
|
+ return super.onConsoleMessage(message);
|
|
708
|
+ }
|
|
709
|
+ // Ignore console logs in non debug builds.
|
|
710
|
+ return true;
|
|
711
|
+ }
|
|
712
|
+
|
|
713
|
+ @Override
|
|
714
|
+ public void onProgressChanged(WebView webView, int newProgress) {
|
|
715
|
+ super.onProgressChanged(webView, newProgress);
|
|
716
|
+ WritableMap event = Arguments.createMap();
|
|
717
|
+ event.putDouble("target", webView.getId());
|
|
718
|
+ event.putString("title", webView.getTitle());
|
|
719
|
+ event.putBoolean("canGoBack", webView.canGoBack());
|
|
720
|
+ event.putBoolean("canGoForward", webView.canGoForward());
|
|
721
|
+ event.putDouble("progress", (float) newProgress / 100);
|
|
722
|
+ dispatchEvent(
|
|
723
|
+ webView,
|
|
724
|
+ new TopLoadingProgressEvent(
|
|
725
|
+ webView.getId(),
|
|
726
|
+ event));
|
|
727
|
+ }
|
|
728
|
+
|
|
729
|
+ @Override
|
|
730
|
+ public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
|
|
731
|
+ callback.invoke(origin, true, false);
|
|
732
|
+ }
|
|
733
|
+
|
|
734
|
+ protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType) {
|
|
735
|
+ getModule(mReactContext).startPhotoPickerIntent(filePathCallback, acceptType);
|
|
736
|
+ }
|
|
737
|
+
|
|
738
|
+ protected void openFileChooser(ValueCallback<Uri> filePathCallback) {
|
|
739
|
+ getModule(mReactContext).startPhotoPickerIntent(filePathCallback, "");
|
|
740
|
+ }
|
|
741
|
+
|
|
742
|
+ protected void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) {
|
|
743
|
+ getModule(mReactContext).startPhotoPickerIntent(filePathCallback, acceptType);
|
|
744
|
+ }
|
|
745
|
+
|
|
746
|
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
747
|
+ @Override
|
|
748
|
+ public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
|
|
749
|
+ String[] acceptTypes = fileChooserParams.getAcceptTypes();
|
|
750
|
+ boolean allowMultiple = fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE;
|
|
751
|
+ Intent intent = fileChooserParams.createIntent();
|
|
752
|
+ return getModule(mReactContext).startPhotoPickerIntent(filePathCallback, intent, acceptTypes, allowMultiple);
|
|
753
|
+ }
|
|
754
|
+
|
|
755
|
+ @Override
|
|
756
|
+ public void onHostResume() {
|
|
757
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && mVideoView != null && mVideoView.getSystemUiVisibility() != FULLSCREEN_SYSTEM_UI_VISIBILITY) {
|
|
758
|
+ mVideoView.setSystemUiVisibility(FULLSCREEN_SYSTEM_UI_VISIBILITY);
|
|
759
|
+ }
|
|
760
|
+ }
|
|
761
|
+
|
|
762
|
+ @Override
|
|
763
|
+ public void onHostPause() { }
|
|
764
|
+
|
|
765
|
+ @Override
|
|
766
|
+ public void onHostDestroy() { }
|
|
767
|
+
|
|
768
|
+ protected ViewGroup getRootView() {
|
|
769
|
+ return (ViewGroup) mReactContext.getCurrentActivity().findViewById(android.R.id.content);
|
|
770
|
+ }
|
|
771
|
+ }
|
|
772
|
+
|
658
|
773
|
/**
|
659
|
774
|
* Subclass of {@link WebView} that implements {@link LifecycleEventListener} interface in order
|
660
|
775
|
* to call {@link WebView#destroy} on activity destroy event and also to clear the client
|