/
MessageHandler.java
159 lines (139 loc) · 6.2 KB
/
MessageHandler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package com.getcapacitor;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import androidx.webkit.JavaScriptReplyProxy;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
import org.apache.cordova.PluginManager;
/**
* MessageHandler handles messages from the WebView, dispatching them
* to plugins.
*/
public class MessageHandler {
private Bridge bridge;
private WebView webView;
private PluginManager cordovaPluginManager;
private JavaScriptReplyProxy javaScriptReplyProxy;
public MessageHandler(Bridge bridge, WebView webView, PluginManager cordovaPluginManager) {
this.bridge = bridge;
this.webView = webView;
this.cordovaPluginManager = cordovaPluginManager;
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER) && !bridge.getConfig().isUsingLegacyBridge()) {
WebViewCompat.WebMessageListener capListener = (view, message, sourceOrigin, isMainFrame, replyProxy) -> {
if (isMainFrame) {
postMessage(message.getData());
javaScriptReplyProxy = replyProxy;
} else {
Logger.warn("Plugin execution is allowed in Main Frame only");
}
};
try {
WebViewCompat.addWebMessageListener(webView, "androidBridge", bridge.getAllowedOriginRules(), capListener);
} catch (Exception ex) {
webView.addJavascriptInterface(this, "androidBridge");
}
} else {
webView.addJavascriptInterface(this, "androidBridge");
}
}
/**
* The main message handler that will be called from JavaScript
* to send a message to the native bridge.
* @param jsonStr
*/
@JavascriptInterface
@SuppressWarnings("unused")
public void postMessage(String jsonStr) {
try {
JSObject postData = new JSObject(jsonStr);
String type = postData.getString("type");
boolean typeIsNotNull = type != null;
boolean isCordovaPlugin = typeIsNotNull && type.equals("cordova");
boolean isJavaScriptError = typeIsNotNull && type.equals("js.error");
String callbackId = postData.getString("callbackId");
if (isCordovaPlugin) {
String service = postData.getString("service");
String action = postData.getString("action");
String actionArgs = postData.getString("actionArgs");
Logger.verbose(
Logger.tags("Plugin"),
"To native (Cordova plugin): callbackId: " +
callbackId +
", service: " +
service +
", action: " +
action +
", actionArgs: " +
actionArgs
);
this.callCordovaPluginMethod(callbackId, service, action, actionArgs);
} else if (isJavaScriptError) {
Logger.error("JavaScript Error: " + jsonStr);
} else {
String pluginId = postData.getString("pluginId");
String methodName = postData.getString("methodName");
JSObject methodData = postData.getJSObject("options", new JSObject());
Logger.verbose(
Logger.tags("Plugin"),
"To native (Capacitor plugin): callbackId: " + callbackId + ", pluginId: " + pluginId + ", methodName: " + methodName
);
this.callPluginMethod(callbackId, pluginId, methodName, methodData);
}
} catch (Exception ex) {
Logger.error("Post message error:", ex);
}
}
public void sendResponseMessage(PluginCall call, PluginResult successResult, PluginResult errorResult) {
try {
PluginResult data = new PluginResult();
data.put("save", call.isKeptAlive());
data.put("callbackId", call.getCallbackId());
data.put("pluginId", call.getPluginId());
data.put("methodName", call.getMethodName());
boolean pluginResultInError = errorResult != null;
if (pluginResultInError) {
data.put("success", false);
data.put("error", errorResult);
Logger.debug("Sending plugin error: " + data.toString());
} else {
data.put("success", true);
if (successResult != null) {
data.put("data", successResult);
}
}
boolean isValidCallbackId = !call.getCallbackId().equals(PluginCall.CALLBACK_ID_DANGLING);
if (isValidCallbackId) {
if (bridge.getConfig().isUsingLegacyBridge()) {
legacySendResponseMessage(data);
} else if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER) && javaScriptReplyProxy != null) {
javaScriptReplyProxy.postMessage(data.toString());
} else {
legacySendResponseMessage(data);
}
} else {
bridge.getApp().fireRestoredResult(data);
}
} catch (Exception ex) {
Logger.error("sendResponseMessage: error: " + ex);
}
if (!call.isKeptAlive()) {
call.release(bridge);
}
}
private void legacySendResponseMessage(PluginResult data) {
final String runScript = "window.Capacitor.fromNative(" + data.toString() + ")";
final WebView webView = this.webView;
webView.post(() -> webView.evaluateJavascript(runScript, null));
}
private void callPluginMethod(String callbackId, String pluginId, String methodName, JSObject methodData) {
PluginCall call = new PluginCall(this, pluginId, callbackId, methodName, methodData);
bridge.callPluginMethod(pluginId, methodName, call);
}
private void callCordovaPluginMethod(String callbackId, String service, String action, String actionArgs) {
bridge.execute(
() -> {
cordovaPluginManager.exec(service, action, callbackId, actionArgs);
}
);
}
}