@calvingoh-hexa/capacitor-inappbrowser 6.9.36
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 +17 -0
- package/LICENSE +21 -0
- package/README.md +690 -0
- package/android/build.gradle +64 -0
- package/android/src/main/AndroidManifest.xml +12 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java +741 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/Options.java +340 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewCallbacks.java +15 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +1177 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/android/src/main/res/drawable/arrow_back_disabled.xml +9 -0
- package/android/src/main/res/drawable/arrow_back_enabled.xml +9 -0
- package/android/src/main/res/drawable/arrow_forward_disabled.xml +9 -0
- package/android/src/main/res/drawable/arrow_forward_enabled.xml +9 -0
- package/android/src/main/res/drawable/ic_clear_24px.xml +9 -0
- package/android/src/main/res/drawable/ic_refresh.xml +9 -0
- package/android/src/main/res/layout/activity_browser.xml +22 -0
- package/android/src/main/res/layout/bridge_layout_main.xml +15 -0
- package/android/src/main/res/layout/content_browser.xml +16 -0
- package/android/src/main/res/layout/tool_bar.xml +72 -0
- package/android/src/main/res/values/browser_theme.xml +3 -0
- package/android/src/main/res/values/colors.xml +5 -0
- package/android/src/main/res/values/dimens.xml +3 -0
- package/android/src/main/res/values/strings.xml +11 -0
- package/android/src/main/res/values/styles.xml +4 -0
- package/dist/docs.json +1865 -0
- package/dist/esm/definitions.d.ts +361 -0
- package/dist/esm/definitions.js +13 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +19 -0
- package/dist/esm/web.js +48 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +75 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +78 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Plugin/Assets.xcassets/Back.imageset/Back.png +0 -0
- package/ios/Plugin/Assets.xcassets/Back.imageset/Back@2x.png +0 -0
- package/ios/Plugin/Assets.xcassets/Back.imageset/Back@3x.png +0 -0
- package/ios/Plugin/Assets.xcassets/Back.imageset/Contents.json +26 -0
- package/ios/Plugin/Assets.xcassets/Contents.json +6 -0
- package/ios/Plugin/Assets.xcassets/Forward.imageset/Contents.json +26 -0
- package/ios/Plugin/Assets.xcassets/Forward.imageset/Forward.png +0 -0
- package/ios/Plugin/Assets.xcassets/Forward.imageset/Forward@2x.png +0 -0
- package/ios/Plugin/Assets.xcassets/Forward.imageset/Forward@3x.png +0 -0
- package/ios/Plugin/Enums.swift +65 -0
- package/ios/Plugin/InAppBrowserPlugin.h +10 -0
- package/ios/Plugin/InAppBrowserPlugin.m +21 -0
- package/ios/Plugin/InAppBrowserPlugin.swift +434 -0
- package/ios/Plugin/Info.plist +24 -0
- package/ios/Plugin/WKWebViewController.swift +1021 -0
- package/package.json +83 -0
|
@@ -0,0 +1,1177 @@
|
|
|
1
|
+
package ee.forgr.capacitor_inappbrowser;
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint;
|
|
4
|
+
import android.app.Activity;
|
|
5
|
+
import android.app.AlertDialog;
|
|
6
|
+
import android.app.Dialog;
|
|
7
|
+
import android.content.ActivityNotFoundException;
|
|
8
|
+
import android.content.Context;
|
|
9
|
+
import android.content.DialogInterface;
|
|
10
|
+
import android.content.Intent;
|
|
11
|
+
import android.content.res.AssetManager;
|
|
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;
|
|
19
|
+
import android.text.TextUtils;
|
|
20
|
+
import android.util.Base64;
|
|
21
|
+
import android.util.Log;
|
|
22
|
+
import android.view.View;
|
|
23
|
+
import android.view.Window;
|
|
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;
|
|
31
|
+
import android.webkit.WebResourceError;
|
|
32
|
+
import android.webkit.WebResourceRequest;
|
|
33
|
+
import android.webkit.WebResourceResponse;
|
|
34
|
+
import android.webkit.WebView;
|
|
35
|
+
import android.webkit.WebViewClient;
|
|
36
|
+
import android.widget.ImageButton;
|
|
37
|
+
import android.widget.TextView;
|
|
38
|
+
import android.widget.Toast;
|
|
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;
|
|
48
|
+
import java.net.URI;
|
|
49
|
+
import java.net.URISyntaxException;
|
|
50
|
+
import java.net.URL;
|
|
51
|
+
import java.nio.charset.StandardCharsets;
|
|
52
|
+
import java.util.Arrays;
|
|
53
|
+
import java.util.HashMap;
|
|
54
|
+
import java.util.Iterator;
|
|
55
|
+
import java.util.List;
|
|
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;
|
|
68
|
+
|
|
69
|
+
public class WebViewDialog extends Dialog {
|
|
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
|
+
|
|
86
|
+
private WebView _webView;
|
|
87
|
+
private Toolbar _toolbar;
|
|
88
|
+
private Options _options;
|
|
89
|
+
private Context _context;
|
|
90
|
+
public Activity activity;
|
|
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
|
+
}
|
|
109
|
+
|
|
110
|
+
private PermissionHandler permissionHandler;
|
|
111
|
+
|
|
112
|
+
public WebViewDialog(
|
|
113
|
+
Context context,
|
|
114
|
+
int theme,
|
|
115
|
+
Options options,
|
|
116
|
+
PermissionHandler permissionHandler,
|
|
117
|
+
WebView capacitorWebView
|
|
118
|
+
) {
|
|
119
|
+
super(context, theme);
|
|
120
|
+
this._options = options;
|
|
121
|
+
this._context = context;
|
|
122
|
+
this.permissionHandler = permissionHandler;
|
|
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
|
+
}
|
|
135
|
+
}
|
|
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")
|
|
158
|
+
public void presentWebView() {
|
|
159
|
+
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
|
160
|
+
setCancelable(true);
|
|
161
|
+
Objects.requireNonNull(getWindow()).setFlags(
|
|
162
|
+
WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
|
163
|
+
WindowManager.LayoutParams.FLAG_FULLSCREEN
|
|
164
|
+
);
|
|
165
|
+
setContentView(R.layout.activity_browser);
|
|
166
|
+
getWindow()
|
|
167
|
+
.setLayout(
|
|
168
|
+
WindowManager.LayoutParams.MATCH_PARENT,
|
|
169
|
+
WindowManager.LayoutParams.MATCH_PARENT
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
this._webView = findViewById(R.id.browser_view);
|
|
173
|
+
_webView.addJavascriptInterface(
|
|
174
|
+
new JavaScriptInterface(),
|
|
175
|
+
"AndroidInterface"
|
|
176
|
+
);
|
|
177
|
+
_webView.addJavascriptInterface(
|
|
178
|
+
new PreShowScriptInterface(),
|
|
179
|
+
"PreShowScriptInterface"
|
|
180
|
+
);
|
|
181
|
+
_webView.getSettings().setJavaScriptEnabled(true);
|
|
182
|
+
_webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
|
|
183
|
+
_webView.getSettings().setDatabaseEnabled(true);
|
|
184
|
+
_webView.getSettings().setDomStorageEnabled(true);
|
|
185
|
+
_webView.getSettings().setAllowFileAccess(true);
|
|
186
|
+
_webView
|
|
187
|
+
.getSettings()
|
|
188
|
+
.setPluginState(android.webkit.WebSettings.PluginState.ON);
|
|
189
|
+
_webView.getSettings().setLoadWithOverviewMode(true);
|
|
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
|
+
);
|
|
264
|
+
|
|
265
|
+
Map<String, String> requestHeaders = new HashMap<>();
|
|
266
|
+
if (_options.getHeaders() != null) {
|
|
267
|
+
Iterator<String> keys = _options.getHeaders().keys();
|
|
268
|
+
while (keys.hasNext()) {
|
|
269
|
+
String key = keys.next();
|
|
270
|
+
if (TextUtils.equals(key.toLowerCase(), "user-agent")) {
|
|
271
|
+
_webView
|
|
272
|
+
.getSettings()
|
|
273
|
+
.setUserAgentString(_options.getHeaders().getString(key));
|
|
274
|
+
} else {
|
|
275
|
+
requestHeaders.put(key, _options.getHeaders().getString(key));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
_webView.loadUrl(this._options.getUrl(), requestHeaders);
|
|
281
|
+
_webView.requestFocus();
|
|
282
|
+
_webView.requestFocusFromTouch();
|
|
283
|
+
|
|
284
|
+
setupToolbar();
|
|
285
|
+
setWebViewClient();
|
|
286
|
+
|
|
287
|
+
if (!this._options.isPresentAfterPageLoad()) {
|
|
288
|
+
show();
|
|
289
|
+
_options.getPluginCall().resolve();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
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
|
+
// To fix android crash issue
|
|
408
|
+
if (_webView == null) return null;
|
|
409
|
+
return _webView.getUrl();
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
public void executeScript(String script) {
|
|
413
|
+
_webView.evaluateJavascript(script, null);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
public void setUrl(String url) {
|
|
417
|
+
Map<String, String> requestHeaders = new HashMap<>();
|
|
418
|
+
if (_options.getHeaders() != null) {
|
|
419
|
+
Iterator<String> keys = _options.getHeaders().keys();
|
|
420
|
+
while (keys.hasNext()) {
|
|
421
|
+
String key = keys.next();
|
|
422
|
+
if (TextUtils.equals(key.toLowerCase(), "user-agent")) {
|
|
423
|
+
_webView
|
|
424
|
+
.getSettings()
|
|
425
|
+
.setUserAgentString(_options.getHeaders().getString(key));
|
|
426
|
+
} else {
|
|
427
|
+
requestHeaders.put(key, _options.getHeaders().getString(key));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
_webView.loadUrl(url, requestHeaders);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
private void setTitle(String newTitleText) {
|
|
435
|
+
TextView textView = (TextView) _toolbar.findViewById(R.id.titleText);
|
|
436
|
+
if (_options.getVisibleTitle()) {
|
|
437
|
+
textView.setText(newTitleText);
|
|
438
|
+
} else {
|
|
439
|
+
textView.setText("");
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
private void setupToolbar() {
|
|
444
|
+
_toolbar = this.findViewById(R.id.tool_bar);
|
|
445
|
+
int color = Color.parseColor("#ffffff");
|
|
446
|
+
try {
|
|
447
|
+
color = Color.parseColor(_options.getToolbarColor());
|
|
448
|
+
} catch (IllegalArgumentException e) {
|
|
449
|
+
// Do nothing
|
|
450
|
+
}
|
|
451
|
+
_toolbar.setBackgroundColor(color);
|
|
452
|
+
_toolbar.findViewById(R.id.backButton).setBackgroundColor(color);
|
|
453
|
+
_toolbar.findViewById(R.id.forwardButton).setBackgroundColor(color);
|
|
454
|
+
_toolbar.findViewById(R.id.closeButton).setBackgroundColor(color);
|
|
455
|
+
_toolbar.findViewById(R.id.reloadButton).setBackgroundColor(color);
|
|
456
|
+
|
|
457
|
+
if (!TextUtils.isEmpty(_options.getTitle())) {
|
|
458
|
+
this.setTitle(_options.getTitle());
|
|
459
|
+
} else {
|
|
460
|
+
try {
|
|
461
|
+
URI uri = new URI(_options.getUrl());
|
|
462
|
+
this.setTitle(uri.getHost());
|
|
463
|
+
} catch (URISyntaxException e) {
|
|
464
|
+
this.setTitle(_options.getTitle());
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
View backButton = _toolbar.findViewById(R.id.backButton);
|
|
469
|
+
backButton.setOnClickListener(
|
|
470
|
+
new View.OnClickListener() {
|
|
471
|
+
@Override
|
|
472
|
+
public void onClick(View view) {
|
|
473
|
+
if (_webView.canGoBack()) {
|
|
474
|
+
_webView.goBack();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
View forwardButton = _toolbar.findViewById(R.id.forwardButton);
|
|
481
|
+
forwardButton.setOnClickListener(
|
|
482
|
+
new View.OnClickListener() {
|
|
483
|
+
@Override
|
|
484
|
+
public void onClick(View view) {
|
|
485
|
+
if (_webView.canGoForward()) {
|
|
486
|
+
_webView.goForward();
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
View closeButton = _toolbar.findViewById(R.id.closeButton);
|
|
493
|
+
closeButton.setOnClickListener(
|
|
494
|
+
new View.OnClickListener() {
|
|
495
|
+
@Override
|
|
496
|
+
public void onClick(View view) {
|
|
497
|
+
// if closeModal true then display a native modal to check if the user is sure to close the browser
|
|
498
|
+
if (_options.getCloseModal()) {
|
|
499
|
+
new AlertDialog.Builder(_context)
|
|
500
|
+
.setTitle(_options.getCloseModalTitle())
|
|
501
|
+
.setMessage(_options.getCloseModalDescription())
|
|
502
|
+
.setPositiveButton(
|
|
503
|
+
_options.getCloseModalOk(),
|
|
504
|
+
new DialogInterface.OnClickListener() {
|
|
505
|
+
public void onClick(DialogInterface dialog, int which) {
|
|
506
|
+
// Close button clicked, do something
|
|
507
|
+
dismiss();
|
|
508
|
+
_options.getCallbacks().closeEvent(_webView.getUrl());
|
|
509
|
+
_webView.destroy();
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
)
|
|
513
|
+
.setNegativeButton(_options.getCloseModalCancel(), null)
|
|
514
|
+
.show();
|
|
515
|
+
} else {
|
|
516
|
+
dismiss();
|
|
517
|
+
_options.getCallbacks().closeEvent(_webView.getUrl());
|
|
518
|
+
_webView.destroy();
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
if (_options.showArrow()) {
|
|
525
|
+
closeButton.setBackgroundResource(R.drawable.arrow_forward_enabled);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (_options.getShowReloadButton()) {
|
|
529
|
+
View reloadButton = _toolbar.findViewById(R.id.reloadButton);
|
|
530
|
+
reloadButton.setVisibility(View.VISIBLE);
|
|
531
|
+
reloadButton.setOnClickListener(
|
|
532
|
+
new View.OnClickListener() {
|
|
533
|
+
@Override
|
|
534
|
+
public void onClick(View view) {
|
|
535
|
+
_webView.reload();
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (TextUtils.equals(_options.getToolbarType(), "activity")) {
|
|
542
|
+
_toolbar.findViewById(R.id.forwardButton).setVisibility(View.GONE);
|
|
543
|
+
_toolbar.findViewById(R.id.backButton).setVisibility(View.GONE);
|
|
544
|
+
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
545
|
+
R.id.buttonNearDone
|
|
546
|
+
);
|
|
547
|
+
buttonNearDoneView.setVisibility(View.GONE);
|
|
548
|
+
//TODO: Add share button functionality
|
|
549
|
+
} else if (TextUtils.equals(_options.getToolbarType(), "navigation")) {
|
|
550
|
+
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
551
|
+
R.id.buttonNearDone
|
|
552
|
+
);
|
|
553
|
+
buttonNearDoneView.setVisibility(View.GONE);
|
|
554
|
+
//TODO: Remove share button when implemented
|
|
555
|
+
} else if (TextUtils.equals(_options.getToolbarType(), "blank")) {
|
|
556
|
+
_toolbar.setVisibility(View.GONE);
|
|
557
|
+
} else {
|
|
558
|
+
_toolbar.findViewById(R.id.forwardButton).setVisibility(View.GONE);
|
|
559
|
+
_toolbar.findViewById(R.id.backButton).setVisibility(View.GONE);
|
|
560
|
+
|
|
561
|
+
Options.ButtonNearDone buttonNearDone = _options.getButtonNearDone();
|
|
562
|
+
if (buttonNearDone != null) {
|
|
563
|
+
AssetManager assetManager = _context.getAssets();
|
|
564
|
+
|
|
565
|
+
// Open the SVG file from assets
|
|
566
|
+
InputStream inputStream = null;
|
|
567
|
+
try {
|
|
568
|
+
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
569
|
+
R.id.buttonNearDone
|
|
570
|
+
);
|
|
571
|
+
buttonNearDoneView.setVisibility(View.VISIBLE);
|
|
572
|
+
|
|
573
|
+
inputStream = assetManager.open(buttonNearDone.getIcon());
|
|
574
|
+
|
|
575
|
+
SVG svg = SVG.getFromInputStream(inputStream);
|
|
576
|
+
Picture picture = svg.renderToPicture(
|
|
577
|
+
buttonNearDone.getWidth(),
|
|
578
|
+
buttonNearDone.getHeight()
|
|
579
|
+
);
|
|
580
|
+
PictureDrawable pictureDrawable = new PictureDrawable(picture);
|
|
581
|
+
|
|
582
|
+
buttonNearDoneView.setImageDrawable(pictureDrawable);
|
|
583
|
+
buttonNearDoneView.setOnClickListener(view ->
|
|
584
|
+
_options.getCallbacks().buttonNearDoneClicked()
|
|
585
|
+
);
|
|
586
|
+
} catch (IOException | SVGParseException e) {
|
|
587
|
+
throw new RuntimeException(e);
|
|
588
|
+
} finally {
|
|
589
|
+
if (inputStream != null) {
|
|
590
|
+
try {
|
|
591
|
+
inputStream.close();
|
|
592
|
+
} catch (IOException e) {
|
|
593
|
+
throw new RuntimeException(e);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
} else {
|
|
598
|
+
ImageButton buttonNearDoneView = _toolbar.findViewById(
|
|
599
|
+
R.id.buttonNearDone
|
|
600
|
+
);
|
|
601
|
+
buttonNearDoneView.setVisibility(View.GONE);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
public void handleProxyResultError(String result, String id) {
|
|
607
|
+
Log.i(
|
|
608
|
+
"InAppBrowserProxy",
|
|
609
|
+
String.format(
|
|
610
|
+
"handleProxyResultError: %s, ok: %s id: %s",
|
|
611
|
+
result,
|
|
612
|
+
false,
|
|
613
|
+
id
|
|
614
|
+
)
|
|
615
|
+
);
|
|
616
|
+
ProxiedRequest proxiedRequest = proxiedRequestsHashmap.get(id);
|
|
617
|
+
if (proxiedRequest == null) {
|
|
618
|
+
Log.e("InAppBrowserProxy", "proxiedRequest is null");
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
proxiedRequestsHashmap.remove(id);
|
|
622
|
+
proxiedRequest.semaphore.release();
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
public void handleProxyResultOk(JSONObject result, String id) {
|
|
626
|
+
Log.i(
|
|
627
|
+
"InAppBrowserProxy",
|
|
628
|
+
String.format("handleProxyResultOk: %s, ok: %s, id: %s", result, true, id)
|
|
629
|
+
);
|
|
630
|
+
ProxiedRequest proxiedRequest = proxiedRequestsHashmap.get(id);
|
|
631
|
+
if (proxiedRequest == null) {
|
|
632
|
+
Log.e("InAppBrowserProxy", "proxiedRequest is null");
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
proxiedRequestsHashmap.remove(id);
|
|
636
|
+
|
|
637
|
+
if (result == null) {
|
|
638
|
+
proxiedRequest.semaphore.release();
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
Map<String, String> responseHeaders = new HashMap<>();
|
|
643
|
+
String body;
|
|
644
|
+
int code;
|
|
645
|
+
|
|
646
|
+
try {
|
|
647
|
+
body = result.getString("body");
|
|
648
|
+
code = result.getInt("code");
|
|
649
|
+
JSONObject headers = result.getJSONObject("headers");
|
|
650
|
+
for (Iterator<String> it = headers.keys(); it.hasNext();) {
|
|
651
|
+
String headerName = it.next();
|
|
652
|
+
String header = headers.getString(headerName);
|
|
653
|
+
responseHeaders.put(headerName, header);
|
|
654
|
+
}
|
|
655
|
+
} catch (JSONException e) {
|
|
656
|
+
Log.e("InAppBrowserProxy", "Cannot parse OK result", e);
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
String contentType = responseHeaders.get("Content-Type");
|
|
661
|
+
if (contentType == null) {
|
|
662
|
+
contentType = responseHeaders.get("content-type");
|
|
663
|
+
}
|
|
664
|
+
if (contentType == null) {
|
|
665
|
+
Log.e("InAppBrowserProxy", "'Content-Type' header is required");
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if (!((100 <= code && code <= 299) || (400 <= code && code <= 599))) {
|
|
670
|
+
Log.e(
|
|
671
|
+
"InAppBrowserProxy",
|
|
672
|
+
String.format("Status code %s outside of the allowed range", code)
|
|
673
|
+
);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
WebResourceResponse webResourceResponse = new WebResourceResponse(
|
|
678
|
+
contentType,
|
|
679
|
+
"utf-8",
|
|
680
|
+
new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8))
|
|
681
|
+
);
|
|
682
|
+
|
|
683
|
+
webResourceResponse.setStatusCodeAndReasonPhrase(
|
|
684
|
+
code,
|
|
685
|
+
getReasonPhrase(code)
|
|
686
|
+
);
|
|
687
|
+
proxiedRequest.response = webResourceResponse;
|
|
688
|
+
proxiedRequest.semaphore.release();
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
private void setWebViewClient() {
|
|
692
|
+
_webView.setWebViewClient(
|
|
693
|
+
new WebViewClient() {
|
|
694
|
+
@Override
|
|
695
|
+
public boolean shouldOverrideUrlLoading(
|
|
696
|
+
WebView view,
|
|
697
|
+
WebResourceRequest request
|
|
698
|
+
) {
|
|
699
|
+
// HashMap<String, String> map = new HashMap<>();
|
|
700
|
+
// map.put("x-requested-with", null);
|
|
701
|
+
// view.loadUrl(request.getUrl().toString(), map);
|
|
702
|
+
Context context = view.getContext();
|
|
703
|
+
String url = request.getUrl().toString();
|
|
704
|
+
|
|
705
|
+
if (!url.startsWith("https://") && !url.startsWith("http://")) {
|
|
706
|
+
try {
|
|
707
|
+
Intent intent;
|
|
708
|
+
if (url.startsWith("intent://")) {
|
|
709
|
+
intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
|
|
710
|
+
} else {
|
|
711
|
+
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
715
|
+
context.startActivity(intent);
|
|
716
|
+
return true;
|
|
717
|
+
} catch (ActivityNotFoundException e) {
|
|
718
|
+
// Do nothing
|
|
719
|
+
} catch (URISyntaxException e) {
|
|
720
|
+
// Do nothing
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return handlePotentialUniversalLink(url, context);
|
|
724
|
+
}
|
|
725
|
+
// Handle potential universal link
|
|
726
|
+
private boolean handlePotentialUniversalLink(String url, Context context) {
|
|
727
|
+
Log.e("Universal", "Enter: " + url);
|
|
728
|
+
Uri uri = Uri.parse(url);
|
|
729
|
+
|
|
730
|
+
// Let WebView handle other URLs
|
|
731
|
+
if (!isKnownAppLinkDomain(uri.getHost())) {
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
try {
|
|
736
|
+
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
|
737
|
+
intent.addCategory(Intent.CATEGORY_BROWSABLE);
|
|
738
|
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
739
|
+
|
|
740
|
+
try {
|
|
741
|
+
context.startActivity(intent); // make sure "context" exists in this class
|
|
742
|
+
return true;
|
|
743
|
+
} catch (ActivityNotFoundException e) {
|
|
744
|
+
// No app can handle it, let WebView handle
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
} catch (Exception e) {
|
|
748
|
+
Log.e("Universal", "Error with universal link: " + url, e);
|
|
749
|
+
return false;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
private boolean isKnownAppLinkDomain(String host) {
|
|
754
|
+
if (host == null) return false;
|
|
755
|
+
|
|
756
|
+
String[] knownDomains = new String[] {
|
|
757
|
+
"tngdigital.com.my"
|
|
758
|
+
// Add other partner domains
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
for (String d : knownDomains) {
|
|
762
|
+
if (host.endsWith(d)) return true;
|
|
763
|
+
}
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
private String randomRequestId() {
|
|
768
|
+
return UUID.randomUUID().toString();
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
private String toBase64(String raw) {
|
|
772
|
+
String s = Base64.encodeToString(raw.getBytes(), Base64.NO_WRAP);
|
|
773
|
+
if (s.endsWith("=")) {
|
|
774
|
+
s = s.substring(0, s.length() - 2);
|
|
775
|
+
}
|
|
776
|
+
return s;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
//
|
|
780
|
+
// void handleRedirect(String currentUrl, Response response) {
|
|
781
|
+
// String loc = response.header("Location");
|
|
782
|
+
// _webView.evaluateJavascript("");
|
|
783
|
+
// }
|
|
784
|
+
//
|
|
785
|
+
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
786
|
+
@Override
|
|
787
|
+
public WebResourceResponse shouldInterceptRequest(
|
|
788
|
+
WebView view,
|
|
789
|
+
WebResourceRequest request
|
|
790
|
+
) {
|
|
791
|
+
Pattern pattern = _options.getProxyRequestsPattern();
|
|
792
|
+
if (pattern == null) {
|
|
793
|
+
return null;
|
|
794
|
+
}
|
|
795
|
+
Matcher matcher = pattern.matcher(request.getUrl().toString());
|
|
796
|
+
if (!matcher.find()) {
|
|
797
|
+
return null;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// Requests matches the regex
|
|
801
|
+
if (Objects.equals(request.getMethod(), "POST")) {
|
|
802
|
+
// Log.e("HTTP", String.format("returned null (ok) %s", request.getUrl().toString()));
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
Log.i(
|
|
807
|
+
"InAppBrowserProxy",
|
|
808
|
+
String.format("Proxying request: %s", request.getUrl().toString())
|
|
809
|
+
);
|
|
810
|
+
|
|
811
|
+
// We need to call a JS function
|
|
812
|
+
String requestId = randomRequestId();
|
|
813
|
+
ProxiedRequest proxiedRequest = new ProxiedRequest();
|
|
814
|
+
proxiedRequestsHashmap.put(requestId, proxiedRequest);
|
|
815
|
+
|
|
816
|
+
// lsuakdchgbbaHandleProxiedRequest
|
|
817
|
+
activity.runOnUiThread(
|
|
818
|
+
new Runnable() {
|
|
819
|
+
@Override
|
|
820
|
+
public void run() {
|
|
821
|
+
StringBuilder headers = new StringBuilder();
|
|
822
|
+
Map<String, String> requestHeaders =
|
|
823
|
+
request.getRequestHeaders();
|
|
824
|
+
for (Map.Entry<
|
|
825
|
+
String,
|
|
826
|
+
String
|
|
827
|
+
> header : requestHeaders.entrySet()) {
|
|
828
|
+
headers.append(
|
|
829
|
+
String.format(
|
|
830
|
+
"h[atob('%s')]=atob('%s');",
|
|
831
|
+
toBase64(header.getKey()),
|
|
832
|
+
toBase64(header.getValue())
|
|
833
|
+
)
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
String s = String.format(
|
|
837
|
+
"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'})}",
|
|
838
|
+
headers,
|
|
839
|
+
toBase64(request.getUrl().toString()),
|
|
840
|
+
request.getMethod(),
|
|
841
|
+
requestId,
|
|
842
|
+
requestId,
|
|
843
|
+
requestId
|
|
844
|
+
);
|
|
845
|
+
// Log.i("HTTP", s);
|
|
846
|
+
capacitorWebView.evaluateJavascript(s, null);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
);
|
|
850
|
+
|
|
851
|
+
// 10 seconds wait max
|
|
852
|
+
try {
|
|
853
|
+
if (proxiedRequest.semaphore.tryAcquire(1, 10, TimeUnit.SECONDS)) {
|
|
854
|
+
return proxiedRequest.response;
|
|
855
|
+
} else {
|
|
856
|
+
Log.e("InAppBrowserProxy", "Semaphore timed out");
|
|
857
|
+
proxiedRequestsHashmap.remove(requestId); // prevent mem leak
|
|
858
|
+
}
|
|
859
|
+
} catch (InterruptedException e) {
|
|
860
|
+
Log.e("InAppBrowserProxy", "Semaphore wait error", e);
|
|
861
|
+
}
|
|
862
|
+
return null;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
@Override
|
|
866
|
+
public void onReceivedHttpAuthRequest(
|
|
867
|
+
WebView view,
|
|
868
|
+
HttpAuthHandler handler,
|
|
869
|
+
String host,
|
|
870
|
+
String realm
|
|
871
|
+
) {
|
|
872
|
+
final String sourceUrl = _options.getUrl();
|
|
873
|
+
final String url = view.getUrl();
|
|
874
|
+
final JSObject credentials = _options.getCredentials();
|
|
875
|
+
|
|
876
|
+
if (
|
|
877
|
+
credentials != null &&
|
|
878
|
+
credentials.getString("username") != null &&
|
|
879
|
+
credentials.getString("password") != null &&
|
|
880
|
+
sourceUrl != null &&
|
|
881
|
+
url != null
|
|
882
|
+
) {
|
|
883
|
+
String sourceProtocol = "";
|
|
884
|
+
String sourceHost = "";
|
|
885
|
+
int sourcePort = -1;
|
|
886
|
+
try {
|
|
887
|
+
URI uri = new URI(sourceUrl);
|
|
888
|
+
sourceProtocol = uri.getScheme();
|
|
889
|
+
sourceHost = uri.getHost();
|
|
890
|
+
sourcePort = uri.getPort();
|
|
891
|
+
if (
|
|
892
|
+
sourcePort == -1 && Objects.equals(sourceProtocol, "https")
|
|
893
|
+
) sourcePort = 443;
|
|
894
|
+
else if (
|
|
895
|
+
sourcePort == -1 && Objects.equals(sourceProtocol, "http")
|
|
896
|
+
) sourcePort = 80;
|
|
897
|
+
} catch (URISyntaxException e) {
|
|
898
|
+
e.printStackTrace();
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
String protocol = "";
|
|
902
|
+
int port = -1;
|
|
903
|
+
try {
|
|
904
|
+
URI uri = new URI(url);
|
|
905
|
+
protocol = uri.getScheme();
|
|
906
|
+
port = uri.getPort();
|
|
907
|
+
if (port == -1 && Objects.equals(protocol, "https")) port = 443;
|
|
908
|
+
else if (port == -1 && Objects.equals(protocol, "http")) port =
|
|
909
|
+
80;
|
|
910
|
+
} catch (URISyntaxException e) {
|
|
911
|
+
e.printStackTrace();
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
if (
|
|
915
|
+
Objects.equals(sourceHost, host) &&
|
|
916
|
+
Objects.equals(sourceProtocol, protocol) &&
|
|
917
|
+
sourcePort == port
|
|
918
|
+
) {
|
|
919
|
+
final String username = Objects.requireNonNull(
|
|
920
|
+
credentials.getString("username")
|
|
921
|
+
);
|
|
922
|
+
final String password = Objects.requireNonNull(
|
|
923
|
+
credentials.getString("password")
|
|
924
|
+
);
|
|
925
|
+
handler.proceed(username, password);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
super.onReceivedHttpAuthRequest(view, handler, host, realm);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
@Override
|
|
934
|
+
public void onLoadResource(WebView view, String url) {
|
|
935
|
+
super.onLoadResource(view, url);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
@Override
|
|
939
|
+
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
|
940
|
+
super.onPageStarted(view, url, favicon);
|
|
941
|
+
try {
|
|
942
|
+
URI uri = new URI(url);
|
|
943
|
+
if (TextUtils.isEmpty(_options.getTitle())) {
|
|
944
|
+
setTitle(uri.getHost());
|
|
945
|
+
}
|
|
946
|
+
} catch (URISyntaxException e) {
|
|
947
|
+
// Do nothing
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
public void doUpdateVisitedHistory(
|
|
952
|
+
WebView view,
|
|
953
|
+
String url,
|
|
954
|
+
boolean isReload
|
|
955
|
+
) {
|
|
956
|
+
if (!isReload) {
|
|
957
|
+
_options.getCallbacks().urlChangeEvent(url);
|
|
958
|
+
}
|
|
959
|
+
super.doUpdateVisitedHistory(view, url, isReload);
|
|
960
|
+
injectJavaScriptInterface();
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
@Override
|
|
964
|
+
public void onPageFinished(WebView view, String url) {
|
|
965
|
+
super.onPageFinished(view, url);
|
|
966
|
+
if (!isInitialized) {
|
|
967
|
+
isInitialized = true;
|
|
968
|
+
_webView.clearHistory();
|
|
969
|
+
if (_options.isPresentAfterPageLoad()) {
|
|
970
|
+
boolean usePreShowScript =
|
|
971
|
+
_options.getPreShowScript() != null &&
|
|
972
|
+
!_options.getPreShowScript().isEmpty();
|
|
973
|
+
if (!usePreShowScript) {
|
|
974
|
+
show();
|
|
975
|
+
_options.getPluginCall().resolve();
|
|
976
|
+
} else {
|
|
977
|
+
executorService.execute(
|
|
978
|
+
new Runnable() {
|
|
979
|
+
@Override
|
|
980
|
+
public void run() {
|
|
981
|
+
if (
|
|
982
|
+
_options.getPreShowScript() != null &&
|
|
983
|
+
!_options.getPreShowScript().isEmpty()
|
|
984
|
+
) {
|
|
985
|
+
injectPreShowScript();
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
activity.runOnUiThread(
|
|
989
|
+
new Runnable() {
|
|
990
|
+
@Override
|
|
991
|
+
public void run() {
|
|
992
|
+
show();
|
|
993
|
+
_options.getPluginCall().resolve();
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
} else if (
|
|
1003
|
+
_options.getPreShowScript() != null &&
|
|
1004
|
+
!_options.getPreShowScript().isEmpty()
|
|
1005
|
+
) {
|
|
1006
|
+
executorService.execute(
|
|
1007
|
+
new Runnable() {
|
|
1008
|
+
@Override
|
|
1009
|
+
public void run() {
|
|
1010
|
+
injectPreShowScript();
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
ImageButton backButton = _toolbar.findViewById(R.id.backButton);
|
|
1017
|
+
if (_webView.canGoBack()) {
|
|
1018
|
+
backButton.setImageResource(R.drawable.arrow_back_enabled);
|
|
1019
|
+
backButton.setEnabled(true);
|
|
1020
|
+
} else {
|
|
1021
|
+
backButton.setImageResource(R.drawable.arrow_back_disabled);
|
|
1022
|
+
backButton.setEnabled(false);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
ImageButton forwardButton = _toolbar.findViewById(R.id.forwardButton);
|
|
1026
|
+
if (_webView.canGoForward()) {
|
|
1027
|
+
forwardButton.setImageResource(R.drawable.arrow_forward_enabled);
|
|
1028
|
+
forwardButton.setEnabled(true);
|
|
1029
|
+
} else {
|
|
1030
|
+
forwardButton.setImageResource(R.drawable.arrow_forward_disabled);
|
|
1031
|
+
forwardButton.setEnabled(false);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
_options.getCallbacks().pageLoaded();
|
|
1035
|
+
injectJavaScriptInterface();
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
@Override
|
|
1039
|
+
public void onReceivedError(
|
|
1040
|
+
WebView view,
|
|
1041
|
+
WebResourceRequest request,
|
|
1042
|
+
WebResourceError error
|
|
1043
|
+
) {
|
|
1044
|
+
super.onReceivedError(view, request, error);
|
|
1045
|
+
_options.getCallbacks().pageLoadError();
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
@SuppressLint("WebViewClientOnReceivedSslError")
|
|
1049
|
+
@Override
|
|
1050
|
+
public void onReceivedSslError(
|
|
1051
|
+
WebView view,
|
|
1052
|
+
SslErrorHandler handler,
|
|
1053
|
+
SslError error
|
|
1054
|
+
) {
|
|
1055
|
+
boolean ignoreSSLUntrustedError = _options.ignoreUntrustedSSLError();
|
|
1056
|
+
if (
|
|
1057
|
+
ignoreSSLUntrustedError &&
|
|
1058
|
+
error.getPrimaryError() == SslError.SSL_UNTRUSTED
|
|
1059
|
+
) handler.proceed();
|
|
1060
|
+
else {
|
|
1061
|
+
super.onReceivedSslError(view, handler, error);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
@Override
|
|
1069
|
+
public void onBackPressed() {
|
|
1070
|
+
if (
|
|
1071
|
+
_webView.canGoBack() &&
|
|
1072
|
+
(TextUtils.equals(_options.getToolbarType(), "navigation") ||
|
|
1073
|
+
_options.getActiveNativeNavigationForWebview())
|
|
1074
|
+
) {
|
|
1075
|
+
_webView.goBack();
|
|
1076
|
+
} else if (!_options.getDisableGoBackOnNativeApplication()) {
|
|
1077
|
+
super.onBackPressed();
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
public static String getReasonPhrase(int statusCode) {
|
|
1082
|
+
switch (statusCode) {
|
|
1083
|
+
case (200):
|
|
1084
|
+
return "OK";
|
|
1085
|
+
case (201):
|
|
1086
|
+
return "Created";
|
|
1087
|
+
case (202):
|
|
1088
|
+
return "Accepted";
|
|
1089
|
+
case (203):
|
|
1090
|
+
return "Non Authoritative Information";
|
|
1091
|
+
case (204):
|
|
1092
|
+
return "No Content";
|
|
1093
|
+
case (205):
|
|
1094
|
+
return "Reset Content";
|
|
1095
|
+
case (206):
|
|
1096
|
+
return "Partial Content";
|
|
1097
|
+
case (207):
|
|
1098
|
+
return "Partial Update OK";
|
|
1099
|
+
case (300):
|
|
1100
|
+
return "Mutliple Choices";
|
|
1101
|
+
case (301):
|
|
1102
|
+
return "Moved Permanently";
|
|
1103
|
+
case (302):
|
|
1104
|
+
return "Moved Temporarily";
|
|
1105
|
+
case (303):
|
|
1106
|
+
return "See Other";
|
|
1107
|
+
case (304):
|
|
1108
|
+
return "Not Modified";
|
|
1109
|
+
case (305):
|
|
1110
|
+
return "Use Proxy";
|
|
1111
|
+
case (307):
|
|
1112
|
+
return "Temporary Redirect";
|
|
1113
|
+
case (400):
|
|
1114
|
+
return "Bad Request";
|
|
1115
|
+
case (401):
|
|
1116
|
+
return "Unauthorized";
|
|
1117
|
+
case (402):
|
|
1118
|
+
return "Payment Required";
|
|
1119
|
+
case (403):
|
|
1120
|
+
return "Forbidden";
|
|
1121
|
+
case (404):
|
|
1122
|
+
return "Not Found";
|
|
1123
|
+
case (405):
|
|
1124
|
+
return "Method Not Allowed";
|
|
1125
|
+
case (406):
|
|
1126
|
+
return "Not Acceptable";
|
|
1127
|
+
case (407):
|
|
1128
|
+
return "Proxy Authentication Required";
|
|
1129
|
+
case (408):
|
|
1130
|
+
return "Request Timeout";
|
|
1131
|
+
case (409):
|
|
1132
|
+
return "Conflict";
|
|
1133
|
+
case (410):
|
|
1134
|
+
return "Gone";
|
|
1135
|
+
case (411):
|
|
1136
|
+
return "Length Required";
|
|
1137
|
+
case (412):
|
|
1138
|
+
return "Precondition Failed";
|
|
1139
|
+
case (413):
|
|
1140
|
+
return "Request Entity Too Large";
|
|
1141
|
+
case (414):
|
|
1142
|
+
return "Request-URI Too Long";
|
|
1143
|
+
case (415):
|
|
1144
|
+
return "Unsupported Media Type";
|
|
1145
|
+
case (416):
|
|
1146
|
+
return "Requested Range Not Satisfiable";
|
|
1147
|
+
case (417):
|
|
1148
|
+
return "Expectation Failed";
|
|
1149
|
+
case (418):
|
|
1150
|
+
return "Reauthentication Required";
|
|
1151
|
+
case (419):
|
|
1152
|
+
return "Proxy Reauthentication Required";
|
|
1153
|
+
case (422):
|
|
1154
|
+
return "Unprocessable Entity";
|
|
1155
|
+
case (423):
|
|
1156
|
+
return "Locked";
|
|
1157
|
+
case (424):
|
|
1158
|
+
return "Failed Dependency";
|
|
1159
|
+
case (500):
|
|
1160
|
+
return "Server Error";
|
|
1161
|
+
case (501):
|
|
1162
|
+
return "Not Implemented";
|
|
1163
|
+
case (502):
|
|
1164
|
+
return "Bad Gateway";
|
|
1165
|
+
case (503):
|
|
1166
|
+
return "Service Unavailable";
|
|
1167
|
+
case (504):
|
|
1168
|
+
return "Gateway Timeout";
|
|
1169
|
+
case (505):
|
|
1170
|
+
return "HTTP Version Not Supported";
|
|
1171
|
+
case (507):
|
|
1172
|
+
return "Insufficient Storage";
|
|
1173
|
+
default:
|
|
1174
|
+
return "";
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|