@capgo/inappbrowser 7.0.0 → 7.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapgoInappbrowser.podspec +1 -1
- package/README.md +448 -54
- package/android/build.gradle +14 -13
- package/android/src/main/AndroidManifest.xml +3 -1
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java +534 -35
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/Options.java +251 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewCallbacks.java +4 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +891 -19
- package/android/src/main/res/drawable/ic_refresh.xml +9 -0
- package/android/src/main/res/layout/tool_bar.xml +25 -3
- package/android/src/main/res/values/strings.xml +2 -0
- package/dist/docs.json +1365 -67
- package/dist/esm/definitions.d.ts +218 -9
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +10 -2
- package/dist/esm/web.js +26 -2
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +26 -2
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +26 -2
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/InAppBrowserPlugin.m +5 -1
- package/ios/Plugin/InAppBrowserPlugin.swift +243 -12
- package/ios/Plugin/WKWebViewController.swift +299 -55
- package/package.json +26 -27
|
@@ -1,46 +1,167 @@
|
|
|
1
1
|
package ee.forgr.capacitor_inappbrowser;
|
|
2
2
|
|
|
3
|
+
import android.annotation.SuppressLint;
|
|
4
|
+
import android.app.Activity;
|
|
5
|
+
import android.app.AlertDialog;
|
|
3
6
|
import android.app.Dialog;
|
|
7
|
+
import android.content.ActivityNotFoundException;
|
|
4
8
|
import android.content.Context;
|
|
9
|
+
import android.content.DialogInterface;
|
|
10
|
+
import android.content.Intent;
|
|
11
|
+
import android.content.res.AssetManager;
|
|
5
12
|
import android.graphics.Bitmap;
|
|
13
|
+
import android.graphics.Color;
|
|
14
|
+
import android.graphics.Picture;
|
|
15
|
+
import android.graphics.drawable.PictureDrawable;
|
|
16
|
+
import android.net.Uri;
|
|
17
|
+
import android.net.http.SslError;
|
|
18
|
+
import android.os.Build;
|
|
6
19
|
import android.text.TextUtils;
|
|
20
|
+
import android.util.Base64;
|
|
21
|
+
import android.util.Log;
|
|
7
22
|
import android.view.View;
|
|
8
23
|
import android.view.Window;
|
|
9
24
|
import android.view.WindowManager;
|
|
25
|
+
import android.webkit.HttpAuthHandler;
|
|
26
|
+
import android.webkit.JavascriptInterface;
|
|
27
|
+
import android.webkit.PermissionRequest;
|
|
28
|
+
import android.webkit.SslErrorHandler;
|
|
29
|
+
import android.webkit.ValueCallback;
|
|
30
|
+
import android.webkit.WebChromeClient;
|
|
10
31
|
import android.webkit.WebResourceError;
|
|
11
32
|
import android.webkit.WebResourceRequest;
|
|
33
|
+
import android.webkit.WebResourceResponse;
|
|
12
34
|
import android.webkit.WebView;
|
|
13
35
|
import android.webkit.WebViewClient;
|
|
14
36
|
import android.widget.ImageButton;
|
|
15
37
|
import android.widget.TextView;
|
|
38
|
+
import android.widget.Toast;
|
|
16
39
|
import android.widget.Toolbar;
|
|
40
|
+
import androidx.annotation.RequiresApi;
|
|
41
|
+
import com.caverock.androidsvg.SVG;
|
|
42
|
+
import com.caverock.androidsvg.SVGParseException;
|
|
43
|
+
import com.getcapacitor.JSObject;
|
|
44
|
+
import java.io.ByteArrayInputStream;
|
|
45
|
+
import java.io.IOException;
|
|
46
|
+
import java.io.InputStream;
|
|
47
|
+
import java.net.CookiePolicy;
|
|
17
48
|
import java.net.URI;
|
|
18
49
|
import java.net.URISyntaxException;
|
|
50
|
+
import java.net.URL;
|
|
51
|
+
import java.nio.charset.StandardCharsets;
|
|
52
|
+
import java.util.Arrays;
|
|
19
53
|
import java.util.HashMap;
|
|
20
54
|
import java.util.Iterator;
|
|
55
|
+
import java.util.List;
|
|
21
56
|
import java.util.Map;
|
|
57
|
+
import java.util.Objects;
|
|
58
|
+
import java.util.UUID;
|
|
59
|
+
import java.util.concurrent.ExecutorService;
|
|
60
|
+
import java.util.concurrent.Executors;
|
|
61
|
+
import java.util.concurrent.Semaphore;
|
|
62
|
+
import java.util.concurrent.TimeUnit;
|
|
63
|
+
import java.util.function.Consumer;
|
|
64
|
+
import java.util.regex.Matcher;
|
|
65
|
+
import java.util.regex.Pattern;
|
|
66
|
+
import org.json.JSONException;
|
|
67
|
+
import org.json.JSONObject;
|
|
22
68
|
|
|
23
69
|
public class WebViewDialog extends Dialog {
|
|
24
70
|
|
|
71
|
+
private class ProxiedRequest {
|
|
72
|
+
|
|
73
|
+
private WebResourceResponse response;
|
|
74
|
+
private Semaphore semaphore;
|
|
75
|
+
|
|
76
|
+
public WebResourceResponse getResponse() {
|
|
77
|
+
return response;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public ProxiedRequest() {
|
|
81
|
+
this.semaphore = new Semaphore(0);
|
|
82
|
+
this.response = null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
25
86
|
private WebView _webView;
|
|
26
87
|
private Toolbar _toolbar;
|
|
27
88
|
private Options _options;
|
|
89
|
+
private Context _context;
|
|
90
|
+
public Activity activity;
|
|
28
91
|
private boolean isInitialized = false;
|
|
92
|
+
private WebView capacitorWebView;
|
|
93
|
+
private HashMap<String, ProxiedRequest> proxiedRequestsHashmap;
|
|
94
|
+
|
|
95
|
+
Semaphore preShowSemaphore = null;
|
|
96
|
+
String preshowError = null;
|
|
97
|
+
|
|
98
|
+
public PermissionRequest currentPermissionRequest;
|
|
99
|
+
public static final int FILE_CHOOSER_REQUEST_CODE = 1000;
|
|
100
|
+
public ValueCallback<Uri> mUploadMessage;
|
|
101
|
+
public ValueCallback<Uri[]> mFilePathCallback;
|
|
102
|
+
ExecutorService executorService = Executors.newFixedThreadPool(1);
|
|
103
|
+
|
|
104
|
+
public interface PermissionHandler {
|
|
105
|
+
void handleCameraPermissionRequest(PermissionRequest request);
|
|
106
|
+
|
|
107
|
+
void handleMicrophonePermissionRequest(PermissionRequest request);
|
|
108
|
+
}
|
|
29
109
|
|
|
30
|
-
|
|
110
|
+
private PermissionHandler permissionHandler;
|
|
111
|
+
|
|
112
|
+
public WebViewDialog(
|
|
113
|
+
Context context,
|
|
114
|
+
int theme,
|
|
115
|
+
Options options,
|
|
116
|
+
PermissionHandler permissionHandler,
|
|
117
|
+
WebView capacitorWebView
|
|
118
|
+
) {
|
|
31
119
|
super(context, theme);
|
|
32
120
|
this._options = options;
|
|
121
|
+
this._context = context;
|
|
122
|
+
this.permissionHandler = permissionHandler;
|
|
33
123
|
this.isInitialized = false;
|
|
124
|
+
this.capacitorWebView = capacitorWebView;
|
|
125
|
+
this.proxiedRequestsHashmap = new HashMap<>();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public class JavaScriptInterface {
|
|
129
|
+
|
|
130
|
+
@JavascriptInterface
|
|
131
|
+
public void postMessage(String message) {
|
|
132
|
+
// Handle message from JavaScript
|
|
133
|
+
_options.getCallbacks().javascriptCallback(message);
|
|
134
|
+
}
|
|
34
135
|
}
|
|
35
136
|
|
|
137
|
+
public class PreShowScriptInterface {
|
|
138
|
+
|
|
139
|
+
@JavascriptInterface
|
|
140
|
+
public void error(String error) {
|
|
141
|
+
// Handle message from JavaScript
|
|
142
|
+
if (preShowSemaphore != null) {
|
|
143
|
+
preshowError = error;
|
|
144
|
+
preShowSemaphore.release();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@JavascriptInterface
|
|
149
|
+
public void success() {
|
|
150
|
+
// Handle message from JavaScript
|
|
151
|
+
if (preShowSemaphore != null) {
|
|
152
|
+
preShowSemaphore.release();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@SuppressLint("SetJavaScriptEnabled")
|
|
36
158
|
public void presentWebView() {
|
|
37
159
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
|
38
160
|
setCancelable(true);
|
|
39
|
-
getWindow()
|
|
40
|
-
.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
);
|
|
161
|
+
Objects.requireNonNull(getWindow()).setFlags(
|
|
162
|
+
WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
|
163
|
+
WindowManager.LayoutParams.FLAG_FULLSCREEN
|
|
164
|
+
);
|
|
44
165
|
setContentView(R.layout.activity_browser);
|
|
45
166
|
getWindow()
|
|
46
167
|
.setLayout(
|
|
@@ -49,23 +170,104 @@ public class WebViewDialog extends Dialog {
|
|
|
49
170
|
);
|
|
50
171
|
|
|
51
172
|
this._webView = findViewById(R.id.browser_view);
|
|
52
|
-
|
|
173
|
+
_webView.addJavascriptInterface(
|
|
174
|
+
new JavaScriptInterface(),
|
|
175
|
+
"AndroidInterface"
|
|
176
|
+
);
|
|
177
|
+
_webView.addJavascriptInterface(
|
|
178
|
+
new PreShowScriptInterface(),
|
|
179
|
+
"PreShowScriptInterface"
|
|
180
|
+
);
|
|
53
181
|
_webView.getSettings().setJavaScriptEnabled(true);
|
|
54
182
|
_webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
|
|
55
183
|
_webView.getSettings().setDatabaseEnabled(true);
|
|
56
184
|
_webView.getSettings().setDomStorageEnabled(true);
|
|
185
|
+
_webView.getSettings().setAllowFileAccess(true);
|
|
57
186
|
_webView
|
|
58
187
|
.getSettings()
|
|
59
188
|
.setPluginState(android.webkit.WebSettings.PluginState.ON);
|
|
60
189
|
_webView.getSettings().setLoadWithOverviewMode(true);
|
|
61
190
|
_webView.getSettings().setUseWideViewPort(true);
|
|
191
|
+
_webView.getSettings().setAllowFileAccessFromFileURLs(true);
|
|
192
|
+
_webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
|
|
193
|
+
_webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
|
|
194
|
+
|
|
195
|
+
_webView.setWebViewClient(new WebViewClient());
|
|
196
|
+
|
|
197
|
+
_webView.setWebChromeClient(
|
|
198
|
+
new WebChromeClient() {
|
|
199
|
+
// Enable file open dialog
|
|
200
|
+
@Override
|
|
201
|
+
public boolean onShowFileChooser(
|
|
202
|
+
WebView webView,
|
|
203
|
+
ValueCallback<Uri[]> filePathCallback,
|
|
204
|
+
WebChromeClient.FileChooserParams fileChooserParams
|
|
205
|
+
) {
|
|
206
|
+
openFileChooser(
|
|
207
|
+
filePathCallback,
|
|
208
|
+
fileChooserParams.getAcceptTypes()[0],
|
|
209
|
+
fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE
|
|
210
|
+
);
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Grant permissions for cam
|
|
215
|
+
@Override
|
|
216
|
+
public void onPermissionRequest(final PermissionRequest request) {
|
|
217
|
+
Log.i(
|
|
218
|
+
"INAPPBROWSER",
|
|
219
|
+
"onPermissionRequest " + Arrays.toString(request.getResources())
|
|
220
|
+
);
|
|
221
|
+
final String[] requestedResources = request.getResources();
|
|
222
|
+
for (String r : requestedResources) {
|
|
223
|
+
Log.i("INAPPBROWSER", "requestedResources " + r);
|
|
224
|
+
if (r.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {
|
|
225
|
+
Log.i("INAPPBROWSER", "RESOURCE_VIDEO_CAPTURE req");
|
|
226
|
+
// Store the permission request
|
|
227
|
+
currentPermissionRequest = request;
|
|
228
|
+
// Initiate the permission request through the plugin
|
|
229
|
+
if (permissionHandler != null) {
|
|
230
|
+
permissionHandler.handleCameraPermissionRequest(request);
|
|
231
|
+
}
|
|
232
|
+
return; // Return here to avoid denying the request
|
|
233
|
+
} else if (r.equals(PermissionRequest.RESOURCE_AUDIO_CAPTURE)) {
|
|
234
|
+
Log.i("INAPPBROWSER", "RESOURCE_AUDIO_CAPTURE req");
|
|
235
|
+
// Store the permission request
|
|
236
|
+
currentPermissionRequest = request;
|
|
237
|
+
// Initiate the permission request through the plugin
|
|
238
|
+
if (permissionHandler != null) {
|
|
239
|
+
permissionHandler.handleMicrophonePermissionRequest(request);
|
|
240
|
+
}
|
|
241
|
+
return; // Return here to avoid denying the request
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// If no matching permission is found, deny the request
|
|
245
|
+
request.deny();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@Override
|
|
249
|
+
public void onPermissionRequestCanceled(PermissionRequest request) {
|
|
250
|
+
super.onPermissionRequestCanceled(request);
|
|
251
|
+
Toast.makeText(
|
|
252
|
+
WebViewDialog.this.activity,
|
|
253
|
+
"Permission Denied",
|
|
254
|
+
Toast.LENGTH_SHORT
|
|
255
|
+
).show();
|
|
256
|
+
// Handle the denied permission
|
|
257
|
+
if (currentPermissionRequest != null) {
|
|
258
|
+
currentPermissionRequest.deny();
|
|
259
|
+
currentPermissionRequest = null;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
);
|
|
62
264
|
|
|
63
265
|
Map<String, String> requestHeaders = new HashMap<>();
|
|
64
266
|
if (_options.getHeaders() != null) {
|
|
65
267
|
Iterator<String> keys = _options.getHeaders().keys();
|
|
66
268
|
while (keys.hasNext()) {
|
|
67
269
|
String key = keys.next();
|
|
68
|
-
if (TextUtils.equals(key, "
|
|
270
|
+
if (TextUtils.equals(key.toLowerCase(), "user-agent")) {
|
|
69
271
|
_webView
|
|
70
272
|
.getSettings()
|
|
71
273
|
.setUserAgentString(_options.getHeaders().getString(key));
|
|
@@ -88,13 +290,134 @@ public class WebViewDialog extends Dialog {
|
|
|
88
290
|
}
|
|
89
291
|
}
|
|
90
292
|
|
|
293
|
+
public void postMessageToJS(Object detail) {
|
|
294
|
+
if (_webView != null) {
|
|
295
|
+
try {
|
|
296
|
+
JSONObject jsonObject = new JSONObject();
|
|
297
|
+
jsonObject.put("detail", detail);
|
|
298
|
+
String jsonDetail = jsonObject.toString();
|
|
299
|
+
String script =
|
|
300
|
+
"window.dispatchEvent(new CustomEvent('messageFromNative', " +
|
|
301
|
+
jsonDetail +
|
|
302
|
+
"));";
|
|
303
|
+
_webView.post(() -> _webView.evaluateJavascript(script, null));
|
|
304
|
+
} catch (Exception e) {
|
|
305
|
+
Log.e(
|
|
306
|
+
"postMessageToJS",
|
|
307
|
+
"Error sending message to JS: " + e.getMessage()
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private void injectJavaScriptInterface() {
|
|
314
|
+
String script =
|
|
315
|
+
"if (!window.mobileApp) { " +
|
|
316
|
+
" window.mobileApp = { " +
|
|
317
|
+
" postMessage: function(message) { " +
|
|
318
|
+
" if (window.AndroidInterface) { " +
|
|
319
|
+
" window.AndroidInterface.postMessage(JSON.stringify(message)); " +
|
|
320
|
+
" } " +
|
|
321
|
+
" } " +
|
|
322
|
+
" }; " +
|
|
323
|
+
"}";
|
|
324
|
+
_webView.evaluateJavascript(script, null);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private void injectPreShowScript() {
|
|
328
|
+
// String script =
|
|
329
|
+
// "import('https://unpkg.com/darkreader@4.9.89/darkreader.js').then(() => {DarkReader.enable({ brightness: 100, contrast: 90, sepia: 10 });window.PreLoadScriptInterface.finished()})";
|
|
330
|
+
|
|
331
|
+
if (preShowSemaphore != null) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
String script =
|
|
336
|
+
"async function preShowFunction() {\n" +
|
|
337
|
+
_options.getPreShowScript() +
|
|
338
|
+
'\n' +
|
|
339
|
+
"};\n" +
|
|
340
|
+
"preShowFunction().then(() => window.PreShowScriptInterface.success()).catch(err => { console.error('Preshow error', err); window.PreShowScriptInterface.error(JSON.stringify(err, Object.getOwnPropertyNames(err))) })";
|
|
341
|
+
|
|
342
|
+
Log.i(
|
|
343
|
+
"InjectPreShowScript",
|
|
344
|
+
String.format("PreShowScript script:\n%s", script)
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
preShowSemaphore = new Semaphore(0);
|
|
348
|
+
activity.runOnUiThread(
|
|
349
|
+
new Runnable() {
|
|
350
|
+
@Override
|
|
351
|
+
public void run() {
|
|
352
|
+
_webView.evaluateJavascript(script, null);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
if (!preShowSemaphore.tryAcquire(10, TimeUnit.SECONDS)) {
|
|
359
|
+
Log.e(
|
|
360
|
+
"InjectPreShowScript",
|
|
361
|
+
"PreShowScript running for over 10 seconds. The plugin will not wait any longer!"
|
|
362
|
+
);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (preshowError != null && !preshowError.isEmpty()) {
|
|
366
|
+
Log.e(
|
|
367
|
+
"InjectPreShowScript",
|
|
368
|
+
"Error within the user-provided preShowFunction: " + preshowError
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
} catch (InterruptedException e) {
|
|
372
|
+
Log.e(
|
|
373
|
+
"InjectPreShowScript",
|
|
374
|
+
"Error when calling InjectPreShowScript: " + e.getMessage()
|
|
375
|
+
);
|
|
376
|
+
} finally {
|
|
377
|
+
preShowSemaphore = null;
|
|
378
|
+
preshowError = null;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private void openFileChooser(
|
|
383
|
+
ValueCallback<Uri[]> filePathCallback,
|
|
384
|
+
String acceptType,
|
|
385
|
+
boolean isMultiple
|
|
386
|
+
) {
|
|
387
|
+
mFilePathCallback = filePathCallback;
|
|
388
|
+
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
389
|
+
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
390
|
+
intent.setType(acceptType); // Default to */*
|
|
391
|
+
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, isMultiple);
|
|
392
|
+
activity.startActivityForResult(
|
|
393
|
+
Intent.createChooser(intent, "Select File"),
|
|
394
|
+
FILE_CHOOSER_REQUEST_CODE
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
public void reload() {
|
|
399
|
+
_webView.reload();
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
public void destroy() {
|
|
403
|
+
_webView.destroy();
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
public String getUrl() {
|
|
407
|
+
return _webView.getUrl();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
public void executeScript(String script) {
|
|
411
|
+
_webView.evaluateJavascript(script, null);
|
|
412
|
+
}
|
|
413
|
+
|
|
91
414
|
public void setUrl(String url) {
|
|
92
415
|
Map<String, String> requestHeaders = new HashMap<>();
|
|
93
416
|
if (_options.getHeaders() != null) {
|
|
94
417
|
Iterator<String> keys = _options.getHeaders().keys();
|
|
95
418
|
while (keys.hasNext()) {
|
|
96
419
|
String key = keys.next();
|
|
97
|
-
if (TextUtils.equals(key, "
|
|
420
|
+
if (TextUtils.equals(key.toLowerCase(), "user-agent")) {
|
|
98
421
|
_webView
|
|
99
422
|
.getSettings()
|
|
100
423
|
.setUserAgentString(_options.getHeaders().getString(key));
|
|
@@ -108,11 +431,27 @@ public class WebViewDialog extends Dialog {
|
|
|
108
431
|
|
|
109
432
|
private void setTitle(String newTitleText) {
|
|
110
433
|
TextView textView = (TextView) _toolbar.findViewById(R.id.titleText);
|
|
111
|
-
|
|
434
|
+
if (_options.getVisibleTitle()) {
|
|
435
|
+
textView.setText(newTitleText);
|
|
436
|
+
} else {
|
|
437
|
+
textView.setText("");
|
|
438
|
+
}
|
|
112
439
|
}
|
|
113
440
|
|
|
114
441
|
private void setupToolbar() {
|
|
115
442
|
_toolbar = this.findViewById(R.id.tool_bar);
|
|
443
|
+
int color = Color.parseColor("#ffffff");
|
|
444
|
+
try {
|
|
445
|
+
color = Color.parseColor(_options.getToolbarColor());
|
|
446
|
+
} catch (IllegalArgumentException e) {
|
|
447
|
+
// Do nothing
|
|
448
|
+
}
|
|
449
|
+
_toolbar.setBackgroundColor(color);
|
|
450
|
+
_toolbar.findViewById(R.id.backButton).setBackgroundColor(color);
|
|
451
|
+
_toolbar.findViewById(R.id.forwardButton).setBackgroundColor(color);
|
|
452
|
+
_toolbar.findViewById(R.id.closeButton).setBackgroundColor(color);
|
|
453
|
+
_toolbar.findViewById(R.id.reloadButton).setBackgroundColor(color);
|
|
454
|
+
|
|
116
455
|
if (!TextUtils.isEmpty(_options.getTitle())) {
|
|
117
456
|
this.setTitle(_options.getTitle());
|
|
118
457
|
} else {
|
|
@@ -153,26 +492,200 @@ public class WebViewDialog extends Dialog {
|
|
|
153
492
|
new View.OnClickListener() {
|
|
154
493
|
@Override
|
|
155
494
|
public void onClick(View view) {
|
|
156
|
-
|
|
157
|
-
_options.
|
|
495
|
+
// if closeModal true then display a native modal to check if the user is sure to close the browser
|
|
496
|
+
if (_options.getCloseModal()) {
|
|
497
|
+
new AlertDialog.Builder(_context)
|
|
498
|
+
.setTitle(_options.getCloseModalTitle())
|
|
499
|
+
.setMessage(_options.getCloseModalDescription())
|
|
500
|
+
.setPositiveButton(
|
|
501
|
+
_options.getCloseModalOk(),
|
|
502
|
+
new DialogInterface.OnClickListener() {
|
|
503
|
+
public void onClick(DialogInterface dialog, int which) {
|
|
504
|
+
// Close button clicked, do something
|
|
505
|
+
dismiss();
|
|
506
|
+
_options.getCallbacks().closeEvent(_webView.getUrl());
|
|
507
|
+
_webView.destroy();
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
)
|
|
511
|
+
.setNegativeButton(_options.getCloseModalCancel(), null)
|
|
512
|
+
.show();
|
|
513
|
+
} else {
|
|
514
|
+
dismiss();
|
|
515
|
+
_options.getCallbacks().closeEvent(_webView.getUrl());
|
|
516
|
+
_webView.destroy();
|
|
517
|
+
}
|
|
158
518
|
}
|
|
159
519
|
}
|
|
160
520
|
);
|
|
161
521
|
|
|
522
|
+
if (_options.showArrow()) {
|
|
523
|
+
closeButton.setBackgroundResource(R.drawable.arrow_forward_enabled);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (_options.getShowReloadButton()) {
|
|
527
|
+
View reloadButton = _toolbar.findViewById(R.id.reloadButton);
|
|
528
|
+
reloadButton.setVisibility(View.VISIBLE);
|
|
529
|
+
reloadButton.setOnClickListener(
|
|
530
|
+
new View.OnClickListener() {
|
|
531
|
+
@Override
|
|
532
|
+
public void onClick(View view) {
|
|
533
|
+
_webView.reload();
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
|
|
162
539
|
if (TextUtils.equals(_options.getToolbarType(), "activity")) {
|
|
163
540
|
_toolbar.findViewById(R.id.forwardButton).setVisibility(View.GONE);
|
|
164
541
|
_toolbar.findViewById(R.id.backButton).setVisibility(View.GONE);
|
|
542
|
+
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
543
|
+
R.id.buttonNearDone
|
|
544
|
+
);
|
|
545
|
+
buttonNearDoneView.setVisibility(View.GONE);
|
|
165
546
|
//TODO: Add share button functionality
|
|
166
547
|
} else if (TextUtils.equals(_options.getToolbarType(), "navigation")) {
|
|
548
|
+
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
549
|
+
R.id.buttonNearDone
|
|
550
|
+
);
|
|
551
|
+
buttonNearDoneView.setVisibility(View.GONE);
|
|
167
552
|
//TODO: Remove share button when implemented
|
|
168
553
|
} else if (TextUtils.equals(_options.getToolbarType(), "blank")) {
|
|
169
554
|
_toolbar.setVisibility(View.GONE);
|
|
170
555
|
} else {
|
|
171
556
|
_toolbar.findViewById(R.id.forwardButton).setVisibility(View.GONE);
|
|
172
557
|
_toolbar.findViewById(R.id.backButton).setVisibility(View.GONE);
|
|
558
|
+
|
|
559
|
+
Options.ButtonNearDone buttonNearDone = _options.getButtonNearDone();
|
|
560
|
+
if (buttonNearDone != null) {
|
|
561
|
+
AssetManager assetManager = _context.getAssets();
|
|
562
|
+
|
|
563
|
+
// Open the SVG file from assets
|
|
564
|
+
InputStream inputStream = null;
|
|
565
|
+
try {
|
|
566
|
+
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
567
|
+
R.id.buttonNearDone
|
|
568
|
+
);
|
|
569
|
+
buttonNearDoneView.setVisibility(View.VISIBLE);
|
|
570
|
+
|
|
571
|
+
inputStream = assetManager.open(buttonNearDone.getIcon());
|
|
572
|
+
|
|
573
|
+
SVG svg = SVG.getFromInputStream(inputStream);
|
|
574
|
+
Picture picture = svg.renderToPicture(
|
|
575
|
+
buttonNearDone.getWidth(),
|
|
576
|
+
buttonNearDone.getHeight()
|
|
577
|
+
);
|
|
578
|
+
PictureDrawable pictureDrawable = new PictureDrawable(picture);
|
|
579
|
+
|
|
580
|
+
buttonNearDoneView.setImageDrawable(pictureDrawable);
|
|
581
|
+
buttonNearDoneView.setOnClickListener(view ->
|
|
582
|
+
_options.getCallbacks().buttonNearDoneClicked()
|
|
583
|
+
);
|
|
584
|
+
} catch (IOException | SVGParseException e) {
|
|
585
|
+
throw new RuntimeException(e);
|
|
586
|
+
} finally {
|
|
587
|
+
if (inputStream != null) {
|
|
588
|
+
try {
|
|
589
|
+
inputStream.close();
|
|
590
|
+
} catch (IOException e) {
|
|
591
|
+
throw new RuntimeException(e);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
} else {
|
|
596
|
+
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
597
|
+
R.id.buttonNearDone
|
|
598
|
+
);
|
|
599
|
+
buttonNearDoneView.setVisibility(View.GONE);
|
|
600
|
+
}
|
|
173
601
|
}
|
|
174
602
|
}
|
|
175
603
|
|
|
604
|
+
public void handleProxyResultError(String result, String id) {
|
|
605
|
+
Log.i(
|
|
606
|
+
"InAppBrowserProxy",
|
|
607
|
+
String.format(
|
|
608
|
+
"handleProxyResultError: %s, ok: %s id: %s",
|
|
609
|
+
result,
|
|
610
|
+
false,
|
|
611
|
+
id
|
|
612
|
+
)
|
|
613
|
+
);
|
|
614
|
+
ProxiedRequest proxiedRequest = proxiedRequestsHashmap.get(id);
|
|
615
|
+
if (proxiedRequest == null) {
|
|
616
|
+
Log.e("InAppBrowserProxy", "proxiedRequest is null");
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
proxiedRequestsHashmap.remove(id);
|
|
620
|
+
proxiedRequest.semaphore.release();
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
public void handleProxyResultOk(JSONObject result, String id) {
|
|
624
|
+
Log.i(
|
|
625
|
+
"InAppBrowserProxy",
|
|
626
|
+
String.format("handleProxyResultOk: %s, ok: %s, id: %s", result, true, id)
|
|
627
|
+
);
|
|
628
|
+
ProxiedRequest proxiedRequest = proxiedRequestsHashmap.get(id);
|
|
629
|
+
if (proxiedRequest == null) {
|
|
630
|
+
Log.e("InAppBrowserProxy", "proxiedRequest is null");
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
proxiedRequestsHashmap.remove(id);
|
|
634
|
+
|
|
635
|
+
if (result == null) {
|
|
636
|
+
proxiedRequest.semaphore.release();
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
Map<String, String> responseHeaders = new HashMap<>();
|
|
641
|
+
String body;
|
|
642
|
+
int code;
|
|
643
|
+
|
|
644
|
+
try {
|
|
645
|
+
body = result.getString("body");
|
|
646
|
+
code = result.getInt("code");
|
|
647
|
+
JSONObject headers = result.getJSONObject("headers");
|
|
648
|
+
for (Iterator<String> it = headers.keys(); it.hasNext();) {
|
|
649
|
+
String headerName = it.next();
|
|
650
|
+
String header = headers.getString(headerName);
|
|
651
|
+
responseHeaders.put(headerName, header);
|
|
652
|
+
}
|
|
653
|
+
} catch (JSONException e) {
|
|
654
|
+
Log.e("InAppBrowserProxy", "Cannot parse OK result", e);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
String contentType = responseHeaders.get("Content-Type");
|
|
659
|
+
if (contentType == null) {
|
|
660
|
+
contentType = responseHeaders.get("content-type");
|
|
661
|
+
}
|
|
662
|
+
if (contentType == null) {
|
|
663
|
+
Log.e("InAppBrowserProxy", "'Content-Type' header is required");
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (!((100 <= code && code <= 299) || (400 <= code && code <= 599))) {
|
|
668
|
+
Log.e(
|
|
669
|
+
"InAppBrowserProxy",
|
|
670
|
+
String.format("Status code %s outside of the allowed range", code)
|
|
671
|
+
);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
WebResourceResponse webResourceResponse = new WebResourceResponse(
|
|
676
|
+
contentType,
|
|
677
|
+
"utf-8",
|
|
678
|
+
new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8))
|
|
679
|
+
);
|
|
680
|
+
|
|
681
|
+
webResourceResponse.setStatusCodeAndReasonPhrase(
|
|
682
|
+
code,
|
|
683
|
+
getReasonPhrase(code)
|
|
684
|
+
);
|
|
685
|
+
proxiedRequest.response = webResourceResponse;
|
|
686
|
+
proxiedRequest.semaphore.release();
|
|
687
|
+
}
|
|
688
|
+
|
|
176
689
|
private void setWebViewClient() {
|
|
177
690
|
_webView.setWebViewClient(
|
|
178
691
|
new WebViewClient() {
|
|
@@ -181,9 +694,199 @@ public class WebViewDialog extends Dialog {
|
|
|
181
694
|
WebView view,
|
|
182
695
|
WebResourceRequest request
|
|
183
696
|
) {
|
|
697
|
+
// HashMap<String, String> map = new HashMap<>();
|
|
698
|
+
// map.put("x-requested-with", null);
|
|
699
|
+
// view.loadUrl(request.getUrl().toString(), map);
|
|
700
|
+
Context context = view.getContext();
|
|
701
|
+
String url = request.getUrl().toString();
|
|
702
|
+
|
|
703
|
+
if (!url.startsWith("https://") && !url.startsWith("http://")) {
|
|
704
|
+
try {
|
|
705
|
+
Intent intent;
|
|
706
|
+
if (url.startsWith("intent://")) {
|
|
707
|
+
intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
|
|
708
|
+
} else {
|
|
709
|
+
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
713
|
+
context.startActivity(intent);
|
|
714
|
+
return true;
|
|
715
|
+
} catch (ActivityNotFoundException e) {
|
|
716
|
+
// Do nothing
|
|
717
|
+
} catch (URISyntaxException e) {
|
|
718
|
+
// Do nothing
|
|
719
|
+
}
|
|
720
|
+
}
|
|
184
721
|
return false;
|
|
185
722
|
}
|
|
186
723
|
|
|
724
|
+
private String randomRequestId() {
|
|
725
|
+
return UUID.randomUUID().toString();
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
private String toBase64(String raw) {
|
|
729
|
+
String s = Base64.encodeToString(raw.getBytes(), Base64.NO_WRAP);
|
|
730
|
+
if (s.endsWith("=")) {
|
|
731
|
+
s = s.substring(0, s.length() - 2);
|
|
732
|
+
}
|
|
733
|
+
return s;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
//
|
|
737
|
+
// void handleRedirect(String currentUrl, Response response) {
|
|
738
|
+
// String loc = response.header("Location");
|
|
739
|
+
// _webView.evaluateJavascript("");
|
|
740
|
+
// }
|
|
741
|
+
//
|
|
742
|
+
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
743
|
+
@Override
|
|
744
|
+
public WebResourceResponse shouldInterceptRequest(
|
|
745
|
+
WebView view,
|
|
746
|
+
WebResourceRequest request
|
|
747
|
+
) {
|
|
748
|
+
Pattern pattern = _options.getProxyRequestsPattern();
|
|
749
|
+
if (pattern == null) {
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
Matcher matcher = pattern.matcher(request.getUrl().toString());
|
|
753
|
+
if (!matcher.find()) {
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// Requests matches the regex
|
|
758
|
+
if (Objects.equals(request.getMethod(), "POST")) {
|
|
759
|
+
// Log.e("HTTP", String.format("returned null (ok) %s", request.getUrl().toString()));
|
|
760
|
+
return null;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
Log.i(
|
|
764
|
+
"InAppBrowserProxy",
|
|
765
|
+
String.format("Proxying request: %s", request.getUrl().toString())
|
|
766
|
+
);
|
|
767
|
+
|
|
768
|
+
// We need to call a JS function
|
|
769
|
+
String requestId = randomRequestId();
|
|
770
|
+
ProxiedRequest proxiedRequest = new ProxiedRequest();
|
|
771
|
+
proxiedRequestsHashmap.put(requestId, proxiedRequest);
|
|
772
|
+
|
|
773
|
+
// lsuakdchgbbaHandleProxiedRequest
|
|
774
|
+
activity.runOnUiThread(
|
|
775
|
+
new Runnable() {
|
|
776
|
+
@Override
|
|
777
|
+
public void run() {
|
|
778
|
+
StringBuilder headers = new StringBuilder();
|
|
779
|
+
Map<String, String> requestHeaders =
|
|
780
|
+
request.getRequestHeaders();
|
|
781
|
+
for (Map.Entry<
|
|
782
|
+
String,
|
|
783
|
+
String
|
|
784
|
+
> header : requestHeaders.entrySet()) {
|
|
785
|
+
headers.append(
|
|
786
|
+
String.format(
|
|
787
|
+
"h[atob('%s')]=atob('%s');",
|
|
788
|
+
toBase64(header.getKey()),
|
|
789
|
+
toBase64(header.getValue())
|
|
790
|
+
)
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
String s = String.format(
|
|
794
|
+
"try {function getHeaders() {const h = {}; %s return h}; window.InAppBrowserProxyRequest(new Request(atob('%s'), {headers: getHeaders(), method: '%s'})).then(async (res) => Capacitor.Plugins.InAppBrowser.lsuakdchgbbaHandleProxiedRequest({ok: true, result: (!!res ? {headers: Object.fromEntries(res.headers.entries()), code: res.status, body: (await res.text())} : null), id: '%s'})).catch((e) => Capacitor.Plugins.InAppBrowser.lsuakdchgbbaHandleProxiedRequest({ok: false, result: e.toString(), id: '%s'}))} catch (e) {Capacitor.Plugins.InAppBrowser.lsuakdchgbbaHandleProxiedRequest({ok: false, result: e.toString(), id: '%s'})}",
|
|
795
|
+
headers,
|
|
796
|
+
toBase64(request.getUrl().toString()),
|
|
797
|
+
request.getMethod(),
|
|
798
|
+
requestId,
|
|
799
|
+
requestId,
|
|
800
|
+
requestId
|
|
801
|
+
);
|
|
802
|
+
// Log.i("HTTP", s);
|
|
803
|
+
capacitorWebView.evaluateJavascript(s, null);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
);
|
|
807
|
+
|
|
808
|
+
// 10 seconds wait max
|
|
809
|
+
try {
|
|
810
|
+
if (proxiedRequest.semaphore.tryAcquire(1, 10, TimeUnit.SECONDS)) {
|
|
811
|
+
return proxiedRequest.response;
|
|
812
|
+
} else {
|
|
813
|
+
Log.e("InAppBrowserProxy", "Semaphore timed out");
|
|
814
|
+
proxiedRequestsHashmap.remove(requestId); // prevent mem leak
|
|
815
|
+
}
|
|
816
|
+
} catch (InterruptedException e) {
|
|
817
|
+
Log.e("InAppBrowserProxy", "Semaphore wait error", e);
|
|
818
|
+
}
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
@Override
|
|
823
|
+
public void onReceivedHttpAuthRequest(
|
|
824
|
+
WebView view,
|
|
825
|
+
HttpAuthHandler handler,
|
|
826
|
+
String host,
|
|
827
|
+
String realm
|
|
828
|
+
) {
|
|
829
|
+
final String sourceUrl = _options.getUrl();
|
|
830
|
+
final String url = view.getUrl();
|
|
831
|
+
final JSObject credentials = _options.getCredentials();
|
|
832
|
+
|
|
833
|
+
if (
|
|
834
|
+
credentials != null &&
|
|
835
|
+
credentials.getString("username") != null &&
|
|
836
|
+
credentials.getString("password") != null &&
|
|
837
|
+
sourceUrl != null &&
|
|
838
|
+
url != null
|
|
839
|
+
) {
|
|
840
|
+
String sourceProtocol = "";
|
|
841
|
+
String sourceHost = "";
|
|
842
|
+
int sourcePort = -1;
|
|
843
|
+
try {
|
|
844
|
+
URI uri = new URI(sourceUrl);
|
|
845
|
+
sourceProtocol = uri.getScheme();
|
|
846
|
+
sourceHost = uri.getHost();
|
|
847
|
+
sourcePort = uri.getPort();
|
|
848
|
+
if (
|
|
849
|
+
sourcePort == -1 && Objects.equals(sourceProtocol, "https")
|
|
850
|
+
) sourcePort = 443;
|
|
851
|
+
else if (
|
|
852
|
+
sourcePort == -1 && Objects.equals(sourceProtocol, "http")
|
|
853
|
+
) sourcePort = 80;
|
|
854
|
+
} catch (URISyntaxException e) {
|
|
855
|
+
e.printStackTrace();
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
String protocol = "";
|
|
859
|
+
int port = -1;
|
|
860
|
+
try {
|
|
861
|
+
URI uri = new URI(url);
|
|
862
|
+
protocol = uri.getScheme();
|
|
863
|
+
port = uri.getPort();
|
|
864
|
+
if (port == -1 && Objects.equals(protocol, "https")) port = 443;
|
|
865
|
+
else if (port == -1 && Objects.equals(protocol, "http")) port =
|
|
866
|
+
80;
|
|
867
|
+
} catch (URISyntaxException e) {
|
|
868
|
+
e.printStackTrace();
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (
|
|
872
|
+
Objects.equals(sourceHost, host) &&
|
|
873
|
+
Objects.equals(sourceProtocol, protocol) &&
|
|
874
|
+
sourcePort == port
|
|
875
|
+
) {
|
|
876
|
+
final String username = Objects.requireNonNull(
|
|
877
|
+
credentials.getString("username")
|
|
878
|
+
);
|
|
879
|
+
final String password = Objects.requireNonNull(
|
|
880
|
+
credentials.getString("password")
|
|
881
|
+
);
|
|
882
|
+
handler.proceed(username, password);
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
super.onReceivedHttpAuthRequest(view, handler, host, realm);
|
|
888
|
+
}
|
|
889
|
+
|
|
187
890
|
@Override
|
|
188
891
|
public void onLoadResource(WebView view, String url) {
|
|
189
892
|
super.onLoadResource(view, url);
|
|
@@ -194,24 +897,77 @@ public class WebViewDialog extends Dialog {
|
|
|
194
897
|
super.onPageStarted(view, url, favicon);
|
|
195
898
|
try {
|
|
196
899
|
URI uri = new URI(url);
|
|
197
|
-
|
|
900
|
+
if (TextUtils.isEmpty(_options.getTitle())) {
|
|
901
|
+
setTitle(uri.getHost());
|
|
902
|
+
}
|
|
198
903
|
} catch (URISyntaxException e) {
|
|
199
904
|
// Do nothing
|
|
200
905
|
}
|
|
201
|
-
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
public void doUpdateVisitedHistory(
|
|
909
|
+
WebView view,
|
|
910
|
+
String url,
|
|
911
|
+
boolean isReload
|
|
912
|
+
) {
|
|
913
|
+
if (!isReload) {
|
|
914
|
+
_options.getCallbacks().urlChangeEvent(url);
|
|
915
|
+
}
|
|
916
|
+
super.doUpdateVisitedHistory(view, url, isReload);
|
|
917
|
+
injectJavaScriptInterface();
|
|
202
918
|
}
|
|
203
919
|
|
|
204
920
|
@Override
|
|
205
921
|
public void onPageFinished(WebView view, String url) {
|
|
206
922
|
super.onPageFinished(view, url);
|
|
207
|
-
_options.getCallbacks().pageLoaded();
|
|
208
923
|
if (!isInitialized) {
|
|
209
924
|
isInitialized = true;
|
|
210
925
|
_webView.clearHistory();
|
|
211
926
|
if (_options.isPresentAfterPageLoad()) {
|
|
212
|
-
|
|
213
|
-
|
|
927
|
+
boolean usePreShowScript =
|
|
928
|
+
_options.getPreShowScript() != null &&
|
|
929
|
+
!_options.getPreShowScript().isEmpty();
|
|
930
|
+
if (!usePreShowScript) {
|
|
931
|
+
show();
|
|
932
|
+
_options.getPluginCall().resolve();
|
|
933
|
+
} else {
|
|
934
|
+
executorService.execute(
|
|
935
|
+
new Runnable() {
|
|
936
|
+
@Override
|
|
937
|
+
public void run() {
|
|
938
|
+
if (
|
|
939
|
+
_options.getPreShowScript() != null &&
|
|
940
|
+
!_options.getPreShowScript().isEmpty()
|
|
941
|
+
) {
|
|
942
|
+
injectPreShowScript();
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
activity.runOnUiThread(
|
|
946
|
+
new Runnable() {
|
|
947
|
+
@Override
|
|
948
|
+
public void run() {
|
|
949
|
+
show();
|
|
950
|
+
_options.getPluginCall().resolve();
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
);
|
|
957
|
+
}
|
|
214
958
|
}
|
|
959
|
+
} else if (
|
|
960
|
+
_options.getPreShowScript() != null &&
|
|
961
|
+
!_options.getPreShowScript().isEmpty()
|
|
962
|
+
) {
|
|
963
|
+
executorService.execute(
|
|
964
|
+
new Runnable() {
|
|
965
|
+
@Override
|
|
966
|
+
public void run() {
|
|
967
|
+
injectPreShowScript();
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
);
|
|
215
971
|
}
|
|
216
972
|
|
|
217
973
|
ImageButton backButton = _toolbar.findViewById(R.id.backButton);
|
|
@@ -233,6 +989,7 @@ public class WebViewDialog extends Dialog {
|
|
|
233
989
|
}
|
|
234
990
|
|
|
235
991
|
_options.getCallbacks().pageLoaded();
|
|
992
|
+
injectJavaScriptInterface();
|
|
236
993
|
}
|
|
237
994
|
|
|
238
995
|
@Override
|
|
@@ -244,6 +1001,23 @@ public class WebViewDialog extends Dialog {
|
|
|
244
1001
|
super.onReceivedError(view, request, error);
|
|
245
1002
|
_options.getCallbacks().pageLoadError();
|
|
246
1003
|
}
|
|
1004
|
+
|
|
1005
|
+
@SuppressLint("WebViewClientOnReceivedSslError")
|
|
1006
|
+
@Override
|
|
1007
|
+
public void onReceivedSslError(
|
|
1008
|
+
WebView view,
|
|
1009
|
+
SslErrorHandler handler,
|
|
1010
|
+
SslError error
|
|
1011
|
+
) {
|
|
1012
|
+
boolean ignoreSSLUntrustedError = _options.ignoreUntrustedSSLError();
|
|
1013
|
+
if (
|
|
1014
|
+
ignoreSSLUntrustedError &&
|
|
1015
|
+
error.getPrimaryError() == SslError.SSL_UNTRUSTED
|
|
1016
|
+
) handler.proceed();
|
|
1017
|
+
else {
|
|
1018
|
+
super.onReceivedSslError(view, handler, error);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
247
1021
|
}
|
|
248
1022
|
);
|
|
249
1023
|
}
|
|
@@ -252,11 +1026,109 @@ public class WebViewDialog extends Dialog {
|
|
|
252
1026
|
public void onBackPressed() {
|
|
253
1027
|
if (
|
|
254
1028
|
_webView.canGoBack() &&
|
|
255
|
-
TextUtils.equals(_options.getToolbarType(), "navigation")
|
|
1029
|
+
(TextUtils.equals(_options.getToolbarType(), "navigation") ||
|
|
1030
|
+
_options.getActiveNativeNavigationForWebview())
|
|
256
1031
|
) {
|
|
257
1032
|
_webView.goBack();
|
|
258
|
-
} else {
|
|
1033
|
+
} else if (!_options.getDisableGoBackOnNativeApplication()) {
|
|
259
1034
|
super.onBackPressed();
|
|
260
1035
|
}
|
|
261
1036
|
}
|
|
1037
|
+
|
|
1038
|
+
public static String getReasonPhrase(int statusCode) {
|
|
1039
|
+
switch (statusCode) {
|
|
1040
|
+
case (200):
|
|
1041
|
+
return "OK";
|
|
1042
|
+
case (201):
|
|
1043
|
+
return "Created";
|
|
1044
|
+
case (202):
|
|
1045
|
+
return "Accepted";
|
|
1046
|
+
case (203):
|
|
1047
|
+
return "Non Authoritative Information";
|
|
1048
|
+
case (204):
|
|
1049
|
+
return "No Content";
|
|
1050
|
+
case (205):
|
|
1051
|
+
return "Reset Content";
|
|
1052
|
+
case (206):
|
|
1053
|
+
return "Partial Content";
|
|
1054
|
+
case (207):
|
|
1055
|
+
return "Partial Update OK";
|
|
1056
|
+
case (300):
|
|
1057
|
+
return "Mutliple Choices";
|
|
1058
|
+
case (301):
|
|
1059
|
+
return "Moved Permanently";
|
|
1060
|
+
case (302):
|
|
1061
|
+
return "Moved Temporarily";
|
|
1062
|
+
case (303):
|
|
1063
|
+
return "See Other";
|
|
1064
|
+
case (304):
|
|
1065
|
+
return "Not Modified";
|
|
1066
|
+
case (305):
|
|
1067
|
+
return "Use Proxy";
|
|
1068
|
+
case (307):
|
|
1069
|
+
return "Temporary Redirect";
|
|
1070
|
+
case (400):
|
|
1071
|
+
return "Bad Request";
|
|
1072
|
+
case (401):
|
|
1073
|
+
return "Unauthorized";
|
|
1074
|
+
case (402):
|
|
1075
|
+
return "Payment Required";
|
|
1076
|
+
case (403):
|
|
1077
|
+
return "Forbidden";
|
|
1078
|
+
case (404):
|
|
1079
|
+
return "Not Found";
|
|
1080
|
+
case (405):
|
|
1081
|
+
return "Method Not Allowed";
|
|
1082
|
+
case (406):
|
|
1083
|
+
return "Not Acceptable";
|
|
1084
|
+
case (407):
|
|
1085
|
+
return "Proxy Authentication Required";
|
|
1086
|
+
case (408):
|
|
1087
|
+
return "Request Timeout";
|
|
1088
|
+
case (409):
|
|
1089
|
+
return "Conflict";
|
|
1090
|
+
case (410):
|
|
1091
|
+
return "Gone";
|
|
1092
|
+
case (411):
|
|
1093
|
+
return "Length Required";
|
|
1094
|
+
case (412):
|
|
1095
|
+
return "Precondition Failed";
|
|
1096
|
+
case (413):
|
|
1097
|
+
return "Request Entity Too Large";
|
|
1098
|
+
case (414):
|
|
1099
|
+
return "Request-URI Too Long";
|
|
1100
|
+
case (415):
|
|
1101
|
+
return "Unsupported Media Type";
|
|
1102
|
+
case (416):
|
|
1103
|
+
return "Requested Range Not Satisfiable";
|
|
1104
|
+
case (417):
|
|
1105
|
+
return "Expectation Failed";
|
|
1106
|
+
case (418):
|
|
1107
|
+
return "Reauthentication Required";
|
|
1108
|
+
case (419):
|
|
1109
|
+
return "Proxy Reauthentication Required";
|
|
1110
|
+
case (422):
|
|
1111
|
+
return "Unprocessable Entity";
|
|
1112
|
+
case (423):
|
|
1113
|
+
return "Locked";
|
|
1114
|
+
case (424):
|
|
1115
|
+
return "Failed Dependency";
|
|
1116
|
+
case (500):
|
|
1117
|
+
return "Server Error";
|
|
1118
|
+
case (501):
|
|
1119
|
+
return "Not Implemented";
|
|
1120
|
+
case (502):
|
|
1121
|
+
return "Bad Gateway";
|
|
1122
|
+
case (503):
|
|
1123
|
+
return "Service Unavailable";
|
|
1124
|
+
case (504):
|
|
1125
|
+
return "Gateway Timeout";
|
|
1126
|
+
case (505):
|
|
1127
|
+
return "HTTP Version Not Supported";
|
|
1128
|
+
case (507):
|
|
1129
|
+
return "Insufficient Storage";
|
|
1130
|
+
default:
|
|
1131
|
+
return "";
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
262
1134
|
}
|