Skip to content

Commit f1f077f

Browse files
authored
Merge pull request #523 from rinlv/master
Listen javascript function from flutter
2 parents 3062d40 + 1f4c8fb commit f1f077f

File tree

17 files changed

+389
-80
lines changed

17 files changed

+389
-80
lines changed

android/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
22
xmlns:tools="http://schemas.android.com/tools"
33
package="com.flutter_webview_plugin">
4+
45
<application>
56
<provider
67
android:name="androidx.core.content.FileProvider"

android/src/main/java/com/flutter_webview_plugin/FlutterWebviewPlugin.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import android.webkit.ValueCallback;
1212
import android.os.Build;
1313

14+
import java.util.ArrayList;
15+
import java.util.List;
1416
import java.util.Map;
1517

1618
import io.flutter.plugin.common.MethodCall;
@@ -27,10 +29,11 @@ public class FlutterWebviewPlugin implements MethodCallHandler, PluginRegistry.A
2729
private Context context;
2830
static MethodChannel channel;
2931
private static final String CHANNEL_NAME = "flutter_webview_plugin";
32+
private static final String JS_CHANNEL_NAMES_FIELD = "javascriptChannelNames";
3033

3134
public static void registerWith(PluginRegistry.Registrar registrar) {
3235
channel = new MethodChannel(registrar.messenger(), CHANNEL_NAME);
33-
final FlutterWebviewPlugin instance = new FlutterWebviewPlugin(registrar.activity(),registrar.activeContext());
36+
final FlutterWebviewPlugin instance = new FlutterWebviewPlugin(registrar.activity(), registrar.activeContext());
3437
registrar.addActivityResultListener(instance);
3538
channel.setMethodCallHandler(instance);
3639
}
@@ -107,7 +110,12 @@ private void openUrl(MethodCall call, MethodChannel.Result result) {
107110
boolean debuggingEnabled = call.argument("debuggingEnabled");
108111

109112
if (webViewManager == null || webViewManager.closed == true) {
110-
webViewManager = new WebviewManager(activity, context);
113+
Map<String, Object> arguments = (Map<String, Object>) call.arguments;
114+
List<String> channelNames = new ArrayList();
115+
if (arguments.containsKey(JS_CHANNEL_NAMES_FIELD)) {
116+
channelNames = (List<String>) arguments.get(JS_CHANNEL_NAMES_FIELD);
117+
}
118+
webViewManager = new WebviewManager(activity, context, channelNames);
111119
}
112120

113121
FrameLayout.LayoutParams params = buildLayoutParams(call);
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2019 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package com.flutter_webview_plugin;
6+
7+
import android.os.Handler;
8+
import android.os.Looper;
9+
import android.webkit.JavascriptInterface;
10+
11+
import java.util.HashMap;
12+
13+
import io.flutter.plugin.common.MethodChannel;
14+
15+
/**
16+
* Added as a JavaScript interface to the WebView for any JavaScript channel that the Dart code sets
17+
* up.
18+
*
19+
* <p>Exposes a single method named `postMessage` to JavaScript, which sends a message over a method
20+
* channel to the Dart code.
21+
*/
22+
class JavaScriptChannel {
23+
private final MethodChannel methodChannel;
24+
private final String javaScriptChannelName;
25+
private final Handler platformThreadHandler;
26+
27+
/**
28+
* @param methodChannel the Flutter WebView method channel to which JS messages are sent
29+
* @param javaScriptChannelName the name of the JavaScript channel, this is sent over the method
30+
* channel with each message to let the Dart code know which JavaScript channel the message
31+
* was sent through
32+
*/
33+
JavaScriptChannel(
34+
MethodChannel methodChannel, String javaScriptChannelName, Handler platformThreadHandler) {
35+
this.methodChannel = methodChannel;
36+
this.javaScriptChannelName = javaScriptChannelName;
37+
this.platformThreadHandler = platformThreadHandler;
38+
}
39+
40+
// Suppressing unused warning as this is invoked from JavaScript.
41+
@SuppressWarnings("unused")
42+
@JavascriptInterface
43+
public void postMessage(final String message) {
44+
Runnable postMessageRunnable =
45+
new Runnable() {
46+
@Override
47+
public void run() {
48+
HashMap<String, String> arguments = new HashMap<>();
49+
arguments.put("channel", javaScriptChannelName);
50+
arguments.put("message", message);
51+
methodChannel.invokeMethod("javascriptChannelMessage", arguments);
52+
}
53+
};
54+
if (platformThreadHandler.getLooper() == Looper.myLooper()) {
55+
postMessageRunnable.run();
56+
} else {
57+
platformThreadHandler.post(postMessageRunnable);
58+
}
59+
}
60+
}

android/src/main/java/com/flutter_webview_plugin/WebviewManager.java

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import android.app.Activity;
77
import android.content.Context;
88
import android.os.Build;
9+
import android.os.Handler;
910
import android.view.KeyEvent;
1011
import android.view.View;
1112
import android.view.ViewGroup;
@@ -17,7 +18,9 @@
1718
import android.webkit.WebView;
1819
import android.widget.FrameLayout;
1920
import android.provider.MediaStore;
21+
2022
import androidx.core.content.FileProvider;
23+
2124
import android.database.Cursor;
2225
import android.provider.OpenableColumns;
2326

@@ -43,7 +46,7 @@ class WebviewManager {
4346

4447
private ValueCallback<Uri> mUploadMessage;
4548
private ValueCallback<Uri[]> mUploadMessageArray;
46-
private final static int FILECHOOSER_RESULTCODE=1;
49+
private final static int FILECHOOSER_RESULTCODE = 1;
4750
private Uri fileUri;
4851
private Uri videoUri;
4952

@@ -56,29 +59,29 @@ private long getFileSize(Uri fileUri) {
5659

5760
@TargetApi(7)
5861
class ResultHandler {
59-
public boolean handleResult(int requestCode, int resultCode, Intent intent){
62+
public boolean handleResult(int requestCode, int resultCode, Intent intent) {
6063
boolean handled = false;
61-
if(Build.VERSION.SDK_INT >= 21){
62-
if(requestCode == FILECHOOSER_RESULTCODE){
64+
if (Build.VERSION.SDK_INT >= 21) {
65+
if (requestCode == FILECHOOSER_RESULTCODE) {
6366
Uri[] results = null;
6467
if (resultCode == Activity.RESULT_OK) {
6568
if (fileUri != null && getFileSize(fileUri) > 0) {
66-
results = new Uri[] { fileUri };
69+
results = new Uri[]{fileUri};
6770
} else if (videoUri != null && getFileSize(videoUri) > 0) {
68-
results = new Uri[] { videoUri };
71+
results = new Uri[]{videoUri};
6972
} else if (intent != null) {
7073
results = getSelectedFiles(intent);
7174
}
7275
}
73-
if(mUploadMessageArray != null){
76+
if (mUploadMessageArray != null) {
7477
mUploadMessageArray.onReceiveValue(results);
7578
mUploadMessageArray = null;
7679
}
7780
handled = true;
7881
}
79-
}else {
82+
} else {
8083
if (requestCode == FILECHOOSER_RESULTCODE) {
81-
Uri result = null;
84+
Uri result = null;
8285
if (resultCode == RESULT_OK && intent != null) {
8386
result = intent.getData();
8487
}
@@ -97,8 +100,8 @@ private Uri[] getSelectedFiles(Intent data) {
97100
// we have one files selected
98101
if (data.getData() != null) {
99102
String dataString = data.getDataString();
100-
if(dataString != null){
101-
return new Uri[]{ Uri.parse(dataString) };
103+
if (dataString != null) {
104+
return new Uri[]{Uri.parse(dataString)};
102105
}
103106
}
104107
// we have multiple files selected
@@ -113,18 +116,20 @@ private Uri[] getSelectedFiles(Intent data) {
113116
return null;
114117
}
115118

119+
private final Handler platformThreadHandler;
116120
boolean closed = false;
117121
WebView webView;
118122
Activity activity;
119123
BrowserClient webViewClient;
120124
ResultHandler resultHandler;
121125
Context context;
122126

123-
WebviewManager(final Activity activity, final Context context) {
127+
WebviewManager(final Activity activity, final Context context, final List<String> channelNames) {
124128
this.webView = new ObservableWebView(activity);
125129
this.activity = activity;
126130
this.context = context;
127131
this.resultHandler = new ResultHandler();
132+
this.platformThreadHandler = new Handler(context.getMainLooper());
128133
webViewClient = new BrowserClient();
129134
webView.setOnKeyListener(new View.OnKeyListener() {
130135
@Override
@@ -145,20 +150,19 @@ public boolean onKey(View v, int keyCode, KeyEvent event) {
145150
}
146151
});
147152

148-
((ObservableWebView) webView).setOnScrollChangedCallback(new ObservableWebView.OnScrollChangedCallback(){
149-
public void onScroll(int x, int y, int oldx, int oldy){
153+
((ObservableWebView) webView).setOnScrollChangedCallback(new ObservableWebView.OnScrollChangedCallback() {
154+
public void onScroll(int x, int y, int oldx, int oldy) {
150155
Map<String, Object> yDirection = new HashMap<>();
151-
yDirection.put("yDirection", (double)y);
156+
yDirection.put("yDirection", (double) y);
152157
FlutterWebviewPlugin.channel.invokeMethod("onScrollYChanged", yDirection);
153158
Map<String, Object> xDirection = new HashMap<>();
154-
xDirection.put("xDirection", (double)x);
159+
xDirection.put("xDirection", (double) x);
155160
FlutterWebviewPlugin.channel.invokeMethod("onScrollXChanged", xDirection);
156161
}
157162
});
158163

159164
webView.setWebViewClient(webViewClient);
160-
webView.setWebChromeClient(new WebChromeClient()
161-
{
165+
webView.setWebChromeClient(new WebChromeClient() {
162166
//The undocumented magic method override
163167
//Eclipse will swear at you if you try to put @Override here
164168
// For Android 3.0+
@@ -168,36 +172,36 @@ public void openFileChooser(ValueCallback<Uri> uploadMsg) {
168172
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
169173
i.addCategory(Intent.CATEGORY_OPENABLE);
170174
i.setType("image/*");
171-
activity.startActivityForResult(Intent.createChooser(i,"File Chooser"), FILECHOOSER_RESULTCODE);
175+
activity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
172176

173177
}
174178

175179
// For Android 3.0+
176-
public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {
180+
public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
177181
mUploadMessage = uploadMsg;
178182
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
179183
i.addCategory(Intent.CATEGORY_OPENABLE);
180184
i.setType("*/*");
181-
activity.startActivityForResult(
185+
activity.startActivityForResult(
182186
Intent.createChooser(i, "File Browser"),
183187
FILECHOOSER_RESULTCODE);
184188
}
185189

186190
//For Android 4.1
187-
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
191+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
188192
mUploadMessage = uploadMsg;
189193
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
190194
i.addCategory(Intent.CATEGORY_OPENABLE);
191195
i.setType("image/*");
192-
activity.startActivityForResult( Intent.createChooser( i, "File Chooser" ), FILECHOOSER_RESULTCODE );
196+
activity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
193197

194198
}
195199

196200
//For Android 5.0+
197201
public boolean onShowFileChooser(
198202
WebView webView, ValueCallback<Uri[]> filePathCallback,
199-
FileChooserParams fileChooserParams){
200-
if(mUploadMessageArray != null){
203+
FileChooserParams fileChooserParams) {
204+
if (mUploadMessageArray != null) {
201205
mUploadMessageArray.onReceiveValue(null);
202206
}
203207
mUploadMessageArray = filePathCallback;
@@ -248,6 +252,7 @@ public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermiss
248252
callback.invoke(origin, true, false);
249253
}
250254
});
255+
registerJavaScriptChannelNames(channelNames);
251256
}
252257

253258
private Uri getOutputFilename(String intentType) {
@@ -334,6 +339,13 @@ private void clearCache() {
334339
webView.clearFormData();
335340
}
336341

342+
private void registerJavaScriptChannelNames(List<String> channelNames) {
343+
for (String channelName : channelNames) {
344+
webView.addJavascriptInterface(
345+
new JavaScriptChannel(FlutterWebviewPlugin.channel, channelName, platformThreadHandler), channelName);
346+
}
347+
}
348+
337349
void openUrl(
338350
boolean withJavascript,
339351
boolean clearCache,
@@ -371,7 +383,7 @@ void openUrl(
371383
webView.getSettings().setAllowUniversalAccessFromFileURLs(allowFileURLs);
372384

373385
webView.getSettings().setUseWideViewPort(useWideViewPort);
374-
386+
375387
// Handle debugging
376388
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
377389
webView.setWebContentsDebuggingEnabled(debuggingEnabled);
@@ -403,7 +415,7 @@ void openUrl(
403415
webView.getSettings().setUserAgentString(userAgent);
404416
}
405417

406-
if(!scrollBar){
418+
if (!scrollBar) {
407419
webView.setVerticalScrollBarEnabled(false);
408420
}
409421

@@ -451,25 +463,28 @@ public void onReceiveValue(String value) {
451463
}
452464
});
453465
}
466+
454467
/**
455-
* Reloads the Webview.
456-
*/
468+
* Reloads the Webview.
469+
*/
457470
void reload(MethodCall call, MethodChannel.Result result) {
458471
if (webView != null) {
459472
webView.reload();
460473
}
461474
}
475+
462476
/**
463-
* Navigates back on the Webview.
464-
*/
477+
* Navigates back on the Webview.
478+
*/
465479
void back(MethodCall call, MethodChannel.Result result) {
466480
if (webView != null && webView.canGoBack()) {
467481
webView.goBack();
468482
}
469483
}
484+
470485
/**
471-
* Navigates forward on the Webview.
472-
*/
486+
* Navigates forward on the Webview.
487+
*/
473488
void forward(MethodCall call, MethodChannel.Result result) {
474489
if (webView != null && webView.canGoForward()) {
475490
webView.goForward();
@@ -479,31 +494,35 @@ void forward(MethodCall call, MethodChannel.Result result) {
479494
void resize(FrameLayout.LayoutParams params) {
480495
webView.setLayoutParams(params);
481496
}
497+
482498
/**
483-
* Checks if going back on the Webview is possible.
484-
*/
499+
* Checks if going back on the Webview is possible.
500+
*/
485501
boolean canGoBack() {
486502
return webView.canGoBack();
487503
}
504+
488505
/**
489-
* Checks if going forward on the Webview is possible.
490-
*/
506+
* Checks if going forward on the Webview is possible.
507+
*/
491508
boolean canGoForward() {
492509
return webView.canGoForward();
493510
}
511+
494512
void hide(MethodCall call, MethodChannel.Result result) {
495513
if (webView != null) {
496514
webView.setVisibility(View.GONE);
497515
}
498516
}
517+
499518
void show(MethodCall call, MethodChannel.Result result) {
500519
if (webView != null) {
501520
webView.setVisibility(View.VISIBLE);
502521
}
503522
}
504523

505-
void stopLoading(MethodCall call, MethodChannel.Result result){
506-
if (webView != null){
524+
void stopLoading(MethodCall call, MethodChannel.Result result) {
525+
if (webView != null) {
507526
webView.stopLoading();
508527
}
509528
}

example/android/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ task clean(type: Delete) {
3232
delete rootProject.buildDir
3333
}
3434

35-
task wrapper(type: Wrapper) {
36-
gradleVersion = '4.10.2'
35+
wrapper {
36+
gradleVersion = '4.4'
37+
distributionUrl = distributionUrl.replace("bin", "all")
3738
}

0 commit comments

Comments
 (0)