UNPKG

@nota/nativescript-webview-ext

Version:

Extended WebView for NativeScript which adds 'x-local' scheme for local-files. events between WebView and native-layer, javascript execution, injecting CSS and JS-files.

682 lines 82.8 kB
import "@nativescript/core"; import { File, knownFolders, Trace } from "@nativescript/core"; import { isEnabledProperty } from "@nativescript/core/ui/core/view"; import { builtInZoomControlsProperty, cacheModeProperty, databaseStorageProperty, debugModeProperty, displayZoomControlsProperty, domStorageProperty, supportZoomProperty, UnsupportedSDKError, WebViewExtBase, } from "./webview-ext-common"; export * from "./webview-ext-common"; const extToMimeType = new Map([ ["html", "text/html"], ["htm", "text/html"], ["xhtml", "text/html"], ["xhtm", "text/html"], ["css", "text/css"], ["gif", "image/gif"], ["jpeg", "image/jpeg"], ["jpg", "image/jpeg"], ["js", "text/javascript"], ["otf", "application/vnd.ms-opentype"], ["png", "image/png"], ["svg", "image/svg+xml"], ["ttf", "application/x-font-ttf"], ]); const extToBinaryEncoding = new Set(["gif", "jpeg", "jpg", "otf", "png", "ttf"]); let cacheModeMap; let WebViewExtClient; let WebChromeViewExtClient; let WebViewBridgeInterface; function initializeWebViewClient() { if (WebViewExtClient) { return; } cacheModeMap = new Map([ ["cache_first", android.webkit.WebSettings.LOAD_CACHE_ELSE_NETWORK], ["cache_only", android.webkit.WebSettings.LOAD_CACHE_ONLY], ["default", android.webkit.WebSettings.LOAD_DEFAULT], ["no_cache", android.webkit.WebSettings.LOAD_NO_CACHE], ["normal", android.webkit.WebSettings.LOAD_NORMAL], ]); var WebViewExtClientImpl = /** @class */ (function (_super) { __extends(WebViewExtClientImpl, _super); function WebViewExtClientImpl(owner) { var _this = _super.call(this) || this; _this.owner = new WeakRef(owner); return global.__native(_this); } /** * Give the host application a chance to take control when a URL is about to be loaded in the current WebView. */ WebViewExtClientImpl.prototype.shouldOverrideUrlLoading = function (view, request) { var owner = this.owner.get(); if (!owner) { console.warn("WebViewExtClientImpl.shouldOverrideUrlLoading(...) - no owner"); return true; } var url = request; var httpMethod = "GET"; var isRedirect = false; var hasGesture = false; var isForMainFrame = false; var requestHeaders = null; if (typeof request === "object") { httpMethod = request.getMethod(); isRedirect = request.isRedirect(); hasGesture = request.hasGesture(); isForMainFrame = request.isForMainFrame(); requestHeaders = request.getRequestHeaders(); url = request.getUrl().toString(); } owner.writeTrace("WebViewClientClass.shouldOverrideUrlLoading(\"" + url + "\") - method:" + httpMethod + " isRedirect:" + isRedirect + " hasGesture:" + hasGesture + " isForMainFrame:" + isForMainFrame + " headers:" + requestHeaders); if (url.startsWith(owner.interceptScheme)) { owner.writeTrace("WebViewClientClass.shouldOverrideUrlLoading(\"" + url + "\") - \"" + owner.interceptScheme + "\" - cancel"); return true; } var shouldOverrideUrlLoading = owner._onShouldOverrideUrlLoading(url, httpMethod); if (shouldOverrideUrlLoading === true) { owner.writeTrace("WebViewClientClass.shouldOverrideUrlLoading(\"" + url + "\") - cancel loading url"); return true; } return false; }; WebViewExtClientImpl.prototype.shouldInterceptRequest = function (view, request) { var owner = this.owner.get(); if (!owner) { console.warn("WebViewExtClientImpl.shouldInterceptRequest(...) - no owner"); return _super.prototype.shouldInterceptRequest.call(this, view, request); } var url; if (typeof request === "string") { url = request; } else if (typeof request === "object") { url = request.getUrl().toString(); } if (typeof url !== "string") { owner.writeTrace("WebViewClientClass.shouldInterceptRequest(\"" + url + "\") - is not a string"); return _super.prototype.shouldInterceptRequest.call(this, view, request); } if (!url.startsWith(owner.interceptScheme)) { return _super.prototype.shouldInterceptRequest.call(this, view, request); } var filepath = owner.getRegisteredLocalResource(url); if (!filepath) { owner.writeTrace("WebViewClientClass.shouldInterceptRequest(\"" + url + "\") - no matching file"); return _super.prototype.shouldInterceptRequest.call(this, view, request); } if (!File.exists(filepath)) { owner.writeTrace("WebViewClientClass.shouldInterceptRequest(\"" + url + "\") - file: \"" + filepath + "\" doesn't exists"); return _super.prototype.shouldInterceptRequest.call(this, view, request); } var tnsFile = File.fromPath(filepath); var javaFile = new java.io.File(tnsFile.path); var stream = new java.io.FileInputStream(javaFile); var ext = tnsFile.extension.substr(1).toLowerCase(); var mimeType = extToMimeType.get(ext) || "application/octet-stream"; var encoding = extToBinaryEncoding.has(ext) || mimeType === "application/octet-stream" ? "binary" : "UTF-8"; owner.writeTrace("WebViewClientClass.shouldInterceptRequest(\"" + url + "\") - file: \"" + filepath + "\" mimeType:" + mimeType + " encoding:" + encoding); var response = new android.webkit.WebResourceResponse(mimeType, encoding, stream); if (android.os.Build.VERSION.SDK_INT < 21 || !response.getResponseHeaders) { return response; } var responseHeaders = response.getResponseHeaders(); if (!responseHeaders) { responseHeaders = new java.util.HashMap(); } responseHeaders.put("Access-Control-Allow-Origin", "*"); response.setResponseHeaders(responseHeaders); return response; }; WebViewExtClientImpl.prototype.onPageStarted = function (view, url, favicon) { _super.prototype.onPageStarted.call(this, view, url, favicon); var owner = this.owner.get(); if (!owner) { console.warn("WebViewExtClientImpl.onPageStarted(\"" + view + "\", \"" + url + "\", \"" + favicon + "\") - no owner"); return; } owner.writeTrace("WebViewClientClass.onPageStarted(\"" + view + "\", \"" + url + "\", \"" + favicon + "\")"); owner._onLoadStarted(url); }; WebViewExtClientImpl.prototype.onPageFinished = function (view, url) { _super.prototype.onPageFinished.call(this, view, url); var owner = this.owner.get(); if (!owner) { console.warn("WebViewExtClientImpl.onPageFinished(\"" + view + "\", " + url + "\") - no owner"); return; } owner.writeTrace("WebViewClientClass.onPageFinished(\"" + view + "\", " + url + "\")"); owner._onLoadFinished(url).catch(function () { return void 0; }); }; WebViewExtClientImpl.prototype.onReceivedError = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (args.length === 4) { var _a = args, view = _a[0], errorCode = _a[1], description = _a[2], failingUrl = _a[3]; this.onReceivedErrorBeforeAPI23(view, errorCode, description, failingUrl); } else { var _b = args, view = _b[0], request = _b[1], error = _b[2]; this.onReceivedErrorAPI23(view, request, error); } }; WebViewExtClientImpl.prototype.onReceivedErrorAPI23 = function (view, request, error) { _super.prototype.onReceivedError.call(this, view, request, error); var owner = this.owner.get(); if (!owner) { console.warn("WebViewExtClientImpl.onReceivedErrorAPI23(...) - no owner"); return; } var url = error.getUrl && error.getUrl(); if (!url && typeof request === "object") { url = request.getUrl().toString(); } owner.writeTrace("WebViewClientClass.onReceivedErrorAPI23(" + error.getErrorCode() + ", " + error.getDescription() + ", " + url + ")"); owner._onLoadFinished(url, error.getDescription() + "(" + error.getErrorCode() + ")").catch(function () { return void 0; }); }; WebViewExtClientImpl.prototype.onReceivedErrorBeforeAPI23 = function (view, errorCode, description, failingUrl) { _super.prototype.onReceivedError.call(this, view, errorCode, description, failingUrl); var owner = this.owner.get(); if (!owner) { console.warn("WebViewExtClientImpl.onReceivedErrorBeforeAPI23(...) - no owner"); return; } owner.writeTrace("WebViewClientClass.onReceivedErrorBeforeAPI23(" + errorCode + ", \"" + description + "\", \"" + failingUrl + "\")"); owner._onLoadFinished(failingUrl, description + "(" + errorCode + ")").catch(function () { return void 0; }); }; return WebViewExtClientImpl; }(android.webkit.WebViewClient)); WebViewExtClient = WebViewExtClientImpl; var WebChromeViewExtClientImpl = /** @class */ (function (_super) { __extends(WebChromeViewExtClientImpl, _super); function WebChromeViewExtClientImpl(owner) { var _this = _super.call(this) || this; _this.owner = new WeakRef(owner); return global.__native(_this); } WebChromeViewExtClientImpl.prototype.onShowCustomView = function (view) { var _this = this; var owner = this.owner.get(); if (!owner) { return; } var callback; if (arguments.length === 3) { callback = arguments[2]; } else if (arguments.length === 2) { callback = arguments[1]; } else { return; } if (owner._onEnterFullscreen(function () { return _this.hideCustomView(); })) { this.showCustomViewCallback = callback; } else { callback.onCustomViewHidden(); } }; WebChromeViewExtClientImpl.prototype.hideCustomView = function () { if (this.showCustomViewCallback) { this.showCustomViewCallback.onCustomViewHidden(); } this.showCustomViewCallback = undefined; }; WebChromeViewExtClientImpl.prototype.onHideCustomView = function () { this.showCustomViewCallback = undefined; var owner = this.owner.get(); if (!owner) { return; } owner._onExitFullscreen(); }; WebChromeViewExtClientImpl.prototype.onProgressChanged = function (view, newProgress) { var owner = this.owner.get(); if (!owner) { return; } owner._loadProgress(newProgress); }; WebChromeViewExtClientImpl.prototype.onReceivedTitle = function (view, title) { var owner = this.owner.get(); if (!owner) { return; } owner._titleChanged(title); }; WebChromeViewExtClientImpl.prototype.onJsAlert = function (view, url, message, result) { var owner = this.owner.get(); if (!owner) { return false; } var gotResponse = false; return owner._webAlert(message, function () { if (!gotResponse) { result.confirm(); } gotResponse = true; }); }; WebChromeViewExtClientImpl.prototype.onJsConfirm = function (view, url, message, result) { var owner = this.owner.get(); if (!owner) { return false; } var gotResponse = false; return owner._webConfirm(message, function (confirmed) { if (!gotResponse) { if (confirmed) { result.confirm(); } else { result.cancel(); } } gotResponse = true; }); }; WebChromeViewExtClientImpl.prototype.onJsPrompt = function (view, url, message, defaultValue, result) { var owner = this.owner.get(); if (!owner) { return false; } var gotResponse = false; return owner._webPrompt(message, defaultValue, function (message) { if (!gotResponse) { if (message) { result.confirm(message); } else { result.confirm(); } } gotResponse = true; }); }; WebChromeViewExtClientImpl.prototype.onConsoleMessage = function () { if (arguments.length !== 1) { return false; } var owner = this.owner.get(); if (!owner) { return false; } var consoleMessage = arguments[0]; if (consoleMessage instanceof android.webkit.ConsoleMessage) { var message = consoleMessage.message(); var lineNo = consoleMessage.lineNumber(); var level = "log"; var _a = android.webkit.ConsoleMessage.MessageLevel, DEBUG = _a.DEBUG, LOG = _a.LOG, WARNING = _a.WARNING; switch (consoleMessage.messageLevel()) { case DEBUG: { level = "debug"; break; } case LOG: { level = "log"; break; } case WARNING: { level = "warn"; break; } } return owner._webConsole(message, lineNo, level); } return false; }; return WebChromeViewExtClientImpl; }(android.webkit.WebChromeClient)); WebChromeViewExtClient = WebChromeViewExtClientImpl; var WebViewBridgeInterfaceImpl = /** @class */ (function (_super) { __extends(WebViewBridgeInterfaceImpl, _super); function WebViewBridgeInterfaceImpl(owner) { var _this = _super.call(this) || this; _this.owner = new WeakRef(owner); return global.__native(_this); } WebViewBridgeInterfaceImpl.prototype.emitEventToNativeScript = function (eventName, data) { var owner = this.owner.get(); if (!owner) { console.warn("WebViewExtClientImpl.emitEventToNativeScript(\"" + eventName + "\") - no owner"); return; } try { if (typeof data == "string" && data) { owner.onWebViewEvent(eventName, JSON.parse(data)); return; } owner.onWebViewEvent(eventName, null); return; } catch (err) { owner.writeTrace("WebViewExtClientImpl.emitEventToNativeScript(\"" + eventName + "\") - couldn't parse data: " + JSON.stringify(data) + " err: " + err); } }; return WebViewBridgeInterfaceImpl; }(dk.nota.webviewinterface.WebViewBridgeInterface)); WebViewBridgeInterface = WebViewBridgeInterfaceImpl; } let instanceNo = 0; export class WebViewExt extends WebViewExtBase { constructor() { super(...arguments); this.localResourceMap = new Map(); this.supportXLocalScheme = true; this.instance = ++instanceNo; } createNativeView() { const nativeView = new android.webkit.WebView(this._context); const settings = nativeView.getSettings(); settings.setJavaScriptEnabled(true); settings.setAllowFileAccess(true); settings.setBuiltInZoomControls(!!this.builtInZoomControls); settings.setDisplayZoomControls(!!this.displayZoomControls); settings.setSupportZoom(!!this.supportZoom); if (android.os.Build.VERSION.SDK_INT >= 21) { settings.setMixedContentMode(android.webkit.WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); } settings.setAllowUniversalAccessFromFileURLs(true); return nativeView; } initNativeView() { super.initNativeView(); initializeWebViewClient(); const nativeView = this.nativeViewProtected; if (!nativeView) { return; } const client = new WebViewExtClient(this); const chromeClient = new WebChromeViewExtClient(this); nativeView.setWebViewClient(client); nativeView.client = client; nativeView.setWebChromeClient(chromeClient); nativeView.chromeClient = chromeClient; const bridgeInterface = new WebViewBridgeInterface(this); nativeView.addJavascriptInterface(bridgeInterface, "androidWebViewBridge"); nativeView.bridgeInterface = bridgeInterface; } disposeNativeView() { const nativeView = this.nativeViewProtected; if (nativeView) { nativeView.client = null; nativeView.chromeClient = null; nativeView.destroy(); } super.disposeNativeView(); } async ensurePromiseSupport() { if (android.os.Build.VERSION.SDK_INT >= 21) { return; } return await super.ensurePromiseSupport(); } _loadUrl(src) { const nativeView = this.nativeViewProtected; if (!nativeView) { return; } this.writeTrace(`WebViewExt<android>._loadUrl("${src}")`); nativeView.loadUrl(src); this.writeTrace(`WebViewExt<android>._loadUrl("${src}") - end`); } _loadData(src) { const nativeView = this.nativeViewProtected; if (!nativeView) { return; } const baseUrl = `file:///${knownFolders.currentApp().path}/`; this.writeTrace(`WebViewExt<android>._loadData("${src}") -> baseUrl: "${baseUrl}"`); nativeView.loadDataWithBaseURL(baseUrl, src, "text/html", "utf-8", null); } get canGoBack() { const nativeView = this.nativeViewProtected; if (nativeView) { return nativeView.canGoBack(); } return false; } stopLoading() { const nativeView = this.nativeViewProtected; if (nativeView) { nativeView.stopLoading(); } } get canGoForward() { const nativeView = this.nativeViewProtected; if (nativeView) { return nativeView.canGoForward(); } return false; } goBack() { const nativeView = this.nativeViewProtected; if (nativeView) { return nativeView.goBack(); } } goForward() { const nativeView = this.nativeViewProtected; if (nativeView) { return nativeView.goForward(); } } reload() { const nativeView = this.nativeViewProtected; if (nativeView) { return nativeView.reload(); } } registerLocalResource(resourceName, path) { resourceName = this.fixLocalResourceName(resourceName); const filepath = this.resolveLocalResourceFilePath(path); if (!filepath) { this.writeTrace(`WebViewExt<android>.registerLocalResource("${resourceName}", "${path}") -> file doesn't exist`, Trace.messageType.error); return; } this.writeTrace(`WebViewExt<android>.registerLocalResource("${resourceName}", "${path}") -> file: "${filepath}"`); this.localResourceMap.set(resourceName, filepath); } unregisterLocalResource(resourceName) { this.writeTrace(`WebViewExt<android>.unregisterLocalResource("${resourceName}")`); resourceName = this.fixLocalResourceName(resourceName); this.localResourceMap.delete(resourceName); } getRegisteredLocalResource(resourceName) { resourceName = this.fixLocalResourceName(resourceName); const result = this.localResourceMap.get(resourceName); this.writeTrace(`WebViewExt<android>.getRegisteredLocalResource("${resourceName}") => "${result}"`); return result; } async ensureFetchSupport() { this.writeTrace("WebViewExt<android>.ensureFetchSupport() - Override 'Fetch API' to support x-local."); await this.executeJavaScript(` try { window.fetch = null; } catch (err) { console.error("null'ing Native Fetch API failed:", err); } `); await this.loadFetchPolyfill(); } async executeJavaScript(scriptCode) { if (android.os.Build.VERSION.SDK_INT < 19) { this.writeTrace(`WebViewExt<android>.executeJavaScript() -> SDK:${android.os.Build.VERSION.SDK_INT} not supported`, Trace.messageType.error); return Promise.reject(new UnsupportedSDKError(19)); } const result = await new Promise((resolve, reject) => { const androidWebView = this.nativeViewProtected; if (!androidWebView) { this.writeTrace(`WebViewExt<android>.executeJavaScript() -> no nativeView?`, Trace.messageType.error); reject(new Error("Native Android not initialized, cannot call executeJavaScript")); return; } androidWebView.evaluateJavascript(scriptCode, new android.webkit.ValueCallback({ onReceiveValue(result) { resolve(result); }, })); }); return await this.parseWebViewJavascriptResult(result); } async getTitle() { return this.nativeViewProtected && this.nativeViewProtected.getTitle(); } zoomIn() { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return false; } return androidWebView.zoomIn(); } zoomOut() { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return false; } return androidWebView.zoomOut(); } zoomBy(zoomFactor) { if (android.os.Build.VERSION.SDK_INT < 21) { this.writeTrace(`WebViewExt<android>.zoomBy - not supported on this SDK`); return; } if (!this.nativeViewProtected) { return; } if (zoomFactor >= 0.01 && zoomFactor <= 100) { return this.nativeViewProtected.zoomBy(zoomFactor); } throw new Error(`ZoomBy only accepts values between 0.01 and 100 both inclusive`); } [debugModeProperty.getDefault]() { return false; } [debugModeProperty.setNative](enabled) { android.webkit.WebView.setWebContentsDebuggingEnabled(!!enabled); } [builtInZoomControlsProperty.getDefault]() { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return false; } const settings = androidWebView.getSettings(); return settings.getBuiltInZoomControls(); } [builtInZoomControlsProperty.setNative](enabled) { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return; } const settings = androidWebView.getSettings(); settings.setBuiltInZoomControls(!!enabled); } [displayZoomControlsProperty.getDefault]() { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return false; } const settings = androidWebView.getSettings(); return settings.getDisplayZoomControls(); } [displayZoomControlsProperty.setNative](enabled) { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return; } const settings = androidWebView.getSettings(); settings.setDisplayZoomControls(!!enabled); } [cacheModeProperty.getDefault]() { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return null; } const settings = androidWebView.getSettings(); const cacheModeInt = settings.getCacheMode(); for (const [key, value] of cacheModeMap) { if (value === cacheModeInt) { return key; } } return null; } [cacheModeProperty.setNative](cacheMode) { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return; } const settings = androidWebView.getSettings(); for (const [key, nativeValue] of cacheModeMap) { if (key === cacheMode) { settings.setCacheMode(nativeValue); return; } } } [databaseStorageProperty.getDefault]() { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return false; } const settings = androidWebView.getSettings(); return settings.getDatabaseEnabled(); } [databaseStorageProperty.setNative](enabled) { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return; } const settings = androidWebView.getSettings(); settings.setDatabaseEnabled(!!enabled); } [domStorageProperty.getDefault]() { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return false; } const settings = androidWebView.getSettings(); return settings.getDomStorageEnabled(); } [domStorageProperty.setNative](enabled) { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return; } const settings = androidWebView.getSettings(); settings.setDomStorageEnabled(!!enabled); } [supportZoomProperty.getDefault]() { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return false; } const settings = androidWebView.getSettings(); return settings.supportZoom(); } [supportZoomProperty.setNative](enabled) { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return; } const settings = androidWebView.getSettings(); settings.setSupportZoom(!!enabled); } [isEnabledProperty.setNative](enabled) { const androidWebView = this.nativeViewProtected; if (!androidWebView) { return; } if (enabled) { androidWebView.setOnTouchListener(null); } else { androidWebView.setOnTouchListener(new android.view.View.OnTouchListener({ onTouch() { return true; }, })); } } } WebViewExt.supportXLocalScheme = true; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2Vidmlldy1leHQuYW5kcm9pZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIndlYnZpZXctZXh0LmFuZHJvaWQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsT0FBTyxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUMvRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNwRSxPQUFPLEVBQ0gsMkJBQTJCLEVBRTNCLGlCQUFpQixFQUNqQix1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLDJCQUEyQixFQUMzQixrQkFBa0IsRUFDbEIsbUJBQW1CLEVBQ25CLG1CQUFtQixFQUNuQixjQUFjLEdBQ2pCLE1BQU0sc0JBQXNCLENBQUM7QUFFOUIsY0FBYyxzQkFBc0IsQ0FBQztBQUVyQyxNQUFNLGFBQWEsR0FBRyxJQUFJLEdBQUcsQ0FBaUI7SUFDMUMsQ0FBQyxNQUFNLEVBQUUsV0FBVyxDQUFDO0lBQ3JCLENBQUMsS0FBSyxFQUFFLFdBQVcsQ0FBQztJQUNwQixDQUFDLE9BQU8sRUFBRSxXQUFXLENBQUM7SUFDdEIsQ0FBQyxNQUFNLEVBQUUsV0FBVyxDQUFDO0lBQ3JCLENBQUMsS0FBSyxFQUFFLFVBQVUsQ0FBQztJQUNuQixDQUFDLEtBQUssRUFBRSxXQUFXLENBQUM7SUFDcEIsQ0FBQyxNQUFNLEVBQUUsWUFBWSxDQUFDO0lBQ3RCLENBQUMsS0FBSyxFQUFFLFlBQVksQ0FBQztJQUNyQixDQUFDLElBQUksRUFBRSxpQkFBaUIsQ0FBQztJQUN6QixDQUFDLEtBQUssRUFBRSw2QkFBNkIsQ0FBQztJQUN0QyxDQUFDLEtBQUssRUFBRSxXQUFXLENBQUM7SUFDcEIsQ0FBQyxLQUFLLEVBQUUsZUFBZSxDQUFDO0lBQ3hCLENBQUMsS0FBSyxFQUFFLHdCQUF3QixDQUFDO0NBQ3BDLENBQUMsQ0FBQztBQUVILE1BQU0sbUJBQW1C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QUFHLGNBQWMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUM5QyxRQUFRLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN2QyxDQUFDO0lBRUQsQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLENBQUMsQ0FBQyxPQUFnQjtRQUMxQyxNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUM7UUFDaEQsSUFBSSxDQUFDLGNBQWMsRUFBRTtZQUNqQixPQUFPO1NBQ1Y7UUFFRCxJQUFJLE9BQU8sRUFBRTtZQUNULGNBQWMsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFLLENBQUMsQ0FBQztTQUM1QzthQUFNO1lBQ0gsY0FBYyxDQUFDLGtCQUFrQixDQUM3QixJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQztnQkFDbEMsT0FBTztvQkFDSCxPQUFPLElBQUksQ0FBQztnQkFDaEIsQ0FBQzthQUNKLENBQUMsQ0FDTCxDQUFDO1NBQ0w7SUFDTCxDQUFDOztBQS9hYSw4QkFBbUIsR0FBRyxJQUFJLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLy8gPHJlZmVyZW5jZSBwYXRoPVwiLi90eXBlcy9hbmRyb2lkL3dlYnZpZXdpbnRlcmZhY2UuZC50c1wiIC8+XG5cbmltcG9ydCBcIkBuYXRpdmVzY3JpcHQvY29yZVwiO1xuaW1wb3J0IHsgRmlsZSwga25vd25Gb2xkZXJzLCBUcmFjZSB9IGZyb20gXCJAbmF0aXZlc2NyaXB0L2NvcmVcIjtcbmltcG9ydCB7IGlzRW5hYmxlZFByb3BlcnR5IH0gZnJvbSBcIkBuYXRpdmVzY3JpcHQvY29yZS91aS9jb3JlL3ZpZXdcIjtcbmltcG9ydCB7XG4gICAgYnVpbHRJblpvb21Db250cm9sc1Byb3BlcnR5LFxuICAgIENhY2hlTW9kZSxcbiAgICBjYWNoZU1vZGVQcm9wZXJ0eSxcbiAgICBkYXRhYmFzZVN0b3JhZ2VQcm9wZXJ0eSxcbiAgICBkZWJ1Z01vZGVQcm9wZXJ0eSxcbiAgICBkaXNwbGF5Wm9vbUNvbnRyb2xzUHJvcGVydHksXG4gICAgZG9tU3RvcmFnZVByb3BlcnR5LFxuICAgIHN1cHBvcnRab29tUHJvcGVydHksXG4gICAgVW5zdXBwb3J0ZWRTREtFcnJvcixcbiAgICBXZWJWaWV3RXh0QmFzZSxcbn0gZnJvbSBcIi4vd2Vidmlldy1leHQtY29tbW9uXCI7XG5cbmV4cG9ydCAqIGZyb20gXCIuL3dlYnZpZXctZXh0LWNvbW1vblwiO1xuXG5jb25zdCBleHRUb01pbWVUeXBlID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZz4oW1xuICAgIFtcImh0bWxcIiwgXCJ0ZXh0L2h0bWxcIl0sXG4gICAgW1wiaHRtXCIsIFwidGV4dC9odG1sXCJdLFxuICAgIFtcInhodG1sXCIsIFwidGV4dC9odG1sXCJdLFxuICAgIFtcInhodG1cIiwgXCJ0ZXh0L2h0bWxcIl0sXG4gICAgW1wiY3NzXCIsIFwidGV4dC9jc3NcIl0sXG4gICAgW1wiZ2lmXCIsIFwiaW1hZ2UvZ2lmXCJdLFxuICAgIFtcImpwZWdcIiwgXCJpbWFnZS9qcGVnXCJdLFxuICAgIFtcImpwZ1wiLCBcImltYWdlL2pwZWdcIl0sXG4gICAgW1wianNcIiwgXCJ0ZXh0L2phdmFzY3JpcHRcIl0sXG4gICAgW1wib3RmXCIsIFwiYXBwbGljYXRpb24vdm5kLm1zLW9wZW50eXBlXCJdLFxuICAgIFtcInBuZ1wiLCBcImltYWdlL3BuZ1wiXSxcbiAgICBbXCJzdmdcIiwgXCJpbWFnZS9zdmcreG1sXCJdLFxuICAgIFtcInR0ZlwiLCBcImFwcGxpY2F0aW9uL3gtZm9udC10dGZcIl0sXG5dKTtcblxuY29uc3QgZXh0VG9CaW5hcnlFbmNvZGluZyA9IG5ldyBTZXQ8c3RyaW5nPihbXCJnaWZcIiwgXCJqcGVnXCIsIFwianBnXCIsIFwib3RmXCIsIFwicG5nXCIsIFwidHRmXCJdKTtcblxuLy8jcmVnaW9uIGFuZHJvaWRfbmF0aXZlX2NsYXNzZXNcbmxldCBjYWNoZU1vZGVNYXA6IE1hcDxDYWNoZU1vZGUsIG51bWJlcj47XG5cbmV4cG9ydCBpbnRlcmZhY2UgQW5kcm9pZFdlYlZpZXdDbGllbnQgZXh0ZW5kcyBhbmRyb2lkLndlYmtpdC5XZWJWaWV3Q2xpZW50IHt9XG5cbmV4cG9ydCBpbnRlcmZhY2UgQW5kcm9pZFdlYlZpZXcgZXh0ZW5kcyBhbmRyb2lkLndlYmtpdC5XZWJWaWV3IHtcbiAgICBjbGllbnQ6IEFuZHJvaWRXZWJWaWV3Q2xpZW50IHwgbnVsbDtcbiAgICBjaHJvbWVDbGllbnQ6IGFuZHJvaWQud2Via2l0LldlYkNocm9tZUNsaWVudCB8IG51bGw7XG4gICAgYnJpZGdlSW50ZXJmYWNlPzogZGsubm90YS53ZWJ2aWV3aW50ZXJmYWNlLldlYlZpZXdCcmlkZ2VJbnRlcmZhY2U7XG59XG5cbmxldCBXZWJWaWV3RXh0Q2xpZW50OiBuZXcgKG93bmVyOiBXZWJWaWV3RXh0KSA9PiBBbmRyb2lkV2ViVmlld0NsaWVudDtcbmxldCBXZWJDaHJvbWVWaWV3RXh0Q2xpZW50OiBuZXcgKG93bmVyOiBXZWJWaWV3RXh0KSA9PiBhbmRyb2lkLndlYmtpdC5XZWJDaHJvbWVDbGllbnQ7XG5sZXQgV2ViVmlld0JyaWRnZUludGVyZmFjZTogbmV3IChvd25lcjogV2ViVmlld0V4dCkgPT4gZGsubm90YS53ZWJ2aWV3aW50ZXJmYWNlLldlYlZpZXdCcmlkZ2VJbnRlcmZhY2U7XG5cbmZ1bmN0aW9uIGluaXRpYWxpemVXZWJWaWV3Q2xpZW50KCk6IHZvaWQge1xuICAgIGlmIChXZWJWaWV3RXh0Q2xpZW50KSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjYWNoZU1vZGVNYXAgPSBuZXcgTWFwPENhY2hlTW9kZSwgbnVtYmVyPihbXG4gICAgICAgIFtcImNhY2hlX2ZpcnN0XCIsIGFuZHJvaWQud2Via2l0LldlYlNldHRpbmdzLkxPQURfQ0FDSEVfRUxTRV9ORVRXT1JLXSxcbiAgICAgICAgW1wiY2FjaGVfb25seVwiLCBhbmRyb2lkLndlYmtpdC5XZWJTZXR0aW5ncy5MT0FEX0NBQ0hFX09OTFldLFxuICAgICAgICBbXCJkZWZhdWx0XCIsIGFuZHJvaWQud2Via2l0LldlYlNldHRpbmdzLkxPQURfREVGQVVMVF0sXG4gICAgICAgIFtcIm5vX2NhY2hlXCIsIGFuZHJvaWQud2Via2l0LldlYlNldHRpbmdzLkxPQURfTk9fQ0FDSEVdLFxuICAgICAgICBbXCJub3JtYWxcIiwgYW5kcm9pZC53ZWJraXQuV2ViU2V0dGluZ3MuTE9BRF9OT1JNQUxdLFxuICAgIF0pO1xuXG4gICAgQE5hdGl2ZUNsYXNzKClcbiAgICBjbGFzcyBXZWJWaWV3RXh0Q2xpZW50SW1wbCBleHRlbmRzIGFuZHJvaWQud2Via2l0LldlYlZpZXdDbGllbnQge1xuICAgICAgICBwcml2YXRlIG93bmVyOiBXZWFrUmVmPFdlYlZpZXdFeHQ+O1xuICAgICAgICBjb25zdHJ1Y3Rvcihvd25lcjogV2ViVmlld0V4dCkge1xuICAgICAgICAgICAgc3VwZXIoKTtcblxuICAgICAgICAgICAgdGhpcy5vd25lciA9IG5ldyBXZWFrUmVmKG93bmVyKTtcblxuICAgICAgICAgICAgcmV0dXJuIGdsb2JhbC5fX25hdGl2ZSh0aGlzKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBHaXZlIHRoZSBob3N0IGFwcGxpY2F0aW9uIGEgY2hhbmNlIHRvIHRha2UgY29udHJvbCB3aGVuIGEgVVJMIGlzIGFib3V0IHRvIGJlIGxvYWRlZCBpbiB0aGUgY3VycmVudCBXZWJWaWV3LlxuICAgICAgICAgKi9cbiAgICAgICAgcHVibGljIHNob3VsZE92ZXJyaWRlVXJsTG9hZGluZyh2aWV3OiBhbmRyb2lkLndlYmtpdC5XZWJWaWV3LCByZXF1ZXN0OiBzdHJpbmcgfCBhbmRyb2lkLndlYmtpdC5XZWJSZXNvdXJjZVJlcXVlc3QpIHtcbiAgICAgICAgICAgIGNvbnN0IG93bmVyID0gdGhpcy5vd25lci5nZXQoKTtcbiAgICAgICAgICAgIGlmICghb3duZXIpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oXCJXZWJWaWV3RXh0Q2xpZW50SW1wbC5zaG91bGRPdmVycmlkZVVybExvYWRpbmcoLi4uKSAtIG5vIG93bmVyXCIpO1xuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGxldCB1cmwgPSByZXF1ZXN0IGFzIHN0cmluZztcbiAgICAgICAgICAgIGxldCBodHRwTWV0aG9kID0gXCJHRVRcIjtcbiAgICAgICAgICAgIGxldCBpc1JlZGlyZWN0ID0gZmFsc2U7XG4gICAgICAgICAgICBsZXQgaGFzR2VzdHVyZSA9IGZhbHNlO1xuICAgICAgICAgICAgbGV0IGlzRm9yTWFpbkZyYW1lID0gZmFsc2U7XG4gICAgICAgICAgICBsZXQgcmVxdWVzdEhlYWRlcnM6IGphdmEudXRpbC5NYXA8c3RyaW5nLCBzdHJpbmc+IHwgbnVsbCA9IG51bGw7XG4gICAgICAgICAgICBpZiAodHlwZW9mIHJlcXVlc3QgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgICAgICAgICAgICBodHRwTWV0aG9kID0gcmVxdWVzdC5nZXRNZXRob2QoKTtcbiAgICAgICAgICAgICAgICBpc1JlZGlyZWN0ID0gcmVxdWVzdC5pc1JlZGlyZWN0KCk7XG4gICAgICAgICAgICAgICAgaGFzR2VzdHVyZSA9IHJlcXVlc3QuaGFzR2VzdHVyZSgpO1xuICAgICAgICAgICAgICAgIGlzRm9yTWFpbkZyYW1lID0gcmVxdWVzdC5pc0Zvck1haW5GcmFtZSgpO1xuICAgICAgICAgICAgICAgIHJlcXVlc3RIZWFkZXJzID0gcmVxdWVzdC5nZXRSZXF1ZXN0SGVhZGVycygpO1xuXG4gICAgICAgICAgICAgICAgdXJsID0gcmVxdWVzdC5nZXRVcmwoKS50b1N0cmluZygpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBvd25lci53cml0ZVRyYWNlKFxuICAgICAgICAgICAgICAgIGBXZWJWaWV3Q2xpZW50Q2xhc3Muc2hvdWxkT3ZlcnJpZGVVcmxMb2FkaW5nKFwiJHt1cmx9XCIpIC0gbWV0aG9kOiR7aHR0cE1ldGhvZH0gaXNSZWRpcmVjdDoke2lzUmVkaXJlY3R9IGhhc0dlc3R1cmU6JHtoYXNHZXN0dXJlfSBpc0Zvck1haW5GcmFtZToke2lzRm9yTWFpbkZyYW1lfSBoZWFkZXJzOiR7cmVxdWVzdEhlYWRlcnN9YCxcbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIGlmICh1cmwuc3RhcnRzV2l0aChvd25lci5pbnRlcmNlcHRTY2hlbWUpKSB7XG4gICAgICAgICAgICAgICAgb3duZXIud3JpdGVUcmFjZShgV2ViVmlld0NsaWVudENsYXNzLnNob3VsZE92ZXJyaWRlVXJsTG9hZGluZyhcIiR7dXJsfVwiKSAtIFwiJHtvd25lci5pbnRlcmNlcHRTY2hlbWV9XCIgLSBjYW5jZWxgKTtcblxuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBzaG91bGRPdmVycmlkZVVybExvYWRpbmcgPSBvd25lci5fb25TaG91bGRPdmVycmlkZVVybExvYWRpbmcodXJsLCBodHRwTWV0aG9kKTtcbiAgICAgICAgICAgIGlmIChzaG91bGRPdmVycmlkZVVybExvYWRpbmcgPT09IHRydWUpIHtcbiAgICAgICAgICAgICAgICBvd25lci53cml0ZVRyYWNlKGBXZWJWaWV3Q2xpZW50Q2xhc3Muc2hvdWxkT3ZlcnJpZGVVcmxMb2FkaW5nKFwiJHt1cmx9XCIpIC0gY2FuY2VsIGxvYWRpbmcgdXJsYCk7XG5cbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgcHVibGljIHNob3VsZEludGVyY2VwdFJlcXVlc3QodmlldzogYW5kcm9pZC53ZWJraXQuV2ViVmlldywgcmVxdWVzdDogc3RyaW5nIHwgYW5kcm9pZC53ZWJraXQuV2ViUmVzb3VyY2VSZXF1ZXN0KSB7XG4gICAgICAgICAgICBjb25zdCBvd25lciA9IHRoaXMub3duZXIuZ2V0KCk7XG4gICAgICAgICAgICBpZiAoIW93bmVyKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiV2ViVmlld0V4dENsaWVudEltcGwuc2hvdWxkSW50ZXJjZXB0UmVxdWVzdCguLi4pIC0gbm8gb3duZXJcIik7XG5cbiAgICAgICAgICAgICAgICByZXR1cm4gc3VwZXIuc2hvdWxkSW50ZXJjZXB0UmVxdWVzdCh2aWV3LCByZXF1ZXN0IGFzIGFuZHJvaWQud2Via2l0LldlYlJlc291cmNlUmVxdWVzdCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGxldCB1cmw6IHN0cmluZyB8IHZvaWQ7XG4gICAgICAgICAgICBpZiAodHlwZW9mIHJlcXVlc3QgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgICAgICAgICB1cmwgPSByZXF1ZXN0O1xuICAgICAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgcmVxdWVzdCA9PT0gXCJvYmplY3RcIikge1xuICAgICAgICAgICAgICAgIHVybCA9IHJlcXVlc3QuZ2V0VXJsKCkudG9TdHJpbmcoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHR5cGVvZiB1cmwgIT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgICAgICAgICBvd25lci53cml0ZVRyYWNlKGBXZWJWaWV3Q2xpZW50Q2xhc3Muc2hvdWxkSW50ZXJjZXB0UmVxdWVzdChcIiR7dXJsfVwiKSAtIGlzIG5vdCBhIHN0cmluZ2ApO1xuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHN1cGVyLnNob3VsZEludGVyY2VwdFJlcXVlc3QodmlldywgcmVxdWVzdCBhcyBhbmRyb2lkLndlYmtpdC5XZWJSZXNvdXJjZVJlcXVlc3QpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIXVybC5zdGFydHNXaXRoKG93bmVyLmludGVyY2VwdFNjaGVtZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gc3VwZXIuc2hvdWxkSW50ZXJjZXB0UmVxdWVzdCh2aWV3LCByZXF1ZXN0IGFzIGFuZHJvaWQud2Via2l0LldlYlJlc291cmNlUmVxdWVzdCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IGZpbGVwYXRoID0gb3duZXIuZ2V0UmVnaXN0ZXJlZExvY2FsUmVzb3VyY2UodXJsKTtcbiAgICAgICAgICAgIGlmICghZmlsZXBhdGgpIHtcbiAgICAgICAgICAgICAgICBvd25lci53cml0ZVRyYWNlKGBXZWJWaWV3Q2xpZW50Q2xhc3Muc2hvdWxkSW50ZXJjZXB0UmVxdWVzdChcIiR7dXJsfVwiKSAtI